#include "Inputs.h"
#include "TerrainProcessor.h"
#include "MapResampler.h"
#include "WeatherProcessor.h"
#include "../Buffer/BufferSpatialCalc.h"
#include <random>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <filesystem>
#include <set>

int Inputs::nCols, Inputs::nRows, Inputs::nCols_temp, Inputs::nRows_temp;
double Inputs::Length_Pixel_Side_m, Inputs::s_trans_avg, Inputs::s_depth_avg, Inputs::NODATA_code, Inputs::cellsize_m, Inputs::xllcorner_m, Inputs::yllcorner_m, Inputs::NODATA_temp, Inputs::cellsize_m_temp, Inputs::xllcorner_m_temp, Inputs::yllcorner_m_temp;
vector<double> Inputs::Waters01, Inputs::Waters0, Inputs::NHDfdr_reverse, Inputs::NHDfdr, Inputs::DEMfdr, Inputs::negDEMfdr, Inputs::ImperviousCover_frac, Inputs::PerCoverdata, Inputs::s_trans, Inputs::s_depth, Inputs::s_Ksat, Inputs::s_wtable, Inputs::GI_Area_m2, Inputs::GI_Contributing_Area_m2, Inputs::LAI_BAI_Tree_m2_p_m2, Inputs::LAI_BAI_SVeg_m2_p_m2, Inputs::LAI_Tree_m2_p_m2, Inputs::LAI_SVeg_m2_p_m2;
unordered_map<string, double> Inputs::SimulationLocationParams, Inputs::SimulationNumericalParams, Inputs::SimulationScenarios, Inputs::VariableInputMaps, Inputs::InputXml, Inputs::InputXml_StandardFolder, Inputs::InputXml_ReferenceStationFolder, Inputs::LAI_BAI_map;
unordered_map<string, string> Inputs::BufferSpatialWeightingStringParams, Inputs::SimulationStringParams;
vector<double> Inputs::LandCover_NLCD_Class;
//Inputs constructor receives string Directory_Input_CLArg which is command line path with backslash
Inputs::Inputs(string& path) : Directory_Input_CLArg(path)
{
	cout << "============================================" << endl;
	cout << "Launching i-Tree HydroPlus. " << endl;
	cout << "Reading input from: " << Directory_Input_CLArg << endl;
	cout << "============================================" << endl;

	//Call readHydroPlusConfigFile function to read HydroPlusConfig.xml 
	readHydroPlusConfigFile();
	
	//SimulationStringParams["InputDirectory"] set to value Directory_Input_CLArg
	SimulationStringParams["InputDirectory"] = Directory_Input_CLArg;

	//file name is dem.asc, format recognized by QGIS and ArcGIS as input type for given header
	string file_dem_location = Directory_Input_CLArg + "dem.asc";
	//If EnsureFileExists(file_dem_location) then obtain header information
	if (EnsureFileExists(file_dem_location)) {
		//TerrainProcessor object creates instance terrainProcessor and uses keyword = this to send input pointer
		TerrainProcessor terrainProcessor(this);
		//terrainProcessor.DEM_Read_Header(this) called to read DEM header for comparison with other input maps
		terrainProcessor.DEM_Read_Header(this);
	}

	//If Model_Selection is SpatialBuffer or SpatialBufferwGI then delete the z_ files
	if (SimulationStringParams["Model_Selection"] == "SpatialBuffer" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {

		// List of files to delete
		vector<string> filesToDelete = {
			Directory_Input_CLArg + "z_TIorganizer.asc",
			Directory_Input_CLArg + "z_SlopeGround_rad.asc",
			Directory_Input_CLArg + "z_AspectGround_N_0_rad.asc",
			Directory_Input_CLArg + "z_FDorganizer.asc",
			Directory_Input_CLArg + "z_FlowAccum.asc",
			Directory_Input_CLArg + "z_TI_ExponentialDecay.csv",
			Directory_Input_CLArg + "z_TI_PowerDecay.csv"
		};

		// Loop through the files and delete if they exist
		for (const auto& file : filesToDelete) {
			//If EnsureFileExists is true then enter
			if (EnsureFileExists(file)) {
				//remove(file.c_str()) function to remove file in filesToDelete
				remove(file.c_str());
			}
		}
	}
	//Note: erase function shortens the length of a string; begin and end return iterators pointing to position in string; 
	//OutputDirectory_String initialized as SimulationStringParams["OutputFolder_Path"] from HydroPlusConfig.xml 
	string OutputDirectory_String = SimulationStringParams["OutputFolder_Path"];
	//OutputDirectory_String is updated by EnsureTrailingBackslash function to ensure it has no parenthesis and ends with backslash
	OutputDirectory_String = EnsureTrailingBackslash(OutputDirectory_String);
	//SimulationStringParams["OutputFolder_Path"] redefined as OutputDirectory_String
	SimulationStringParams["OutputFolder_Path"] = OutputDirectory_String;

	//namespace fileSystem_instance = filesystem; filesystem will allow us to create a new directory
	namespace fileSystem_instance = filesystem;
	//if (!fileSystem_instance::exists(OutputDirectory_String)); if OutputDirectory_String does not exist then enter
	if (!fileSystem_instance::exists(OutputDirectory_String)) {
		try {
			//fileSystem_instance::create_directories(OutputDirectory_String); creates OutputFolder_Path
			fileSystem_instance::create_directories(OutputDirectory_String);
			cout << "Created output directory: " << OutputDirectory_String << endl;
		}
		catch (const fileSystem_instance::filesystem_error& e_error) {
			cerr << "Error creating output directory: " << e_error.what() << endl;
		}
	}

	//If SimulationStringParams["Model_Selection"] not ECDynamic then readWeatherFile, readEvaporationFile, and readPollutionData functions
	if (SimulationStringParams["Model_Selection"] != "ECDynamic" && SimulationStringParams["Model_Selection"] != "SpatialBuffer")	{
		if ((SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro" && SimulationStringParams["Flag_MultipleStations"] == "0" && SimulationNumericalParams["Flag_HottestDaySimulated"] == 1)) {
			cout << "Warning: The HydroPlusConfig.xml has parameter Flag_HottestDaySimulated = 1 when Flag_MultipleStations = 0." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: Hottest day option only works for multiple station scenarios in CoolAir." << endl;
			cout << "Correction: If you do not wish to auto simulate the hottest day for one weather station, then ..." << endl;
			cout << "... in the HydroPlusConfig.xml file, set the Flag_HottestDaySimulated = 0." << endl;
			cout << "Correction: If you wish to auto simulate the hottest day for one weather station, then ..." << endl;
			cout << "... in the HydroPlusConfig.xml file, set the Flag_MultipleStations = 1, and provide the inputs for one station ..." << endl;
			cout << "... in a file linked by its path in the HydroPlusConfig.xml element RefWeatherLocationAttributeFile." << endl;
			cout << "... An example is available in the folder TestingFilesAndScript\\TestCases\\SpatialCoolAir\\MultipleStations\\." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
		//If (Model = SpatialTemperatureHydro or StatisticalHydro) & Flag_MultipleStations = 1 then call readMultipleWeatherStations_List_CSV function
		if ((SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro" || SimulationStringParams["Model_Selection"] == "StatisticalHydro") && SimulationNumericalParams["Flag_MultipleStations"] == 1) {
			//Call readMultipleWeatherStations_List_CSV function
			WeatherProcessor::readMultipleWeatherStations_List_CSV(this);
		}
		//Else If Model_Selection = SpatialTemperatureHydro or StatisticalHydro without Flag_MultipleStations then
		else {
			//WeatherProcessor::readWeatherFile function sends this as Inputs pointer to process Weather.csv input data
			WeatherProcessor::readWeatherFile(this);
			//WeatherProcessor::readRadiationFile function sends this as Inputs pointer to process Radiation.csv input data
			WeatherProcessor::readRadiationFile(this);
			//HydroPlusConfig_StopDay_str_YYYYMMDD is string form of HydroPlusConfig.xml input StopDate_YYYYMMDD
			string HydroPlusConfig_StopDay_str_YYYYMMDD = to_string(SimulationNumericalParams["StopDate_YYYYMMDD"]);
			//HydroPlusConfig_StopDay_YYYYMMDD is integer form of substring from HydroPlusConfig_StopDay_str_YYYYMMDD, taking first 8 elements
			int HydroPlusConfig_StopDay_YYYYMMDD = atoi(HydroPlusConfig_StopDay_str_YYYYMMDD.substr(0, 8).c_str());
			//WeatherInput_StopDay_YYYYMMDD is integer form of Weather.csv input vector last item
			int WeatherInput_StopDay_YYYYMMDD = SimulationDate_YYYYMMDD.back();
			//If WeatherInput_StopDay_YYYYMMDD < HydroPlusConfig_StopDay_YYYYMMDD then alert user and abort
			if (WeatherInput_StopDay_YYYYMMDD < HydroPlusConfig_StopDay_YYYYMMDD) {
				cout << "Warning: The Weather.csv 1st column for date, YYYYMMDD, has a ending date of " << WeatherInput_StopDay_YYYYMMDD << " ..." << endl;
				cout << "... which is earlier than the HydroPlusConfig.xml parameter StopDate_YYYYMMDD, set to " << HydroPlusConfig_StopDay_YYYYMMDD << "." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: The HydroPlus model needs Weather.csv to contain all dates up to the StopDate_YYYYMMDD." << endl;
				cout << "Correction: Either generate new Weather.csv or change the StopDate_YYYYMMDD in the HydroPlusConfig.xml file." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//Check if Radiation and Weather files have same length
			//Length_RadiationInputs is integer length of 1st variable from Radiation.csv
			int Length_RadiationInputs = Radiation_Shortwave_Direct_Wpm2.size();
			//Length_WeatherInputs is integer length of 1st variable from Weather.csv
			int Length_WeatherInputs = Tair_C.size();

			//If Length_WeatherInputs not equal to Length_RadiationInputs then alert user and abort
			if (Length_WeatherInputs != Length_RadiationInputs) {
				cout << "Warning: The Weather.csv has " << Length_WeatherInputs << " items, but Radiation.csv has " << Length_RadiationInputs << " items." << endl;
				cout << "This program cannot verify if they are correct datasets properly overlapping in time." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: The HydroPlus model needs Weather.csv and Radiation.csv to contain identical rows of data." << endl;
				cout << "Correction: Generate new Weather.csv or Radiation.csv data, or make those datasets have the same row count and dates." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}

			//If Flag_ReadEvaporationData = 1 then proceed to call readEvaporationFile function
			if (SimulationNumericalParams["Flag_ReadEvaporationData"] == 1 && SimulationStringParams["Model_Selection"] != "SpatialTemperatureHydro") {
				//WeatherProcessor::readEvaporationFile function sends this as Inputs pointer to process Evaporation.csv input data
				WeatherProcessor::readEvaporationFile(this);
				//Check if Evaporation and Weather files have same length
				//Length_EvaporationInputs is integer length of 1st variable from Evaporation.csv
				int Length_EvaporationInputs = PotentialEvapotranspiration_TreeCover_m.size();
				//Length_WeatherInputs is integer length of 1st variable from Weather.csv
				int Length_WeatherInputs = Tair_C.size();

				//Note: Consider refactor to get this to work for CoolRiver_Unsteady TestCase; lengths could be different for simulation dates
				/*
				//If Length_WeatherInputs not equal to Length_EvaporationInputs then alert user and abort
				if (Length_WeatherInputs != Length_EvaporationInputs) {
					cout << "Warning: The Weather.csv has " << Length_WeatherInputs << " items, but Evaporation.csv has " << Length_EvaporationInputs << " items." << endl;
					cout << "This program cannot verify if they are correct datasets properly overlapping in time." << endl;
					cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
					cout << "Explanation: The HydroPlus model needs Weather.csv and Evaporation.csv to contain identical rows of data." << endl;
					cout << "Correction: Generate new Weather.csv or Evaporation.csv data, or make those datasets have the same row count and dates." << endl;
					//Call abort function, which ends the HydroPlus.exe simulation
					abort();
				}
				*/
			}
			//If HydroPlusConfig.xml element Flag_CompareToObservedRunoff not equal to 0 then read Observed Runoff
			//Note: HydroPlusConfig.xmls element Flag_CompareToObservedRunoff can equal 0, 1 (routed runoff), or 2 (unrouted runoff)
			if (SimulationNumericalParams["Flag_CompareToObservedRunoff"] != 0) {
				//readRunoff_ObservedFile function will process Runoff_Observed_for_Calibration.csv input data
				readRunoff_ObservedFile();
			}
		}
		//If Model_Selection not CoolRiver then call readPollutionData function to process input data
		//Note: If condition above included CoolRiver with SpatialTemperatureHydro and Statistical Hydro to read in Weather.csv etc.
		if (SimulationStringParams["Model_Selection"] != "CoolRiver") {
			//Note: Pollution input data are optional, as default values are stored within code
			readPollutionData(Directory_Input_CLArg);
		}
	}
	//If Model_Selection is SpatialTemperatureHydro
	if (SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro")	{
		//Call ProcessTemperatureExecutionParams function to read HydroPlusConfig.xml inputs from TemperatureExecutionParams section
		//Note: This call must occur before Inputs::LandCoverAssignment_by_NLCD_Class function
		ProcessTemperatureExecutionParams();
	
		//Call readNLCDLandCoverFile function to read landcover.asc file containing NLCD Class 
		//Note: Class values are from the Multi Resolution Land Cover dataset: 11, 21, 22, .., 41, etc. for all pixels with data
		readNLCDLandCoverFile(Directory_Input_CLArg, Inputs::NODATA_code);

		//Call readImperviousFile function to read imperviouscover.asc file containing impervious cover percent for all pixels with data
		//Note: Area_Impervious_frac computed for use in TerrainProcessor to compute TopUrban form of topographic index
		readImperviousFile(Directory_Input_CLArg, Inputs::NODATA_code);

		//Call readTreeCoverFile function
		readTreeCoverFile(Directory_Input_CLArg, Inputs::NODATA_code);
		
		//Call readAnthropogenicHeatMap to read anthropenic heat maps obtained from AH4GUC
		//Note: If Flag_CoolAir_AnthropogenicHeat_Flux equals 0, and no AH4GUC maps are available, zeros will be assigned
		readAnthropogenicHeatMap(Directory_Input_CLArg, Inputs::NODATA_code);

		//Call readBlockGroupOutputFileMap function
		readBlockGroupOutputFileMap(Directory_Input_CLArg, Inputs::NODATA_code);

		// After reading the maps, initialize beC for SpatialTemperatureHydro
		// ----------------------------------------------------------------
		// Base number of drawers = all raster pixels (map grid)
		const int Npix = nCols * nRows;

		// Check if we need to append "virtual drawers" for reference stations outside the map.
		// - Single-station case: add 1 extra drawer
		// - Multi-station case: add Station_OBJECTID_vec.size() extra drawers
		int extra_drawers = 0;
		if (SimulationStringParams["ReferenceStationOutsideMap"] == "1") {
			extra_drawers = (int)SimulationNumericalParams["Flag_MultipleStations"] == 1
				? (int)Station_OBJECTID_vec.size()
				: 1;
		}
	
		// Total drawers = map pixels + appended reference-station drawers
		const int total_drawers = Npix + extra_drawers;

		// Build the per-drawer folder counts vector
		// - Each entry = number of folders for that drawer
		// - For map pixels: set to 0 if NoData, else use folder count from config
		// - For appended stations: always assign full folder count
		vector<int> folders_per_drawer_vec;
		folders_per_drawer_vec.reserve(total_drawers);

		// Assume a constant folders-per-pixel/folder-per-station, from config definition
		//const int folders_per_drawer = (int)DataDrawers[0].size();
		const int folders_per_drawer = 1;

		// Map pixels: assign 0 for nodata pixels, else full folder count
		for (int pix = 0; pix < Npix; ++pix) {
			const bool is_nodata = (int(LandCover_NLCD_Class[pix]) == Inputs::NODATA_code);
			folders_per_drawer_vec.push_back(is_nodata ? 0 : folders_per_drawer);
		}

		// Appended reference-station drawers: always full folder count
		for (int k = 0; k < extra_drawers; ++k) {
			folders_per_drawer_vec.push_back(folders_per_drawer);
		}
		
		// Initialize the backend container (beC) with ragged layout
		// - One slot per (drawer,folder)
		// - Starts with 0 variables (columns), values initialized to 0.0
		beC.init_ragged(/*per_drawer_folders=*/folders_per_drawer_vec,/*nvars=*/0,/*init=*/0.0);

		//1) declare the variables(columns) but leave all slots at the init value(e.g., 0.0).
		for (int DataDrawer_ID = 0; DataDrawer_ID < (int)DataDrawers.size(); ++DataDrawer_ID) {
			for (int DataFolder_ID = 0; DataFolder_ID < (int)DataDrawers[DataDrawer_ID].size(); ++DataFolder_ID) {
				const auto& src = DataDrawers[DataDrawer_ID][DataFolder_ID]; // map<string,double>
				const auto& src_str = StringDataDrawers[DataDrawer_ID][DataFolder_ID]; // map<string,string>
				for (const auto& kv : src) {
					const string& ParamName = kv.first;
					beC.add_var(ParamName, /*init=*/0.0);   // create column if missing
					//No assignment here for SpatialHydro:
					//beC.by_key(DataDrawer_ID, DataFolder_ID, ParamName) = kv.second;
				}
				for (const auto& kv : src_str) {
					beC.ensure_var_str(kv.first, ""); // create empty string column
				}
			}
		}
		
		//Drawer_ID, Folder_ID intialized
		int Default_Drawer_ID = 0, Default_Folder_ID = 0;
		//ValidateAndExtractDrawerFolderID called to test validity of HydroPlusConfig.xml Default_DrawerID_FolderID DataDrawer and DataFolder location
		//Note: InputXml_ReferenceStationFolder and InputXmlString_ReferenceStationFolder are replaced by InputXml and InputXmlString
		//Note: This set of IDs defines the standard for all model folders, including the reference station, unless Flag_InputXml_ReferenceStationFolder is true
		Inputs::ValidateAndExtractDrawerFolderID("Default_DrawerID_FolderID", SimulationStringParams["Default_DrawerID_FolderID"], DataDrawers, StringDataDrawers, Default_Drawer_ID, Default_Folder_ID);
		// 2) Broadcast: copy values from the template drawer into every (drawer, folder) slot that exists.
		const auto& src = DataDrawers[Default_Drawer_ID][Default_Folder_ID]; // map<string,double>
		const auto& src_str = StringDataDrawers[Default_Drawer_ID][Default_Folder_ID]; // map<string,string>
		for (int DataDrawer_ID = 0; DataDrawer_ID < Npix; ++DataDrawer_ID) {
			const int nFoldersHere = beC.folders_per_drawer[DataDrawer_ID]; // 0 for NODATA drawers
			for (int DataFolder_ID = 0; DataFolder_ID < nFoldersHere; ++DataFolder_ID) {
				for (const auto& kv : src) {
					beC.by_key(DataDrawer_ID, DataFolder_ID, kv.first) = kv.second;
				}
				for (const auto& kv : src_str)
					beC.by_key_str(DataDrawer_ID, DataFolder_ID, kv.first) = kv.second;
			}
		}

		// 3) If a reference-station folder is specified and lives OUTSIDE the map, copy its parameters into the appended drawers at the end.
		if (SimulationStringParams["ReferenceStationOutsideMap"] == "1" ) {
			//Drawer_ID_RefStation, Folder_ID_RefStation intialized
			int Drawer_ID_RefStation = Default_Drawer_ID, Folder_ID_RefStation = Default_Folder_ID;
			//if user porvided ID of DataDrawer & DataFolder w/ distinct Reference Station parameters
			if (SimulationStringParams.count("RefWeather_DrawerID_FolderID") > 0) {
				//ValidateAndExtractDrawerFolderID called to test validity of HydroPlusConfig.xml RefWeather_DrawerID_FolderID DataDrawer and DataFolder location
				ValidateAndExtractDrawerFolderID("RefWeather_DrawerID_FolderID", SimulationStringParams["RefWeather_DrawerID_FolderID"], DataDrawers, StringDataDrawers, Drawer_ID_RefStation, Folder_ID_RefStation);
			}
			// Target = the appended "virtual pixel" at the end.
			// With your earlier logic: total_drawers = Npix + extra_drawers;
			const auto& refSrc = DataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation];          // numeric
			const auto& refSrc_str = StringDataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation];    // string
			// Safety: ensure that target drawer actually has that folder index
			for (int k = 0; k < extra_drawers; ++k) {
				const int dstDrawerID = Npix + k;
				// We copy into the SAME folder index as in the config (Folder_ID_RefStation),
				// provided it exists in the destination drawer.
				// Ensure all columns exist (safe re-adds)
				for (const auto& kv : refSrc) beC.add_var(kv.first, 0.0);

				// Assign
				for (const auto& kv : refSrc) {
					beC.by_key(dstDrawerID, 0, kv.first) = kv.second;
				}
				// else: silently skip if destination drawer has fewer folders.
				 // strings: auto-register + assign
				for (const auto& kv : refSrc_str) {
					beC.by_key_str(dstDrawerID, 0, kv.first) = kv.second;
				}	
			}
		}
			
		/*
		// Collect all param names from config
		set<string> configKeys;
		for (const auto& drawer : DataDrawers)
			for (const auto& folderMap : drawer)
				for (const auto& kv : folderMap)
					configKeys.insert(kv.first);

		cout << "=== Config Parameter Values (drawer, folder, param, value) ===" << endl;

		int drawerCount = (int)beC.folders_per_drawer.size();
		for (int d = 0; d < drawerCount; ++d) {
			int fcount = beC.folders_per_drawer[d];
			for (int f = 0; f < fcount; ++f) {
				for (const auto& key : configKeys) {
					try {
						double val = beC.by_key(d, f, key);
						cout << d << "," << f << "," << key << "," << setprecision(10) << val << endl;
					}
					catch (...) {
						// silently skip if not found in beC
					}
				}
			}
		}

		cout << "=== End of config param dump ===" << endl;
	*/

		//If map_Irrigation_Area_frac is 1 and Flag_Scenario_CoolAir_Irrigate is 1 then read map
		if (VariableInputMaps.count("map_Irrigation_Area_frac") > 0 && VariableInputMaps["map_Irrigation_Area_frac"] > 0 && SimulationScenarios["Flag_Scenario_CoolAir_Irrigate"] > 0) {
			//readHydroPlusConfigVariablesAsMap(Directory_Input_CLArg, Inputs::NODATA_code, "map_Irrigation_Area_frac.asc", map_Irrigation_Area_frac) called
			readHydroPlusConfigVariablesAsMap(Directory_Input_CLArg, Inputs::NODATA_code, "map_Irrigation_Area_frac.asc", map_Irrigation_Area_frac);
		}

		//If map_Permeable_Area_of_IC_frac is 1 then read map
		if (VariableInputMaps.count("map_Permeable_Area_of_IC_frac") > 0 && VariableInputMaps["map_Permeable_Area_of_IC_frac"] > 0) {
			//readHydroPlusConfigVariablesAsMap(Directory_Input_CLArg, Inputs::NODATA_code, "map_Permeable_Area_of_IC_frac.asc", map_Permeable_Area_of_IC_frac) called
			readHydroPlusConfigVariablesAsMap(Directory_Input_CLArg, Inputs::NODATA_code, "map_Permeable_Area_of_IC_frac.asc", map_Permeable_Area_of_IC_frac);
		}

		//Call ProcessTopographyData function
		//Note: Called after reading land cover to have Inputs::PerCoverdata for TerrainProcessor::DEM_to_FlowAccumulation_Manager
		ProcessTopographyData();

		//Call Check_Map_Size_and_NoData to ensure maps are correct size relative to dem.asc
		//vector<tuple<string, string, void*>> mapList_SpatialTemperatureHydro contains 1D map file name and vector as pointer
		vector<pair<string, void*>> mapList_SpatialTemperatureHydro = {
			{"landcover.asc", static_cast<void*>(&LandCover_NLCD_Class)},
			{"treecover.asc", static_cast<void*>(&TreeCover_frac)},
			{"imperviouscover.asc", static_cast<void*>(&ImperviousCover_frac)},
			{"blockgroup.asc", static_cast<void*>(&BlockGroup_ID)},
			{"AH_flux_Qtot_avg_Wpm2.asc", static_cast<void*>(&AnthropogenicHeat_Flux_Qtot_Avg_Wpm2)},
			{"AH_flux_Qcr_avg_Wpm2.asc", static_cast<void*>(&AnthropogenicHeat_Flux_Qcr_Avg_Wpm2)},
			{"AH_flux_Qncr_avg_Wpm2.asc", static_cast<void*>(&AnthropogenicHeat_Flux_Qncr_Avg_Wpm2)},
			{"map_irrigation_area_frac.asc", static_cast<void*>(&map_Irrigation_Area_frac)},
			{"map_Permeable_Area_of_IC_frac.asc", static_cast<void*>(&map_Permeable_Area_of_IC_frac)}
		};

		//For loop and dispatch to overloaded functions, with map_1D_name, fileName, and pointer_Tuple called from tuple
		for (const auto& [map_1D_name, pointer_Tuple] : mapList_SpatialTemperatureHydro) {
			//If map_1D_name == "blockgroup.asc" then map_1D_vector is integer
			if (map_1D_name == "blockgroup.asc") {
				//auto* map_1D_vector = static_cast<vector<int>*>(pointer_Tuple); assigns pointer_Tuple to map_1D_vector
				auto* map_1D_vector = static_cast<vector<int>*>(pointer_Tuple);
				//If !map_1D_vector->empty then call Check_Map_Size_and_NoData
				if (!map_1D_vector->empty()) {
					//Call overloaded Check_Map_Size_and_NoData(Elevation_DEM_m, *map_1D_vector, "dem.asc", map_1D_name)
					//Note: Check_Map_Size_and_NoData always compares against dem.asc
					Check_Map_Size_and_NoData(Elevation_DEM_m, *map_1D_vector, "dem.asc", map_1D_name);
				}
			}
			//Else map_1D_vector is double
			else {
				//auto* map_1D_vector = static_cast<vector<int>*>(pointer_Tuple); assigns pointer_Tuple to map_1D_vector
				auto* map_1D_vector = static_cast<vector<double>*>(pointer_Tuple);
				//If !map_1D_vector->empty then call Check_Map_Size_and_NoData
				if (!map_1D_vector->empty()) {
					//Note: Check_Map_Size_and_NoData always compares against dem.asc
					Check_Map_Size_and_NoData(Elevation_DEM_m, *map_1D_vector, "dem.asc", map_1D_name);
				}
			}
		}

		//If ReferenceStationOutsideMap is 1 but Flag_MultipleStations is 0 then append a single reference station outside the map
		//Note: Assignments below presume reasonable values provided by HydroPlusConfig.xml elements
		if (SimulationStringParams["ReferenceStationOutsideMap"] == "1" && SimulationNumericalParams["Flag_MultipleStations"] != 1) {
			//NumberOfLocations redefined nCols * nRows + 1; a single reference station is outside the map
			SimulationNumericalParams["NumberOfLocations"] = nCols * nRows + 1;

			//BlockGroup_ID updated with HydroPlusConfig.xml element RefWeatherLocationBlockgroup
			BlockGroup_ID.push_back(TemperatureCalculationParams["RefWeatherLocationBlockgroup"]);
			//TI_Value updated with TI_Value_Average (Avg) given Map value is not acceptable when ReferenceStationOutsideMap is true
			TI_Value.push_back(TI_Value_Average);
			//Elevation_DEM_m.push_back(TemperatureCalculationParams["RefWeatherLocationElevation_m"]) updated for reference station
			Elevation_DEM_m.push_back(TemperatureCalculationParams["RefWeatherLocationElevation_m"]);
			//AspectGround_N_0_rad updated with HydroPlusConfig.xml element RefWeatherLocationAspect_deg * Ratio_Radian_to_Degree
			AspectGround_N_0_rad.push_back(TemperatureCalculationParams["RefWeatherLocationAspect_deg"] * Ratio_Radian_to_Degree);
			//SlopeGround_rad updated with HydroPlusConfig.xml element RefWeatherLocationSlope_deg * Ratio_Radian_to_Degree
			SlopeGround_rad.push_back(TemperatureCalculationParams["RefWeatherLocationSlope_deg"] * Ratio_Radian_to_Degree);
			//LandCover_NLCD_Class updated with HydroPlusConfig.xml element RefWeatherLocationNLCD_Class
			LandCover_NLCD_Class.push_back(TemperatureCalculationParams["RefWeatherLocationNLCD_Class"]);
			//TreeCover_frac updated with HydroPlusConfig.xml element RefWeatherLocationTreeCover_percent * Ratio_Decimal_to_Percent
			TreeCover_frac.push_back(TemperatureCalculationParams["RefWeatherLocationTreeCover_percent"] * Ratio_Decimal_to_Percent);
			//ImperviousCover_frac updated with HydroPlusConfig.xml element RefWeatherLocationImperviousCover_percent * Ratio_Decimal_to_Percent
			ImperviousCover_frac.push_back(TemperatureCalculationParams["RefWeatherLocationImperviousCover_percent"] * Ratio_Decimal_to_Percent);
			//If Flag_CoolAir_AnthropogenicHeat_Flux is 1 then add the HydroPlusConfig.xml element RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2
			if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1) {
				//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 updated with HydroPlusConfig.xml element RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2
				AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(TemperatureCalculationParams["RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"]);
			}
			//Else Flag_CoolAir_AnthropogenicHeat_Flux is not 1 then
			else {
				//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(0.0); append 0 when not simulating AH Flux
				AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(0.0);
			}
			//If Flag_CoolAir_AnthropogenicHeat_Flux and Flag_AH_Flux_Qcr_Qncr_not_Qtot are 1 then update Qcr and Qncr vectors
			if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1 && SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"] == 1) {
				AnthropogenicHeat_Flux_Qcr_Avg_Wpm2.push_back(0.0);
				AnthropogenicHeat_Flux_Qncr_Avg_Wpm2.push_back(0.0);
			}

			//Increase size of all land cover vectors to include reference station outside of map
			//Note: Inputs::readNLCDLandCoverFile sized vectors to nRows * nCols, 
			TreeCover_base_frac.push_back(0.0);
			TotalMapCover_frac.push_back(0.0);
			SoilCoverNoTC_frac.push_back(0.0);
			WaterCoverNoTC_frac.push_back(0.0);
			TreeCoverOnPerviousCover_frac.push_back(0.0);
			TreeCoverOnImperviousCover_frac.push_back(0.0);
			ImperviousCoverNoTreeCover_frac.push_back(0.0);
			ShortVegCoverNoTC_frac.push_back(0.0);
			ConnectedImpervious.push_back(0.0);
			TreeEvergreen_frac.push_back(0.0);
			ShortVegEvergreen_frac.push_back(0.0);
			PermeablePavementCover_frac.push_back(0.0);
		}
		//Else If ReferenceStationOutsideMap and Flag_MultipleStations == 1 then append reference stations outside of map
		else if (SimulationStringParams["ReferenceStationOutsideMap"] == "1" && SimulationNumericalParams["Flag_MultipleStations"] == 1) {
			//NumberOfLocations defined nCols * nRows + Station_OBJECTID_vec.size() 
			SimulationNumericalParams["NumberOfLocations"] = nCols * nRows + Station_OBJECTID_vec.size();

			//For (size_t i = 0; i < Station_OBJECTID_vec.size(); ++i); loop through Station_OBJECTID_vec
			//Note: To find specific stations within vector use the congruent values of Station_OBJECTID_vec
			for (size_t i = 0; i < Station_OBJECTID_vec.size(); ++i) {

				//BlockGroup_ID updated with Station_BlockGroup_ID_vec
				BlockGroup_ID.push_back(Station_BlockGroup_ID_vec[i]);
				//TI_Value updated with TI_Value_Average (Avg) given Map value is not acceptable when ReferenceStationOutsideMap is true
				TI_Value.push_back(TI_Value_Average);
				//Elevation_DEM_m.push_back(Station_Elev_m_vec) updated for reference station
				Elevation_DEM_m.push_back(Station_Elev_m_vec[i]);
				//AspectGround_N_0_rad updated with Station_Aspect_deg_vec * Ratio_Radian_to_Degree
				AspectGround_N_0_rad.push_back(Station_Aspect_deg_vec[i] * Ratio_Radian_to_Degree);
				//SlopeGround_rad updated with Station_Slope_deg_vec * Ratio_Radian_to_Degree
				SlopeGround_rad.push_back(Station_Slope_deg_vec[i] * Ratio_Radian_to_Degree);
				//LandCover_NLCD_Class updated with Station_NLCD_Class_vec
				LandCover_NLCD_Class.push_back(Station_NLCD_Class_vec[i]);
				//TreeCover_frac updated with Station_TC_per_vec * Ratio_Decimal_to_Percent
				TreeCover_frac.push_back(Station_TC_per_vec[i] * Ratio_Decimal_to_Percent);
				//ImperviousCover_frac updated with Station_IC_per_vec * Ratio_Decimal_to_Percent
				ImperviousCover_frac.push_back(Station_IC_per_vec[i] * Ratio_Decimal_to_Percent);
				//If Flag_CoolAir_AnthropogenicHeat_Flux is 1 then add the HydroPlusConfig.xml element RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2
				if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1) {
					//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 updated with Station_AH_Flux_Wpm2_vec
					AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(Station_AH_Flux_Wpm2_vec[i]);
				}
				//Else Flag_CoolAir_AnthropogenicHeat_Flux is not 1 then
				else {
					//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(0.0); append 0 when not simulating AH Flux
					AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.push_back(0.0);
				}
				//If Flag_CoolAir_AnthropogenicHeat_Flux and Flag_AH_Flux_Qcr_Qncr_not_Qtot are 1 then update Qcr and Qncr vectors
				if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1 && SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"] == 1) {
					AnthropogenicHeat_Flux_Qcr_Avg_Wpm2.push_back(0.0);
					AnthropogenicHeat_Flux_Qncr_Avg_Wpm2.push_back(0.0);
				}

				//Increase size of all land cover vectors to include reference station outside of map
				//Note: Inputs::readNLCDLandCoverFile sized vectors to nRows * nCols, 
				TreeCover_base_frac.push_back(0.0);
				TotalMapCover_frac.push_back(0.0);
				SoilCoverNoTC_frac.push_back(0.0);
				WaterCoverNoTC_frac.push_back(0.0);
				TreeCoverOnPerviousCover_frac.push_back(0.0);
				TreeCoverOnImperviousCover_frac.push_back(0.0);
				ImperviousCoverNoTreeCover_frac.push_back(0.0);
				ShortVegCoverNoTC_frac.push_back(0.0);
				ConnectedImpervious.push_back(0.0);
				TreeEvergreen_frac.push_back(0.0);
				ShortVegEvergreen_frac.push_back(0.0);
				PermeablePavementCover_frac.push_back(0.0);

				int MapPixel_ID = nRows * nCols + static_cast<int>(i);
				//MultipleStationObjectID_to_MapPixelID_map connects Station_OBJECTID_vec with MapPixel_ID
				//Note: Map created for rapid access of MapPixel_ID for each Station_OBJECTID_vec, useful in debugging
				MultipleStationObjectID_to_MapPixelID_map[Station_OBJECTID_vec[i]] = MapPixel_ID;
				//MultipleStationObjectID_to_OrganizerDataDrawersIndex_map contains the Station_OBJECTID and its index in the vector
				//Note: Map created for rapid access of MapPixel_ID index for each Station_OBJECTID_vec, useful in debugging
				MultipleStationObjectID_to_OrganizerDataDrawersIndex_map[Station_OBJECTID_vec[i]] = i;
			}
		}
		//Else ReferenceStationOutsideMap != "1" then enter to update map arrays inside the map
		else {
			//NumberOfLocations defined as nCols * nRows from HydroPlusConfig.xml SimulationLocationParams
			SimulationNumericalParams["NumberOfLocations"] = nCols * nRows;
			//Ref_MapPixel_ID = MapPixel_ReferenceStation_IDs[0] is reference station index
			int Ref_MapPixel_ID = MapPixel_ReferenceStation_IDs[0];
			//If (TemperatureCalculationParams["RefWeatherLocationTopographicIndex"] != -1) then use TI_Value_Average value
			//Note: If RefWeatherLocationTopographicIndex is -1 or "Map" then TI_Value defined by dem.asc, ...
			//If (RefWeatherLocationBlockgroup != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationBlockgroup"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationBlockgroup") != 0) {
				BlockGroup_ID[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationBlockgroup"];
			}
			if (TemperatureCalculationParams["RefWeatherLocationTopographicIndex"] = -1) {
				TI_Value[Ref_MapPixel_ID] = TI_Value_Average;
			}
			//If (RefWeatherLocationElevation_m != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationElevation_m"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationElevation_m") != 0) {
				Elevation_DEM_m[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationElevation_m"];
			}
			//If (RefWeatherLocationAspect_deg != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationAspect_deg"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationAspect_deg") != 0) {
				AspectGround_N_0_rad[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationAspect_deg"] * Ratio_Radian_to_Degree;
			}
			//If (RefWeatherLocationSlope_deg != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationSlope_deg"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationSlope_deg") != 0) {
				SlopeGround_rad[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationSlope_deg"] * Ratio_Radian_to_Degree;
			}
			//If (RefWeatherLocationNLCD_Class != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationNLCD_Class"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationNLCD_Class") != 0) {
				LandCover_NLCD_Class[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationNLCD_Class"];
			}
			//If (RefWeatherLocationTreeCover_percent != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationTreeCover_percent"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationTreeCover_percent") != 0) {
				TreeCover_frac[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationTreeCover_percent"] * Ratio_Decimal_to_Percent;
			}
			//If (RefWeatherLocationImperviousCover_percent != -1 & TemperatureExecutionParams.count != 0) then use HydroPlusConfig.xml element value
			if (TemperatureCalculationParams["RefWeatherLocationImperviousCover_percent"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationImperviousCover_percent") != 0) {
				ImperviousCover_frac[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationImperviousCover_percent"] * Ratio_Decimal_to_Percent;
			}
			//If Flag_CoolAir_AnthropogenicHeat_Flux is 1 and Flag_CoolAir_AnthropogenicHeat_Flux != -1  & TemperatureExecutionParams.count != 0 then use HydroPlusConfig.xml element value
			if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1 && TemperatureCalculationParams["RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"] != -1 && TemperatureExecutionParams.count("RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2") != 0) {
				AnthropogenicHeat_Flux_Qtot_Avg_Wpm2[Ref_MapPixel_ID] = TemperatureCalculationParams["RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"];

			}
		}

		//Build the BG index after reading blockgroup.asc and  know nCols/nRows, and handling the ref station (e.g. add -1 to input->BlockGroup_ID) Initializing beBG happens here too
		buildBlockGroupIndex(Inputs::NODATA_code);

		//If Flag_CoolAir_AnthropogenicHeat_Flux and Flag_AH_Flux_Maps_Resample are 1 then resample AHE map products
		if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1 && SimulationNumericalParams["Flag_AH_Flux_Maps_Resample"] > 0) {
			//Map_OriginalScaleGrid_m defined as 900 m, which is an average value across mid-latitudes with 0.0833 deg
			int Map_OriginalScaleGrid_m = 900;
			//call MapResampler::ResampleAnthropogenicHeatFlux_Map function after calling functions readImperviousFile and readAnthropogenicHeatMap
			//Note: Send AnthropogenicHeat_Flux_Qtot_Avg_Wpm2, AnthropogenicHeat_Flux_Qcr_Avg_Wpm2, and AnthropogenicHeat_Flux_Qncr_Avg_Wpm2 even if one is used
			//Note: Flag_AH_Flux_Qcr_Qncr_not_Qtot with Qcr is commercial residential AH flux, and Qncr is non commercial residential AH flux
			MapResampler::ResampleAnthropogenicHeatFlux_Map(this, AnthropogenicHeat_Flux_Qtot_Avg_Wpm2, AnthropogenicHeat_Flux_Qcr_Avg_Wpm2, AnthropogenicHeat_Flux_Qncr_Avg_Wpm2, ImperviousCover_frac, Inputs::nCols, Inputs::nRows, Inputs::xllcorner_m, Inputs::yllcorner_m, Inputs::Length_Pixel_Side_m, Inputs::NODATA_code, SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"], SimulationNumericalParams["Flag_AH_Flux_Maps_Resample"], Map_OriginalScaleGrid_m, AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2, AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2, AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2, ImperviousCover_Sum_frac, SimulationStringParams["OutputFolder_Path"], TemperatureCalculationParams["RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"]);
		}

		//Call LandCoverAssignment_by_NLCD_Class function; this will assign land cover fractions within pixel based on NLCD Class
		LandCoverAssignment_by_NLCD_Class(int(SimulationNumericalParams["NumberOfLocations"]), Inputs::NODATA_code);

		//If Flag_ExtendedOutputs = 1 And land cover change is designated by 1 of 4 elements then write maps of changed land cover
		//Note: Land cover changes when any of these elements are > 0: TreeCanopyCover_overImpervious_frac, Scenario_CoolAir_Base_TCI_to_TC_frac, Scenario_CoolAir_Alternative_TC_Min_frac; or if this element is < 1: Scenario_CoolAir_Alternative_IC_Max_frac
		if (SimulationNumericalParams["Flag_ExtendedOutputs"] == 1 && (InputXml["TreeCanopyCover_overImpervious_frac"] > 0 || Scenario_CoolAir_Base_TCI_to_TC_frac > 0 || Scenario_CoolAir_Alternative_TC_Min_frac > 0 || Scenario_CoolAir_Alternative_IC_Max_frac < 1)) {
			//Call function Inputs::writeAsciiMap("TreeCoverOnPerviousCover_frac", TreeCoverOnPerviousCover_frac), ...
			//Note: ... sending variable name and data to write z_ map
			Inputs::writeAsciiMap("ImperviousCoverNoTreeCover_frac", ImperviousCoverNoTreeCover_frac);
			Inputs::writeAsciiMap("PermeablePavementCover_frac", PermeablePavementCover_frac);
			Inputs::writeAsciiMap("TreeCoverOnPerviousCover_frac", TreeCoverOnPerviousCover_frac);
			Inputs::writeAsciiMap("TreeCoverOnImperviousCover_frac", TreeCoverOnImperviousCover_frac);
			Inputs::writeAsciiMap("ShortVegCoverNoTC_frac", ShortVegCoverNoTC_frac);
			Inputs::writeAsciiMap("SoilCoverNoTC_frac", SoilCoverNoTC_frac);
			Inputs::writeAsciiMap("WaterCoverNoTC_frac", WaterCoverNoTC_frac);
			Inputs::writeAsciiMap("ImperviousCover_frac", ImperviousCover_frac);
			//Inputs::writeAsciiMap("TotalMapCover_frac", TotalMapCover_frac);
		}
		//If not Flag_LatitudeLongitude_Computed then compute Latitude_Longitude_vectorPair_dd values for all map pixels
		//Note: For Flag_MultipleStations = 1 the reference station Radiation.csv and WeatherStationAttributeTable.csv conveys geographic & solar data
		if (!Flag_LatitudeLongitude_Computed) {
			//MapProjection_WKID defined as SimulationNumericalParams["MapProjection_WKID"] from HydroPlusConfig.xml
			int MapProjection_WKID = SimulationNumericalParams["MapProjection_WKID"];
			//If AlbersParamMap.find(MapProjection_WKID) != AlbersParamMap.end(), MapProjection_WKID == 3034,  >= 32601 && <= 32660, >= 32701 && <= 32760
			if (AlbersParamMap.find(MapProjection_WKID) != AlbersParamMap.end() || LambertParamMap.find(MapProjection_WKID) != LambertParamMap.end() || 
				((MapProjection_WKID >= 32601 && MapProjection_WKID <= 32660) || (MapProjection_WKID >= 32701 && MapProjection_WKID <= 32760))) {
				//Inputs::LatitudeLongitude_Projection_Manager called
				Inputs::LatitudeLongitude_Projection_Manager(Latitude_Longitude_vectorPair_dd);
				//Flag_LatitudeLongitude_Computed set to true so this is not entered again
				Flag_LatitudeLongitude_Computed = true;
			}
		}
	}
	//If Model_Selection equals StatisticalHydro then define Area_Impervious_frac
	if (SimulationStringParams["Model_Selection"] == "StatisticalHydro") {
		//Area_Impervious_frac (fraction) is sum of ImperviousCover_noTreeCanopy_frac, TreeCanopyCover_overImpervious_frac, WaterCover_noTreeCanopy_frac
		SimulationLocationParams["Area_Impervious_frac"] = InputXml["ImperviousCover_noTreeCanopy_frac"] + InputXml["TreeCanopyCover_overImpervious_frac"] + InputXml["WaterCover_noTreeCanopy_frac"];
	}

	//If Model_Selection equals CoolRiver then call readHecRasFile, readRiverSWTimeSeriesFile, readRiverGWTimeSeriesFile, readRiverTimeSeriesFile and readRiverStationSeriesFile functions
	if (SimulationStringParams["Model_Selection"] == "CoolRiver") {
		//Call readHecRasFile; 
		readHecRasFile();
		readRiverSWTimeSeriesFile(SimulationNumericalParams["StartDate_YYYYMMDD"], SimulationNumericalParams["StopDate_YYYYMMDD"]);
		readRiverGWTimeSeriesFile(SimulationNumericalParams["StartDate_YYYYMMDD"], SimulationNumericalParams["StopDate_YYYYMMDD"]);
		readRiverTimeSeriesFile(SimulationNumericalParams["StartDate_YYYYMMDD"], SimulationNumericalParams["StopDate_YYYYMMDD"]);
		readRiverStationSeriesFile(Directory_Input_CLArg, Inputs::NODATA_code);
	}
	
	//If Model_Selection equals SpatialBuffer then call readNLCDLandCoverFile function
	if (SimulationStringParams["Model_Selection"] == "SpatialBuffer"|| SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
		if (SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
			//reads HydroPlusConfig.xml inputs for Spatial Buffer with GI
			ProcessSpatialBufferGIExecutionParams();
		}
		//Call readNLCDLandCoverFile function
		readNLCDLandCoverFile(Directory_Input_CLArg, Inputs::NODATA_code);

		//If Flag_FlowDirection_Source equals NHD then call readWaterFeaturesMap and readNHDFlowDirectionMap functions
		//Note: Uses data from NHDplus (National Hydrography Data, managed by USEPA)
		if (BufferSpatialWeightingStringParams["Flag_FlowDirection_Source"] == "NHD") {
			//force flow routing method to be D8
			if (SimulationStringParams["Algorithm_FlowDirection"] != "D8") {
				SimulationStringParams["Algorithm_FlowDirection"] = "D8";
				//write the log message and warning message to user
				cout << "Warning: You've specified Flag_FlowDirection_Source as NHD, but Algorithm_FlowDirection is not set to D8." << endl;
				cout << "Algorithm_FlowDirection is now being forced to D8." << endl;
				cout << "Explanation: NHDPlus provides flow direction in D8 format, making Flag_FlowDirection_Source = D8 the only valid option for the subsequent computation." << endl;
			}
			//readWaterFeaturesMap function reads a pixel map of hydrography, such as rivers and lakes
			readWaterFeaturesMap();
			//readNHDFlowDirectionMap function reads NHD (National Hydrography Database) map of flow directions, corrected by NHD to align with planimetric maps of hydrography
			readNHDFlowDirectionMap();
		}

		// Determine if subsurface calculations are enabled based on surface contribution flags
		bool isSubsurfaceCalculationEnabled = (
			BufferSpatialWeightingNumericalParams["ECTP_surface_contribution_frac"] < 1 ||
			BufferSpatialWeightingNumericalParams["ECTN_surface_contribution_frac"] < 1 ||
			BufferSpatialWeightingNumericalParams["ECTSS_surface_contribution_frac"] < 1
			);

		// Check if specific subsurface conditions require SSURGO data
		if (isSubsurfaceCalculationEnabled) {
			//readHydraulicConductivityMap_umps function obtains SSURGO hydraulic conductivity (um/s)
			readHydraulicConductivityMap_umps();
			//readSoilDepthMap_cm function obtains SSURGO soil depth (cm)
			readSoilDepthMap_cm();
			//calculateSoilTransmissivityChangeUnits function converts transmissivity from SSURGO units to HydroPlus model units
			calculateSoilTransmissivityChangeUnits();

			// Check if subsurface slope method uses the water table slope, which requires SSURGO data
			if (BufferSpatialWeightingStringParams["Flag_ElevationMethod_Subsurface"] == "WEM") {
				//if the p and s_WTable_avg can't be read, then a s_wtable map is needed
				if (!(BufferSpatialWeightingNumericalParams["f_param"] > 0 && BufferSpatialWeightingNumericalParams["S_WTable_avg"] > 0)){
					//read watertable map;
					if (!readWatertableMap_cm()) {
						// read f, k_avg or read watertable map
						cout << "Warning: Flag_ElevationMethod_Subsurface is set to WEM, but required parameters or watertable map are missing." << endl;
						cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
						cout << "Explanation: Using of watertable elevation requires either a water table map file (s_wtable.asc) or values for 'f_param' and 'S_WTable_avg' (average soil water table depth and transmissivity)." << endl;
						cout << "Correction: Ensure that 's_wtable.asc' is available in the input directory, or provide both 'f_param' and 'S_WTable_avg' values in the configuration file." << endl;
						abort();
					}
				}	
			}
		}
	}

	//If Model_Selection other than ECDynamic and CoolRiver and SpatialTemperatureHydro then call ProcessTopographyData function
	//Note: ProcessTopographyData called above for SpatialTemperatureHydro, to ensure DEM.asc array was available
	if (SimulationStringParams["Model_Selection"] != "ECDynamic" && SimulationStringParams["Model_Selection"] != "CoolRiver" && SimulationStringParams["Model_Selection"] != "SpatialTemperatureHydro") {
		//Call ProcessTopographyData function
		ProcessTopographyData();
	}

	//If SimulationStringParams["Model_Selection"] equals ECDynamic then enter 
	if (SimulationStringParams["Model_Selection"] == "ECDynamic" || SimulationStringParams["Model_Selection"] == "SpatialBuffer" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
		//If inputs do not include NLCD map or discharge input, then just generate export coefficient database
		if (SimulationStringParams["Model_Selection"] == "ECDynamic" && (readNLCDLandCoverFile(Directory_Input_CLArg, Inputs::NODATA_code) == false || readDischargeData(Directory_Input_CLArg) == false)) {
			cout << "NLCD or discharge data is not provided. Calculating export coefficient database without running simulation ..." << endl;
			//run_ECDynamicModel set to zero
			run_ECDynamicModel = 0;
		}
		//Else if NLCD map and discharge data are included then were already been read in the previous if condition then
		else {
			cout << "Calculating export coefficient database." << endl;
			
		}
		//Call readExportCoefficientTable function to read the White et al. (2015) database
		readExportCoefficientTable();
	}
}

void Inputs::readHydroPlusConfigFile() {

	//Note: XMLReader contains six interacting .cpp files intended to extract data from inputs, such as HydroPlusConfig.xml, and build new document tree of element nodes: 
	//Note: Tokenizer.cpp remove whitespace and constructs new document using XMLElementParts.cpp
	//Note: Node.cpp creates nodes which Tree.cpp uses to create document tree with XML_Document.cpp

	//Create Tree_pointer as Tree class for organizing HydroPlusConfig.xml input
	Tree Tree_pointer;
	//Create doc as XML_Document class for containing HydroPlusConfig.xml file
	XML_Document doc;
	//Create Node_pointer_XML_file as pointer to Node within XML document
	Node* Node_pointer_XML_file;
	//Define Node_pointer_XML_file as XML_Document HydroPlusConfig.xml file, loaded from directory given by command prompt
	Node_pointer_XML_file = doc.Load(Directory_Input_CLArg + "HydroPlusConfig.xml", true);
	//Define Node_pointer_XML_element as pointer to HydroPlusConfig node, known as child within XML_Document
	Node* Node_pointer_XML_element = Node_pointer_XML_file->getChildByTagName("HydroPlusConfig");

	//Node_pointer_SimulationStringParams queried with Node_pointer_XML_element->getChildByTagName("SimulationStringParams")
	Node* Node_pointer_SimulationStringParams = Node_pointer_XML_element->getChildByTagName("SimulationStringParams");
	//If Node_pointer_SimulationStringParams != nullptr then it exists; which is optional
	if (Node_pointer_SimulationStringParams != nullptr) {
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for element: SimulationStringParams" << endl;
		//Tree_pointer.WalkTree(Node_pointer_SimulationStringParams, SimulationStringParams); obtain elements within tag
		//Note: Tree_pointer using WalkTree, found as child within HydroPlusConfig node called Node_pointer_XML_element
		Tree_pointer.WalkTree(Node_pointer_SimulationStringParams, SimulationStringParams);
	}
	else {
		cout << "Warning: HydroPlusConfig.xml requires SimulationStringParams section." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Correction: Check for HydroPlusConfig.xml examples in \\iTree\\HydroPlus\\TestingFilesAndScript\\TestCases\\." << endl;
		abort();
	}

	//Node_pointer_SimulationNumericalParams queried with Node_pointer_XML_element->getChildByTagName("SimulationNumericalParams")
	Node* Node_pointer_SimulationNumericalParams = Node_pointer_XML_element->getChildByTagName("SimulationNumericalParams");
	//If Node_pointer_SimulationNumericalParams != nullptr then it exists; which is optional
	if (Node_pointer_SimulationNumericalParams != nullptr) {
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for element: SimulationNumericalParams" << endl;
		//Tree_pointer.WalkTree(Node_pointer_SimulationNumericalParams, SimulationNumericalParams); obtain elements within tag
		//Note: Tree_pointer using WalkTree, found as child within HydroPlusConfig node called Node_pointer_XML_element
		Tree_pointer.WalkTree(Node_pointer_SimulationNumericalParams, SimulationNumericalParams);
	}
	else {
		cout << "Warning: HydroPlusConfig.xml requires SimulationNumericalParams section." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Correction: Check for HydroPlusConfig.xml examples in \\iTree\\HydroPlus\\TestingFilesAndScript\\TestCases\\." << endl;
		abort();
	}

	//Node_pointer_SimulationScenarios queried with Node_pointer_XML_element->getChildByTagName("SimulationScenarios")
	Node* Node_pointer_SimulationScenarios = Node_pointer_XML_element->getChildByTagName("SimulationScenarios");
	//If Node_pointer_SimulationScenarios != nullptr then it exists; which is optional
	if (Node_pointer_SimulationScenarios != nullptr) {
		//Tree_pointer.WalkTree(Node_pointer_SimulationScenarios, SimulationScenarios); obtain elements within tag
		Tree_pointer.WalkTree(Node_pointer_SimulationScenarios, SimulationScenarios);
	}

	//Node_pointer_VariableInputMaps queried with Node_pointer_XML_element->getChildByTagName("VariableInputMaps")
	Node* Node_pointer_VariableInputMaps = Node_pointer_XML_element->getChildByTagName("VariableInputMaps");
	//If Node_pointer_VariableInputMaps != nullptr then it exists; which is optional
	if (Node_pointer_VariableInputMaps != nullptr) {
		//Tree_pointer.WalkTree(Node_pointer_VariableInputMaps, VariableInputMaps); obtain elements within tag
		Tree_pointer.WalkTree(Node_pointer_VariableInputMaps, VariableInputMaps);
	}

	//If SimulationStringParams["OutputFolder_Path"].size() or Model_Selection or Algorithm_SoilKsatDecay has no length, then the HydroPlusConfig.xml file is erroneous, likely outdated
	if (SimulationStringParams["OutputFolder_Path"].size() <= 0 || SimulationStringParams["Model_Selection"].size() <= 0 || SimulationStringParams["Algorithm_SoilKsatDecay"].size() <= 0) {
		cout << "Warning: HydroPlusConfig.xml elements needed in the SimulationStringParams section are not present." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: You may be trying to use an outdated HydroPlusConfig with a newer HydroPlus.exe." << endl;
		cout << "Correction: Update your HydroPlusConfig.xml elements using a ..\\TestingFilesAndScript\\TestCases template." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	//Routine to establish StartDate_YYYYMMDD and StopDate_YYYYMMDD parameters for use in handling model simulation inputs, runtime, etc.
	//DateParts_Gregorian[4] integer array initialized to hold parts of date for ExtractGregorianDateParts function
	int DateParts_Gregorian[4];
	//ExtractGregorianDateParts function extracts StopDate_YYYYMMDD year, month, day, hour from Gregorian date, format YYYYMMDDHH
	ExtractGregorianDateParts(int(SimulationNumericalParams["StopDate_YYYYMMDD"]), DateParts_Gregorian);
	//If DateParts_Gregorian[3] < 0 for StopDate_YYYYMMDD, then hour not included, and set to 23, last hour of day
	if (DateParts_Gregorian[3] < 0) {
		//DateParts_Gregorian[3] of hour HH is defined as 23 for StopDate_YYYYMMDD, presuming user wants to simulate a complete day
		DateParts_Gregorian[3] = 23;
		//SimulationDateStop_GDH contains Gregorian Date and Hour, extending 8 to 10 digits by multiplication with 100, adding hour
		SimulationDateStop_YYYYMMDDHH = (SimulationNumericalParams["StopDate_YYYYMMDD"] * 100) + DateParts_Gregorian[3];
	}
	else {
		//SimulationDateStop_GDH contains Gregorian Date and Hour as 10 digit number from input
		SimulationDateStop_YYYYMMDDHH = SimulationNumericalParams["StopDate_YYYYMMDD"];
	}

	//ExtractGregorianDateParts function extracts StartDate_YYYYMMDD year, month, day, hour from Gregorian date, format YYYYMMDDHH
	ExtractGregorianDateParts(int(SimulationNumericalParams["StartDate_YYYYMMDD"]), DateParts_Gregorian);
	//If DateParts_Gregorian[3] < 0 for StartDate_YYYYMMDD, then hour not included, and set to 0, first hour of day
	//Note: DateParts_Gregorian[3] = HH
	if (DateParts_Gregorian[3] < 0) {
		//DateParts_Gregorian[3] of hour HH is defined as 0 for StartDate_YYYYMMDD, presuming user wants to simulate a complete day
		DateParts_Gregorian[3] = 0;
		//SimulationDateStart_GDH contains Gregorian Date and Hour, extending 8 to 10 digits by multiplication with 100, adding hour
		SimulationDateStart_YYYYMMDDHH = (SimulationNumericalParams["StartDate_YYYYMMDD"] * 100) + DateParts_Gregorian[3];
	}
	else {
		//SimulationDateStart_GDH contains Gregorian Date and Hour as 10 digit number from input
		SimulationDateStart_YYYYMMDDHH = SimulationNumericalParams["StartDate_YYYYMMDD"];
	}

	//If (SimulationDateStop_YYYYMMDDHH - SimulationDateStart_YYYYMMDDHH) < 0then abort program
	if ((SimulationDateStop_YYYYMMDDHH - SimulationDateStart_YYYYMMDDHH) < 0) {
		cout << "Warning: The HydroPlusConfig.xml has parameter StartDate_YYYYMMDD < StartDate_YYYYMMDD." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The HydroPlus model needs to progress forward in time." << endl;
		cout << "Correction: In the HydroPlusConfig.xml file, set the StartDate_YYYYMMDD to equal or precede StartDate_YYYYMMDD." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	
	//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
	cout << "Reading HydroPlusConfig.xml file for element: DataOrganizer" << endl;

	// ---------------------<Extracting Parameters from HydroPlusConfig.xml Tree>---------------------------------------
	//Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("DataOrganizer"), DataDrawers, StringDataDrawers);
	//Note: Call WalkTree to populate DataDrawers and StringDataDrawers from the XML node <DataOrganizer>.
	//Note: Each <DataDrawer> in the XML becomes a Drawer_ID in the outer vector.
	//Note: Each <DataFolder> inside a <DataDrawer> becomes a column in that Drawer_ID (i.e., DataDrawers[Drawer_ID][Folder_ID]).
	//Note: Tree_pointer.WalkTree dynamically resizes DataDrawers and StringDataDrawers based on the number of XML elements.
	//Note: After this call, DataDrawers.size() = number of <DataDrawer> elements, and ...
	//Note: ... DataDrawers[Drawer_ID].size() = number of <DataFolder> elements inside that <DataDrawer>.
	Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("DataOrganizer"), DataDrawers, StringDataDrawers);
	// Loop through drawers and folders to confirm counts
	/*for (int i = 0; i < DataDrawers.size(); i++) {
		cout << " DataDrawer " << i << endl;
		for (int j = 0; j < DataDrawers[i].size(); j++) {
			cout << "   DataFolder " << j << endl;
		}
	}*/

	// Declare a single global/owned container somewhere accessible (Inputs, or a higher-level context)
	if (SimulationStringParams["Model_Selection"] == "StatisticalHydro" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
		// 1) Build per-drawer folder counts
		vector<int> folders_per_drawer_vec;
		folders_per_drawer_vec.reserve(DataDrawers.size());
		for (size_t DataDrawers_ID = 0; DataDrawers_ID < DataDrawers.size(); ++DataDrawers_ID) {
			folders_per_drawer_vec.push_back(static_cast<int>(DataDrawers[DataDrawers_ID].size()));
		}
		// 2) Statistical: Init ragged backend with 0 vars 
		beC.init_ragged(folders_per_drawer_vec, /*nvars=*/0, /*init=*/0.0);
		

		// 3a) Copy values from XML per (drawer,folder) into beC
		for (int DataDrawer_ID = 0; DataDrawer_ID < static_cast<int>(DataDrawers.size()); ++DataDrawer_ID) {
			for (int DataFolder_ID = 0; DataFolder_ID < static_cast<int>(DataDrawers[DataDrawer_ID].size()); ++DataFolder_ID) {

				// Each entry in DataDrawers[d][f] is the numeric map extracted by WalkTree
				const auto& src = DataDrawers[DataDrawer_ID][DataFolder_ID]; // map<string,double>

				// Loop through every parameter and copy it into beC
				for (const auto& kv : src) {
					const string& ParamName = kv.first;
					const double ParamValue = kv.second;

					// Ensure variable exists in beC (creates it only if missing)
					//CompactRagged::add_var checks the name and only reallocates when the column is new, then calling add_var inside the loop —only the first time a name appears will it grow the table; later calls are no-ops.
					beC.add_var(ParamName, 0.0);
					// Assign the value to the right (drawer, folder, variable)
					beC.by_key(DataDrawer_ID, DataFolder_ID, ParamName) = ParamValue;
				}

				// --- NEW: String values ---
				if (DataDrawer_ID < (int)StringDataDrawers.size() &&
					DataFolder_ID < (int)StringDataDrawers[DataDrawer_ID].size()) {

					const auto& src_str = StringDataDrawers[DataDrawer_ID][DataFolder_ID];
					for (const auto& kv : src_str) {
						beC.by_key_str(DataDrawer_ID, DataFolder_ID, kv.first) = kv.second;
					}
				}
			}
		}
	}
	
	//Drawer_ID, Folder_ID intialized
	int Drawer_ID, Folder_ID;
	//ValidateAndExtractDrawerFolderID called to test validity of HydroPlusConfig.xml Default_DrawerID_FolderID DataDrawer and DataFolder location
	//Note: InputXml_ReferenceStationFolder and InputXmlString_ReferenceStationFolder are replaced by InputXml and InputXmlString
	//Note: This set of IDs defines the standard for all model folders, including the reference station, unless Flag_InputXml_ReferenceStationFolder is true
	Inputs::ValidateAndExtractDrawerFolderID("Default_DrawerID_FolderID", SimulationStringParams["Default_DrawerID_FolderID"], DataDrawers, StringDataDrawers, Drawer_ID, Folder_ID);
	//InputXml_StandardFolder = DataDrawers[Drawer_ID][Folder_ID] is standard parameter location for doubles
	//Note: DataDrawers[Drawer_ID][Folder_ID] holds all numeric (double) key-value pairs from one <DataFolder>.
	//Note: The 2D vectors are copied into flat maps (InputXml and InputXml_string) for easy use elsewhere in the program.
	InputXml_StandardFolder = DataDrawers[Drawer_ID][Folder_ID];
	//InputXmlString_StandardFolder = StringDataDrawers[Drawer_ID][Folder_ID] is standard parameter location for strings
	//Note: StringDataDrawers[Drawer_ID][Folder_ID] holds any string key-value pairs (e.g., Type) from one <DataFolder>. 
	//Note: The 2D vectors are copied into flat maps (InputXml and InputXml_string) for easy use elsewhere in the program.
	InputXmlString_StandardFolder = StringDataDrawers[Drawer_ID][Folder_ID];

	
	// 3b) For GI folders, fill any *missing* numeric params from the Default folder.
	//     Mirrors legacy Set_GI_MissingParameters behavior, but writes into beC.
	//     - We do NOT overwrite values that the GI folder already defined.
	//     - BulkArea folders are skipped.
	// Keep a pointer to the current BulkArea (default) folder
	auto* defaultSrc = &DataDrawers[Drawer_ID][Folder_ID];
	auto* defaultSrc_Str = &StringDataDrawers[Drawer_ID][Folder_ID];

	if (SimulationStringParams["Model_Selection"] == "StatisticalHydro" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI")
	{
		for (int DataDrawer_ID = 0; DataDrawer_ID < static_cast<int>(DataDrawers.size()); ++DataDrawer_ID) {
			for (int DataFolder_ID = 0; DataFolder_ID < static_cast<int>(DataDrawers[DataDrawer_ID].size()); ++DataFolder_ID) {

				// Get Type from StringDataDrawers (defensively)
				string typeStr;
				if (DataDrawer_ID < static_cast<int>(StringDataDrawers.size()) && DataFolder_ID < static_cast<int>(StringDataDrawers[DataDrawer_ID].size()))
				{
					auto itType = StringDataDrawers[DataDrawer_ID][DataFolder_ID].find("Type");
					if (itType != StringDataDrawers[DataDrawer_ID][DataFolder_ID].end()) {
						typeStr = itType->second;
					}
				}

				// Only apply to GI (non-BulkArea) folders
				if (typeStr == "BulkArea") {
					// Update the "current default" to this BulkArea folder for subsequent GI folders
					defaultSrc = &DataDrawers[DataDrawer_ID][DataFolder_ID];
					defaultSrc_Str = &StringDataDrawers[DataDrawer_ID][DataFolder_ID];
					continue; // nothing to fill for BulkArea itself
				}
					
				// This GI folder's own numeric keys from XML (so we can detect "missing")
				const auto& giSrc = DataDrawers[DataDrawer_ID][DataFolder_ID];
				const auto& giSrc_Str = StringDataDrawers[DataDrawer_ID][DataFolder_ID];
				// For every key in the Default folder, fill if the GI folder didn't define it
				for (const auto& keyvalue : *defaultSrc) {
					const string& key = keyvalue.first;
					const double  defVal = keyvalue.second;

					if (giSrc.find(key) == giSrc.end()) {
						// Ensure the column exists in beC (no-op if already there)
						beC.add_var(key,0.0);
						// Assign default only if the slot exists (it does for statistical beC)
						beC.by_key(DataDrawer_ID, DataFolder_ID, key) = defVal;
						//cout << " DataDrawer_ID " << DataDrawer_ID << " DataFolder_ID " << DataFolder_ID << " " << key << " = " << defVal << endl;
					}
				}
				// For every key in the Default folder, fill if the GI folder didn't define it
				for (const auto& keyvalue : *defaultSrc_Str) {
					const string& key = keyvalue.first;
					const string  defVal = keyvalue.second;

					if (giSrc_Str.find(key) == giSrc_Str.end()) {
						// Assign default only if the slot exists (it does for statistical beC)
						beC.by_key_str(DataDrawer_ID, DataFolder_ID, key) = defVal;
					}
				}
			}
		}
	}

	//Flag_InputXml_ReferenceStationFolder set to true if SimulationStringParams.count("RefWeather_DrawerID_FolderID") > 0
	//Note: InputXml_ReferenceStationFolder and InputXmlString_ReferenceStationFolder are replaced by InputXml and InputXmlString
	Flag_InputXml_ReferenceStationFolder = SimulationStringParams.count("RefWeather_DrawerID_FolderID") > 0;

	//If Flag_InputXml_ReferenceStationFolder is true then enter
	if (Flag_InputXml_ReferenceStationFolder) {
		//Drawer_ID_RefStation, Folder_ID_RefStationintialized
		int Drawer_ID_RefStation, Folder_ID_RefStation;
		//ValidateAndExtractDrawerFolderID called to test validity of HydroPlusConfig.xml RefWeather_DrawerID_FolderID DataDrawer and DataFolder location
		ValidateAndExtractDrawerFolderID("RefWeather_DrawerID_FolderID", SimulationStringParams["RefWeather_DrawerID_FolderID"], DataDrawers, StringDataDrawers, Drawer_ID_RefStation, Folder_ID_RefStation);

		//If (Drawer_ID_RefStation != Drawer_ID || Folder_ID_RefStation != Folder_ID) then it is a unique DataDrawer and/or DataFolder
		if (Drawer_ID_RefStation != Drawer_ID || Folder_ID_RefStation != Folder_ID) {
			//InputXml_ReferenceStationFolder = DataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation] is reference station parameter doubles
			InputXml_ReferenceStationFolder = DataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation];
			//InputXmlString_ReferenceStationFolder = StringDataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation] is reference station parameter strings
			InputXmlString_ReferenceStationFolder = StringDataDrawers[Drawer_ID_RefStation][Folder_ID_RefStation];
		}
		//Else convert Flag_InputXml_ReferenceStationFolder to false, as the DataDrawer and DataFolder are shared by Default_DrawerID_FolderID
		else {
			Flag_InputXml_ReferenceStationFolder = false; 
		}
	}

	//InputXml defined with ternary using Flag_InputXml_ReferenceStationFolder, InputXml_ReferenceStationFolder if true, else InputXml_StandardFolder
	//Note: InputXml replaces InputXml_StandardFolder and InputXml_ReferenceStationFolder 
	InputXml = (Flag_InputXml_ReferenceStationFolder)
		? InputXml_ReferenceStationFolder
		: InputXml_StandardFolder;

	//InputXml_string defined with ternary, InputXmlString_ReferenceStationFolder if true, else InputXmlString_StandardFolder
	//Note: InputXml_string replaces InputXmlString_StandardFolder and InputXmlString_ReferenceStationFolder 
	InputXml_string = (Flag_InputXml_ReferenceStationFolder)
		? InputXmlString_ReferenceStationFolder
		: InputXmlString_StandardFolder;

	//if Model equals SpatialTemperatureHydro then XML elements read into memory from HydroPlusConfig.xml sections
	if (SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro")
	{
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for CoolAir element: TemperatureCalculationParams" << endl;
		//TemperatureCalculationParams element from HydroPlusConfig.xml is called TemperatureCalculationParams, and all sub-elements are read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("TemperatureCalculationParams"), TemperatureCalculationParams);

		//Node* Node_pointer_XML_TempCalcParam_RefSta = Node_pointer_XML_element->getChildByTagName("TemperatureCalculationParams_ReferenceStation")
		//Note: TemperatureCalculationParams_RefStation is an optional element used for scenarios when map area diverges from reference station values
		Node* Node_pointer_XML_TempCalcParam_RefSta = Node_pointer_XML_element->getChildByTagName("TemperatureCalculationParams_ReferenceStation");
		//If (Node_pointer_XML_TempCalcParam_RefSta != nullptr) then enter
		if (Node_pointer_XML_TempCalcParam_RefSta != nullptr) {
			cout << "Reading HydroPlusConfig.xml file for CoolAir element: TemperatureCalculationParams_ReferenceStation" << endl;
			//Tree_pointer.WalkTree(Node_pointer_XML_TempCalcParam_RefSta, TemperatureCalculationParams_ReferenceStation); access WalkTree
			Tree_pointer.WalkTree(Node_pointer_XML_TempCalcParam_RefSta, TemperatureCalculationParams_ReferenceStation);
			//Flag_TemperatureCalculationParams_ReferenceStation_Exists = true; used in HeatFluxCal
			Flag_TemperatureCalculationParams_ReferenceStation_Exists = true;
		}
		else {
			//Flag_TemperatureCalculationParams_ReferenceStation_Exists = true; used in HeatFluxCal
			Flag_TemperatureCalculationParams_ReferenceStation_Exists = false;
		}

		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for CoolAir element: TemperatureExecutionParams" << endl;
		//TemperatureExecutionParams element from HydroPlusConfig.xml is called TemperatureExecutionParams, and all sub-elements are read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("TemperatureExecutionParams"), TemperatureExecutionParams);

		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for CoolAir element: SimulationLocationParams" << endl;
		//Node_pointer_XML_TempLocParam Node pointer takes on returned getChildByTagName function value of tagName SimulationLocationParams
		//Note: Once no HydroPlusConfig.xml files use TemperatureLocationParams as element tagName, then replace this line and if conditionals w ..
		//Note: ... Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("SimulationLocationParams"), SimulationLocationParams);
		Node* Node_pointer_XML_TempLocParam = Node_pointer_XML_element->getChildByTagName("SimulationLocationParams");

		//If Node_pointer_XML_TempLocParam equals nullpter from getChildByTagName with tagName = SimulationLocationParams, then try older tagName 
		if (Node_pointer_XML_TempLocParam == nullptr) {
			//Node_pointer_XML_TempLocParam takes on returned getChildByTagName function value of tagName TemperatureLocationParams
			Node_pointer_XML_TempLocParam = Node_pointer_XML_element->getChildByTagName("TemperatureLocationParams");
		}
		//If Node_pointer_XML_TempLocParam not equal to nullpter then Tree_pointer.WalkTree will function properly 
		if (Node_pointer_XML_TempLocParam != nullptr) {
			//Note: Tree_pointer.WalkTree function assigns Node_pointer_XML_TempLocParam to SimulationLocationParams, and all HydroPlusConfig.xml sub-elements are read into memory
			Tree_pointer.WalkTree(Node_pointer_XML_TempLocParam, SimulationLocationParams);
		}
	}
	else if (SimulationStringParams["Model_Selection"] == "SpatialBuffer" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {

		//cout message to notify user HydroPlusConfig.xml is being read for SimulationLocationParams
		cout << "Reading HydroPlusConfig.xml file for CoolAir element: SimulationLocationParams" << endl;
		//SimulationLocationParams element from HydroPlusConfig.xml is called SimulationLocationParams, and all sub-elements are read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("SimulationLocationParams"), SimulationLocationParams);
	}
	//Else, Model must equal StatisticalHydro and SimulationLocationParams not available from HydroPlusConfig.xml and will need to be defined below
	else
	{
		SimulationNumericalParams["NumberOfLocations"] = 1.0;
	}

	//if Model equals CoolRiver then XML elements read into memory from HydroPlusConfig.xml sections
	if (SimulationStringParams["Model_Selection"] == "CoolRiver")
	{
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for CoolRiver element: CoolRiver_Variables" << endl;
		//CoolRiver_Variables element from HydroPlusConfig.xml is called CoolRiverVariables, and all sub-elements are read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("CoolRiver_Variables"), CoolRiverVariables);
	}
	//if Model equals ECDynamic then XML elements read into memory from HydroPlusConfig.xml sections
	if (SimulationStringParams["Model_Selection"] == "ECDynamic")
	{
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for SpatialBuffer element: BufferNutrientsStringParams" << endl;
		//BufferNutrientsStringParams HydroPlusConfig.xml element called BufferNutrientsStringParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("BufferNutrientsStringParams"), BufferNutrientsStringParams);
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for ECDynamic element: BufferTemporalLoadingNumericalParams" << endl;
		//BufferTemporalLoadingNumericalParams element from HydroPlusConfig.xml is called BufferTemporalLoadingNumericalParams, and all sub-elements are read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("BufferTemporalLoadingNumericalParams"), BufferTemporalLoadingNumericalParams);
	}
	//If Model equals SpatialBuffer then XML elements read into memory from HydroPlusConfig.xml sections
	if (SimulationStringParams["Model_Selection"] == "SpatialBuffer" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for SpatialBuffer element: BufferNutrientsStringParams" << endl;
		//BufferNutrientsStringParams HydroPlusConfig.xml element called BufferNutrientsStringParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("BufferNutrientsStringParams"), BufferNutrientsStringParams);
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for SpatialBuffer element: BufferSpatialWeightingStringParams" << endl;
		//BufferSpatialWeightingStringParams HydroPlusConfig.xml element called BufferSpatialWeightingStringParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("BufferSpatialWeightingStringParams"), BufferSpatialWeightingStringParams);
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for SpatialBuffer element: BufferSpatialWeightingNumericalParams" << endl;
		//BufferSpatialWeightingNumericalParams HydroPlusConfig.xml element called BufferSpatialWeightingNumericalParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("BufferSpatialWeightingNumericalParams"), BufferSpatialWeightingNumericalParams);
	}
	//If Model equals CoolBuilding then XML elements read into memory from HydroPlusConfig.xml sections
	if (SimulationStringParams["Model_Selection"] == "CoolBuilding") {
		//cout message to notify user HydroPlusConfig.xml is being read, in case HydroPlusConfig.xml causes program to exit
		cout << "Reading HydroPlusConfig.xml file for CoolBuilding element: CoolBuildingStringParams" << endl;
		//CoolBuildingStringParams HydroPlusConfig.xml element called CoolBuildingStringParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("CoolBuildingStringParams"), CoolBuildingStringParams);
		//CoolBuildingNumericalParams HydroPlusConfig.xml element called CoolBuildingNumericalParams, sub-elements read into memory
		Tree_pointer.WalkTree(Node_pointer_XML_element->getChildByTagName("CoolBuildingNumericalParams"), CoolBuildingNumericalParams);
	}
}


//void Inputs::ProcessTopographyData function will manage calls to TerrainProcessor function
void Inputs::ProcessTopographyData()
{
	//initialize NAC as number of topographic index bins TopographicIndexBins
	//Note: TI_Value.size() determined by NAC when reading in dem.asc, 
	//Note: otherwise, TI_Value.size() determined based on rows of data within z_TI_ExponentialDecay.csv or z_TI_PowerDecay.csv
	int NAC = int(SimulationNumericalParams["TopographicIndexBins"]);
	//if NAC value is not greater than 0 
	if (NAC * 1 > 0) {}
	//reset NAC to default size of 30 bins
	else {
		NAC = 30;
		cout << "Inputs: The number of topographic index bins, NAC, was reset to: " << NAC << endl;
	}

	//TerrainProcessor object creates instance terrainProcessor and uses keyword = this to send input pointer
	TerrainProcessor terrainProcessor(this);

	//If Model_Selection is StatisticalHydro or SpatialBufferwGI then call TopographicIndex_Histogram_Read for TI Histogram
	if (SimulationStringParams["Model_Selection"] == "StatisticalHydro" || SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") {
		//terrainProcessor.TopographicIndex_Histogram_Read function called sending this as pointer to Inputs
		//Note: If z_TI_PowerDecay.csv or z_TI_ExponentialDecay.csv do not exist, or Flag_Recompute_TopographicIndex true, then TI is computed
		terrainProcessor.TopographicIndex_Histogram_Read(this);
	}
	//Else If Model_Selection is SpatialTemperatureHydro or CoolRiver or SpatialBuffer then call TopographicIndex_Map_Read for TI map
	else if (SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro" || SimulationStringParams["Model_Selection"] == "CoolRiver" || SimulationStringParams["Model_Selection"] == "SpatialBuffer") {
		//terrainProcessor.TopographicIndex_Map_Read function called sending this as pointer to Inputs
		//Note: If z_TIorganizer.asc or z_FDorganizer.asc do not exist, or Flag_Recompute_TopographicIndex true, then TI is computed
		terrainProcessor.TopographicIndex_Map_Read(this);
	}

	//TI_Value_Average is watershed area weighted average of Topographic Index, often called Lambda, computed with terrainProcessor.TI_Value_Average
	TI_Value_Average = terrainProcessor.TI_Value_Average;

	//Ratio_TI_Avg_Target_to_DEM is initialized to 1
	//Note: Ratio_TI_Avg_Target_to_DEM is ratio of topographic index average for drainage basin to dem.asc, equals 1 in ideal case
	double Ratio_TI_Avg_Target_to_DEM = 1.0;
	//If InputXml["Ratio_TI_Avg_Target_to_DEM"] is > 0 & <= 3 and defined then allow for redefinition of Ratio_TI_Avg_Target_to_DEM
	//Note: InputXml["Ratio_TI_Avg_Target_to_DEM"] is optional element in HydroPlusConfig.xml
	if (InputXml["Ratio_TI_Avg_Target_to_DEM"] > 0 && InputXml["Ratio_TI_Avg_Target_to_DEM"] <= 3) {
		//Ratio_TI_Avg_Target_to_DEM redefined based on HydroPlusConfig.xml element Ratio_TI_Avg_Target_to_DEM 
		Ratio_TI_Avg_Target_to_DEM = InputXml["Ratio_TI_Avg_Target_to_DEM"];
		//Notify user of adjustment
		cout << "Note: HydroPlusConfig.xml element Ratio_TI_Avg_Target_to_DEM will adjust TI_Value_Average by " << Ratio_TI_Avg_Target_to_DEM << "." << endl;
	}

	//TI_Value_Average = TI_Value_Average * Ratio_TI_Avg_Target_to_DEM, where Ratio_TI_Avg_Target_to_DEM is typically unity, 1
	//Note: If Ratio_TI_Avg_Target_to_DEM != 1 then TI_Value_Average adjusted to represent value from drainage basin rather than dem.asc map
	//Note: Concern is dem.asc may represent a non-drainage basin area, resulting in an TI_Value_Average value skewed to small or large 
	TI_Value_Average = TI_Value_Average * Ratio_TI_Avg_Target_to_DEM;

	//Note: Consider refactor if there is a need for returning SZQ parameter
	//AveSMD (m) is terrainProcessor.getAveSoilMoistureDeficit(this) with this as pointer to Inputs for average soil moisture deficit
	AveSMD = terrainProcessor.getAveSoilMoistureDeficit(this);

	//Size_TI_Value_Vector is number of index values in TI_Value vector 
	//Note: equal to bins in statistically distributed, equal to count of map pixels with real data in spatially distributed
	Size_TI_Value_Vector = int(terrainProcessor.TI_Value.size());
	//TI_Value is topographic index (TI) vector
	TI_Value = terrainProcessor.TI_Value;
	//Size_TI_Area_Vector is number of topographic index values in TI fractional area vector (equal to bins + 1 in statistically distributed, equal to count of map pixels with real data in spatially distributed)
	Size_TI_Area_Vector = int(terrainProcessor.TI_Area_frac.size());
	//TI_Area_frac is fraction of catchment area associated with TI_Value value
	TI_Area_frac = terrainProcessor.TI_Area_frac;

	setrows(terrainProcessor.getrows());
	setcols(terrainProcessor.getcols());
	setxllcorner(terrainProcessor.getxllcorner());
	setyllcorner(terrainProcessor.getyllcorner());

	//Note: Consider refactor to have TerrainProcessor::DEM_Read_and_Prepare create a DEM_raw_m file so we are not reading here
	//If Model is SpatialTemperatureHydro then enter to get maps of DEM and derived slope and aspect products
	if (SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro") {
		//Note: Consider refactor to create SlopeGround_rad and AspectGround_N_0_rad within TerrainProcessor
		//terrainProcessor.Ground_Slope_and_Aspect_File_Read function to read in z_SlopeGround_rad.asc and z_AspectGround_N_0_rad.asc
		terrainProcessor.Ground_Slope_and_Aspect_File_Read();
		//SlopeGround_rad is vector derived from terrainProcessor.slopeGround_1D_rad, read from z_SlopeGround_rad.asc
		SlopeGround_rad = terrainProcessor.slopeGround_1D_rad;
		//AspectGround_N_0_rad is vector derived from terrainProcessor.aspectGround_1D_N_0_rad, read from z_AspectGround_N_0_rad.asc
		AspectGround_N_0_rad = terrainProcessor.aspectGround_1D_N_0_rad;

	}
	cout << "Completed the terrain processing functions." << endl;
}

//Inputs::ProcessTemperatureExecutionParams function reads HydroPlusConfig.xml inputs for CoolAir 
//Note: TemperatureExecutionParams string variables converted to TemperatureCalculationParams numeric variables
void Inputs::ProcessTemperatureExecutionParams()
{
	//If TemperatureExecutionParams["PrintBlockGroupRange"] = all or All then HydroPlusConfig.xml has indicated all blockgroups should be printed, set flag to 1
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams["PrintBlockGroupRange"]) == "all") {
		TemperatureCalculationParams["PrintAllBlockGroups"] = 1; 
	}
	//If TemperatureExecutionParams["RowColOutputPeriod"] = all or All then HydroPlusConfig.xml has indicated all time steps should be printed, set flag to 1
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams["RowColOutputPeriod"]) == "all")	{
		TemperatureCalculationParams["RowColOutputAllTimeSteps"] = 1;
	}
	//If TemperatureExecutionParams["PrintRowCol"] = all or All then HydroPlusConfig.xml has indicated all row col pairs should be printed, set flag to 1
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams["PrintRowCol"]) == "all") {
		TemperatureCalculationParams["PrintAllRowColOutput"] = 1; 
	}
	//If TemperatureExecutionParams["MapBasedOutputPeriod"] = all or All then HydroPlusConfig.xml has indicated all time steps should be printed, set flag to 1
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams["MapBasedOutputPeriod"]) == "all") {
		TemperatureCalculationParams["MapBasedOutputAllTimeSteps"] = 1; 
	}
	//If TemperatureExecutionParams["PrintMapBasedResults"] = all or All then HydroPlusConfig.xml has indicated all time based maps should be printed, set flag to 1
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams["PrintMapBasedResults"]) == "all") {
		TemperatureCalculationParams["PrintAllTimeBasedResults"] = 1; 
	}

	//HydroPlusConfig_TemperatureExecutionParams_Keys defined to contain HydroPlusConfig.xml TemperatureExecutionParams elements
	vector<string> HydroPlusConfig_TemperatureExecutionParams_Keys = {
		"RefWeatherLocationEasting_m",
		"RefWeatherLocationNorthing_m",
		"RefWeatherLocationBlockgroup",
		"RefWeatherLocationTopographicIndex",
		"RefWeatherLocationElevation_m",
		"RefWeatherLocationSlope_deg",
		"RefWeatherLocationAspect_deg",
		"RefWeatherLocationNLCD_Class",
		"RefWeatherLocationTreeCover_percent",
		"RefWeatherLocationImperviousCover_percent",
		"RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"
	};

	//For (const auto& TemperatureExecutionParams_str : HydroPlusConfig_TemperatureExecutionParams_Keys) to loop 
	for (const auto& TemperatureExecutionParams_str : HydroPlusConfig_TemperatureExecutionParams_Keys) {
		//Process_Scalar_TemperatureExecutionParams(TemperatureExecutionParams_str) call to find map flag or value
		Process_Scalar_TemperatureExecutionParams(TemperatureExecutionParams_str);
	}

	//MapPixel_ReferenceStation_IDs.clear() clears vector
	MapPixel_ReferenceStation_IDs.clear();
	//If RefWeatherLocationRowCol is provided in HydroPlusConfig.xml, extract row and column
	if (TemperatureExecutionParams.count("RefWeatherLocationRowCol") > 0) {
		//row_ReferenceStation = atoi(...) extracts row index from before comma
		row_ReferenceStation = atoi(TemperatureExecutionParams["RefWeatherLocationRowCol"].substr(0, TemperatureExecutionParams["RefWeatherLocationRowCol"].find(",")).c_str());
		//col_ReferenceStation = atoi(...) extracts column index from after comma
		col_ReferenceStation = atoi(TemperatureExecutionParams["RefWeatherLocationRowCol"].substr(TemperatureExecutionParams["RefWeatherLocationRowCol"].find(",") + 1).c_str());
		//If (row_ReferenceStation >= 0 && col_ReferenceStation >= 0) then reference station is inside the map
		if (row_ReferenceStation >= 0 && col_ReferenceStation >= 0 && SimulationNumericalParams["Flag_MultipleStations"] != 1) {
			//MapPixel_ReferenceStation_IDs.push_back(row_ReferenceStation * nCols + col_ReferenceStation);
			//Note: Using row and column start at 0 when defining reference station in HydroPlusCofig.xml, but at 1 for maps
			MapPixel_ReferenceStation_IDs.push_back(row_ReferenceStation * nCols + col_ReferenceStation);
			//ReferenceStationOutsideMap set to 0 to indicate MapPixel_ReferenceStation_IDs is inside the map
			SimulationStringParams["ReferenceStationOutsideMap"] = "0";
		}
		//Else If SimulationNumericalParams["Flag_MultipleStations"] == 1 then 
		else if (SimulationNumericalParams["Flag_MultipleStations"] == 1) {
			//ReferenceStationOutsideMap set to 1 to indicate MapPixel_ReferenceStation_IDs is outside the map
			SimulationStringParams["ReferenceStationOutsideMap"] = "1";
			//For (size_t i = 0; i < Station_OBJECTID_vec.size(); ++i); loop through Station_OBJECTID_vec multiple stations
			for (size_t i = 0; i < Station_OBJECTID_vec.size(); ++i) {
				//MapPixel_ReferenceStation_IDs.push_back(nRows * nCols + i); 
				//Note: index for reference station located at end of nCols*nRows; increment by i
				MapPixel_ReferenceStation_IDs.push_back(nRows * nCols + static_cast<int>(i));
			}
		}
		//Else if row_ReferenceStation or col_ReferenceStation are less than 0 then reference station is outside the map
		//Note: If RefWeatherLocationRowCol, then no ReferenceStation data should be read from map
		else {
			//ReferenceStationOutsideMap set to 1 to indicate MapPixel_ReferenceStation_IDs is outside the map
			SimulationStringParams["ReferenceStationOutsideMap"] = "1";
			//MapPixel_ReferenceStation_IDs.push_back(nCols * nRows); index for reference station located at end of nCols*nRows
			MapPixel_ReferenceStation_IDs.push_back(nCols * nRows);
			//ReferenceStationKeys list of parameter keys to check
			vector<string> ReferenceStationKeys = {
				"RefWeatherLocationEasting_m_or_Longitude_dd",
				"RefWeatherLocationNorthing_m_or_Latitude_dd",
				"RefWeatherLocationBlockgroup",
				"RefWeatherLocationTopographicIndex",
				"RefWeatherLocationElevation_m",
				"RefWeatherLocationSlope_deg",
				"RefWeatherLocationAspect_deg",
				"RefWeatherLocationNLCD_Class",
				"RefWeatherLocationTreeCover_percent",
				"RefWeatherLocationImperviousCover_percent",
				"RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"
			};
			for (const auto& key : ReferenceStationKeys) {
				auto it = TemperatureCalculationParams.find(key);
				if (it != TemperatureCalculationParams.end() && it->second == -1) {
					cerr << endl;
					cerr << "Warning: HydroPlusConfig.xml element RefWeatherLocationRowCol has negative value and is therefore outside map ..." << endl;
					cerr << "... But HydroPlusConfig.xml element " << key << " value is Map, which is impossible given the above." << endl;
					cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
					cerr << "Explanation: If RefWeatherLocationRowCol is outside map, then " << key << " is outside map." << endl;
					cerr << "Correction: Change " << key << " from Map to explicit values or ..." << endl;
					cerr << "... Change RefWeatherLocationRowCol to a map pixel." << endl;
					abort();
				}
			}
		}
	}
	//If TemperatureExecutionParams.count("AH_Flux_Hour_Ratio") > 0 then element exists and call function
	if (TemperatureExecutionParams.count("AH_Flux_Hour_Ratio") > 0) {
		//call Process_Vector_TemperatureExecutionParams(TemperatureExecutionParams["AH_Flux_Hour_Ratio"], AH_Flux_Hour_Ratio)
		//Note: AH_Flux_Hour_Ratio element in TemperatureExecutionParams section of HydroPlusConfig.xml has 24 values 
		//Note: Values are average hour to average annual anthropogenic heat emission or flux (W/m2)
		//Note: AH4GUC is a NetCDF with AHE (anthropogenic heat emission, W/m2), https://urbanclimate.tse.ens.titech.ac.jp/ah4guc/
		//Note: The NetCDF of AHE (W/m2) contains 12 months and 24 hours per month which were averaged to obtain the annual average hour value
		//Note: AH_Flux_Hour_Ratio and AH_Flux_Month_Ratio are used in coordination to obtain hourly values from annual average values
		Process_Vector_TemperatureExecutionParams(TemperatureExecutionParams["AH_Flux_Hour_Ratio"], AH_Flux_Hour_Ratio);
	}

	//If TemperatureExecutionParams.count("AH_Flux_Month_Ratio") > 0 then element exists and call function
	if (TemperatureExecutionParams.count("AH_Flux_Month_Ratio") > 0) {
		//Process_Vector_TemperatureExecutionParams(TemperatureExecutionParams["AH_Flux_Month_Ratio"], AH_Flux_Month_Ratio)
		//Note: AH_Flux_Month_Ratio element in TemperatureExecutionParams section of HydroPlusConfig.xml has 12 values 
		Process_Vector_TemperatureExecutionParams(TemperatureExecutionParams["AH_Flux_Month_Ratio"], AH_Flux_Month_Ratio);
	}

	//AHE_Hour_default_Ratio was computed for New York State with the AH4GUC 2010 data
	string AHE_Hour_default_Ratio = "1.401,1.161,1.131,0.899,0.774,0.572,0.412,0.306,0.270,0.311,0.360,0.498,0.814,1.258,1.263,1.385,1.376,1.375,1.358,1.384,1.383,1.384,1.440,1.483";
	//AHE_Month_default_Ratio was computed for New York State with the AH4GUC 2010 data
	string AHE_Month_default_Ratio = "1.212,1.231,1.159,1.024,0.891,0.807,0.800,0.789,0.845,0.938,1.095,1.198";

	//validateRatioVector function called with AH_Flux_Hour_Ratio, 24, "AH_Flux_Hour_Ratio", AHE_Hour_default_Ratio
	validateRatioVector(AH_Flux_Hour_Ratio, 24, "AH_Flux_Hour_Ratio", AHE_Hour_default_Ratio);
	//validateRatioVector function called with AH_Flux_Month_Ratio, 12, "AH_Flux_Month_Ratio", AHE_Month_default_Ratio
	validateRatioVector(AH_Flux_Month_Ratio, 12, "AH_Flux_Month_Ratio", AHE_Month_default_Ratio);

	//For (auto i : TemperatureExecutionParams) Loop through some elements of TemperatureExecutionParams in HydroPlusConfig.xml 
	//Note: Flags activated above: PrintAllRowColOutput, RowColOutputAllTimeSteps, MapBasedOutputAllTimeSteps, PrintAllBlockGroups
	//Note: Elements may have #s appended, e.g., PrintRowCol0, RowColOutputPeriod0, MapBasedOutputPeriod0, PrintBlockGroupRange0
	//Note: Elements typically contain two values, such as row, col or date_begin, date_end or blockgroup_start, blockgroup_end
	for (auto i : TemperatureExecutionParams) {
		if (i.second == "") {
			continue;
		}
		//If Not PrintAllRowColOutput and If PrintRowCol (with number appended) is present, and Not npos, then
		if (!TemperatureCalculationParams["PrintAllRowColOutput"] && i.first.find("PrintRowCol") != string::npos) {
			int row = atoi(i.second.substr(0, i.second.find(",")).c_str());
			int col = atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str());
			int MapPixel_ID = row * nCols + col;
			LocationIdsToPrintTempOutput.insert(MapPixel_ID);
		}
		//If Not RowColOutputAllTimeSteps and If RowColOutputPeriod (with number appended) is present, and Not npos, then
		if (!TemperatureCalculationParams["RowColOutputAllTimeSteps"] && i.first.find("RowColOutputPeriod") != string::npos) {
			int startDateHour = atoi(i.second.substr(0, i.second.find(",")).c_str());
			int endDateHour = atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str());
			StartDatesForRowColTempOutput.push_back(startDateHour);
			EndDatesForRowColTempOutput.push_back(endDateHour);
		}
		//If Not MapBasedOutputAllTimeSteps and If MapBasedOutputPeriod (with number appended) is present, and Not npos, then
		if (!TemperatureCalculationParams["MapBasedOutputAllTimeSteps"] && i.first.find("MapBasedOutputPeriod") != string::npos) {
			int startDateHour = atoi(i.second.substr(0, i.second.find(",")).c_str());
			int endDateHour = atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str());
			StartDatesForTimeBasedTempOutput.push_back(startDateHour);
			EndDatesForTimeBasedTempOutput.push_back(endDateHour);
		}
		//If Not PrintAllBlockGroups and If PrintBlockGroupRange (with number appended) is present, and Not npos, then
		if (!TemperatureCalculationParams["PrintAllBlockGroups"] && i.first.find("PrintBlockGroupRange") != string::npos)	{
			int startBlockId = atoi(i.second.substr(0, i.second.find(",")).c_str());
			int endBlockId = atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str());
			StartingBlockIdsToPrintTempOutput.push_back(startBlockId);
			EndingBlockIdsToPrintTempOutput.push_back(endBlockId);
		}
	}
	//if TemperatureCalculationParams["PrintAllBlockGroups"] equals 1 or StartingBlockIdsToPrintTempOutput.size() > 0
	if (TemperatureCalculationParams["PrintAllBlockGroups"] == 1 || StartingBlockIdsToPrintTempOutput.size() > 0) {
		auto PrintBlockGroupDailyAndHourly = TemperatureExecutionParams.find("PrintBlockGroupDailyAndHourly");
		//If PrintBlockGroupDailyAndHourly != TemperatureExecutionParams.end() && !PrintBlockGroupDailyAndHourly->second.empty()) then
		if (PrintBlockGroupDailyAndHourly != TemperatureExecutionParams.end() && !PrintBlockGroupDailyAndHourly->second.empty()) {
			//Flag_PrintBlockGroupDailyAndHourly_str set to TemperatureExecutionParams["PrintBlockGroupDailyAndHourly"]
			//Note: PrintBlockGroupDailyAndHourly options: 0=no blockgroup output; 1=day and hour ouput; 2=day only output; 3=hour only output
			Flag_PrintBlockGroupDailyAndHourly_str = PrintBlockGroupDailyAndHourly->second;
		}
		//Else Flag_PrintBlockGroupDailyAndHourly_str will retain the assignment of 0
		else {
			Flag_PrintBlockGroupDailyAndHourly_str = "0";
		}
	}
}


//Process_Scalar_TemperatureExecutionParams to read HydroPlusConfig.xml scalars of map values from TemperatureExecutionParams
//Note: Function assigns -1 to TemperatureCalculationParams if element is 'map'; later if TemperatureCalculationParams is not -1, it assigns element value
//Note: Function receives: "RefWeatherLocationEasting_m_or_Longitude_dd", "RefWeatherLocationNorthing_m_or_Latitude_dd", "RefWeatherLocationBlockgroup", "RefWeatherLocationTopographicIndex", "RefWeatherLocationElevation_m", "RefWeatherLocationSlope_deg", "RefWeatherLocationAspect_deg",  "RefWeatherLocationNLCD_Class", "RefWeatherLocationTreeCover_percent", "RefWeatherLocationImperviousCover_percent", "RefWeatherLocationAH_Flux_Qtot_Avg_Wpm2"
void Inputs::Process_Scalar_TemperatureExecutionParams(const string& TemperatureExecutionParams_str) {
	//Check if the key exists in TemperatureExecutionParams before proceeding
	if (TemperatureExecutionParams.count(TemperatureExecutionParams_str) == 0) {
		//Key not found, return without assigning anything
		return;
	}
	//If (Convert_String_to_Lower_Case(TemperatureExecutionParams[TemperatureExecutionParams_str]) == "map") then 
	if (Convert_String_to_Lower_Case(TemperatureExecutionParams[TemperatureExecutionParams_str]) == "map") {
		//TemperatureCalculationParams[TemperatureExecutionParams_str] = -1; this will indicate to use map value
		TemperatureCalculationParams[TemperatureExecutionParams_str] = -1;
	}
	//Else If (Convert_String_to_Lower_Case(TemperatureExecutionParams[TemperatureExecutionParams_str]) == "avg") then 
	//Note: Case for RefWeatherLocationTopographicIndex
	else if (Convert_String_to_Lower_Case(TemperatureExecutionParams[TemperatureExecutionParams_str]) == "avg") {
		//TemperatureCalculationParams[TemperatureExecutionParams_str] = -1; this will indicate to use map value
		TemperatureCalculationParams[TemperatureExecutionParams_str] = 1;
	}
	//Else map is not present and read in value from HydroPlusConfig.xml
	else {
		//TemperatureCalculationParams[TemperatureExecutionParams_str] = stod(TemperatureExecutionParams[TemperatureExecutionParams_str]) 
		//Note: This obtains HydroPlusConfig.xml value, changing from string to double
		TemperatureCalculationParams[TemperatureExecutionParams_str] = stod(TemperatureExecutionParams[TemperatureExecutionParams_str]);
	}
}

//Process_Vector_TemperatureExecutionParams to read HydroPlusConfig.xml vectors of anthropogenic heat and degree hour from TemperatureExecutionParams
void Inputs::Process_Vector_TemperatureExecutionParams(const string& TemperatureExecutionParams_str, vector<double>& AH_Flux_Hour_Month_Ratio_vector) {
	//stringstream TemperatureExecutionParam_ss(TemperatureExecutionParams_str) assigns TemperatureExecutionParams_str to stringstream
	stringstream TemperatureExecutionParam_ss(TemperatureExecutionParams_str);
	//While TemperatureExecutionParam_ss.good() will loop through vector while items are present
	while (TemperatureExecutionParam_ss.good()) {
		string TemperatureExecutionParam_str;
		//getline (TemperatureExecutionParam_ss, TemperatureExecutionParam_str, ',') will obtain variables as strings separated by comma
		getline(TemperatureExecutionParam_ss, TemperatureExecutionParam_str, ',');
		//double TemperatureExecutionParam_double = stod(TemperatureExecutionParam_str); converts str to double
		double TemperatureExecutionParam_double = stod(TemperatureExecutionParam_str);
		//AH_Flux_Hour_Month_Ratio_vector.push_back(TemperatureExecutionParam_double) pushes double to AH_Flux_Hour_Month_Ratio_vector sent to function
		AH_Flux_Hour_Month_Ratio_vector.push_back(TemperatureExecutionParam_double);
	}
}

//validateRatioVector function to validate and replace a vector with default values if its size is incorrect
void Inputs::validateRatioVector(vector<double>& RatioVector, int expected_size, const string& ratio_name, const string& default_csv) {

	//If RatioVector.size() not equal to expected_size then repopulate with default values
	if (RatioVector.size() != expected_size) {
		//RatioVector.clear() to ensure default values are correctly added
		RatioVector.clear();
		//stringstream ss(default_csv) reads default_csv to ss
		stringstream ss(default_csv);
		//ratio_str and ratio_value defined
		string ratio_str;
		double ratio_value;

		//While loop using getline to populate ratio_str from csv ss, continue while RatioVector.size() < expected_size
		while (getline(ss, ratio_str, ',') && RatioVector.size() < expected_size) {
			//ratio_value = stod(ratio_str) using stod string to double function 
			ratio_value = stod(ratio_str);
			//RatioVector.push_back with ratio_value read from csv
			RatioVector.push_back(ratio_value);
		}

		// Alert user if modifying due to incorrect input size
		if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] == 1 && (ratio_name == "AH_Flux_Hour_Ratio" || ratio_name == "AH_Flux_Month_Ratio")) {
			cout << "Note: The HydroPlusConfig.xml element " << ratio_name << " did not have size " << expected_size << endl;
			cout << "Note: This inconsistency results in using the default values." << endl;
		}
		// Alert user if modifying due to incorrect input size
		if (SimulationNumericalParams["Flag_DegreeHour_Dependent_AH_Flux_Qcr"] == 1 && (ratio_name != "DegreeHour_Year_Ratio" && ratio_name != "DegreeHour_Month_Ratio" && ratio_name != "DegreeHour_Hour_Ratio")) {
			cout << "Note: The HydroPlusConfig.xml element " << ratio_name << " did not have size " << expected_size << endl;
			cout << "Note: This inconsistency results in using the default values." << endl;
		}
	}
}
//InitializeLAIVariables prepares variables for CalcTreeLAI
//Note: i-Tree LocationSpecies Database uses Last Frost and First Frost dates to indicate LeafOn_MidPoint_JD and LeafOff_MidPoint_JD
//Note: LeafOn_Start_JD = LeafOnDate_JDay - LeafTransition_Day * 0.5; LeafOn_Stop_JD = LeafOnDate_JDay + LeafTransition_Day * 0.5;
//Note: LeafOff_Start_JD = LeafOffDate_JDay - LeafTransition_Day * 0.5; LeafOff_Stop_JD = LeafOffDate_JDay + LeafTransition_Day * 0.5;
//References:
//Hirabayashi, S., & Nowak, D. J. (2016). Comprehensive national database of tree effects on air quality and human health in the United States. Environmental Pollution, 215, 48-57. DOI: https://doi.org/10.1016/j.envpol.2016.04.068
//Nowak, D. J. (2024). Understanding i-Tree: 2023 summary of programs and methods. Retrieved from http://dx.doi.org/10.2737/NRS-GTR-200-2023
void Inputs::InitializeLAIVariables() {

	//LAI variables are initialized

	//LeafOn_Start_JD (Julian Day) is LeafOnDay minus 0.5 * LeafTransDays, half of leaf transition days
	LAI_BAI_map["LeafOn_Start_JD"] = InputXml["LeafOnDate_JDay"] - InputXml["LeafTransition_Day"] * 0.5;
	//LeafOn_Stop_JD (Julian Day) is LeafOnDay plus 0.5 * LeafTransDays, half of leaf transition days
	LAI_BAI_map["LeafOn_Stop_JD"] = InputXml["LeafOnDate_JDay"] + InputXml["LeafTransition_Day"] * 0.5;
	//LeafOff_Start_JD (Julian Day) is LeafOffDay minus 0.5 * LeafTransDays, half of leaf transition days
	LAI_BAI_map["LeafOff_Start_JD"] = InputXml["LeafOffDate_JDay"] - InputXml["LeafTransition_Day"] * 0.5;
	LAI_BAI_map["LeafOff_Stop_JD"] = InputXml["LeafOffDate_JDay"] + InputXml["LeafTransition_Day"] * 0.5;
	//LAI_BAI_Tree_min_m2pm2 (m2/m2) is minimum tree leaf area index plus bark area index
	LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"] = InputXml["LeafAreaIndex_Tree_m2pm2"] * InputXml["TreeCover_Evergreen_frac"] + InputXml["BarkAreaIndex_Tree_m2pm2"];
	//LAI_BAI_Tree_max_m2pm2 (m2/m2) is maximum tree leaf area index plus bark area index
	LAI_BAI_map["LAI_BAI_Tree_max_m2pm2"] = InputXml["LeafAreaIndex_Tree_m2pm2"] + InputXml["BarkAreaIndex_Tree_m2pm2"];
	//LAI_BAI_SVeg_min_m2pm2 (m2/m2) is minimum short vegetation leaf area index plus bark area index
	LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"] = InputXml["LeafAreaIndex_SVeg_m2pm2"] * InputXml["SVegCover_Evergreen_frac"] + InputXml["BarkAreaIndex_SVeg_m2pm2"];
	//LAI_BAI_SVeg_min_m2pm2 (m2/m2) is maximum short vegetation leaf area index
	LAI_BAI_map["LAI_BAI_SVeg_max_m2pm2"] = InputXml["LeafAreaIndex_SVeg_m2pm2"] + InputXml["BarkAreaIndex_SVeg_m2pm2"];

	//LAI_BAI_Tree_min_m2pm2 (m2/m2) is minimum tree leaf area index
	LAI_BAI_map["LAI_Tree_min_m2pm2"] = InputXml["LeafAreaIndex_Tree_m2pm2"] * InputXml["TreeCover_Evergreen_frac"];
	//LAI_BAI_Tree_max_m2pm2 (m2/m2) is maximum tree leaf area index
	LAI_BAI_map["LAI_Tree_max_m2pm2"] = InputXml["LeafAreaIndex_Tree_m2pm2"];
	//LAI_BAI_SVeg_min_m2pm2 (m2/m2) is minimum short vegetation leaf area index
	LAI_BAI_map["LAI_SVeg_min_m2pm2"] = InputXml["LeafAreaIndex_SVeg_m2pm2"] * InputXml["SVegCover_Evergreen_frac"];
	//LAI_BAI_Tree_max_m2pm2 (m2/m2) is maximum short vegetation leaf area index
	LAI_BAI_map["LAI_SVeg_max_m2pm2"] = InputXml["LeafAreaIndex_SVeg_m2pm2"];

}

//cte 2025 Refactor to ensure it handles southern and northern hemisphere as in WeatherPrep.TreeData.cs
/// Northern Hemisphere
/// 
///  OnStart         OnEnd                      OffStart        OffEnd
///   <------> <------>                          <------> <------> 
///              
///   +-------+-------+-------- -------//--------+-------+-------+---------+
///         leafOnDOY                                 leafOffDOY    
///            <----------------------------------------->
///                            Days_LeafOnDuration
///   <----------------------------------------->
///                   Days_LeafOnDuration
/// 

/// Southern Hemisphere
/// 
///  OffStart       OffEnd                      OnStart         OnEnd
///   <------> <------>                          <------> <------> 
///              
///   +-------+-------+-------- -------//--------+-------+-------+---------+
///       leafOffDOY                                 leafOnDOY    
///            <----------------------------------------->
///                            lfOffDuration
///   <----------------------------------------->
///                   lfOffDuration
/// 
//CalcTreeLAI function should compute tree leaf area index for any model time step for Northern Hemisphere
//Note: Consider refactor to remove LAI_BAI_Tree_m2pm2, only use phenology LAI_Tree_m2pm2, and maintain LAI_Tree_m2pm2 and BAI_Tree_m2pm2
//Note: i-Tree LocationSpecies Database uses Last Frost and First Frost dates to indicate LeafOn_MidPoint_JD and LeafOff_MidPoint_JD
//Note: LeafOn_Start_JD = LeafOnDate_JDay - LeafTransition_Day * 0.5; LeafOn_Stop_JD = LeafOnDate_JDay + LeafTransition_Day * 0.5;
//Note: LeafOff_Start_JD = LeafOffDate_JDay - LeafTransition_Day * 0.5; LeafOff_Stop_JD = LeafOffDate_JDay + LeafTransition_Day * 0.5;
//References:
//Hirabayashi, S., & Nowak, D. J. (2016). Comprehensive national database of tree effects on air quality and human health in the United States. Environmental Pollution, 215, 48-57. DOI: https://doi.org/10.1016/j.envpol.2016.04.068
//Nowak, D. J. (2024). Understanding i-Tree: 2023 summary of programs and methods. Retrieved from http://dx.doi.org/10.2737/NRS-GTR-200-2023
void Inputs::CalcTreeLAI(int JulianDay)
{
	double LAI_BAI_Tree_m2pm2 = 0;
	double LAI_Tree_m2pm2 = 0;
	double Coeff_LeafTransition = 0;
	//If JulianDay less than or equal to LeafOn_Start_JD then 
	if (JulianDay <= LAI_BAI_map["LeafOn_Start_JD"]) {
		LAI_BAI_Tree_m2pm2 = LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"];
		LAI_Tree_m2pm2 = LAI_BAI_map["LAI_Tree_min_m2pm2"];
	}
	//Else If JulianDay > LeafOn_Start_JD and < LeafOn_Stop_JD then transition leaf on
	else if (JulianDay > LAI_BAI_map["LeafOn_Start_JD"] && JulianDay < LAI_BAI_map["LeafOn_Stop_JD"]) {
		//
		//Coeff_LeafTransition is 0.37 from Eq 1 of Hirabayashi and Nowak (2016) 
		//Note: Function to approximate sinosoidal leaf transition with exponential function
		Coeff_LeafTransition = (JulianDay - InputXml["LeafOnDate_JDay"]) * 0.37;
		LAI_BAI_Tree_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_BAI_Tree_max_m2pm2"] - LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"]) + LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"];
		LAI_Tree_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_Tree_max_m2pm2"] - LAI_BAI_map["LAI_Tree_min_m2pm2"]) + LAI_BAI_map["LAI_Tree_min_m2pm2"];
	}
	//Else If JulianDay >= LeafOn_Stop_JD and less than LeafOff_Start_JD then set to maximum LAI + BAI
	else if (JulianDay >= LAI_BAI_map["LeafOn_Stop_JD"] && JulianDay <= LAI_BAI_map["LeafOff_Start_JD"]) {
		LAI_BAI_Tree_m2pm2 = LAI_BAI_map["LAI_BAI_Tree_max_m2pm2"];
		LAI_Tree_m2pm2 = LAI_BAI_map["LAI_Tree_max_m2pm2"];
	}
	//Else If JulianDay > LeafOff_Start_JD and < LeafOff_Stop_JD then transition leaf off
	else if (JulianDay > LAI_BAI_map["LeafOff_Start_JD"] && JulianDay < LAI_BAI_map["LeafOff_Stop_JD"])	{
		Coeff_LeafTransition = (InputXml["LeafOffDate_JDay"] - JulianDay) * 0.37;
		LAI_BAI_Tree_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_BAI_Tree_max_m2pm2"] - LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"]) + LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"];
		LAI_Tree_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_Tree_max_m2pm2"] - LAI_BAI_map["LAI_Tree_min_m2pm2"]) + LAI_BAI_map["LAI_Tree_min_m2pm2"];
	}
	//Else If JulianDay >= LeafOff_Stop_JD then set to minimum LAI + BAI
	else if (JulianDay >= LAI_BAI_map["LeafOff_Stop_JD"]) {
		LAI_BAI_Tree_m2pm2 = LAI_BAI_map["LAI_BAI_Tree_min_m2pm2"];
		LAI_Tree_m2pm2 = LAI_BAI_map["LAI_Tree_min_m2pm2"];
	}
	LAI_BAI_Tree_m2_p_m2.push_back(LAI_BAI_Tree_m2pm2);
	LAI_Tree_m2_p_m2.push_back(LAI_Tree_m2pm2);
}

//CalcShortVegLAI function should compute tree leaf area index for any model time step for Northern Hemisphere
//Note: Consider refactor to remove LAI_BAI_SVeg_m2pm2, only use phenology LAI_SVeg_m2pm2, and maintain LAI_SVeg_m2pm2 and BAI_SVeg_m2pm2
//Note: i-Tree LocationSpecies Database uses Last Frost and First Frost dates to indicate LeafOn_MidPoint_JD and LeafOff_MidPoint_JD
//Note: LeafOn_Start_JD = LeafOn_MidPoint_JD - 14; LeafOn_Stop_JD = LeafOn_MidPoint_JD + 14;
//Note: LeafOff_Start_JD = LeafOff_MidPoint_JD - 14; LeafOff_Stop_JD = LeafOff_MidPoint_JD + 14;
void Inputs::CalcShortVegLAI(int JulianDay)
{
	double LAI_BAI_SVeg_m2pm2 = 0;
	double LAI_SVeg_m2pm2 = 0;
	double Coeff_LeafTransition = 0;
	if (JulianDay <= LAI_BAI_map["LeafOn_Start_JD"]) {
		LAI_BAI_SVeg_m2pm2 = LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"];
		LAI_SVeg_m2pm2 = LAI_BAI_map["LAI_SVeg_min_m2pm2"];
	}
	else if (JulianDay > LAI_BAI_map["LeafOn_Start_JD"] && JulianDay < LAI_BAI_map["LeafOn_Stop_JD"]) {
		Coeff_LeafTransition = (JulianDay - InputXml["LeafOnDate_JDay"]) * 0.37;
		LAI_BAI_SVeg_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_BAI_SVeg_max_m2pm2"] - LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"]) + LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"];
		LAI_SVeg_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_BAI_SVeg_max_m2pm2"] - LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"]) + LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"];
	}
	else if (JulianDay >= LAI_BAI_map["LeafOn_Stop_JD"] && JulianDay <= LAI_BAI_map["LeafOff_Start_JD"]) {
		LAI_BAI_SVeg_m2pm2 = LAI_BAI_map["LAI_BAI_SVeg_max_m2pm2"];
		LAI_SVeg_m2pm2 = LAI_BAI_map["LAI_SVeg_max_m2pm2"];
	}
	else if (JulianDay > LAI_BAI_map["LeafOff_Start_JD"] && JulianDay < LAI_BAI_map["LeafOff_Stop_JD"]) {
		Coeff_LeafTransition = (InputXml["LeafOffDate_JDay"] - JulianDay) * 0.37;
		LAI_BAI_SVeg_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_BAI_SVeg_max_m2pm2"] - LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"]) + LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"];
		LAI_SVeg_m2pm2 = 1 / (1 + exp(-Coeff_LeafTransition)) * (LAI_BAI_map["LAI_SVeg_max_m2pm2"] - LAI_BAI_map["LAI_SVeg_min_m2pm2"]) + LAI_BAI_map["LAI_SVeg_min_m2pm2"];
	}
	else if (JulianDay >= LAI_BAI_map["LeafOff_Stop_JD"]) {
		LAI_BAI_SVeg_m2pm2 = LAI_BAI_map["LAI_BAI_SVeg_min_m2pm2"];
	}
	LAI_BAI_SVeg_m2_p_m2.push_back(LAI_BAI_SVeg_m2pm2);
	LAI_SVeg_m2_p_m2.push_back(LAI_SVeg_m2pm2);
}

//readNLCDLandCoverFile function will read input map file landcover.asc (#) containing NLCD Classes
bool Inputs::readNLCDLandCoverFile(string& Directory_Input_CLArg, double NODATA_code)
{
	//readLandCover_map assigned to landcover.asc file from Directory_Input_CLArg; if file does not exist it is given a value of false
	ifstream readLandCover_map(Directory_Input_CLArg + "landcover.asc");
	//readLandCover_table assigned to landcover.asc file from Directory_Input_CLArg; if file does not exist it is given a value of false
	ifstream readLandCover_table(Directory_Input_CLArg + "LandCover_table.txt");

	//If readLandCover_map is false and readLandCover_table is false, then
	if (!readLandCover_map && !readLandCover_table) {
		//In EC dynamic model, it will calculate EC and EMC database without running Buffer simulation if not providing LC and Discharge.
		if (SimulationStringParams["Model_Selection"] != "ECDynamic") {
			cout << "Warning: landcover.asc or LandCover_table.txt could not be opened." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: When the landcover.asc or LandCover_table.txt file is missing there is no land cover data to drive the simulation." << endl;
			cout << "Correction: Create a landcover.asc or LandCover_table.txt file, either manually or perhaps using the i-Tree Cool Air python utility." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
		//if Model_Selection == ECDynamic
		else {
			cout << "Warning:landcover.asc or LandCover_table.txt could not be opened." << endl;
			//false reading NLCD will trigger the generation of EC database without executing the entire model; don't remove this line
			return false;
		}
	}
	//buffer defined as string to take value of header data
	string buffer;

	//if readLandCover_map is not false, then read header into buffer string
	if (readLandCover_map) {
		readLandCover_map >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
		//Check_Map_Header called to compare with dem.asc header
		Check_Map_Header("landcover.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
		
		//tempdata and count initialized as zero
		int tempdata;
		int count = 0;

		//LandCover_NLCD_Class.clear() and clear other vectors
		LandCover_NLCD_Class.clear();
		TotalMapCover_frac.clear();
		SoilCoverNoTC_frac.clear();
		WaterCoverNoTC_frac.clear();
		ImperviousCoverNoTreeCover_frac.clear();
		ShortVegCoverNoTC_frac.clear();
		ConnectedImpervious.clear();
		TreeEvergreen_frac.clear();
		ShortVegEvergreen_frac.clear();
		TreeCoverOnPerviousCover_frac.clear();
		TreeCoverOnImperviousCover_frac.clear();
		PermeablePavementCover_frac.clear();

		//while loop continues executing as long as two conditions are met:
		//Note: Data read from the file stream object into a variable named "tempdata" using the stream extraction operator (>>).
		//Note: Variable count is less than the product of nRows and nCols representing map size; the number of data points read so far.
		while (readLandCover_map >> tempdata && count < Inputs::nRows * Inputs::nCols) {
			//If tempdata is not NODATA_code, then read NLCD Class value to LandCover_NLCD_Class, 
			//Note: Initialize other land cover fraction vectors with 0 value
			if (tempdata != Inputs::NODATA_code) {
				//If tempdata not equal to static_cast<int>(tempdata) then the real value is not equal to its integer value
				if (tempdata != static_cast<int>(tempdata)) {
					cout << "Warning: landcover.asc contains data with non-integer values." << endl;
					cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
					cout << "Explanation: When the landcover.asc file contains non-integer values, it breaks from structure." << endl;
					cout << "Correction: Create a landcover.asc file with integer values, minimum values of zero." << endl;
					//Call abort function, which ends the HydroPlus.exe simulation
					abort();
				}

				LandCover_NLCD_Class.push_back(tempdata);
				//TotalMapCover_frac defined as total TreeCover and ImperviousCover
				TotalMapCover_frac.push_back(0.0);
				SoilCoverNoTC_frac.push_back(0.0);
				WaterCoverNoTC_frac.push_back(0.0);
				ImperviousCoverNoTreeCover_frac.push_back(0.0);
				ShortVegCoverNoTC_frac.push_back(0.0);
				ConnectedImpervious.push_back(0.0);
				TreeEvergreen_frac.push_back(0.0);
				ShortVegEvergreen_frac.push_back(0.0);
				TreeCoverOnPerviousCover_frac.push_back(0.0);
				TreeCoverOnImperviousCover_frac.push_back(0.0);
				PermeablePavementCover_frac.push_back(0.0);
			}
			//If else NODATA_code is value, then assign NODATA_code
			else {
				LandCover_NLCD_Class.push_back(NODATA_code);
				TotalMapCover_frac.push_back(NODATA_code);
				SoilCoverNoTC_frac.push_back(NODATA_code);
				WaterCoverNoTC_frac.push_back(NODATA_code);
				ImperviousCoverNoTreeCover_frac.push_back(NODATA_code);
				ShortVegCoverNoTC_frac.push_back(NODATA_code);
				ConnectedImpervious.push_back(NODATA_code);
				TreeEvergreen_frac.push_back(NODATA_code);
				ShortVegEvergreen_frac.push_back(NODATA_code);
				TreeCoverOnPerviousCover_frac.push_back(NODATA_code);
				TreeCoverOnImperviousCover_frac.push_back(NODATA_code);
				PermeablePavementCover_frac.push_back(NODATA_code);
			}

			//if tempdata equals NLCD value 21, then increase LC21 scalar
			if (tempdata == 21) { LC21 += 1; }
			if (tempdata == 22) { LC22 += 1; }
			if (tempdata == 23) { LC23 += 1; }
			if (tempdata == 24) { LC24 += 1; }
			if (tempdata == 31) { LC31 += 1; }
			if (tempdata == 41) { LC41 += 1; }
			if (tempdata == 42) { LC42 += 1; }
			if (tempdata == 43) { LC43 += 1; }
			if (tempdata == 52) { LC52 += 1; }
			if (tempdata == 71) { LC71 += 1; }
			if (tempdata == 81) { LC81 += 1; }
			if (tempdata == 82) { LC82 += 1; }
			if (tempdata == 11) { LC11 += 1; }
			if (tempdata == 90) { LC90 += 1; }
			if (tempdata == 95) { LC95 += 1; }

			//count advanced by 1
			++count;
		}
		
		//if LandCover_NLCD_Class has size 0, then abort program
		//Note: This often happens when values are negative or the file has other non-numeric symbols
		if (LandCover_NLCD_Class.size() == 0 || LandCover_NLCD_Class.size() < nCols * nRows) {
			cout << "Warning: landcover.asc contains data with negative values or other non-numeric symbols." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: When the landcover.asc file contains non-numeric symbols, space-time becomes warped." << endl;
			cout << "Correction: Create a landcover.asc file with minimum values of zero, within the realm of reality." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
		readLandCover_map.close();

	}
	else if (readLandCover_table) {
		string LCtitle;//to save the row name
		double LCnum;//to save land cover number
		//read the header
		getline(readLandCover_table, buffer);
		while (readLandCover_table >> LCtitle) {
			readLandCover_table >> LCnum;
			if (LCtitle.find("21") != string::npos) {
				LC21 = LCnum;
			}
			if (LCtitle.find("22") != string::npos) {
				LC22 = LCnum;
			}
			if (LCtitle.find("23") != string::npos) {
				LC23 = LCnum;
			}
			if (LCtitle.find("24") != string::npos) {
				LC24 = LCnum;
			}
			if (LCtitle.find("31") != string::npos) {
				LC31 = LCnum;
			}
			if (LCtitle.find("41") != string::npos) {
				LC41 = LCnum;
			}
			if (LCtitle.find("42") != string::npos) {
				LC42 = LCnum;
			}
			if (LCtitle.find("43") != string::npos) {
				LC43 = LCnum;
			}
			if (LCtitle.find("52") != string::npos) {
				LC52 = LCnum;
			}
			if (LCtitle.find("71") != string::npos) {
				LC71 = LCnum;
			}
			if (LCtitle.find("81") != string::npos) {
				LC81 = LCnum;
			}
			if (LCtitle.find("82") != string::npos) {
				LC82 = LCnum;
			}
			//water area
			if (LCtitle.find("11") != string::npos) {
				LC11 = LCnum;
			}
			if (LCtitle.find("90") != string::npos) {
				LC90 = LCnum;
			}
			if (LCtitle.find("95") != string::npos) {
				LC95 = LCnum;
			}
		}
		readLandCover_table.close();		
	}
	cell_count = LC21 + LC22 + LC23 + LC24 + LC31 + LC41 + LC42 + LC43 + LC52 + LC71 + LC81 + LC82 + LC11 + LC90 + LC95;
	cell_count_Nowater = LC21 + LC22 + LC23 + LC24 + LC31 + LC41 + LC42 + LC43 + LC52 + LC71 + LC81 + LC82;
	// LC 31 Barren Land -> LC71 Grassland
	// LC 52 Shrub/Scrub ->LC 41 Deciduous Forest
	Cell_number.push_back(LC21);
	Cell_number.push_back(LC22);
	Cell_number.push_back(LC23);
	Cell_number.push_back(LC24);
	Cell_number.push_back(int(LC41 + LC52));
	Cell_number.push_back(LC42);
	Cell_number.push_back(LC43);
	Cell_number.push_back(int(LC71 + LC31));
	Cell_number.push_back(LC81);
	Cell_number.push_back(LC82);

	//return true
	return true;
}

//readTreeCoverFile function will read input map file of tree cover (percent), treecover.asc (%) 
void Inputs::readTreeCoverFile(string& Directory_Input_CLArg, double NODATA_code)
{
	ifstream readTreeCover(Directory_Input_CLArg + "treecover.asc");
	if (!readTreeCover) {
		cout << "Warning: treecover.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the treecover.asc file is missing there is no tree cover data to drive the simulation." << endl;
		cout << "Correction: Create a treecover.asc file, either manually or perhaps using the i-Tree Cool Air python utility." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readTreeCover >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("treecover.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
	
	double tempdata = 0.0;
	int count = 0;

	//while loop to read tree cover as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readTreeCover >> tempdata && count < nRows * nCols) {
		//If tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			//If tempdata < 0 then abort, as positive values needed
			if (tempdata < 0) {
				cout << "Warning: treecover.asc contains data with negative values." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the treecover.asc file contains non-numeric symbols, space-time becomes warped." << endl;
				cout << "Correction: Create a treecover.asc file with minimum values of zero, within the realm of reality." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//TreeCover_frac  (fraction) is converted from tempdata (percent) to decimal using Ratio_Decimal_to_Percent 
			TreeCover_frac.push_back(tempdata * Ratio_Decimal_to_Percent);
			//TreeCover_base_frac  (fraction) is converted from tempdata (percent) to decimal using Ratio_Decimal_to_Percent 
			TreeCover_base_frac.push_back(tempdata * Ratio_Decimal_to_Percent);
		}
		//else tempdata equals NODATA_code 
		else {
			//TreeCover_frac (%) is defined as NODATA_code
			TreeCover_frac.push_back(NODATA_code);
			//TreeCover_base_frac (%) is defined as NODATA_code
			TreeCover_base_frac.push_back(NODATA_code);
		}
		++count;
	}

	//if TreeCover_frac has size 0, then abort program
	//Note: This often happens when values are negative or the file has other non-numeric symbols
	if (TreeCover_frac.size() == 0 || TreeCover_frac.size() < nCols * nRows) {
		cout << "Warning: treecover.asc contains data with negative values or other non-numeric symbols." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the treecover.asc file contains non-numeric symbols, space-time becomes warped." << endl;
		cout << "Correction: Create a treecover.asc file with minimum values of zero, within the realm of reality." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	readTreeCover.close();
}

//readImperviousFile function will read input map file of impervious cover (percent), imperviouscover.asc (%) 
void Inputs::readImperviousFile(string& Directory_Input_CLArg, double NODATA_code)
{
	ifstream readImperviousFile(Directory_Input_CLArg + "imperviouscover.asc");
	if (!readImperviousFile) {
		cout << "Warning: imperviouscover.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the imperviouscover.asc file is missing there is no impervious cover data to drive the simulation." << endl;
		cout << "Correction: Create a imperviouscover.asc file, either manually or perhaps using the i-Tree Cool Air python utility." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readImperviousFile >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("imperviouscover.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
	
	double tempdata = 0.0;
	int count = 0;
	//imperviousArea_mapCells_count initialized to zero
	int imperviousArea_mapCells_count = 0;
	//imperviousArea_mapTotal_frac initialized to zero
	double imperviousArea_mapTotal_frac = 0;
	double imperviousArea_mapAverage_frac = 0;
	//ImperviousCover_Sum_frac initialized to 0; used to determine AH_Flux / IC_frac
	ImperviousCover_Sum_frac = 0;
	//while loop to read impervious cover as tempdata (percent) from readImperviousFile, while count is less than product of nCols and nRows
	while (readImperviousFile >> tempdata && count < nCols * nRows)	{
		//If tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			//If tempdata < 0 then abort, as positive values needed
			if (tempdata < 0) {
				cout << "Warning: imperviouscover.asc contains data with negative values." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the imperviouscover.asc file contains non-numeric symbols, space-time becomes warped." << endl;
				cout << "Correction: Create a imperviouscover.asc file with minimum values of zero, within the realm of reality." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}

			//ImperviousCover_frac (fraction) is converted from tempdata (percent) to decimal using Ratio_Decimal_to_Percent 
			ImperviousCover_frac.push_back(tempdata * Ratio_Decimal_to_Percent);
			PerCoverdata.push_back(1- tempdata * Ratio_Decimal_to_Percent);
			imperviousArea_mapCells_count = imperviousArea_mapCells_count + 1;
			//imperviousArea_mapTotal_frac (fraction) is summation for quotient of tempdata (percent) and 100.0
			imperviousArea_mapTotal_frac = imperviousArea_mapTotal_frac + tempdata * Ratio_Decimal_to_Percent;
			//ImperviousCover_Sum_frac summed to divide into AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 and Qcr and Qncr sums for AH_Flux / IC_frac
			ImperviousCover_Sum_frac = ImperviousCover_Sum_frac + tempdata * Ratio_Decimal_to_Percent;
		}
		//else tempdata equals NODATA_code 
		else {
			//ImperviousCover_frac (percent) is defined as NODATA_code
			ImperviousCover_frac.push_back(NODATA_code);
			PerCoverdata.push_back(NODATA_code);
		}
		++count;
	}

	//if ImperviousCover_frac has size 0, then abort program
	//Note: This often happens when values are negative or the file has other non-numeric symbols
	if (ImperviousCover_frac.size() == 0 || ImperviousCover_frac.size() < nCols * nRows) {
		cout << "Warning: imperviouscover.asc contains data with negative values or other non-numeric symbols." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the imperviouscover.asc file contains non-numeric symbols, space-time becomes warped." << endl;
		cout << "Correction: Create a imperviouscover.asc file with minimum values of zero, within the realm of reality." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	//imperviousArea_mapAverage_frac (fraction) = imperviousArea_mapTotal_frac / imperviousArea_mapCells_count
	//Note: HydroPlusConfig.xml Flag_TI_PerviousOnly=1 excludes impervious area in FlowAccumulation_Map ...
	//... keeping with TopUrban theory of Valeo and Moin (2000)
	imperviousArea_mapAverage_frac = imperviousArea_mapTotal_frac / imperviousArea_mapCells_count;
	//Area_Impervious_frac (fraction) is imperviousArea_mapAverage_frac, all impervious area in imperviouscover.asc
	SimulationLocationParams["Area_Impervious_frac"] = imperviousArea_mapAverage_frac;

	//close function to close readImperviousFile 
	readImperviousFile.close();
}

//readBlockGroupOutputFileMap function will create vector integer BlockGroup_ID holding blockgroup.asc values
void Inputs::readBlockGroupOutputFileMap(string& Directory_Input_CLArg, double NODATA_code)
{
	//Flag_BlockGroupIsLandCover used to control when blockgroup.asc missing
	ifstream readBlockGroupOutputFileMap(Directory_Input_CLArg + "blockgroup.asc");
	if (!readBlockGroupOutputFileMap) {
		//Note: input->Flag_PrintBlockGroupDailyAndHourly_str variable set to 0 when HydroPlusConfig.xml has no indication of reading Block Group data
		cout << "Notice: If simulating Block Groups, then place a blockgroup.asc file into the input folder." << endl;
		return;
	}

	string buffer;
	readBlockGroupOutputFileMap >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("blockgroup.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
	
	int tempdata;
	int count = 0;

	while (readBlockGroupOutputFileMap >> tempdata && count < nCols * nRows) {
		//If tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			//If tempdata not equal to static_cast<int>(tempdata) then the real value is not equal to its integer value
			if (tempdata != static_cast<int>(tempdata) || tempdata < 0) {
				cout << "Warning: blockgroup.asc contains data with non-integer or negative values." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the blockgroup.asc file contains non-integer or negative values, structure breaks." << endl;
				cout << "Correction: Create a blockgroup.asc file with integer values, minimum values of zero." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//BlockGroup_ID appended with tempdata
			BlockGroup_ID.push_back(tempdata);
		}
		//else tempdata equals NODATA_code 
		else {
			//BlockGroup_ID is defined as NODATA_code
			BlockGroup_ID.push_back(NODATA_code);
		}
		//advance count
		count = count + 1;
	}
	//if BlockGroup_ID has size 0 or size < nCols * nRows, then abort program
	//Note: This often happens when values are negative or the file has other non-numeric symbols
	if (BlockGroup_ID.size() == 0 || BlockGroup_ID.size() < nCols * nRows) {
		cout << "Warning: blockgroup.asc contains data that are non-integer or negative, or other non-numeric symbols." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the blockgroup.asc file contains such values, space-time becomes warped." << endl;
		cout << "Correction: Create a blockgroup.asc file with integer values, minimum values of zero." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	

	readBlockGroupOutputFileMap.close();
}

//Inputs::readAnthropogenicHeatMap bool function to read anthropogenic heat maps from AH4GUC for average annual hourly value (W/m2)
//Note: HydroPlusConfig.xml Flag_AH_Flux_Qcr_Qncr_not_Qtot determines if map input is AH_flux_Qtot_avg_Wpm2.asc or AH_flux_Qcr_avg_Wpm2.asc & AH_flux_Qncr_avg_Wpm2.asc
void Inputs::readAnthropogenicHeatMap(string& Directory_Input_CLArg, double NODATA_code)
{
	//If Flag_CoolAir_AnthropogenicHeat_Flux is not 1 or Flag_AH_Flux_Qcr_Qncr_not_Qtot is 1, then create AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 of 0
	//Note: Flag_AH_Flux_Qcr_Qncr_not_Qtot with Qcr is commercial residential AH flux, and Qncr is non commercial residential AH flux
	if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] != 1 || SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"] == 1) {
		//total_size defined as nCols * nRows
		int total_size = nCols * nRows;
		//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 sized to ImperviousCover_frac 
		//Note: Elevation_DEM_m has not yet be read so cannot be used
		AnthropogenicHeat_Flux_Qtot_Avg_Wpm2.resize(ImperviousCover_frac.size());
		//For size_t MapPixel_ID = 0; MapPixel_ID < ImperviousCover_frac.size(); ++MapPixel_ID assign the ImperviousCover_frac NODATA_code
		for (size_t MapPixel_ID = 0; MapPixel_ID < ImperviousCover_frac.size(); ++MapPixel_ID) {
			//If (ImperviousCover_frac[MapPixel_ID] == Inputs::NODATA_code) then assign to AnthropogenicHeat_Flux_Qtot_Avg_Wpm2
			if (ImperviousCover_frac[MapPixel_ID] == Inputs::NODATA_code) {
				//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2[MapPixel_ID] = Inputs::NODATA_code;
				AnthropogenicHeat_Flux_Qtot_Avg_Wpm2[MapPixel_ID] = Inputs::NODATA_code;
			}
			//Else assign 0.0
			else {
				AnthropogenicHeat_Flux_Qtot_Avg_Wpm2[MapPixel_ID] = 0.0;
			}
		}
		//If Flag_CoolAir_AnthropogenicHeat_Flux is not 1 then exit function
		//Note: If Flag_CoolAir_AnthropogenicHeat_Flux not 1, then no need for continuing and reading .asc files below
		if (SimulationNumericalParams["Flag_CoolAir_AnthropogenicHeat_Flux"] != 1) {
			//return true exits the function
			return;
		}
	}

	//files_to_read is defined as vector pair with string and vector double, as file names and vector of values based on Flag_AH_Flux_Qcr_Qncr_not_Qtot
	vector<pair<string, vector<double>&>> files_to_read;
	//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str, AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str, etc. defined for use in MapResample.cpp
	AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str = "AH_flux_Qtot_avg_Wpm2.asc";
	AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str = "AH_flux_Qcr_avg_Wpm2.asc";
	AnthropogenicHeat_Flux_Qncr_Avg_Wpm2_str = "AH_flux_Qncr_avg_Wpm2.asc";
	//If Flag_AH_Flux_Qcr_Qncr_not_Qtot is 1 then files_to_read will contain two file names and their associated vector of values
	//Note: Flag_AH_Flux_Qcr_Qncr_not_Qtot with Qcr is commercial residential AH flux, and Qncr is non commercial residential AH flux
	//Note: AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 = AnthropogenicHeat_Flux_Qcr_Avg_Wpm2 + AnthropogenicHeat_Flux_Qncr_Avg_Wpm2
	//Note: AnthropogenicHeat_Flux_Qcr_Avg_Wpm2 is commercial residential, and AnthropogenicHeat_Flux_Qncr_Avg_Wpm2 is non-commercial-residential
	if (SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"] == 1) {
		//files_to_read receives AH_flux_Qcr_avg_Wpm2.asc and AnthropogenicHeat_Flux_Qcr_Avg_Wpm2; Qcr is commercial residential
		files_to_read.push_back({ AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str, AnthropogenicHeat_Flux_Qcr_Avg_Wpm2 });
		//files_to_read receives AH_flux_Qncr_avg_Wpm2.asc and AnthropogenicHeat_Flux_Qncr_Avg_Wpm2; Qncr is non commercial residential
		files_to_read.push_back({ AnthropogenicHeat_Flux_Qncr_Avg_Wpm2_str, AnthropogenicHeat_Flux_Qncr_Avg_Wpm2 });
	}
	//Else if Flag_AH_Flux_Qcr_Qncr_not_Qtot is not 1 then files_to_read will contain one file name and its associated vector of values
	else {
		//files_to_read receives AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str and AnthropogenicHeat_Flux_Qtot_Avg_Wpm2; Qtot is total
		files_to_read.push_back({ AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str, AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 });
	}

	//AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2, Qcr (commercial residential) and Qncr initialized sum values to zero
	AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 = 0;
	AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2 = 0;
	AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2 = 0;

	//For loop of file_ahe_input in files_to_read vector
	for (auto& file_ahe_input : files_to_read) {
		//file_path is Directory_Input_CLArg + file_ahe_input.first
		string file_path = Directory_Input_CLArg + file_ahe_input.first;
		//ahe_target_vector_Wpm2 is file_ahe_input.second, AnthropogenicHeat_Flux_Qtot_Avg_Wpm2 or AnthropogenicHeat_Flux_Qcr_Avg_Wpm2 or AnthropogenicHeat_Flux_Qncr_Avg_Wpm2
		vector<double>& ahe_target_vector_Wpm2 = file_ahe_input.second;
		//readAnthropogenicHeatMapFile obtains file_path with ifstream function
		ifstream readAnthropogenicHeatMapFile(file_path);

		//if not readAnthropogenicHeatMapFile then abort
		if (!readAnthropogenicHeatMapFile) {
			cout << "Warning: " << file_ahe_input.first << " could not be opened." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: When the file is missing there is no anthropogenic heating data to drive the simulation." << endl;
			cout << "Correction: Create a new file, either manually or perhaps using the i-Tree Cool Air python utility, or ..." << endl;
			cout << "Correction: ... set HydroPlusConfig.xml element Flag_CoolAir_AnthropogenicHeat_Flux = 0 to turn off this option, or ..." << endl;
			cout << "Correction: ... set HydroPlusConfig.xml element Flag_AH_Flux_Qcr_Qncr_not_Qtot = 0 to turn off this input option." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}

		//buffer initialized as string for reading header names in .asc file, variables preceding values
		string buffer;
		//nCols, nRows initialized as int
		int nCols, nRows;
		//file_xllcorner, file_yllcorner, file_cellsize, file_NODATA initialized as double
		double file_xllcorner, file_yllcorner, file_cellsize, file_NODATA;
		//readAnthropogenicHeatMapFile sent to buffer, nCols, 
		readAnthropogenicHeatMapFile >> buffer >> nCols
			>> buffer >> nRows
			>> buffer >> file_xllcorner
			>> buffer >> file_yllcorner
			>> buffer >> file_cellsize
			>> buffer >> file_NODATA;
		//Check_Map_Header called to compare with dem.asc header
		Check_Map_Header(file_ahe_input.first, nCols, nRows, file_NODATA, file_cellsize, file_xllcorner, file_yllcorner);

		//tempdata initialized as double
		double tempdata;
		//count initialized as int
		int count = 0;
		//While loop through values in readAnthropogenicHeatMapFile, assigned to tempdata, condition count < nCols * nRows
		while (readAnthropogenicHeatMapFile >> tempdata && count < nCols * nRows) {
			//If tempdata not file_NODATA then check if it is valid and assign to vector variable
			if (tempdata != file_NODATA) {
				//If tempdata < 0 it is not valid
				if (tempdata < 0) {
					cout << "Warning: " << file_ahe_input.first << " contains data with negative values." << endl;
					cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
					cout << "Explanation: When the file contains non-numeric symbols, space-time becomes warped." << endl;
					cout << "Correction: Create a new file with minimum values of zero, within the realm of reality." << endl;
					//Call abort function, which ends the HydroPlus.exe simulation
					abort();
				}
				//ahe_target_vector_Wpm2 uses push_back to receive tempdata
				ahe_target_vector_Wpm2.push_back(tempdata);
				//If file_ahe_input.first == AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str then sum the AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2
				if (file_ahe_input.first == AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str) {
					AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 = AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 + tempdata;
				}
				//Else If file_ahe_input.first == AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str then sum the AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2
				else if (file_ahe_input.first == AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str) {
					AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2 = AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2 + tempdata;
				}
				//Else If file_ahe_input.first == AnthropogenicHeat_Flux_Qncr_Avg_Wpm2_str then sum the AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2
				else if (file_ahe_input.first == AnthropogenicHeat_Flux_Qncr_Avg_Wpm2_str) {
					AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2 = AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2 + tempdata;
				}
			}
			//Else tempdata is file_NODATA
			else {
				//ahe_target_vector_Wpm2 uses push_back to receive NODATA_code
				ahe_target_vector_Wpm2.push_back(NODATA_code);
			}
			//count incremented
			++count;
		}

		//If ahe_target_vector_Wpm2.size 0 or < nCols * nRows file is not complete
		if (ahe_target_vector_Wpm2.size() == 0 || ahe_target_vector_Wpm2.size() < nCols * nRows) {
			cout << "Warning: " << file_ahe_input.first << " has too few values for input map size nCols and nRows." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: When the file must be the same size as the other input maps." << endl;
			cout << "Correction: Create a new file with the same number of inputs as the other maps." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
		//readAnthropogenicHeatMapFile closed
		readAnthropogenicHeatMapFile.close();
	}

	//If Flag_AH_Flux_Qcr_Qncr_not_Qtot is 1 then define AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 as sum of max Qcr and Qncr
	//Note: Flag_AH_Flux_Qcr_Qncr_not_Qtot with Qcr is commercial residential AH flux, and Qncr is non commercial residential AH flux
	if (SimulationNumericalParams["Flag_AH_Flux_Qcr_Qncr_not_Qtot"] == 1) {
		AnthropogenicHeat_Flux_Qtot_Map_Sum_Wpm2 = AnthropogenicHeat_Flux_Qcr_Map_Sum_Wpm2 + AnthropogenicHeat_Flux_Qncr_Map_Sum_Wpm2; 
	}
}

//Inputs::readHydroPlusConfigVariablesAsMap bool function to read HydroPlus variable maps that are optional
void Inputs::readHydroPlusConfigVariablesAsMap(string& Directory_Input_CLArg, double NODATA_code, string Variable_name, vector<double> &Variable_map)
{
	//total_size defined as nCols * nRows
	int total_size = nCols * nRows;
	//Variable_map sized to total_size with 0.0 values
	Variable_map.assign(total_size, 0.0);

	//files_to_read is defined as vector pair with string and vector double
	vector<pair<string, vector<double>&>> files_to_read;
	//AnthropogenicHeat_Flux_Qtot_Avg_Wpm2_str, AnthropogenicHeat_Flux_Qcr_Avg_Wpm2_str, etc. defined for use in MapResample.cpp
	string map_Variable_str = Variable_name;

	//file_path is Directory_Input_CLArg + Variable_name
	string file_path = Directory_Input_CLArg + Variable_name;
	//readHydroPlusConfigMapFile obtains file_path with ifstream function
	ifstream readHydroPlusConfigMapFile(file_path);

	//if not readHydroPlusConfigMapFile then abort
	if (!readHydroPlusConfigMapFile) {
		cout << "Warning: " << Variable_name << " could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the file is missing there is no map data to drive the simulation." << endl;
		cout << "Correction: Create a new map or change the HydroPlusConfig.xml settings." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	//buffer initialized as string for reading header names in .asc file, variables preceding values
	string buffer;
	//nCols, nRows initialized as int
	int nCols, nRows;
	//file_xllcorner, file_yllcorner, file_cellsize, file_NODATA initialized as double
	double file_xllcorner, file_yllcorner, file_cellsize, file_NODATA;
	//readHydroPlusConfigMapFile sent to buffer, nCols, 
	readHydroPlusConfigMapFile >> buffer >> nCols
		>> buffer >> nRows
		>> buffer >> file_xllcorner
		>> buffer >> file_yllcorner
		>> buffer >> file_cellsize
		>> buffer >> file_NODATA;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header(Variable_name, nCols, nRows, file_NODATA, file_cellsize, file_xllcorner, file_yllcorner);

	//tempdata initialized as double
	double tempdata;
	//count initialized as int
	int count = 0;
	//While loop through values in readHydroPlusConfigMapFile, assigned to tempdata, condition count < nCols * nRows
	while (readHydroPlusConfigMapFile >> tempdata && count < nCols * nRows) {
		//If tempdata not file_NODATA then check if it is valid and assign to vector variable
		if (tempdata != file_NODATA) {
			//If tempdata < 0 it is not valid
			if (tempdata < 0) {
				cout << "Warning: " << Variable_name << " contains data with negative values." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the file contains non-numeric symbols, space-time becomes warped." << endl;
				cout << "Correction: Create a new file with minimum values of zero, within the realm of reality." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//Variable_map uses push_back to receive tempdata
			Variable_map[count] = tempdata;
		}
		//Else tempdata is file_NODATA
		else {
			//Variable_map uses push_back to receive NODATA_code
			Variable_map[count] = NODATA_code;
		}
		//count advanced by 1
		count++;
	}

	//If irrigate_area_vector_frac.size 0 or < nCols * nRows file is not complete
	if (Variable_map.size() == 0 || Variable_map.size() < nCols * nRows) {
		cout << "Warning: " << Variable_name << " has too few values for input map size nCols and nRows." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the file must be the same size as the other input maps." << endl;
		cout << "Correction: Create a new file with the same number of inputs as the other maps." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	//readHydroPlusConfigMapFile closed
	readHydroPlusConfigMapFile.close();
}

//LandCoverAssignment_by_NLCD_Class function for Model = SpatialTemperatureHydro uses NLCD Class values to determine land cover fractions
//Note: NumberOfLocations will be different than mapPixel_Count (i.e., nCols * nRows) when ReferenceStationOutsideMap is true
//Note: Land Cover vectors are passed to BuildDataOrganizer::Folder_CoverData_Spatial 
void Inputs::LandCoverAssignment_by_NLCD_Class(int NumberOfLocations, double NODATA_code)
{
	//HydroPlusConfig.xml DataFolder elements provide values for TreeEvergreen_frac, ShortVegEvergreen_frac, ConnectedImpervious
	double DCIA_frac = InputXml["DirectlyConnectedImperviousArea_frac"];
	double EvergreenTreeCover_frac = InputXml["TreeCover_Evergreen_frac"];
	double EverGreenShrubCover_frac = InputXml["SVegCover_Evergreen_frac"];

	//LoadScenarioParametersFromSimulationScenarios function checks HydroPlusConfig.xml for scenario data
	Inputs::LoadScenarioParametersFromSimulationScenarios();

	//TreeCanopyCover_overImpervious_frac (frac) is defined from HydroPlusConfig.xml element TreeCanopyCover_overImpervious_frac
	//Note: TreeCanopyCover_overImpervious_frac in Model = SpatialTemperatureHydro, e.g., CoolAir, is fraction of total tree cover
	//Note: TreeCanopyCover_overImpervious_frac in Model = StatisticalHydro, e.g., Hydro, is fraction of total land cover
	double TreeCanopyCover_overImpervious_frac = InputXml["TreeCanopyCover_overImpervious_frac"];
	//Clamp to ensure variable is between 0 and 1 
	TreeCanopyCover_overImpervious_frac = clamp(TreeCanopyCover_overImpervious_frac, 0.0, 1.0);

	//If Flag_Scenario_CoolAir_LandCover > 1 for alternative, and Scenario_CoolAir_Base_TCI_to_TC_frac undefined then set to base case value
	//Note: Scenario_CoolAir_Base_TCI_to_TC_frac initialized to -1 in Inputs.h for use in this conditional
	if (Flag_Scenario_CoolAir_LandCover > 1 && Scenario_CoolAir_Base_TCI_to_TC_frac < 0) {
		Scenario_CoolAir_Base_TCI_to_TC_frac = TreeCanopyCover_overImpervious_frac;
	}		

	//PerviousCover_FullSkyView_frac (fraction) defined as area not within TotalMapCover_frac (tree cover and impervious cover)
	double PerviousCover_FullSkyView_frac = 0;
	//PerviousCover_FullSkyView_with_SVeg_frac (fraction) is defined as the fraction of PerviousCover_FullSkyView_frac in short vegetation
	//Note: PerviousCover_FullSkyView_with_SVeg_frac value defined within each NLCD Class using PerviousCover_FullSkyView_frac
	//Note: ShortVegCoverNoTC_frac (frac) = PerviousCover_FullSkyView_frac * SVPerviousCover_FullSkyView_with_SVeg_frac
	//Note: For example, if NLCD Class 21-24, then SVPerviousCover_FullSkyView_with_SVeg_frac = 0.5
	double PerviousCover_FullSkyView_with_SVeg_frac = 0;

	//For loop for MapPixel_ID to NumberOfLocations
	//Note: NumberOfLocations will be different than mapPixel_Count (MapPixel_ID.e., nCols * nRows) when ReferenceStationOutsideMap is true
	for (int MapPixel_ID = 0; MapPixel_ID < NumberOfLocations; MapPixel_ID++)	{

		//If (TreeCover_frac[MapPixel_ID] == NODATA_code) then continue to next item in loop
		if (TreeCover_frac[MapPixel_ID] == NODATA_code) {
			continue;
		}

		//Flag_Use_ReferenceStationFolder is true if Flag_InputXml_ReferenceStationFolder && isReferenceStationPixel(MapPixel_ID)
		//Note: Flag_InputXml_ReferenceStationFolder indicates HydroPlusConfig.xml contains DataFolder distinct for reference station
		bool Flag_Use_ReferenceStationFolder = (Flag_InputXml_ReferenceStationFolder && isReferenceStationPixel(MapPixel_ID));
		//InputXml defined with ternary using Flag_InputXml_ReferenceStationFolder, InputXml_ReferenceStationFolder if true, else InputXml_StandardFolder
		//Note: InputXml replaces InputXml_StandardFolder and InputXml_ReferenceStationFolder 
		InputXml = Flag_Use_ReferenceStationFolder
			? InputXml_ReferenceStationFolder
			: InputXml_StandardFolder;

		//SVeg_in_Gap_DevelopedArea_frac is SVeg_in_Gap_DevelopedArea_frac; assignment from Default_DrawerID_FolderID in HydroPlusConfig.xml, or ...
		//Note: ... if unique RefWeather_DrawerID_FolderID exists in HydroPlusconfig.xml then InputXml points to distinct SVeg_in_Gap_DevelopedArea_frac
		double SVeg_in_Gap_DevelopedArea_frac = InputXml["SVeg_in_Gap_DevelopedArea_frac"];
		//If isReferenceStationPixel(MapPixel_ID) and SimulationNumericalParams["Flag_MultipleStations"] are true then enter to reassign
		if (isReferenceStationPixel(MapPixel_ID) && SimulationNumericalParams["Flag_MultipleStations"] == 1) {
			//ObjectID_index = MapPixel_ID - (nCols * nRows)
			//Note: In this condition, the MapPixel_ID is greater than nCols * nRows, and ObjectID indices were appended
			int ObjectID_index = MapPixel_ID - (nCols * nRows);
			//SVeg_in_Gap_DevelopedArea_frac is defined from Station_SVeg_in_Gap_frac_vec[ObjectID_index]
			SVeg_in_Gap_DevelopedArea_frac = Station_SVeg_in_Gap_frac_vec[ObjectID_index];
		}

		//TreeCover_base_frac equals TreeCover_frac to establish base case scenario
		TreeCover_base_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
		double ImperCover_base_frac = 0;
		double TotalCover_base_frac = 0;
		double TreeOnPervious_max_frac = 0;
		//Flag_Scenario_TC_Max_Active initialized to false
		bool Flag_Scenario_TC_Max_Active = false;

		//If map_Permeable_Area_of_IC_frac.size() > 0 then redefine ImperviousCover_frac
		if (map_Permeable_Area_of_IC_frac.size() > 0) {

			//map_Permeable_Area_of_IC_frac[MapPixel_ID] = clamp(map_Permeable_Area_of_IC_frac[MapPixel_ID], 0.0, 1.0)
			//Note: Consider refactor to notify user if map not within proper range
			map_Permeable_Area_of_IC_frac[MapPixel_ID] = clamp(map_Permeable_Area_of_IC_frac[MapPixel_ID], 0.0, 1.0);

			//PermeablePavementCover_frac (frac) = map_Permeable_Area_of_IC_frac * ImperviousCover_frac
			//Note: If map_Permeable_Area_of_IC_frac > 0 and ImperviousCover_frac is reduced, TotalMapCover_frac is unchanged bc ...
			//Note: ... TreeCover_frac + ImperviousCover_frac = TreeCover_frac + ImperviousCover_frac + PermeablePavementCover_frac ...
			//Note: ... when PermeablePavementCover_frac replaced ImperviousCover_frac based on map_Permeable_Area_of_IC_frac 
			PermeablePavementCover_frac[MapPixel_ID] = map_Permeable_Area_of_IC_frac[MapPixel_ID] * ImperviousCover_frac[MapPixel_ID];

			//ImperviousCover_frac (frac) = ImperviousCover_frac - map_Permeable_Area_of_IC_frac * ImperviousCover_frac
			ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] - map_Permeable_Area_of_IC_frac[MapPixel_ID] * ImperviousCover_frac[MapPixel_ID];
		}

		//If LandCover_NLCD_Class Class > 20 and < 25 AND  &&  !isReferenceStationPixel(MapPixel_ID) then enter 
		//Note: This is for non-reference station urban areas ...
		if (LandCover_NLCD_Class[MapPixel_ID] > 20 && LandCover_NLCD_Class[MapPixel_ID] < 25 && !isReferenceStationPixel(MapPixel_ID)) {

			//ImperCover_base_frac (frac) equals ImperviousCover_frac (frac)
			ImperCover_base_frac = ImperviousCover_frac[MapPixel_ID];
			//TotalCover_base_frac (frac) equals TreeCover_base_frac (frac) + ImperCover_base_frac (frac)
			TotalCover_base_frac = TreeCover_base_frac[MapPixel_ID] + ImperCover_base_frac;
			//TreeOnPervious_max_frac (frac) = 1.0 - ImperCover_base_frac (frac)
			//Note: TreeOnPervious_max_frac used to nudge new tree cover over impervious cover, which can become permeable pavement
			TreeOnPervious_max_frac = 1.0 - ImperCover_base_frac;

			//If Flag_Scenario_CoolAir_LandCover > 1 then implement scenarios for land cover change
			//Note: Scenarios are described above where Flag_Scenario_CoolAir_LandCover is read from HydroPlusConfig.xml
			if (Flag_Scenario_CoolAir_LandCover > 1) {

				//If Scenario_CoolAir_Alternative_TC_Change_frac not equal to 0.0 then enter
				//Note: Implement Scenario_CoolAir_Alternative_TC_Change_frac prior to Scenario_CoolAir_Alternative_TC_Max_frac & _Min_Frac
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Change_frac") &&
					Scenario_CoolAir_Alternative_TC_Change_frac != 0.0) {
					//TC_change_frac = Scenario_CoolAir_Alternative_TC_Change_frac; assume Absolute change
					double TC_change_frac = Scenario_CoolAir_Alternative_TC_Change_frac;
					//If Scenario_CoolAir_Alternative_Change_type == 1 then recompute based on Relative change
					if (Scenario_CoolAir_Alternative_Change_type == 1) {
						//TC_change_frac = TreeCover_frac[MapPixel_ID] * Scenario_CoolAir_Alternative_TC_Change_frac; Relative change
						TC_change_frac = TreeCover_frac[MapPixel_ID] * Scenario_CoolAir_Alternative_TC_Change_frac;
					}
					//TreeCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID] + TC_change_frac
					TreeCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID] + TC_change_frac;
					//Clamp to ensure variable is between 0 and 1 
					TreeCover_frac[MapPixel_ID] = clamp(TreeCover_frac[MapPixel_ID], 0.0, 1.0);
					//If TC_change_frac < 0 then removing tree cover and updating impervious cover similar to Scenario_CoolAir_Alternative_TC_Max_frac
					if (TC_change_frac < 0) {
						//Flag_Scenario_TC_Max_Active set to true
						Flag_Scenario_TC_Max_Active = true;
						//IC_increased_frac = TC_change_frac * Scenario_CoolAir_Alternative_IC_replacing_TC_frac
						//Note: Scenario_CoolAir_Alternative_IC_replacing_TC_frac allows impervious cover to replace lost tree cover
						double IC_increased_frac = TC_change_frac * Scenario_CoolAir_Alternative_IC_replacing_TC_frac;
						//ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_increased_frac
						ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_increased_frac;
						//Clamp to ensure variable is between 0 and 1 
						ImperviousCover_frac[MapPixel_ID] = clamp(ImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
					}
				}
				//If Scenario_CoolAir_Alternative_IC_Change_frac not equal to 0.0 then enter
				//Note: Implement Scenario_CoolAir_Alternative_IC_Change_frac prior to Scenario_CoolAir_Alternative_IC_Max_frac & _Min_Frac
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Change_frac") &&
					Scenario_CoolAir_Alternative_IC_Change_frac != 0.0) {
					//IC_change_frac = Scenario_CoolAir_Alternative_IC_Change_frac; assume Absolute change
					double IC_change_frac = Scenario_CoolAir_Alternative_IC_Change_frac;
					//If Scenario_CoolAir_Alternative_Change_type == 1 then recompute based on Relative change
					if (Scenario_CoolAir_Alternative_Change_type == 1) {
						//IC_change_frac = ImperviousCover_frac[MapPixel_ID] * Scenario_CoolAir_Alternative_IC_Change_frac; Relative change
						IC_change_frac = ImperviousCover_frac[MapPixel_ID] * Scenario_CoolAir_Alternative_IC_Change_frac;
					}
					//ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_change_frac
					ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_change_frac;
					//Clamp to ensure variable is between 0 and 1 
					ImperviousCover_frac[MapPixel_ID] = clamp(ImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
				}
				//If TreeCover_frac[MapPixel_ID] > Scenario_CoolAir_Alternative_TC_Max_frac then enter
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Max_frac") &&
					TreeCover_frac[MapPixel_ID] > Scenario_CoolAir_Alternative_TC_Max_frac) {
					//Flag_Scenario_TC_Max_Active set to true
					Flag_Scenario_TC_Max_Active = true;
					//TC_decreased_frac = TreeCover_frac[MapPixel_ID] - Scenario_CoolAir_Alternative_TC_Max_frac
					//Note: TC_decreased_frac is default replaced SVeg and Soil based on SVeg_in_Gap_DevelopedArea_frac
					double TC_decreased_frac = TreeCover_frac[MapPixel_ID] - Scenario_CoolAir_Alternative_TC_Max_frac;
					//TreeCover_frac[MapPixel_ID] = Scenario_CoolAir_Alternative_TC_Max_frac is reduced
					TreeCover_frac[MapPixel_ID] = Scenario_CoolAir_Alternative_TC_Max_frac;
					//IC_increased_frac = TC_decreased_frac * Scenario_CoolAir_Alternative_IC_replacing_TC_frac 
					//Note: Scenario_CoolAir_Alternative_IC_replacing_TC_frac allows impervious cover to replace lost tree cover
					double IC_increased_frac = TC_decreased_frac * Scenario_CoolAir_Alternative_IC_replacing_TC_frac;
					//ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_increased_frac
					ImperviousCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] + IC_increased_frac;
					//Clamp to ensure variable is between 0 and 1 
					ImperviousCover_frac[MapPixel_ID] = clamp(ImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
				}
				//If TreeCover_frac (frac) < Scenario_CoolAir_Alternative_TC_Min_frac (frac) then use larger value 
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Min_frac") &&
					TreeCover_frac[MapPixel_ID] < Scenario_CoolAir_Alternative_TC_Min_frac) {
					//TreeCover_frac (frac) equals Scenario_CoolAir_Alternative_TC_Min_frac (frac) as the larger value
					TreeCover_frac[MapPixel_ID] = Scenario_CoolAir_Alternative_TC_Min_frac;
					//Clamp to ensure variable is between 0 and 1 
					TreeCover_frac[MapPixel_ID] = clamp(TreeCover_frac[MapPixel_ID], 0.0, 1.0); 
				}
				//If ImperviousCover_frac (frac) > Scenario_CoolAir_Alternative_IC_Max_frac (frac) then use smaller value 
				//Note: Scenario_CoolAir_Alternative_IC_Max_frac (frac) is maximum allowable IC and defaults to 1.0
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Max_frac") &&
					ImperviousCover_frac[MapPixel_ID] > Scenario_CoolAir_Alternative_IC_Max_frac) {
					//ImperviousCover_frac (frac) equals Scenario_CoolAir_Alternative_IC_Max_frac (frac) as the smaller value
					ImperviousCover_frac[MapPixel_ID] = Scenario_CoolAir_Alternative_IC_Max_frac;
					//Clamp to ensure variable is between 0 and 1 
					ImperviousCover_frac[MapPixel_ID] = clamp(ImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
				}
				//If ImperviousCover_frac (frac) < Scenario_CoolAir_Alternative_IC_Min_frac (frac) then use larger value 
				//Note: Scenario_CoolAir_Alternative_IC_Min_frac (frac) is minimum allowable IC and defaults to 0.0
				if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Min_frac") &&
					ImperviousCover_frac[MapPixel_ID] < Scenario_CoolAir_Alternative_IC_Min_frac) {
					//ImperviousCover_frac (frac) equals Scenario_CoolAir_Alternative_IC_min_frac (frac) as the larger value
					ImperviousCover_frac[MapPixel_ID] = Scenario_CoolAir_Alternative_IC_Min_frac;
					//Clamp to ensure variable is between 0 and 1 
					ImperviousCover_frac[MapPixel_ID] = clamp(ImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
				}
			}
		}

		//TotalMapCover_frac (fraction) is TreeCover_frac + ImperviousCover_frac + PermeablePavementCover_frac 
		//Note: If map_Permeable_Area_of_IC_frac > 0 then ImperviousCover_frac is converted to PermeablePavementCover_frac ...
		TotalMapCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID] + ImperviousCover_frac[MapPixel_ID] + PermeablePavementCover_frac[MapPixel_ID];

		//Note: TreeEvergreen_frac and ShortVegEvergreen_frac not currently used by SpatialTemperatureHydro, could be used to set LAI_BAI_Tree_min_m2pm2, minimum Tree LAI
		TreeEvergreen_frac[MapPixel_ID] = EvergreenTreeCover_frac;
		ShortVegEvergreen_frac[MapPixel_ID] = EverGreenShrubCover_frac;
		//ConnectedImpervious (frac) is used in Folder_CoverData_Spatial function to create ImperviousCover_DrainsToOutlet_frac to partition runoff
		ConnectedImpervious[MapPixel_ID] = DCIA_frac;
		//If NLCD Class within 20 to 25 range then within developed pixels
		//Note: Class 21 = Developed open space, 22 = Developed low intensity, 23 = Developed medium intensity, 24 = Developed high intensity
		if (LandCover_NLCD_Class[MapPixel_ID] > 20 && LandCover_NLCD_Class[MapPixel_ID] < 25) {
			//WaterCoverNoTC_frac (fraction) is zero given NLCD class is terrestrial
			WaterCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) = SVeg_in_Gap_DevelopedArea_frac (frac) 
			//Note: PerviousCover_FullSkyView_with_SVeg_frac is HydroPlusConfig.xml element SVeg_in_Gap_DevelopedArea_frac (frac); typically 0.5
			//Note: PerviousCover_FullSkyView_with_SVeg_frac for non-urban defined below for each NLCD Class based on sub-grid heterogeneity
			PerviousCover_FullSkyView_with_SVeg_frac = SVeg_in_Gap_DevelopedArea_frac;
			//If PerviousCover_FullSkyView_with_SVeg_frac <= 0 then redefine to 0; HydroPlusConfig.xml missing SVeg_in_Gap_DevelopedArea_frac
			if (PerviousCover_FullSkyView_with_SVeg_frac <= 0) { PerviousCover_FullSkyView_with_SVeg_frac = 0; }
			//PerviousCover_FullSkyView_frac (frac) = 1 - TotalMapCover_frac (frac); area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//Clamp to ensure variable is between 0 and 1 
			PerviousCover_FullSkyView_frac = clamp(PerviousCover_FullSkyView_frac, 0.0, 1.0);
			//SoilCoverNoTC_frac (frac) = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			SoilCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//If TotalMapCover_frac (frac) > 1.0 then initially assign TreeCoverOnImperviousCover_frac (frac) = TotalMapCover_frac (frac) - 1.0
			if (TotalMapCover_frac[MapPixel_ID] > 1.0) {
				//TreeCoverOnImperviousCover_frac (frac) is tree cover above impervious area, defined as all cover greater than 1.0
				//Note: For Example: If 1.2 TotalMapCover_frac = 0.6 TC + 0.6 IC, then 0.2 TreeCoverOnImperviousCover_frac = 1.2 - 1.0
				TreeCoverOnImperviousCover_frac[MapPixel_ID] = TotalMapCover_frac[MapPixel_ID] - 1.0;
				//TreeCoverOnPerviousCover_frac (frac) is tree cover above pervious, defined as that which is not above impervious
				//Note: For Example: 0.4 TreeCoverOnPerviousCover_frac = 0.6 TC - 0.2 TreeCoverOnImperviousCover_frac 
				TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID] - TreeCoverOnImperviousCover_frac[MapPixel_ID];
				//ImperviousCoverNoTreeCover_frac (frac) is impervious cover with full sky view, defined as all impervious cover without trees 
				//Note: For Example: 0.4 ImperviousCoverNoTreeCover_frac = 0.6 ImperviousCover_frac - 0.2 TreeCoverOnImperviousCover_frac 
				ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID] - TreeCoverOnImperviousCover_frac[MapPixel_ID];
			}
			//Else If TotalMapCover_frac (frac) <= 1.0 then initially assign TreeCoverOnImperviousCover_frac (frac) = 0
			else {
				//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
				TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
				//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
				TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
				//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
				ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
			}

			//If !isReferenceStationPixel(MapPixel_ID) then not reference station and enter
			//Note: Reclassification of tree cover onto impervious cover is not allowed at the reference station
			if (!isReferenceStationPixel(MapPixel_ID)) {

				//tci_actual_frac (frac) = TreeCover_base_frac (frac) * TreeCanopyCover_overImpervious_frac (frac)
				double tci_actual_frac = TreeCover_base_frac[MapPixel_ID] * TreeCanopyCover_overImpervious_frac;
				//If Flag_Scenario_CoolAir_LandCover >= 2 then 
				if (Flag_Scenario_CoolAir_LandCover >= 2) {
					//tci_actual_frac (frac) = TreeCover_base_frac (frac) * Scenario_CoolAir_Base_TCI_to_TC_frac (frac)
					tci_actual_frac = TreeCover_base_frac[MapPixel_ID] * Scenario_CoolAir_Base_TCI_to_TC_frac;
				}
				//tcp_actual_frac (frac) = TreeCover_base_frac (frac) - tci_actual_frac (frac)
				double tcp_actual_frac = TreeCover_base_frac[MapPixel_ID] - tci_actual_frac;
				//ic_no_tc_actual_frac (frac) = 0 is impervious cover with no tree cover above
				double ic_no_tc_actual_frac = 0;
				//tci_target_frac (frac) = 0 is tree cover over impervious cover
				double tci_target_frac = 0;
				//tcp_target_frac (frac) = 0 is tc over pervious cover
				double tcp_target_frac = 0;
				//ic_no_tc_target_frac (frac) = 0 is impervious cover with no tree cover above
				double ic_no_tc_target_frac = 0;

				//If tcp_actual_frac (frac) > TreeOnPervious_max_frac (frac) then 
				if (tcp_actual_frac > TreeOnPervious_max_frac) {
					//tcp_actual_frac (frac) = TreeOnPervious_max_frac (frac)
					tcp_actual_frac = TreeOnPervious_max_frac;
					//tci_actual_frac (frac) = TreeCover_base_frac (frac) - tcp_actual_frac (frac)
					tci_actual_frac = TreeCover_base_frac[MapPixel_ID] - tcp_actual_frac;
				}
				//Clamp to ensure variable is between 0 and 1 
				tci_actual_frac = clamp(tci_actual_frac, 0.0, 1.0);
				//If (ImperviousCover_frac (frac) + tci_actual_frac (frac) + tcp_actual_frac (frac)) > 1.0 then
				//Note: Conditional indicates IC_no_TC + TCI + TCP > 1, therefor IC_no_TC is reduced, becoming TCI
				if ((ImperviousCover_frac[MapPixel_ID] + tci_actual_frac + tcp_actual_frac) > 1.0) {
					//ic_no_tc_actual_frac (frac) = 1.0 - (tci_actual_frac (frac) + tcp_actual_frac (frac))
					ic_no_tc_actual_frac = 1.0 - (tci_actual_frac + tcp_actual_frac);
				}
				//Else If Flag_Scenario_CoolAir_LandCover = 0 or Flag_Scenario_CoolAir_LandCover = 2 then 
				else if (Flag_Scenario_CoolAir_LandCover == 0 || Flag_Scenario_CoolAir_LandCover == 2) {
					//ic_no_tc_actual_frac (frac) =  ImperCover_base_frac (frac) - tci_actual_frac (frac)
					ic_no_tc_actual_frac = ImperCover_base_frac - tci_actual_frac;
				}
				//Else If Flag_Scenario_CoolAir_LandCover = 1 or Flag_Scenario_CoolAir_LandCover >= 3 then 
				else if (Flag_Scenario_CoolAir_LandCover == 1 || Flag_Scenario_CoolAir_LandCover >= 3) {
					//ic_no_tc_actual_frac (frac) = ImperviousCover_frac (frac)
					ic_no_tc_actual_frac = ImperviousCover_frac[MapPixel_ID];
				}
				//Clamp to ensure variable is between 0 and 1 
				ic_no_tc_actual_frac = clamp(ic_no_tc_actual_frac, 0.0, 1.0);

				//If Flag_Scenario_CoolAir_LandCover <= 1 then enter for scenario base, e.g., 0 or 1
				if (Flag_Scenario_CoolAir_LandCover <= 1) {
					//TreeCoverOnPerviousCover_frac (frac) = tcp_actual_frac (frac)
					TreeCoverOnPerviousCover_frac[MapPixel_ID] = tcp_actual_frac;
					//TreeCoverOnImperviousCover_frac (frac) = tci_actual_frac (frac)
					TreeCoverOnImperviousCover_frac[MapPixel_ID] = tci_actual_frac;
					//ImperviousCoverNoTreeCover_frac (frac) = ic_no_tc_actual_frac (frac)
					ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ic_no_tc_actual_frac;
				}

				//If Flag_Scenario_CoolAir_LandCover >= 2 then enter for scenario alternative, e.g., 2 or 3 or 4
				if (Flag_Scenario_CoolAir_LandCover >= 2) {
					//If (TreeCover_frac (frac) > TreeCover_base_frac (frac) then
					if (TreeCover_frac[MapPixel_ID] > TreeCover_base_frac[MapPixel_ID]) {
						//If Flag_Scenario_CoolAir_LandCover = 2 or 3 then enter for scenario alternative
						if (Flag_Scenario_CoolAir_LandCover == 2 || Flag_Scenario_CoolAir_LandCover == 3) {
							//tci_target_frac (frac) = tci_actual_frac (frac) + (TreeCover_frac (frac) - TreeCover_base_frac (frac)) * TreeCanopyCover_overImpervious_frac (frac)
							tci_target_frac = tci_actual_frac + (TreeCover_frac[MapPixel_ID] - TreeCover_base_frac[MapPixel_ID]) * TreeCanopyCover_overImpervious_frac;
						}
						//If Flag_Scenario_CoolAir_LandCover = 4 then enter for scenario alternative
						if (Flag_Scenario_CoolAir_LandCover == 4) {
							//tci_target_frac (frac) = tci_actual_frac (frac)
							tci_target_frac = tci_actual_frac; 
						}
						//If (tci_target_frac (frac) > (tci_actual_frac (frac) + ic_no_tc_actual_frac (frac)))
						if (tci_target_frac > (tci_actual_frac + ic_no_tc_actual_frac)) {
							//tci_target_frac (frac) = (tci_actual_frac (frac) + ic_no_tc_actual_frac (frac))
							tci_target_frac = (tci_actual_frac + ic_no_tc_actual_frac);
						}
					}
					//Else (TreeCover_frac (frac) <= TreeCover_base_frac (frac) then
					else {
						//tci_target_frac (frac) = tci_actual_frac (frac)
						tci_target_frac = tci_actual_frac;
					}
					
					//tci_min_geom = max(0.0, TreeCover_frac[MapPixel_ID] + ImperviousCover_frac[MapPixel_ID] - 1.0)
					//Note: Feasibility clamp ensures pervious >= 0 and TCI given condition of TreeCover_frac + ImperviousCover_frac
					double tci_min_geom = max(0.0, TreeCover_frac[MapPixel_ID] + ImperviousCover_frac[MapPixel_ID] - 1.0);
					//tci_max_geom = min(TreeCover_frac[MapPixel_ID], ImperviousCover_frac[MapPixel_ID]); TCI can’t exceed trees OR impervious
					double tci_max_geom = min(TreeCover_frac[MapPixel_ID], ImperviousCover_frac[MapPixel_ID]);
					// clamp’s contract requires that the lower bound ≤ upper bound. Swapping ensures that invariant is always true.
					if (tci_min_geom > tci_max_geom)
						swap(tci_min_geom, tci_max_geom);
					//Clamp TCI into feasible range, then derive TCP and IC_no_TC
					tci_target_frac = clamp(tci_target_frac, tci_min_geom, tci_max_geom);
					tcp_target_frac = clamp(TreeCover_frac[MapPixel_ID] - tci_target_frac, 0.0, 1.0);
					ic_no_tc_target_frac = clamp(ImperviousCover_frac[MapPixel_ID] - tci_target_frac, 0.0, 1.0);

					//tcp_target_frac (frac) = TreeCover_frac (frac) - tci_target_frac (frac)
					tcp_target_frac = TreeCover_frac[MapPixel_ID] - tci_target_frac;
					//Clamp to ensure variable is between 0 and 1 
					tci_target_frac = clamp(tci_target_frac, 0.0, 1.0);
					//Clamp to ensure variable is between 0 and 1 
					tcp_target_frac = clamp(tcp_target_frac, 0.0, 1.0);

					//If (Flag_Scenario_TC_Max_Active == true && tcp_target_frac + tci_target_frac) > TreeCover_frac[MapPixel_ID]) then adjust
					//Note: When removing trees, tcp_target_frac + tci_target_frac must be less than TreeCover_frac
					if (Flag_Scenario_TC_Max_Active == true && (tcp_target_frac + tci_target_frac) > TreeCover_frac[MapPixel_ID]) {
						//If (tci_target_frac > TreeCover_frac[MapPixel_ID]) then enter and reduce tci_target_frac to TreeCover_frac
						//Note: Algorithm presumes we keep this tci_target_frac rather than some pervious target
						if (tci_target_frac > TreeCover_frac[MapPixel_ID]) {
							tci_target_frac = TreeCover_frac[MapPixel_ID];
							//tcp_target_frac = TreeCover_frac[MapPixel_ID] - tci_target_frac; this should be equivalent to 0
							tcp_target_frac = TreeCover_frac[MapPixel_ID] - tci_target_frac;
						}
						//Else tci_target_frac not > TreeCover_frac[MapPixel_ID] then tci_target_frac can remain unchanged
						else {
							//tcp_target_frac = TreeCover_frac[MapPixel_ID] - tci_target_frac; this should be greater than 0
							tcp_target_frac = TreeCover_frac[MapPixel_ID] - tci_target_frac;
						}
					}
					//Clamp to ensure variable is between 0 and 1 
					tci_target_frac = clamp(tci_target_frac, 0.0, 1.0);
					//Clamp to ensure variable is between 0 and 1 
					tcp_target_frac = clamp(tcp_target_frac, 0.0, 1.0);

					//If (ImperviousCover_frac[MapPixel_ID] (frac) + tci_target_frac (frac) + tcp_target_frac (frac)) > 1.0 then
					//Note: Conditional indicates IC_no_TC + TCI + TCP > 1, therefor IC_no_TC is reduced, becoming TCI
					if ((ImperviousCover_frac[MapPixel_ID] + tci_target_frac + tcp_target_frac) > 1.0) {
						//ic_no_tc_target_frac (frac) = 1 - (tci_target_frac (frac) + tcp_target_frac (frac))
						ic_no_tc_target_frac = 1 - (tci_target_frac + tcp_target_frac);
					}
					//Else If Flag_Scenario_CoolAir_LandCover = 2 then
					else if (Flag_Scenario_CoolAir_LandCover == 2) {
						//ic_no_tc_target_frac (frac) = ImperviousCover_frac[MapPixel_ID] (frac) - tci_target_frac (frac)
						ic_no_tc_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac;
					}
					//Else If Flag_Scenario_CoolAir_LandCover = 3 then
					else if (Flag_Scenario_CoolAir_LandCover == 3) {
						//ic_no_tc_target_frac (frac) = ic_no_tc_actual_frac
						ic_no_tc_target_frac = ic_no_tc_actual_frac;
					}
					//Else If Flag_Scenario_CoolAir_LandCover = 4 then
					else if (Flag_Scenario_CoolAir_LandCover == 4) {
						//ic_no_tc_target_frac (frac) = ImperviousCover_frac[MapPixel_ID] - tci_target_frac - (TreeCover_frac[MapPixel_ID] - TreeCover_base_frac) * TreeCanopyCover_overImpervious_frac
						ic_no_tc_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac - (TreeCover_frac[MapPixel_ID] - TreeCover_base_frac[MapPixel_ID]) * TreeCanopyCover_overImpervious_frac;
					}
					//Clamp to ensure variable is between 0 and 1 
					ic_no_tc_target_frac = clamp(ic_no_tc_target_frac, 0.0, 1.0);

					//If (Flag_Scenario_TC_Max_Active == true && (ic_no_tc_target_frac + tci_target_frac) > ImperviousCover_frac[MapPixel_ID]) then enter
					//Note: ic_no_tc_target_frac + tci_target_frac cannot sum to > ImperviousCover_frac[MapPixel_ID]
					if (Flag_Scenario_TC_Max_Active == true && (ic_no_tc_target_frac + tci_target_frac) > ImperviousCover_frac[MapPixel_ID]) {
						//If (tci_target_frac > ImperviousCover_frac[MapPixel_ID]) then enter and reduce tci_target_frac to ImperviousCover_frac
						//Note: Algorithm presumes we keep this tci_target_frac rather than some pervious target
						if (tci_target_frac > ImperviousCover_frac[MapPixel_ID]) {
							tci_target_frac = ImperviousCover_frac[MapPixel_ID];
							//tcp_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac; this should be equivalent to 0
							ic_no_tc_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac;
						}
						//Else tci_target_frac not > ImperviousCover_frac[MapPixel_ID] then tci_target_frac can remain unchanged
						else {
							//ic_no_tc_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac; this should be greater than 0
							ic_no_tc_target_frac = ImperviousCover_frac[MapPixel_ID] - tci_target_frac;
						}
					}
					//Clamp to ensure variable is between 0 and 1 
					ic_no_tc_target_frac = clamp(ic_no_tc_target_frac, 0.0, 1.0);

					//If TreeCover_frac (frac) > TreeCover_base_frac (frac) And ((tci_target_frac + ic_no_tc_target_frac) > (tci_actual_frac + ic_no_tc_actual_frac)) then
					if ((TreeCover_frac[MapPixel_ID] > TreeCover_base_frac[MapPixel_ID]) && ((tci_target_frac + ic_no_tc_target_frac) > (tci_actual_frac + ic_no_tc_actual_frac))) {
						//ic_no_tc_target_frac (frac) = (tci_actual_frac (frac) + ic_no_tc_actual_frac (frac)) - tci_target_frac (frac)
						ic_no_tc_target_frac = (tci_actual_frac + ic_no_tc_actual_frac) - tci_target_frac;
					}
					//Clamp to ensure variable is between 0 and 1 
					ic_no_tc_target_frac = clamp(ic_no_tc_target_frac, 0.0, 1.0);

					//If (tci_target_frac + ic_no_tc_target_frac) > 1.0 then 
					//Note: Consider refactor if this conditional not needed
					if ((tci_target_frac + ic_no_tc_target_frac) > 1.0) {
						//ic_no_tc_target_frac (frac) = 1.0 - tci_target_frac (frac)
						ic_no_tc_target_frac = 1.0 - tci_target_frac;
					}

					//Final feasibility clamp to ensure TCI ≤ IC and pervious ≥ 0
					double tci_min_geom_final = max(0.0, TreeCover_frac[MapPixel_ID] + ImperviousCover_frac[MapPixel_ID] - 1.0);
					double tci_max_geom_final = min(TreeCover_frac[MapPixel_ID], ImperviousCover_frac[MapPixel_ID]);
					// clamp’s contract requires that the lower bound ≤ upper bound. Swapping ensures that invariant is always true.
					if (tci_min_geom_final > tci_max_geom_final)
						swap(tci_min_geom_final, tci_max_geom_final);
					tci_target_frac = clamp(tci_target_frac, tci_min_geom_final, tci_max_geom_final);
					tcp_target_frac = clamp(TreeCover_frac[MapPixel_ID] - tci_target_frac, 0.0, 1.0);
					ic_no_tc_target_frac = clamp(ImperviousCover_frac[MapPixel_ID] - tci_target_frac, 0.0, 1.0);

					//TreeCoverOnPerviousCover_frac (frac) = tcp_target_frac (frac)
					TreeCoverOnPerviousCover_frac[MapPixel_ID] = tcp_target_frac;
					//TreeCoverOnImperviousCover_frac (frac) = tci_target_frac (frac)
					TreeCoverOnImperviousCover_frac[MapPixel_ID] = tci_target_frac;
					//ImperviousCoverNoTreeCover_frac (frac) = ic_no_tc_target_frac (frac)
					ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ic_no_tc_target_frac;
				}

				//If (1 - ImperviousCoverNoTreeCover_frac (frac) - TreeCoverOnImperviousCover_frac (frac) - TreeCoverOnPerviousCover_frac (frac)) > 0 then
				//Note: Conditional indicates IC_no_TC + TCI + TCP > 1, therefor IC_no_TC is reduced, becoming TCI
				if ((1.0 - ImperviousCoverNoTreeCover_frac[MapPixel_ID] - TreeCoverOnImperviousCover_frac[MapPixel_ID] - TreeCoverOnPerviousCover_frac[MapPixel_ID]) > 0) {
					//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for NLCD Class based on best estimates of sub-grid heterogeneity
					//PerviousCover_FullSkyView_with_SVeg_frac is HydroPlusConfig.xml element SVeg_in_Gap_DevelopedArea_frac (frac); typically 0.5
					PerviousCover_FullSkyView_with_SVeg_frac = SVeg_in_Gap_DevelopedArea_frac;
					//If PerviousCover_FullSkyView_with_SVeg_frac <= 0 then redefine; likely due to HydroPlusConfig.xml not containing SVeg_in_Gap_DevelopedArea_frac
					if (PerviousCover_FullSkyView_with_SVeg_frac <= 0) {
						//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0 
						PerviousCover_FullSkyView_with_SVeg_frac = 0;
					}
					//PerviousCover_FullSkyView_frac defined as area not within tree cover and impervious cover
					PerviousCover_FullSkyView_frac = 1.0 - ImperviousCoverNoTreeCover_frac[MapPixel_ID] - TreeCoverOnImperviousCover_frac[MapPixel_ID] - TreeCoverOnPerviousCover_frac[MapPixel_ID];
					//SoilCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
					SoilCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
					//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
					ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
				}
				//Else (1 - ImperviousCoverNoTreeCover_frac (frac) - TreeCoverOnImperviousCover_frac (frac) - TreeCoverOnPerviousCover_frac (frac)) < 0 then
				else {
					//SoilCoverNoTC_frac (frac) = 0
					SoilCoverNoTC_frac[MapPixel_ID] = 0;
					//ShortVegCoverNoTC_frac (frac) = 0
					ShortVegCoverNoTC_frac[MapPixel_ID] = 0;
				}
			}
		}
		//Else If NLCD Class is water type
		//Note: Class 11 = Open Water or 12 = Perennial Ice/Snow then
		else if (LandCover_NLCD_Class[MapPixel_ID] == 11 || LandCover_NLCD_Class[MapPixel_ID] == 12) {
			//SoilCoverNoTC_frac (fraction) is zero given NLCD class is water
			SoilCoverNoTC_frac[MapPixel_ID] = 0;
			//ShortVegCoverNoTC_frac (fraction) is zero given NLCD class is water
			ShortVegCoverNoTC_frac[MapPixel_ID] = 0;
			//WaterCoverNoTC_frac (fraction) occupies all remaining space not occupied by TotalMapCover_frac
			WaterCoverNoTC_frac[MapPixel_ID] = (1.0 - TotalMapCover_frac[MapPixel_ID]);
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}
		//Else If NLCD Class is barren or rocky 
		//Note: Class 31 = Barren land and rocky then
		else if (LandCover_NLCD_Class[MapPixel_ID] == 31) {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0.0 
			PerviousCover_FullSkyView_with_SVeg_frac = 0.0;
			//WaterCoverNoTC_frac (fraction) is zero given NLCD class is terrestrial
			WaterCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//SoilCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			SoilCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}
		//Else If NLCD Class is forest types
		//Note: CLass 41 = Deciduous forest, 42 = Evergreen forest, or 43 = Mixed forest then
		else if (LandCover_NLCD_Class[MapPixel_ID] == 41 || LandCover_NLCD_Class[MapPixel_ID] == 42 || LandCover_NLCD_Class[MapPixel_ID] == 43) {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 1.0 
			PerviousCover_FullSkyView_with_SVeg_frac = 1.0;
			//WaterCoverNoTC_frac (fraction) is zero given NLCD class is terrestrial
			WaterCoverNoTC_frac[MapPixel_ID] = 0;
			//SoilCoverNoTC_frac (fraction) is zero given NLCD class is forest
			SoilCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
			//Note: If NLCD value 41 = Deciduous forest, fraction of evergreen remains as defined in HydroPlusConfig.xml
			//If NLCD value 42 = Evergreen forest, fraction of evergreen in respective covers redfined as 1
			if (LandCover_NLCD_Class[MapPixel_ID] == 42) {
				TreeEvergreen_frac[MapPixel_ID] = 1;
				ShortVegEvergreen_frac[MapPixel_ID] = 1;
			}
			//If NLCD value 43 = Mixed forest, fraction of evergreen in respective covers redefined as 0.5
			if (LandCover_NLCD_Class[MapPixel_ID] == 43) {
				TreeEvergreen_frac[MapPixel_ID] = 0.5;
				ShortVegEvergreen_frac[MapPixel_ID] = 0.5;
			}
		}
		//Else If NLCD Class is agricultural types
		//Note: Class 51 = Dwarf Scrub, 52 = Shrub / Scrub, 71 = Grassland / Herbaceous, 72 = Sedge / Herbaceous, 73 = Lichens, 74 = Moss, ...
		//Note: ... 81 = Pasture / Hay, or 82 = Cultivated Crops
		else if (LandCover_NLCD_Class[MapPixel_ID] == 51 || LandCover_NLCD_Class[MapPixel_ID] == 52 || LandCover_NLCD_Class[MapPixel_ID] == 71 || LandCover_NLCD_Class[MapPixel_ID] == 72 ||
			LandCover_NLCD_Class[MapPixel_ID] == 73 || LandCover_NLCD_Class[MapPixel_ID] == 74 || LandCover_NLCD_Class[MapPixel_ID] == 81 || LandCover_NLCD_Class[MapPixel_ID] == 82) {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0.85 
			PerviousCover_FullSkyView_with_SVeg_frac = 0.85;
			//WaterCoverNoTC_frac (fraction) is zero given NLCD class is terrestrial
			WaterCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//SoilCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			SoilCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}
		//Else If NLCD Class is woody wetlands type
		//Note: Class 90 = Woody wetlands then 
		else if (LandCover_NLCD_Class[MapPixel_ID] == 90) {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0.65 
			PerviousCover_FullSkyView_with_SVeg_frac = 0.65;
			//SoilCoverNoTC_frac (fraction) is zero given NLCD class is wetland
			SoilCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//WaterCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			//Note: If WaterCoverNoTC_frac <= 55%, then scenarios where IC is changed should avoid Tair_IC_0% > Tair_IC_2% 
			WaterCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}
		//Else If NLCD Class is emergent wetlands type
		//Note: Class 95 = Emergent herbaceous wetlands then 
		else if (LandCover_NLCD_Class[MapPixel_ID] == 95) {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0.85 
			PerviousCover_FullSkyView_with_SVeg_frac = 0.85;
			//SoilCoverNoTC_frac (fraction) is zero given NLCD class is wetland
			SoilCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//WaterCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			//Note: If WaterCoverNoTC_frac <= 55%, then scenarios where IC is changed should avoid Tair_IC_0% > Tair_IC_2% 
			WaterCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}
		//Else NLCD Class is some class not defined above and treat as NLCD Class 21 to 24
		else {
			//PerviousCover_FullSkyView_with_SVeg_frac (frac) defined for each NLCD Class based on best estimates of sub-grid heterogeneity
			//Note: PerviousCover_FullSkyView_with_SVeg_frac defined here as 0.5 
			PerviousCover_FullSkyView_with_SVeg_frac = 0.5;
			//WaterCoverNoTC_frac (fraction) is zero given NLCD class is presumed terrestrial
			WaterCoverNoTC_frac[MapPixel_ID] = 0;
			//PerviousCover_FullSkyView_frac defined as area not within TotalMapCover_frac (tree cover and impervious cover)
			PerviousCover_FullSkyView_frac = 1.0 - TotalMapCover_frac[MapPixel_ID];
			//SoilCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac)
			SoilCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * (1 - PerviousCover_FullSkyView_with_SVeg_frac);
			//ShortVegCoverNoTC_frac (frac) defined as PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac
			ShortVegCoverNoTC_frac[MapPixel_ID] = PerviousCover_FullSkyView_frac * PerviousCover_FullSkyView_with_SVeg_frac;
			//TreeCoverOnImperviousCover_frac (fraction) is zero given TotalMapCover_frac < 100%
			TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0;
			//TreeCoverOnPerviousCover_frac (fraction) is all TreeCover_frac given TotalMapCover_frac < 100%
			TreeCoverOnPerviousCover_frac[MapPixel_ID] = TreeCover_frac[MapPixel_ID];
			//ImperviousCoverNoTreeCover_frac (fraction) is all impervious cover given TotalMapCover_frac < 100%
			ImperviousCoverNoTreeCover_frac[MapPixel_ID] = ImperviousCover_frac[MapPixel_ID];
		}

		//Check for small numbers, using Constant_1E_negative5 threshold used for land area fractions, if < 1E-5 then = 0.0
		if (ImperviousCoverNoTreeCover_frac[MapPixel_ID] < Constant_1E_negative5) { ImperviousCoverNoTreeCover_frac[MapPixel_ID] = 0.0; }
		if (TreeCoverOnPerviousCover_frac[MapPixel_ID] < Constant_1E_negative5) { TreeCoverOnPerviousCover_frac[MapPixel_ID] = 0.0; }
		if (TreeCoverOnImperviousCover_frac[MapPixel_ID] < Constant_1E_negative5) { TreeCoverOnImperviousCover_frac[MapPixel_ID] = 0.0; }
		if (WaterCoverNoTC_frac[MapPixel_ID] < Constant_1E_negative5) { WaterCoverNoTC_frac[MapPixel_ID] = 0.0; }
		if (SoilCoverNoTC_frac[MapPixel_ID] < Constant_1E_negative5) { SoilCoverNoTC_frac[MapPixel_ID] = 0.0; }
		if (ShortVegCoverNoTC_frac[MapPixel_ID] < Constant_1E_negative5) { ShortVegCoverNoTC_frac[MapPixel_ID] = 0.0; }
		if (PermeablePavementCover_frac[MapPixel_ID] < Constant_1E_negative5) { PermeablePavementCover_frac[MapPixel_ID] = 0.0; }
		
		//ImperviousCoverNoTreeCover_frac (frac) =roundToDecimalPlaces(ImperviousCoverNoTreeCover_frac[MapPixel_ID], 3)
		//Note: RHS function divides by 1000 after taking integer of the term that takes product with 1000 to round up
		ImperviousCoverNoTreeCover_frac[MapPixel_ID] = roundToDecimalPlaces(ImperviousCoverNoTreeCover_frac[MapPixel_ID], 3);
		TreeCoverOnImperviousCover_frac[MapPixel_ID] = roundToDecimalPlaces(TreeCoverOnImperviousCover_frac[MapPixel_ID], 3);
		TreeCoverOnPerviousCover_frac[MapPixel_ID] = roundToDecimalPlaces(TreeCoverOnPerviousCover_frac[MapPixel_ID], 3);
		SoilCoverNoTC_frac[MapPixel_ID] = roundToDecimalPlaces(SoilCoverNoTC_frac[MapPixel_ID], 3);
		ShortVegCoverNoTC_frac[MapPixel_ID] = roundToDecimalPlaces(ShortVegCoverNoTC_frac[MapPixel_ID], 3);
		PermeablePavementCover_frac[MapPixel_ID] = roundToDecimalPlaces(PermeablePavementCover_frac[MapPixel_ID], 3);
		WaterCoverNoTC_frac[MapPixel_ID] = roundToDecimalPlaces(WaterCoverNoTC_frac[MapPixel_ID], 3);

		//TotalCover_frac (frac) initialized to equal sum of ImperviousCoverNoTreeCover_frac (frac), TreeCoverOnImperviousCover_frac (frac), TreeCoverOnPerviousCover_frac (frac), SoilCoverNoTC_frac (frac), ShortVegCoverNoTC_frac (frac), PermeablePavementCover_frac (frac), WaterCoverNoTC_frac (frac)
		double TotalCover_frac = ImperviousCoverNoTreeCover_frac[MapPixel_ID] + TreeCoverOnImperviousCover_frac[MapPixel_ID] + TreeCoverOnPerviousCover_frac[MapPixel_ID] + SoilCoverNoTC_frac[MapPixel_ID] + ShortVegCoverNoTC_frac[MapPixel_ID] + PermeablePavementCover_frac[MapPixel_ID] + WaterCoverNoTC_frac[MapPixel_ID];
		//TotalCover_Remainder_frac (frac) initialized to equal abs(1.0 - TotalCover_frac)
		//Note: Absolute function not likely needed as TotalCover_frac is presumed always less than 1
		double TotalCover_Remainder_frac = abs(1.0 - TotalCover_frac);
		
		//If TotalCover_Remainder_frac != 0.0 then add the remaining gap in a sequence that lastly updates tree cover, presumed correct
		//Note: Algorithm could be improved to search for smallest unit of area (frac) before setting anything to zero but ...
		//Note: ... Algorithm will never likely set anything to zero due to (1.0 - TotalCover_frac) naturally positive, without need for abs
		if (TotalCover_Remainder_frac != 0.0) {
			if (PermeablePavementCover_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - PermeablePavementCover_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - PermeablePavementCover_frac[MapPixel_ID]);
				//PermeablePavementCover_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				PermeablePavementCover_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				PermeablePavementCover_frac[MapPixel_ID] = clamp(PermeablePavementCover_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (SoilCoverNoTC_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - SoilCoverNoTC_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - SoilCoverNoTC_frac[MapPixel_ID]);
				//SoilCoverNoTC_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				SoilCoverNoTC_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				SoilCoverNoTC_frac[MapPixel_ID] = clamp(SoilCoverNoTC_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (WaterCoverNoTC_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - WaterCoverNoTC_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - WaterCoverNoTC_frac[MapPixel_ID]);
				//WaterCoverNoTC_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3)(frac)
				WaterCoverNoTC_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				WaterCoverNoTC_frac[MapPixel_ID] = clamp(WaterCoverNoTC_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (ShortVegCoverNoTC_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - ShortVegCoverNoTC_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - ShortVegCoverNoTC_frac[MapPixel_ID]);
				//ShortVegCoverNoTC_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				ShortVegCoverNoTC_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				ShortVegCoverNoTC_frac[MapPixel_ID] = clamp(ShortVegCoverNoTC_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (ImperviousCoverNoTreeCover_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - ImperviousCoverNoTreeCover_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - ImperviousCoverNoTreeCover_frac[MapPixel_ID]);
				//ImperviousCoverNoTreeCover_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				ImperviousCoverNoTreeCover_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				ImperviousCoverNoTreeCover_frac[MapPixel_ID] = clamp(ImperviousCoverNoTreeCover_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (TreeCoverOnImperviousCover_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - TreeCoverOnImperviousCover_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - TreeCoverOnImperviousCover_frac[MapPixel_ID]);
				//TreeCoverOnImperviousCover_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				TreeCoverOnImperviousCover_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				TreeCoverOnImperviousCover_frac[MapPixel_ID] = clamp(TreeCoverOnImperviousCover_frac[MapPixel_ID], 0.0, 1.0);
			}
			else if (TreeCoverOnPerviousCover_frac[MapPixel_ID] > 0.0) {
				//TotalCover_Remainder_wo_frac (frac) initialized to equal 1.0 - (TotalCover_frac - TreeCoverOnPerviousCover_frac[MapPixel_ID])
				double TotalCover_Remainder_wo_frac = 1.0 - (TotalCover_frac - TreeCoverOnPerviousCover_frac[MapPixel_ID]);
				//TreeCoverOnPerviousCover_frac (frac) = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3) (frac)
				TreeCoverOnPerviousCover_frac[MapPixel_ID] = Inputs::roundToDecimalPlaces(TotalCover_Remainder_wo_frac, 3);
				//Clamp to ensure variable is between 0 and 1 
				TreeCoverOnPerviousCover_frac[MapPixel_ID] = clamp(TreeCoverOnPerviousCover_frac[MapPixel_ID], 0.0, 1.0);
			}
		}
	}
}

//readRunoff_ObservedFile function reads in Runoff_Observed_for_Calibration.csv time series data for HydroPlus models
//Note: i-Tree HydroPlus Runoff_Observed_for_Calibration.csv.csv header row: YYYYMMDD,HH:MM:SS,Discharge(m) which is basin total runoff depth for time step
void Inputs::readRunoff_ObservedFile()
{
	//readWeather created as part of fstream, defined as the Directory_Input_CLArg and file name for Weather.csv
	fstream readRunoff_Observed(Directory_Input_CLArg + "Runoff_Observed_for_Calibration.csv");

	//If Runoff_Observed_for_Calibration.csv input data file does not exist, then alert user and abort
	if (!readRunoff_Observed) {
		cout << "Warning: Runoff_Observed_for_Calibration.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The Runoff_Observed_for_Calibration.csv file is required when HydroPlusConfig.xml element Flag_CompareToObservedRunoff > 0." << endl;
		cout << "Correction: Provide Runoff_Observed_for_Calibration.csv in the input folder, or set HydroPlusConfig.xml Flag_CompareToObservedRunoff = 0." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	//Initialize variables
	Runoff_Observed_m.clear();
	string startline;
	string temp;
	string HrMinSec;
	//initialize Date_YYYYMMDD as -1, other variables to -99999
	double Date_YYYYMMDD = -1.0;
	double tempRunoff_Observed_m = -99999;
	double date_InputDataRow_HH;
	double date_InputDataRow_GDH;
	int Date_JulianDay;
	int Counter_inputRow = 0;

	//Theory on getline function of fstream library; takes characters from 1st item, stores them in 2nd item, until delimitation character found
	//getline reads in header from readRunoff_Observed, stores in temp
	getline(readRunoff_Observed, temp);

	//while readRunoff_Observed uses fstream good function to return true unless error flag is thrown
	while (readRunoff_Observed.good())
	{
		//getline reads 1st column characters of readRunoff_Observed before reaching comma, assigns to temp
		//Note: Reads Gregorian Date YYYYMMDD 
		getline(readRunoff_Observed, temp, ',');
		//avoid reading the empty line at the end of the input file
		if (temp[0] != '\n') {
			//advance Counter_inputRow 
			Counter_inputRow = Counter_inputRow + 1;
			//istringstream stores an instance of the string temp in the new ss string
			istringstream ss(temp);
			//ss string is passed into Date_YYYYMMDD as double
			ss >> Date_YYYYMMDD;

			//If Counter_inputRow equals 1 then at start of file then check if Weather.csv first date is not greater than HydroPlusConfig.xml StartDate_YYYYMMDD
			if (Counter_inputRow == 1) {
				//Check if year matches HydroPlusConfig.xml StartDate_YYYYMMDD Year (and below if Weather.csv contains full YYYYMMDD called for in HydroPlusConfig.xml)
				//HydroPlusConfig_StartDay_str_YYYYMMDD is string form of HydroPlusConfig.xml input StartDate_YYYYMMDD
				string HydroPlusConfig_StartDay_str_YYYYMMDD = to_string(SimulationNumericalParams["StartDate_YYYYMMDD"]);
				//Runoff_ObservedInput_StartDay_str_YYYYMMDD  is string form of Weather.csv input date column
				string Runoff_ObservedInput_StartDay_str_YYYYMMDD = to_string(Date_YYYYMMDD);
				//Runoff_ObservedInput_StartDay_YYYYMMDD defined as integer of Date_YYYYMMDD input
				int Runoff_ObservedInput_StartDay_YYYYMMDD = Date_YYYYMMDD;
				//HydroPlusConfig_StartDay_YYYYMMDD is integer form of substring from HydroPlusConfig_StartDay_str_YYYYMMDD, taking first 8 elements
				int HydroPlusConfig_StartDay_YYYYMMDD = atoi(HydroPlusConfig_StartDay_str_YYYYMMDD.substr(0, 8).c_str());

				//if HydroPlusConfig_StartDay_YYYYMMDD > HydroPlusConfig_StartDay_YYYYMMDD then error with inputs or expectations
				if (Runoff_ObservedInput_StartDay_YYYYMMDD > HydroPlusConfig_StartDay_YYYYMMDD) {
					cout << "Warning: The Runoff_Observed_for_Calibration.csv 1st column for date, YYYYMMDD, has a starting date of " << Runoff_ObservedInput_StartDay_YYYYMMDD << " ..." << endl;
					cout << "... which is later than the HydroPlusConfig.xml parameter StartDate_YYYYMMDD, set to " << HydroPlusConfig_StartDay_YYYYMMDD << "." << endl;
					cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
					cout << "Explanation: The HydroPlus model needs the Runoff_Observed_for_Calibration.csv to contain the date given in HydroPlusConfig.xml." << endl;
					cout << "Correction: Either generate new Runoff_Observed_for_Calibration.csv or change the StartDate_YYYYMMDD in the HydroPlusConfig.xml file." << endl;
					//Call abort function, which ends the HydroPlus.exe simulation
					abort();
				}
			}

			//getline reads column characters of readRunoff_Observed before reaching comma, assigns to temp
			//Note: Reads Time HH:MM:SS 
			getline(readRunoff_Observed, temp, ',');
			//istringstream stores an instance of the string temp in the new ssTime string
			istringstream ssTime(temp);
			//ssTime string is passed into HrMinSec as double
			ssTime >> HrMinSec;

			//Algorithm to extract hour integer from section of HrMinSec string, used to refine location where input rows are read
			//date_InputDataRow_HH integer from string with atoi function, extracting 2 digits of temp w substr function; c_str() points to temp
			date_InputDataRow_HH = atoi(HrMinSec.substr(0, 2).c_str());
			//date_InputDataRow_GDH contains Gregorian Date and Hour, extending 8 to 10 digits by multiplication with 100, adding hour
			date_InputDataRow_GDH = (Date_YYYYMMDD * 100) + date_InputDataRow_HH;

			//If date_InputDataRow_GDH is between SimulationDateStop_GDH and SimulationDateStart_GDH then proceed to read input
			if (date_InputDataRow_GDH <= SimulationDateStop_YYYYMMDDHH &&
				date_InputDataRow_GDH >= SimulationDateStart_YYYYMMDDHH) {

				//Reading air observed runoff depth input (m)
				//Linebreak \n is read to signify end of row
				getline(readRunoff_Observed, temp, '\n');
				istringstream ssRunoff_Observed(temp);
				ssRunoff_Observed >> tempRunoff_Observed_m;
				Runoff_Observed_m.push_back(tempRunoff_Observed_m);
			}
			//Else If date_InputDataRow_GDH is not between SimulationDateStop_GDH and SimulationDateStart_GDH then advance to next date
			else {
				getline(readRunoff_Observed, temp);
			}
		}
	}
	//readRunoff_Observed is closed
	readRunoff_Observed.close();
}

//readRiverTimeSeriesFile function reads time series input data for i-Tree CoolRiver model
void Inputs::readRiverTimeSeriesFile(double startDate, double endDate)
{
	fstream readRiverTimeSeries(Directory_Input_CLArg + "RiverTimeSeries.csv");
	if (!readRiverTimeSeries) {
		cout << "Warning: RiverTimeSeries.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: This file contains river time series data required for model inputs." << endl;
		cout << "Correction: Please ensure RiverTimeSeries.csv exists in the input directory, and compare its format with a working test case." << endl;
		abort();
	}

	string temp;
	double date = -1.0;
	string HrMinSec;
	double tempSedi = -99999;
	double tempHumid = -99999;
	double tempCloudy = -99999;

	string startline;
	getline(readRiverTimeSeries, temp);

	while (readRiverTimeSeries.good())
	{
		getline(readRiverTimeSeries, temp, ',');
		istringstream ss(temp);
		ss >> date;
		if (date <= endDate && date >= startDate) {
			if (readRiverTimeSeries.eof()) {
				break;
			}
			DatesRiverTime.push_back(static_cast<int>(date));
			getline(readRiverTimeSeries, temp, ',');
			istringstream ssTime(temp);
			ssTime >> HrMinSec;
			RiverTimeSeries.push_back(HrMinSec);

			getline(readRiverTimeSeries, temp, ',');
			istringstream ssSedimentTC(temp);
			ssSedimentTC >> tempSedi;
			Sediment_T_C.push_back(tempSedi);

			getline(readRiverTimeSeries, temp, ',');
			istringstream ssCloudinessF(temp);
			ssCloudinessF >> tempCloudy;
			Cloudiness_F.push_back(tempCloudy);

			getline(readRiverTimeSeries, temp, '\n');
			istringstream ssHumidityP(temp);
			ssHumidityP >> tempHumid;
			Humidity_Per.push_back(tempHumid);
		}
		else {
			getline(readRiverTimeSeries, temp);
		}
	}
	readRiverTimeSeries.close();
}

//readRiverStationSeriesFile function reads spatial series input data for i-Tree CoolRiver model
void Inputs::readRiverStationSeriesFile(string& Directory_Input_CLArg, double NODATA_code)
{
	fstream readRiverStationSeries(Directory_Input_CLArg + "RiverStationSeries.csv");
	if (!readRiverStationSeries) {
		cout << "Warning: RiverStationSeries.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: This file contains station Series data required for model inputs." << endl;
		cout << "Correction: Please ensure RiverStationSeries.csv exists in the input directory, and compare its format with a working test case." << endl;
		abort();
	}

	string temp;
	string tempType;
	double stationNumber = -1.0;
	double stationDistance = -1.0;
	double tempDepthOfMesu = -99999;
	double tempHorizontalBedCon = -99999;
	double tempBedParticle = -99999;
	double tempEmbeddedness = -99999;
	double tempArea = -99999;
	double tempWidth = -99999;
	double tempDepth = -99999;
	double tempSlope = -99999;
	double tempRow = -99999;
	double tempColumn = -99999;
	double tempLongitude = -99999;
	double tempLatitude = -99999;
	double tempssZRiver = -99999;

	double tempEastBankH = -99999;
	double tempEastTreeH = -99999;
	double tempEastBuildingH = -99999;
	double tempEastBankDist = -99999;
	double tempEastCanDist = -99999;
	double tempEastBuildingDist = -99999;
	double tempEastBuffer = -99999;
	double tempWestBankH = -99999;
	double tempWestTreeH = -99999;
	double tempWestBuildingH = -99999;
	double tempWestBankDist = -99999;
	double tempWestCanDist = -99999;
	double tempWestBuildingDist = -99999;
	double tempWestBuffer = -99999;
	double tempElevation = -99999;
	double tempStreamAzi = -99999;
	double tempShadeFactor = -99999;
	double tempViewToSky = -99999;
	double stationDistanceMorphology = -1.0;
	double stationDistanceShading = -1.0;
	double stationDistanceShadingPercent = -1.0;

	string startline;
	getline(readRiverStationSeries, temp);

	while (readRiverStationSeries.good())
	{
		if (readRiverStationSeries.eof()) {
			break;
		}
		getline(readRiverStationSeries, temp, ',');
		if (temp == "") {
			break;
		}
		istringstream ss(temp);
		ss >> stationNumber;
		StationNumber.push_back(static_cast<int>(stationNumber));
		getline(readRiverStationSeries, temp, ',');
		istringstream ssDistance(temp);
		ssDistance >> stationDistance;
		StationDistanceBedData.push_back(static_cast<int>(stationDistance));

		getline(readRiverStationSeries, temp, ',');
		istringstream ssDepthOfMes(temp);
		ssDepthOfMes >> tempDepthOfMesu;
		DepthMesurments.push_back(tempDepthOfMesu);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssType(temp);
		ssType >> tempType;
		TypeOfRiver.push_back(tempType);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssHorizontalBedCon(temp);
		ssHorizontalBedCon >> tempHorizontalBedCon;
		HorizontalBedConnectivity.push_back(tempHorizontalBedCon);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssBedParticle(temp);
		ssBedParticle >> tempBedParticle;
		BedParticleSize.push_back(tempBedParticle);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssEmbeddedness(temp);
		ssEmbeddedness >> tempEmbeddedness;
		Embeddedness.push_back(tempEmbeddedness);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssstationDistanceMorphology(temp);
		ssstationDistanceMorphology >> stationDistanceMorphology;
		StationDistanceMorphology.push_back(static_cast<int>(stationDistanceMorphology));

		getline(readRiverStationSeries, temp, ',');
		istringstream ssArea(temp);
		ssArea >> tempArea;
		AreaR_m2.push_back(tempArea);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssWidth(temp);
		ssWidth >> tempWidth;
		WidthR_m.push_back(tempWidth);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssDepth(temp);
		ssDepth >> tempDepth;
		DepthR_m.push_back(tempDepth);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssSlope(temp);
		ssSlope >> tempSlope;
		SlopeR_f.push_back(tempSlope);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssRow(temp);
		ssRow >> tempRow;
		RowRiver.push_back(static_cast<int>(tempRow));

		getline(readRiverStationSeries, temp, ',');
		istringstream sscolumn(temp);
		sscolumn >> tempColumn;
		ColRiver.push_back(static_cast<int>(tempColumn));

		getline(readRiverStationSeries, temp, ',');
		istringstream ssLongitude(temp);
		ssLongitude >> tempLongitude;
		LongitudeR_frac.push_back(tempLongitude);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssLatitude(temp);
		ssLatitude >> tempLatitude;
		LatitudeR_frac.push_back(tempLatitude);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssZRiver(temp);
		ssZRiver >> tempssZRiver;
		ZRiver_m.push_back(tempssZRiver);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssstationDistanceShading(temp);
		ssstationDistanceShading >> stationDistanceShading;
		StationDistanceShading.push_back(static_cast<int>(stationDistanceShading));

		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastBankH(temp);
		ssEastBankH >> tempEastBankH;
		EastBankHR_m.push_back(tempEastBankH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastTreeH(temp);
		ssEastTreeH >> tempEastTreeH;
		EastTreeHR_m.push_back(tempEastTreeH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastBuildingH(temp);
		ssEastBuildingH >> tempEastBuildingH;
		EastBuildingHR_m.push_back(tempEastBuildingH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastBankDist(temp);
		ssEastBankDist >> tempEastBankDist;
		EastBankDistR_m.push_back(tempEastBankDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastCanDist(temp);
		ssEastCanDist >> tempEastCanDist;
		EastCanDistR_m.push_back(tempEastCanDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastBuildingDist(temp);
		ssEastBuildingDist >> tempEastBuildingDist;
		EastBuildingDistR_m.push_back(tempEastBuildingDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssEastBuffer(temp);
		ssEastBuffer >> tempEastBuffer;
		EastBufferWR_m.push_back(tempEastBuffer);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestBankH(temp);
		ssWestBankH >> tempWestBankH;
		WestBankHR_m.push_back(tempWestBankH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestTreeH(temp);
		ssWestTreeH >> tempWestTreeH;
		WestTreeHR_m.push_back(tempWestTreeH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestBuildingH(temp);
		ssWestBuildingH >> tempWestBuildingH;
		WestBuildingHR_m.push_back(tempWestBuildingH);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestBankDist(temp);
		ssWestBankDist >> tempWestBankDist;
		WestBankDistR_m.push_back(tempWestBankDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestCanDist(temp);
		ssWestCanDist >> tempWestCanDist;
		WestCanDistR_m.push_back(tempWestCanDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestBuildingDist(temp);
		ssWestBuildingDist >> tempWestBuildingDist;
		WestBuildingDistR_m.push_back(tempWestBuildingDist);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssWestBuffer(temp);
		ssWestBuffer >> tempWestBuffer;
		WestBufferWR_m.push_back(tempWestBuffer);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssElevation(temp);
		ssElevation >> tempElevation;
		ElevationR_m.push_back(tempElevation);
		getline(readRiverStationSeries, temp, ',');
		istringstream ssStreamAzi(temp);
		ssStreamAzi >> tempStreamAzi;
		StreamAzimuthR_deg.push_back(tempStreamAzi);

		getline(readRiverStationSeries, temp, ',');
		istringstream ssstationDistanceShadingPercent(temp);
		ssstationDistanceShadingPercent >> stationDistanceShadingPercent;
		StationDistanceShadingPercent.push_back(static_cast<int>(stationDistanceShadingPercent));

		getline(readRiverStationSeries, temp, ',');
		istringstream ssShadeFactor(temp);
		ssShadeFactor >> tempShadeFactor;
		ShadeFactorR_f.push_back(tempShadeFactor);
		//Linebreak \n is read to signify end of row
		getline(readRiverStationSeries, temp, '\n');
		istringstream ssViewToSky(temp);
		ssViewToSky >> tempViewToSky;
		ViewToSkyR_f.push_back(tempViewToSky);
		if (readRiverStationSeries.eof()) {
			break;
		}
	}

	readRiverStationSeries.close();
}

//readRiverGWTimeSeriesFile function reads time series input data for i-Tree CoolRiver model
void Inputs::readRiverGWTimeSeriesFile(double startDate, double endDate)
{
	fstream readRiverGWTimeSeries(Directory_Input_CLArg + "RiverGWTimeSeries.csv");
	if (!readRiverGWTimeSeries) {
		cout << "Warning: RiverGWTimeSeries.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: This file contains groundwater discharge and temperature data for river connections." << endl;
		cout << "Correction: Please ensure RiverGWTimeSeries.csv exists in the input folder and compare its format with a working test case." << endl;
		abort();
	}

	string temp;
	double date = -1.0;
	string HrMinSec;
	double tempGWQcms = -99999;
	double tempTCcms = -99999;
	int indexValueForGW = -99999;
	int indexValueForTC = -99999;

	string delimiter = ",";
	vector<double> headerValues;
	getline(readRiverGWTimeSeries, temp);
	int size = int(temp.size());
	size_t pos = 0;
	int counter = 0;
	string token;
	bool flag = true;
	int maxNumberOfVectors = 10;
	while ((pos = temp.find(delimiter)) != string::npos)
	{
		token = temp.substr(0, pos);
		int pos1 = int(token.find("#"));
		if (pos1 != -1) {
			string str3 = token.substr(pos1 + 1);
			int val = stoi(str3);
			headerValues.push_back(val);
			counter++;
		}
		temp.erase(0, pos + delimiter.length());
	}
	int pos1 = int(token.find("#")); // position of "live" in str
	if (pos1 != -1)	{
		string str3 = token.substr(pos1 + 1);
		int val = stoi(str3);
		headerValues.push_back(val);
		counter++;
	}
	while (readRiverGWTimeSeries.good())
	{
		getline(readRiverGWTimeSeries, temp, ',');
		istringstream ss(temp);
		ss >> date;
		if (date <= endDate && date >= startDate) {
			tempGWQcms = -99999;
			tempTCcms = -99999;
			if (readRiverGWTimeSeries.eof()) {
				break;
			}
			getline(readRiverGWTimeSeries, temp, ',');
			for (int i = 0; i < counter;) {
				getline(readRiverGWTimeSeries, temp, ',');
				istringstream ssGWQcms(temp);
				ssGWQcms >> tempGWQcms;
				indexValueForGW = int(headerValues.at(i));
				map<int, vector<double>>::iterator it = GW_Q_cms1.find(indexValueForGW);
				vector<double> tempVector;
				if (it == GW_Q_cms1.end()) {
					tempVector.push_back(tempGWQcms);
					GW_Q_cms1.insert(pair<int, vector<double>>(indexValueForGW, tempVector));
				}
				else {
					GW_Q_cms1[indexValueForGW].push_back(tempGWQcms);
				}
				if (i != counter - 2) {
					getline(readRiverGWTimeSeries, temp, ',');
				}
				else {
					//Linebreak \n is read to signify end of row
					getline(readRiverGWTimeSeries, temp, '\n');
				}
				i = i + 1;
				istringstream ssTCcms(temp);
				ssTCcms >> tempTCcms;
				indexValueForTC = int(headerValues.at(i));

				map<int, vector<double>>::iterator it1 = GW_T_C1.find(indexValueForTC);
				vector<double> tempVector1;
				if (it1 == GW_T_C1.end()) {
					tempVector1.push_back(tempTCcms);
					GW_T_C1.insert(pair<int, vector<double>>(indexValueForTC, tempVector1));
				}
				else {
					GW_T_C1[indexValueForTC].push_back(tempTCcms);
				}
				i = i + 1;
			}
		}
		else {
			getline(readRiverGWTimeSeries, temp);
		}
	}
	readRiverGWTimeSeries.close();
}

//readRiverSWTimeSeriesFile function reads time series input data for i-Tree CoolRiver model
//Consider Refactor to replace counter not use startDate or endDate in while (readRiverSWTimeSeries.good(), as done in readRiverGWTimeSeriesFile and readRiverTimeSeriesFile
void Inputs::readRiverSWTimeSeriesFile(double startDate, double endDate)
{
	fstream readRiverSWTimeSeries(Directory_Input_CLArg + "RiverSWTimeSeries.csv");
	if (!readRiverSWTimeSeries) {
		cout << "Warning: RiverSWTimeSeries.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: This file contains surface water discharge and temperature time series required for simulation." << endl;
		cout << "Correction: Please make sure RiverSWTimeSeries.csv exists in the input directory and compare its format with a working test case.." << endl;
		abort();
	}

	string temp;
	double date = -1.0;
	string HrMinSec;
	double tempGWQcms = -99999;
	double tempTCcms = -99999;
	int indexValueForGW = -99999;
	int indexValueForTC = -99999;

	string delimiter = ",";
	vector<double> headerValues;

	getline(readRiverSWTimeSeries, temp);
	size_t pos = 0;
	int counter = 0;
	string token;
	int maxNumberOfVectors = 10;
	
	while ((pos = temp.find(delimiter)) != string::npos)
	{
		token = temp.substr(0, pos);
		int pos1 = int(token.find("#"));
		if (pos1 != -1)	{
			string str3 = token.substr(pos1 + 1);
			int val = stoi(str3);
			if (val > maxNumberOfVectors) {
				maxNumberOfVectors = val;
			}
			headerValues.push_back(val);
			counter++;
		}
		temp.erase(0, pos + delimiter.length());
	}
	int pos1 = int(token.find("#")); // position of "live" in str
	if (pos1 != -1) {
		string str3 = token.substr(pos1 + 1);
		int val = stoi(str3);
		if (val > maxNumberOfVectors) {
			maxNumberOfVectors = val;
		}
		headerValues.push_back(val);
		counter++;
	}
	
	while (readRiverSWTimeSeries.good())
	{
		getline(readRiverSWTimeSeries, temp, ',');
		istringstream ss(temp);
		ss >> date;
	
		tempGWQcms = -99999;
		tempTCcms = -99999;
		
		if (readRiverSWTimeSeries.eof()) {
			break;
		}
		
		getline(readRiverSWTimeSeries, temp, ',');
		for (int i = 0; i < counter;) {
			getline(readRiverSWTimeSeries, temp, ',');
			istringstream ssGWQcms(temp);
			ssGWQcms >> tempGWQcms;
			indexValueForGW = int(headerValues.at(i));
			map<int, vector<double>>::iterator it = SW_Q_cms1.find(indexValueForGW);
			vector<double> tempVector;
			if (it == SW_Q_cms1.end()) {
				tempVector.push_back(tempGWQcms);
				SW_Q_cms1.insert(pair<int, vector<double>>(indexValueForGW, tempVector));
			}
			else {
				SW_Q_cms1[indexValueForGW].push_back(tempGWQcms);
			}
			if (i != counter - 2) {
				getline(readRiverSWTimeSeries, temp, ',');
			}
			else {
				getline(readRiverSWTimeSeries, temp, '\n');
			}
			i = i + 1;
			istringstream ssTCcms(temp);
			ssTCcms >> tempTCcms;
			indexValueForTC = int(headerValues.at(i));

			map<int, vector<double>>::iterator it1 = SW_T_C1.find(indexValueForTC);
			vector<double> tempVector1;
			if (it1 == SW_T_C1.end()) {
				tempVector1.push_back(tempTCcms);
				SW_T_C1.insert(pair<int, vector<double>>(indexValueForTC, tempVector1));
			}
			else {
				SW_T_C1[indexValueForTC].push_back(tempTCcms);
			}
			i = i + 1;
		}
	}
	readRiverSWTimeSeries.close();
}

//readHecRasFile function reads HEC-RAS output as spatial series input data for i-Tree CoolRiver model
void Inputs::readHecRasFile()
{
	fstream readHecRas(Directory_Input_CLArg + "HecRasData.csv");
	if (!readHecRas.good()) {
		cout << "Warning: HecRasData.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: This file contains HEC-RAS output as spatial series data required for model inputs." << endl;
		cout << "Correction: Please ensure HecRasData.csv exists in the input directory, and compare its format with a working test case." << endl;
		abort();
	}
	//cout << "Reading HecRasData.csv file\n" << endl;

	string temp;
	double tempDummy = -1.0;

	double temp_dist = -99999;
	double temp_totalQ = -99999;
	double temp_chElev = -99999;
	double temp_waterSurElev = -99999;
	double temp_velChnl = -99999;
	double temp_xArea = -99999;
	double temp_topWidth = -99999;
	double temp_waterSurSlope = -99999;
	double temp_wettedPerimeter = -99999;

	// getting the header line and the blank line out of the way
	getline(readHecRas, temp);

	// reading in a CSV
	while (readHecRas.good())
	{
		getline(readHecRas, temp, ',');
		getline(readHecRas, temp, ',');
		istringstream temp1(temp);
		temp1 >> temp_dist;
		downStreamDist.push_back(temp_dist);
		getline(readHecRas, temp, ',');
		istringstream temp2(temp);
		temp2 >> temp_totalQ;
		totalQ.push_back(temp_totalQ);
		getline(readHecRas, temp, ',');
		istringstream temp3(temp);
		temp3 >> temp_chElev;
		chElev.push_back(temp_chElev);
		getline(readHecRas, temp, ',');
		istringstream temp4(temp);
		temp4 >> temp_waterSurElev;
		waterSurElev.push_back(temp_waterSurElev);
		getline(readHecRas, temp, ',');
		istringstream temp5(temp);
		temp5 >> temp_velChnl;
		velChnl.push_back(temp_velChnl);
		getline(readHecRas, temp, ',');
		istringstream temp6(temp);
		temp6 >> temp_xArea;
		xArea.push_back(temp_xArea);
		getline(readHecRas, temp, ',');
		istringstream temp7(temp);
		temp7 >> temp_topWidth;
		topWidth.push_back(temp_topWidth);
		getline(readHecRas, temp, ',');
		istringstream temp8(temp);
		temp8 >> temp_waterSurSlope;
		waterSurSlope.push_back(temp_waterSurSlope);
		//Linebreak \n is read to signify end of row
		getline(readHecRas, temp, '\n');
		istringstream temp9(temp);
		temp9 >> temp_wettedPerimeter;
		wettedPerimeter.push_back(temp_wettedPerimeter);
	}
	totalCount = int(downStreamDist.size());
	readHecRas.close();
}

//readDischargeData function used by Model = ECDynamic to obtain discharge data
bool Inputs::readDischargeData(string& Directory_Input_CLArg)
{
	//If Flag_Runoff_Type is OBS (observed discharge)
	if (SimulationStringParams["Flag_Runoff_Type"] == "OBS") {
		//Read observed daily discharge data obtained from USGS gages
		ifstream readOBSDischarge(Directory_Input_CLArg + "Runoff_Observed.csv");
		//if unable to read observed discharge data
		if (!readOBSDischarge) {
			//In EC dynamic model, it will calculate EC and EMC database without running Buffer simulation if not providing LC and Discharge.
			if (SimulationStringParams["Model_Selection"] != "ECDynamic") {
				cout << "Warning: Runoff_Observed.csv could not be opened." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the Runoff_Observed.csv file is missing there is no gage daily mean discharge data to drive the simulation." << endl;
				cout << "Correction: Create a Runoff_Observed.csv file, either manually or perhaps using the version in the director ..." << endl;
				cout << "Correction: ... \\HydroPlus\\TestingFilesAndScript\\TestCases\\StatDynBuffer\\OnondagaLake\\input\\Runoff_Observed.csv." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//if Model_Selection == ECDynamic
			else {
				cout << "Warning:Runoff_Observed.csv could not be opened." << endl;
				cout << "Explanation: When the Runoff_Observed.csv file is missing there is no gage daily mean discharge data to drive the simulation." << endl;
				cout << "Correction: Create a Runoff_Observed.csv file, either manually or perhaps using the version in the director ..." << endl;
				cout << "Correction: ... \\HydroPlus\\TestingFilesAndScript\\TestCases\\StatDynBuffer\\OnondagaLake\\input\\Runoff_Observed.csv." << endl;
				cout << "The model will generate the local EC database without executing the entire model. " << endl;
				//false reading discharge will trigger the generation of EC database without executing the entire model; don't remove this line
				return false;
			}

		}
		else {
			string header, agency_cd, site_no, tempDischarge, tempmonth, tempday, tempyear;
			string temp_line;

			//read the fist line of the database: the header
			getline(readOBSDischarge, header);
			//Continue reading while data exist
			while (getline(readOBSDischarge, temp_line)) {
				istringstream ss(temp_line);
				getline(ss, agency_cd, ',');
				getline(ss, site_no, ',');
				getline(ss, tempmonth, ',');
				getline(ss, tempday, ',');
				getline(ss, tempyear, ',');
				getline(ss, tempDischarge, ',');

				//Remove the NA data -9999, and set up the Discharge threshold, for this case, we consider all events, th=0.
				if (int(stod(tempDischarge)) != -9999) {
					DischargeRecord.push_back(stod(tempDischarge));
					DischargeYear.push_back(int(stod(tempyear)));
					DischargeMonth.push_back(int(stod(tempmonth)));
					DischargeDay.push_back(int(stod(tempday)));
				}
			}
			return true;
		}
		// Unknown runoff type
		cout << "Error: Unknown Flag_Runoff_Type = " << SimulationStringParams["Flag_Runoff_Type"] << ". Expected 'OBS' or 'MOD'." << endl;
		abort();
	}

	//If Flag_Runoff_Type is MOD (predicted discharge)
	if (SimulationStringParams["Flag_Runoff_Type"] == "MOD") {
		//Read predicted discharge botained from HydroPlus simulation
		ifstream readMODDischarge(Directory_Input_CLArg + "Runoff_Predicted.csv");
		//if unable to read predicted discharge data
		if (!readMODDischarge) {
			//In EC dynamic model, it will calculate EC and EMC database without running Buffer simulation if not providing LC and Discharge.
			if (SimulationStringParams["Model_Selection"] != "ECDynamic") {
				cout << "Warning: Runoff_Predicted.csv could not be opened." << endl;
				cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
				cout << "Explanation: When the Runoff_Predicted.csv file is missing there is no predicted daily mean discharge data to drive the simulation." << endl;
				cout << "Correction: Generate a Runoff_Predicted_for_Calibration.csv by running HydroPlus StatisticalHydro and turn Flag_CompareToObservedRunoff to 1..." << endl;
				cout << "Correction: ... then copy this file to your input folder and rename the file as Runoff_Predicted.csv." << endl;
				//Call abort function, which ends the HydroPlus.exe simulation
				abort();
			}
			//if Model_Selection == ECDynamic
			else {
				cout << "Warning:Runoff_Predicted.csv could not be opened." << endl;
				cout << "Explanation: When the Runoff_Predicted.csv file is missing there is no predicted daily mean discharge data to drive the simulation." << endl;
				cout << "Correction: Generate a Runoff_Predicted_for_Calibration.csv by running HydroPlus StatisticalHydro and turn Flag_CompareToObservedRunoff to 1..." << endl;
				cout << "Correction: ... then copy this file to your input folder and rename the file as Runoff_Predicted.csv." << endl;
				cout << "The model will generate the local EC database without executing the entire model. " << endl;
				//false reading discharge will trigger the generation of EC database without executing the entire model; don't remove this line
				return false;
			}
		}
		else {
			string header, YYYYMMDD, HHMMSS, tempDischarge;
			string temp_line;

			//read the fist line of the database: the header
			getline(readMODDischarge, header);
			//Continue reading while data exist
			while (getline(readMODDischarge, temp_line)) {
				istringstream ss(temp_line);
				getline(ss, YYYYMMDD, ',');
				getline(ss, HHMMSS, ',');
				getline(ss, tempDischarge, ',');

				//Remove the NA data -9999, and set up the Discharge threshold, for this case, we consider all events, th=0.
				if (int(stod(tempDischarge)) != -9999) {
					DischargeRecord.push_back(stod(tempDischarge));
					//read the substring YYYY as int year
					string yearString = YYYYMMDD.substr(0, 4);
					string monthString = YYYYMMDD.substr(4, 2);
					string dayString = YYYYMMDD.substr(6, 2);

					// Convert strings to integers
					DischargeYear.push_back(stoi(yearString));
					DischargeMonth.push_back(stoi(monthString));
					DischargeDay.push_back(stoi(dayString));
				}
			}
			return true;
		}
	}
}

//sanitizeLineEndings function removes any newline or carriage return characters from the given string 
//Note: This ensures that dynamically constructed variable names (e.g., for pollutants) remain on a single line and consistent across platforms (Windows/Linux).
string Inputs::sanitizeLineEndings(const string& variable_string) {
	string clean = variable_string;
	clean.erase(remove(clean.begin(), clean.end(), '\n'), clean.end());
	clean.erase(remove(clean.begin(), clean.end(), '\r'), clean.end());
	return clean;
}

//readPollutionData function reads the Pollutants.csv file and is used in Model = StatisticalHydro, including GI treatment in Removal_StormwaterPollutants.cpp
//Note Pollutants.csv has 1st row any text, 2nd row pollutant names, 3rd row median concentrations (mg/L), 4th row mean concentrations(mg/L)
//References:
//Smullen, J. T., Shallcross,A. L., and Cave, K. A. 1999. Updating the U.S. nationwide urban runoff quality database. Water Science Technology 39(12), 9-16.  
//Stephan, E. A., & Endreny, T. A. (2016). Weighting Nitrogen and Phosphorus Pixel Pollutant Loads to Represent Runoff and Buffering Likelihoods. JAWRA Journal of the American Water Resources Association, 52(2), 336-349. doi:doi:10.1111/1752-1688.12390
void Inputs::readPollutionData(string& Directory_Input_CLArg)
{
	string headerline;
	string LineOfInput;
	string PollutantConcentrationMedian_mg_p_L, PollutantConcentrationMean_mg_p_L;
	string RowName_string;
	string PollutantName_string;

	//Pollutants.csv has concentration values in mg/L
	ifstream readPollutant(Directory_Input_CLArg + "Pollutants.csv");

	//If not able to open Pollutants.csv then it was not likely provided, and use default values taken from Smullen et al. (1999)
	if (!readPollutant) {
		//Command Prompt Notification to user that default pollutant event mean concentration values are being used
		cout << "Note: Pollutant input concentrations set to default given absence of Pollutants.csv in input folder." << endl;

		//Assign default values for pollutant event mean concentration values from Smullen et al. (1999), using a pooled USGS and NURP average
		//Note: Optional concentrations are provided in Stephan and Endreny (2016), for a variety of land cover types, some units in ug/L
		//Pollutant_Name are TSS, BOD, COD, TP, SolP, TKN, NO2_3, Cu, Pb, ZN
		Pollutant_Name = { "TSS","BOD", "COD", "TP", "SolP", "TKN", "NO2_3", "Cu", "Pb", "Zn" };
		//Pollutant_ConcentrationMedian_mg_p_L are median concentrations in units of mg/L
		Pollutant_ConcentrationMedian_mg_p_L = { 54.5, 11.5, 44.7, 0.259, 0.103, 1.47, 0.533, 0.0111, 0.0507, 0.129 };
		//Pollutant_ConcentrationMean_mg_p_L are mean concentrations in units of mg/L, as reported in the HydroPlus_TechnicalManual.pdf
		Pollutant_ConcentrationMean_mg_p_L = { 78.4, 14.1, 52.8, 0.315, 0.129, 1.73, 0.658, 0.0135, 0.0675, 0.162 };
	}
	else {
		// Read the header lines
		getline(readPollutant, headerline);
		getline(readPollutant, headerline);

		//Read the pollutant names
		istringstream PollutantNameList_string(headerline);
		//Skip the first field
		getline(PollutantNameList_string, RowName_string, ',');
		//while PollutantNameList_string has remaining items, send them to PollutantName_string
		while (getline(PollutantNameList_string, PollutantName_string, ',')) {
			//Pollutant_Name vector stores the pollutant names (TSS BOD COD TP SolP TKN NO2_3 Cu Pb Zn)
			//Note: These pollutant names must equal the names used in HydroPlusConfig.xml for removal efficiencies, as the names are matched
			Pollutant_Name.push_back(PollutantName_string);
		}

		// to sanitize Pollutant_Name entries only on Linux
		//sanitize function removes any newline or carriage return characters 
		#if defined(__linux__) || defined(__gnu_linux__)
				for (auto& pollutantLabel : Pollutant_Name) {
					pollutantLabel = Inputs::sanitizeLineEndings(pollutantLabel);
				}
		#endif

		//getline function reads 1st data line from pollutant file, the Median concentration (mg/L)
		getline(readPollutant, LineOfInput);
		//istringstream function constructs an object PollutantConcentrationMedian_string to hold strings from LineOfInput
		istringstream PollutantConcentrationMedian_string(LineOfInput);
		//Skip the first field
		getline(PollutantConcentrationMedian_string, RowName_string, ',');
		//while PollutantConcentrationMedian_string has remaining items, send them to PollutantConcentrationMedian_mg_p_L
		while (getline(PollutantConcentrationMedian_string, PollutantConcentrationMedian_mg_p_L, ','))
		{
			//Pollutant_ConcentrationMedian_mg_p_L vector stores the median values (mg/L)
			Pollutant_ConcentrationMedian_mg_p_L.push_back(stod(PollutantConcentrationMedian_mg_p_L));
		}

		//getline function reads 2nd data line from readPollutant file, the Mean concentration (mg/L)
		getline(readPollutant, LineOfInput);
		//istringstream function constructs an object PollutantConcentrationMean_string to hold strings from LineOfInput
		istringstream PollutantConcentrationMean_string(LineOfInput);
		//Skip the first field
		getline(PollutantConcentrationMean_string, RowName_string, ',');
		//while PollutantConcentrationMean_string has remaining items, send them to PollutantConcentrationMean_mg_p_L
		while (getline(PollutantConcentrationMean_string, PollutantConcentrationMean_mg_p_L, ','))
		{
			//Pollutant_ConcentrationMean_mg_p_L vector stores the mean values (mg/L)
			Pollutant_ConcentrationMean_mg_p_L.push_back(stod(PollutantConcentrationMean_mg_p_L));
		}
		readPollutant.close();
	}
}

//RataDieNumber function converts Gregorian date YYYY MM DD into an integer number based on Rata Die System; https://en.wikipedia.org/wiki/Rata_Die
//Note: Rata Die Dates (RDD) are equivalent to Julian Dates (JD), where RDD = JD - 1,721,424.5
//Note: Leap years are incorporated into the Rata Die System: 
//Note: Leap year = yes when the YYYY number is divisible by 4, except for end-of-century years, which must be divisible by 400. 
//Note: Examples of Leap and non-Leap years: 1900 no; 2000 yes; 2020, 2024 and 2028 yes. 
int Inputs::RataDieNumber(int y, int m, int d)
{
	//Date_RataDieMethod is a number assigned to a calendar date, 
	int Date_RataDieMethod;

	//Algorithm for Month and Year adjustment for Rata Die Date and Julian Date functions
	//if m month is less than 3, January or February
	if (m < 3) {
		//y year is decreased by 1
		y = y - 1;
		//m month is increased by 12, becoming 13 or 14 month
		m = m + 12;
	}
	//Date_RataDieMethod Equation from https://stackoverflow.com/questions/14218894/number-of-days-between-two-dates-c
	//Note: Date_RataDieMethod = 365 * y + y / 4 - y / 100 + y / 400 + (153 * m - 457) / 5 + d - 306
	//Note: Method requires integer division, which C++ upholds if numerator and denominator are integers
	Date_RataDieMethod = 365 * y + y / 4 - y / 100 + y / 400 + (153 * m - 457) / 5 + d - 306;

	return Date_RataDieMethod;
}


//ExtractGregorianDateParts extracts YYYY MM DD and if available HH into an array, where int DateParts_Gregorian[4]
//Note: Function is sent Date_YYYYMMDDHH value and an empty DateParts_Gregorian, a vector integer
void Inputs::ExtractGregorianDateParts(int Date_YYYYMMDDHH, int DateParts_Gregorian[]) {

	//DateParts_Gregorian is integer array containing the Gregorian date parts, YYYY, MM, DD, and if available HH
	string Date_YYYYMMDD = to_string(Date_YYYYMMDDHH);
	//DateParts_Gregorian[0] as year YYYY from Date_YYYYMMDD, defined to contain result of atoi and substr(,).c_str() functions
	DateParts_Gregorian[0] = atoi(Date_YYYYMMDD.substr(0, 4).c_str());
	//DateParts_Gregorian[1] as month MM from Date_YYYYMMDD, defined to contain result of atoi and substr(,).c_str() functions
	DateParts_Gregorian[1] = atoi(Date_YYYYMMDD.substr(4, 2).c_str());
	//DateParts_Gregorian[2] as day DD from Date_YYYYMMDD, defined to contain result of atoi and substr(,).c_str() functions
	DateParts_Gregorian[2] = atoi(Date_YYYYMMDD.substr(6, 2).c_str());

	//if Date_YYYYMMDD length > 8 then it exceeds YYYYMMDD and contains HH, hour
	if (Date_YYYYMMDD.length() > 8) {
		//DateParts_Gregorian[3] as day HH from Date_YYYYMMDD, defined to contain result of atoi and substr(,).c_str() functions
		DateParts_Gregorian[3] = atoi(Date_YYYYMMDD.substr(8, 2).c_str());

		//If DateParts_Gregorian[3] > 23, then set to 23
		//Note: HH in the YYYYMMDDHH string should range from 0 to 23
		if (DateParts_Gregorian[3] > 23) {
			//DateParts_Gregorian[3] defined as 23, final hour of day
			DateParts_Gregorian[3] = 23;
			//Command Prompt to allert user
			cout << "Note: HydroPlusConfig.xml element ...Date_YYYYMMDD = " << Date_YYYYMMDDHH << ", > max hour, so HH=23 used in simulation." << endl;
		}
		//If DateParts_Gregorian[3] < 0, then set to 0
		//Note: HH in the YYYYMMDDHH string should range from 0 to 23
		if (DateParts_Gregorian[3] < 0) {
			//DateParts_Gregorian[3] defined as 0, final hour of day
			DateParts_Gregorian[3] = 0;
			//Command Prompt to allert user
			cout << "Note: HydroPlusConfig.xml element ...Date_YYYYMMDD = " << Date_YYYYMMDDHH << ", < min hour, so HH=00 used in simulation." << endl;
		}
	}
	//if Date_YYYYMMDD length < 8 then it does not contain hour
	else {
		//DateParts_Gregorian[3] as day HH from Date_YYYYMMDD, defined to contain -1 and indicate it has not been set
		DateParts_Gregorian[3] = -1;
	}

}

//GregorianToJulianDate function converts Gregorian date YYYY MM DD into number of days since January 1, 4713 BCE
//Note: DateParts_Gregorian contains the 0=YYYY, 1=MM, 2=DD, 3=HH
//Note: Called by JulianDateToWeekday to generate day of week for daylight savings time functions
//Reference: Reda, I., & Andreas, A. (2003, 2008). Solar Position Algorithm for Solar Radiation Applications National Renewable Energy Laboratory, NREL/TP-560-34302. Revised 2008, 55.  
int Inputs::GregorianToJulianDate(int DateParts_Gregorian[])
{
	//Date_JulianDate_base_4713BCE_int, _01, _02 are parts of equation to obtain a number from a calendar date
	int Date_JulianMethod_01;
	int Date_JulianMethod_02;
	//Date_JulianDate_base_4713BCE_int number as integer, number of days since UTC 0:00 January 1, 4713 BCE 
	int Date_JulianDate_base_4713BCE_int;
	//Date_JulianDate_base_4713BCE_float number as float, number of days since UTC 12:00 January 1, 4713 BCE 
	float Date_JulianDate_base_4713BCE_float;

	//Gregorian date parts: Year_YYYY is year, Month_MM is month, Day_DD is day
	int Year_YYYY, Month_MM, Day_DD;
	Year_YYYY = DateParts_Gregorian[0];
	Month_MM = DateParts_Gregorian[1];
	Day_DD = DateParts_Gregorian[2];

	//Algorithm for Month and Year adjustment for Julian Date functions
	//if Month_MM month is less than 3, January or February
	if (Month_MM < 3) {
		//Year_YYYY year is decreased by 1
		Year_YYYY = Year_YYYY - 1;
		//Month_MM month is increased by 12, becoming 13 or 14 month
		Month_MM = Month_MM + 12;
	}

	//Date_JulianDate_base_4713BCE_int Equation from https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
	//Note: Reda et al (2003, 2008) has similar calculation in Eq 4. 
	//Note: Date_JulianDate_base_4713BCE_int = 2 -(Year_YYYY/100) + (Year_YYYY/100)/4 + Day_DD 
	//Note: ... + 365.25 * (Year_YYYY + 4716) + 30.6001 * (Month_MM+1) - 1524.5
	//Note: Method requires integer division, which C++ upholds if numerator and denominator are integers
	//Note: Method requires integer multiplication, which requires type casting with (int) 
	 
	//Date_JulianMethod_01 is first terms on RHS of equation, which remain integer
	Date_JulianMethod_01 = 2 - (Year_YYYY / 100) + (Year_YYYY / 100) / 4 + Day_DD;
	//Date_JulianMethod_02 is middle terms on RHS of equation, which uses floor in multiplication to get integer products 
	Date_JulianMethod_02 = int(floor(365.25 * (Year_YYYY + 4716)) + floor(30.6001 * (Month_MM + 1)));

	//Date_JulianDate_base_4713BCE_float is combination of 1st integer term, 2nd integer term, last RHS float term of equation
	//Note: Date_JulianDate_base_4713BCE_float is not returned from function
	Date_JulianDate_base_4713BCE_float = float(Date_JulianMethod_01 + Date_JulianMethod_02 - 1524.5);
	//Date_JulianDate_base_4713BCE_int is combination of 1st integer term, 2nd integer term, last RHS integer term of equation
	Date_JulianDate_base_4713BCE_int = Date_JulianMethod_01 + Date_JulianMethod_02 - 1524;

	//return Date_JulianDate_base_4713BCE_int
	//Date_JulianDate_base_4713BCE_int is the real number of days since start of January 1, 4713 BCE 
	return Date_JulianDate_base_4713BCE_int;
}

//JulianDateToWeekday function converts Julian Date System integer into weekday integer, ranging from 0=Sunday to 6=Saturday
//Note: Julian Dates in HydroPlus are integer, adjusted to the prior UTC midnight
//Note: See GregorianToJulianDate function for definitions of Julian Dates, and conversion from float to integer 
int Inputs::JulianDateToWeekday(int Date_JulianDate_base_4713BCE_int) {
	//Date_WeekDayCount_Code_0Sunday number as integer for 0 to 6, indicating weekdays 0=Sunday to 6=Saturday
	int Date_WeekDayCount_Code_0Sunday;
	//Convert Julian Date to Day of Week Equation: Date_WeekDayCount_Code_0Sunday = (Date_JulianDate_base_4713BCE_int + 1) % 7
	//Note: Theory and Equation at: https://en.wikipedia.org/wiki/Julian_day
	//Note: WeekDay integers: 0=Sunday | 1=Monday | 2= Tuesday | 3=Wednesday | 4=Thursday | 5=Friday | 6=Saturday
	//Date_WeekDayCount_Code_0Sunday number as integer for 0 to 6, indicating weekdays 0=Sunday to 6=Saturday
	Date_WeekDayCount_Code_0Sunday = (Date_JulianDate_base_4713BCE_int + 1) % 7;

	//return Date_WeekDayCount_Code_0Sunday 
	return Date_WeekDayCount_Code_0Sunday;
}


//Date_YYYYMMDD_to_JulianDay function optains Julian day of a calendar date during a given year
//Note: Date_YYYYMMDD_YYYYMMDDHH can be 8 or more digits long, including hour HH
int Inputs::Date_YYYYMMDD_to_JulianDay(int Date_YYYYMMDD_YYYYMMDDHH) {

	int Date_YYYYMMDD;
	//Date_YYYYMMDD_ambiguous_str is to_string(Date_YYYYMMDD_YYYYMMDDHH) using to_string function
	//Note: Date_YYYYMMDD_ambiguous_str allows for 8 or more digits, in case HH was sent
	string Date_YYYYMMDD_ambiguous_str = to_string(Date_YYYYMMDD_YYYYMMDDHH);
	//Date_YYYYMMDD_str is Date_YYYYMMDD_ambiguous_str.substr(0, 8)
	//Note: First 8 digits of Date_YYYYMMDD_ambiguous_str using substring function
	string Date_YYYYMMDD_str = Date_YYYYMMDD_ambiguous_str.substr(0, 8);
	//Date_YYYYMMDD = stoi(Date_YYYYMMDD_str)
	Date_YYYYMMDD = stoi(Date_YYYYMMDD_str);

	//Year_YYYY = Date_YYYYMMDD / 10000, which is division by 10000 to remove the MMDD digits
	int Year_YYYY = Date_YYYYMMDD / 10000;
	//Month_MM = (Date_YYYYMMDD / 100) % 100, which is division by 100 to remove the DD digits, and modulus of 100 capturing last 2 digits
	int Month_MM = (Date_YYYYMMDD / 100) % 100;
	//Day_DD = Date_YYYYMMDD % 100, which is modulus of 100 capturing last 2 digits
	int Day_DD = Date_YYYYMMDD % 100;

	//JulianDay_StartOfMonth_vec is vector for the cumulative number of days at the start of each month for a non-leap year
	vector<int> JulianDay_StartOfMonth_vec = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

	//Flag_LeapYear is ((Year_YYYY % 4 == 0 && Year_YYYY % 100 != 0) || (Year_YYYY % 400 == 0)) ? 1 : 0;
	//Note: Flag_LeapYear is 1 if a leap year or 0 if not, set using ternary function
	//Note: Leap years are divisible by 4 and not divisible by 100, or divisible by 400
	//Note: Examples of Leap and non-Leap years: 1900 no; 2000 yes; 2020, 2024 and 2028 yes. 
	int Flag_LeapYear = ((Year_YYYY % 4 == 0 && Year_YYYY % 100 != 0) || (Year_YYYY % 400 == 0)) ? 1 : 0;

	//Day_JulianDay = JulianDay_StartOfMonth_vec[Month_MM] + Day_DD, adding day to start of month 
	//Note: This calculates the Julian day
	int Day_JulianDay = JulianDay_StartOfMonth_vec[Month_MM] + Day_DD;

	//If Flag_LeapYear is 1 or true, and Month_MM is after February, add 1 day
	if (Flag_LeapYear && Month_MM > 2) {
		//Day_JulianDay += 1, adding one day
		Day_JulianDay += 1;
	}
	return Day_JulianDay;
}

//Get_DaysInMonth function will return days in the month
int Inputs::Month_Year_to_Days_in_Month(int Year_YYYY, int Month_MM) {
	int Days_in_Month = 0;

	//if (Month_MM < 1 || Month_MM > 12) then return 0, to validate month input
	if (Month_MM < 1 || Month_MM > 12) {
		//Note: Consider refactor to send warning
		//return Days_in_Month 
		return Days_in_Month;
	}

	//DaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	//Note: Vector contains number of days in each month for non-leap year
	const int DaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	//if (Month_MM == 2)
	//Note: Special case for February in leap years
	if (Month_MM == 2) {
		//Flag_LeapYear = ((Year_YYYY % 4 == 0 && Year_YYYY % 100 != 0) || (Year_YYYY % 400 == 0))
		//Note: Flag_LeapYear is true if a leap year or false if not
		//Note: Leap years are divisible by 4 and not divisible by 100, or divisible by 400
		//Note: Examples of Leap and non-Leap years: 1900 no; 2000 yes; 2020, 2024 and 2028 yes. 
		bool Flag_LeapYear = ((Year_YYYY % 4 == 0 && Year_YYYY % 100 != 0) || (Year_YYYY % 400 == 0));

		//Days_in_Month is Flag_LeapYear ? 29 : 28 using ternary for true = 29, false = 28
		Days_in_Month = Flag_LeapYear ? 29 : 28;
		//return Days_in_Month 
		return Days_in_Month;
	}

	//Days_in_Month = DaysInMonth[Month_MM - 1]
	//Note: Indexing starts at 0, and Month_MM - 1 achieves indexing
	//Note: This assignment only happens if not February, which returned value above
	Days_in_Month = DaysInMonth[Month_MM - 1];

	//return Days_in_Month 
	return Days_in_Month;
}

void Inputs::printIncompleteECDataWarning(const string& landCoverType, const string& landCoverRange, const string& huc) {
	cout << endl;
	cout << "Warning: Incomplete Data for " << landCoverType << " Land Cover in WhiteData.csv for the specified HUC." << endl;
	cout << "Explanation: The HUC specified by " << huc << " includes " << landCoverType << " land cover types (" << landCoverRange << "), but the EC value for " << landCoverType << " is missing in WhiteData.csv." << endl;
	if (landCoverType == "Pasture_hay" || landCoverType == "Cropland") {
		string otherLandCoverType = (landCoverType == "Pasture_hay") ? "Cropland" : "Pasture_hay";
		cout << "Correction: If EC values of " << otherLandCoverType << " are found in WhiteData.csv, the EC values will be defaulted to " << otherLandCoverType << ". Otherwise, the EC values will be defaulted to 0. Please update the EC value for " << landCoverType << " land cover in WhiteData.csv for accurate simulation results." << endl;
	}
	else {
		cout << "Correction: The EC values will be defaulted to 0. Please update the EC value for " << landCoverType << " land cover in WhiteData.csv for accurate simulation results." << endl;
	}
	cout << endl;
}

void Inputs::readExportCoefficientTable()
{
	//ExportCoefficientTable_str set to value of HydroPlusConfig.xml element RefWeatherLocationAttributeFile
	string ExportCoefficientTable_str;
	ExportCoefficientTable_str = BufferNutrientsStringParams["ExportCoefficientFile"];

	//If ExportCoefficientTable_str is not populated, then HydroPlusConfig.xml is missing file, and look in local directory
	//Note: This option is for those our NUCFAC National runs
	if (ExportCoefficientTable_str.size() < 1) {
		ExportCoefficientTable_str = Directory_Input_CLArg + "ExportCoefficientTable.csv";
	}

	fstream readExportCoefficientTable(ExportCoefficientTable_str);
	//if readExportCoefficientTable does not exist, alert user
	if (!readExportCoefficientTable) {
		cout << "Warning: ExportCoefficientFile.csv could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the ExportCoefficientFile.csv file is missing there is no export coefficient database to drive the simulation." << endl;
		cout << "Correction: Create a ExportCoefficientFile.csv file, either manually or perhaps using the version in the director ..." << endl;
		cout << "Correction: ... C:\\iTree\\HydroPlus\\TestingFilesAndScript\\TestCases\\BufferNutrients\\WhiteData.csv." << endl;
		cout << "Then, update the file path in the <ExportCoefficientFile> section of HydroPlusConfig.xml." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	// Check if HUC8 is provided
	if (BufferNutrientsStringParams["HUC8"].empty()) {
		cout << "Warning: No HUC8 provided." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The HUC8 code is essential for determining the EC value for each land type within the simulated basin."  << endl;
		cout << "Correction: Please specify a valid HUC8 in the HydroPlusConfig.xml." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}

	string HUC8, header, Landuse, Parameter, SampleCount;
	string temp_line, temp_string;
	string Spercentile_5, Spercentile_10, Spercentile_25, Spercentile_50, Spercentile_75, Spercentile_95, Spercentile_100;
	double percentile_5, percentile_10, percentile_25, percentile_50, percentile_75, percentile_95, percentile_100;
	//EC vectors
	vector<double> Cropland_Sediment_vec_kg_p_ha(7, 0.0), Cropland_Total_N_vec_kg_p_ha(7, 0.0), Cropland_Total_P_vec_kg_p_ha(7, 0.0);
	vector<double> Forest_Sediment_vec_kg_p_ha(7, 0.0), Forest_Total_N_vec_kg_p_ha(7, 0.0), Forest_Total_P_vec_kg_p_ha(7, 0.0);
	vector<double> Rangeland_Sediment_vec_kg_p_ha(7, 0.0), Rangeland_Total_N_vec_kg_p_ha(7, 0.0), Rangeland_Total_P_vec_kg_p_ha(7, 0.0);
	vector<double> Pasture_hay_Sediment_vec_kg_p_ha(7, 0.0), Pasture_hay_Total_N_vec_kg_p_ha(7, 0.0), Pasture_hay_Total_P_vec_kg_p_ha(7, 0.0);
	vector<double> Urban_Sediment_vec_kg_p_ha(7, 0.0), Urban_Total_N_vec_kg_p_ha(7, 0.0), Urban_Total_P_vec_kg_p_ha(7, 0.0);
	//EMC vectors
	/*vector<double> Cropland_Sediment_vec_mg_p_L, Cropland_Total_N_vec_mg_p_L, Cropland_Total_P_vec_mg_p_L;
	vector<double> Forest_Sediment_vec_mg_p_L, Forest_Total_N_vec_mg_p_L, Forest_Total_P_vec_mg_p_L;
	vector<double> Rangeland_Sediment_vec_mg_p_L, Rangeland_Total_N_vec_mg_p_L, Rangeland_Total_P_vec_mg_p_L;
	vector<double> Pasture_hay_Sediment_vec_mg_p_L, Pasture_hay_Total_N_vec_mg_p_L, Pasture_hay_Total_P_vec_mg_p_L;
	vector<double> Urban_Sediment_vec_mg_p_L, Urban_Total_N_vec_mg_p_L, Urban_Total_P_vec_mg_p_L;*/
	//read water volumn to calculate EMC, surface runoff and water yield
	vector<double> Cropland_SURQ_mm(7, 0.0), Forest_SURQ_mm(7, 0.0), Rangeland_SURQ_mm(7, 0.0), Pasture_hay_SURQ_mm(7, 0.0), Urban_SURQ_mm(7, 0.0);
	vector<double> Cropland_WYLD_mm(7, 0.0), Forest_WYLD_mm(7, 0.0), Rangeland_WYLD_mm(7, 0.0), Pasture_hay_WYLD_mm(7, 0.0), Urban_WYLD_mm(7, 0.0);


	//read the fist line of the database: the header
	getline(readExportCoefficientTable, header);
	//For some HUCs in White's database, cropland or pasture_hay data is missing, this is the flag to track if cropland data exist 
	bool db_cropland_found = false;
	bool db_pasturehay_found = false;
	bool db_forest_found = false;
	bool db_rangeland_found = false;
	bool db_urban_found = false;
	bool HUC8_found = false;
	//If the next data exist, read a line in as the temp_line
	while (getline(readExportCoefficientTable, temp_line)) {
		//read cells within this line
		istringstream readlinedata(temp_line);
		//if (!getline(readlinedata, temp_string, ',')) break;
		getline(readlinedata, HUC8, ',');

		//cout << temp_line << endl;
		//cout << BufferNutrientsStringParams["HUC8"] << "and " << HUC8 << endl;
		
		if (HUC8 == BufferNutrientsStringParams["HUC8"] || BufferNutrientsStringParams["HUC8"].substr(1) == HUC8) {
			//found the HUC 8 in the EC database
			HUC8_found = true;
			//read each row: 1 landuse, 1 parameter & 7 values, then saving the EC values into a temp vector:ECValue
			//readExportCoefficientTable >> Landuse >> Parameter >> header >> percentile_5 >> percentile_10 >> percentile_25 >> percentile_50 >> percentile_75 >> percentile_95 >> percentile_100;
			getline(readlinedata, Landuse, ',');
			getline(readlinedata, Parameter, ',');
			getline(readlinedata, SampleCount, ',');
			getline(readlinedata, Spercentile_5, ',');
			getline(readlinedata, Spercentile_10, ',');
			getline(readlinedata, Spercentile_25, ',');
			getline(readlinedata, Spercentile_50, ',');
			getline(readlinedata, Spercentile_75, ',');
			getline(readlinedata, Spercentile_95, ',');
			getline(readlinedata, Spercentile_100, ',');
            //stod() from string to double
			percentile_5 = stod(Spercentile_5);
			percentile_10 = stod(Spercentile_10);
			percentile_25 = stod(Spercentile_25);
			percentile_50 = stod(Spercentile_50);
			percentile_75 = stod(Spercentile_75);
			percentile_95 = stod(Spercentile_95);
			percentile_100 = stod(Spercentile_100);
			//cout << Landuse << ',' << Parameter << ',' << header << ',' << percentile_5 << ',' << percentile_10 << ',' << percentile_25 << ',' << percentile_50 << ',' << percentile_75 << ',' << percentile_95 << ',' << percentile_100 << endl;
			//npos returns the index of the first occurrence of the substring in the string from given starting position. The default value of starting position is 0.
			//remove the first character to avoid the error caused by the upper and lower case. Ex. "ropland" mwans Cropland.
			//cout << Landuse << endl;
			if (Landuse.find("ropland") != string::npos) {
				db_cropland_found = true;
				if (Parameter.find("ediment") != string::npos) {
					Cropland_Sediment_vec_kg_p_ha = { percentile_5 * Ratio_Tonne_to_kg,percentile_10 * Ratio_Tonne_to_kg,percentile_25 * Ratio_Tonne_to_kg,percentile_50 * Ratio_Tonne_to_kg,percentile_75 * Ratio_Tonne_to_kg,percentile_95 * Ratio_Tonne_to_kg,percentile_100 * Ratio_Tonne_to_kg };
				}
				else if (Parameter.find("Total_N") != string::npos) {
					Cropland_Total_N_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Total_P") != string::npos) {
					Cropland_Total_P_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Surface Runoff") != string::npos) {
					Cropland_SURQ_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Water Yield") != string::npos) {
					Cropland_WYLD_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
			}
			else if (Landuse.find("orest") != string::npos) {
				db_forest_found = true;
				if (Parameter.find("ediment") != string::npos) {
					Forest_Sediment_vec_kg_p_ha = { percentile_5 * Ratio_Tonne_to_kg,percentile_10 * Ratio_Tonne_to_kg,percentile_25 * Ratio_Tonne_to_kg,percentile_50 * Ratio_Tonne_to_kg,percentile_75 * Ratio_Tonne_to_kg,percentile_95 * Ratio_Tonne_to_kg,percentile_100 * Ratio_Tonne_to_kg };
				}
				else if (Parameter.find("Total_N") != string::npos) {
					Forest_Total_N_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Total_P") != string::npos) {
					Forest_Total_P_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Surface Runoff") != string::npos) {
					Forest_SURQ_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Water Yield") != string::npos) {
					Forest_WYLD_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
			}
			else if (Landuse.find("angeland") != string::npos) {
				db_rangeland_found = true;
				if (Parameter.find("ediment") != string::npos) {
					Rangeland_Sediment_vec_kg_p_ha = { percentile_5 * Ratio_Tonne_to_kg,percentile_10 * Ratio_Tonne_to_kg,percentile_25 * Ratio_Tonne_to_kg,percentile_50 * Ratio_Tonne_to_kg,percentile_75 * Ratio_Tonne_to_kg,percentile_95 * Ratio_Tonne_to_kg,percentile_100 * Ratio_Tonne_to_kg };
				}
				else if (Parameter.find("Total_N") != string::npos) {
					Rangeland_Total_N_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Total_P") != string::npos) {
					Rangeland_Total_P_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Surface Runoff") != string::npos) {
					Rangeland_SURQ_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Water Yield") != string::npos) {
					Rangeland_WYLD_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
			}
			else if (Landuse.find("hay") != string::npos) {
				db_pasturehay_found = true;
				if (Parameter.find("ediment") != string::npos) {
					Pasture_hay_Sediment_vec_kg_p_ha = { percentile_5 * Ratio_Tonne_to_kg,percentile_10 * Ratio_Tonne_to_kg,percentile_25 * Ratio_Tonne_to_kg,percentile_50 * Ratio_Tonne_to_kg,percentile_75 * Ratio_Tonne_to_kg,percentile_95 * Ratio_Tonne_to_kg,percentile_100 * Ratio_Tonne_to_kg };
				}
				else if (Parameter.find("Total_N") != string::npos) {
					Pasture_hay_Total_N_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Total_P") != string::npos) {
					Pasture_hay_Total_P_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Surface Runoff") != string::npos) {
					Pasture_hay_SURQ_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Water Yield") != string::npos) {
					Pasture_hay_WYLD_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
			}
			else if (Landuse.find("rban") != string::npos) {
				db_urban_found = true;
				if (Parameter.find("ediment") != string::npos) {
					Urban_Sediment_vec_kg_p_ha = { percentile_5 * Ratio_Tonne_to_kg,percentile_10 * Ratio_Tonne_to_kg,percentile_25 * Ratio_Tonne_to_kg,percentile_50 * Ratio_Tonne_to_kg,percentile_75 * Ratio_Tonne_to_kg,percentile_95 * Ratio_Tonne_to_kg,percentile_100 * Ratio_Tonne_to_kg };
				}
				else if (Parameter.find("Total_N") != string::npos) {
					Urban_Total_N_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Total_P") != string::npos) {
					Urban_Total_P_vec_kg_p_ha = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Surface Runoff") != string::npos) {
					Urban_SURQ_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
				else if (Parameter.find("Water Yield") != string::npos) {
					Urban_WYLD_mm = { percentile_5,percentile_10,percentile_25,percentile_50,percentile_75,percentile_95,percentile_100 };
				}
			}
		}
	}

	if (!HUC8_found) {
		cout << "Warning: HUC8 value '" << BufferNutrientsStringParams["HUC8"] << "' not found in the export coefficient database." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The HUC8 code is essential for determining the EC value for each land type within the simulated basin." << endl;
		cout << "Correction: Verify the HUC8 value and confirm it is present in the ExportCoefficientFile.csv." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	//20231117 LI for debugging
	/*cout << "LC21 + LC22 + LC23 + LC24 " << LC21 + LC22 + LC23 + LC24 << endl;
	cout << "LC41 + LC42 + LC43 " << LC41 + LC42 + LC43 << endl;
	cout << "LC71 " << LC71 << endl;
	cout << "81 " << LC81 << endl;
	cout << "82 " << LC82 << endl;*/

	if ((LC21 + LC22 + LC23 + LC24) > 0 && db_urban_found == false) {
		printIncompleteECDataWarning("Urban", "21 - 24", BufferNutrientsStringParams["HUC8"]);
	}
	if ((LC41 + LC42 + LC43) > 0 && db_forest_found == false) {
		printIncompleteECDataWarning("Forest", "41 - 43", BufferNutrientsStringParams["HUC8"]);
	}
	if ((LC71) > 0 && db_rangeland_found == false) {
		printIncompleteECDataWarning("Rangeland", "71", BufferNutrientsStringParams["HUC8"]);
	}
	if ((LC81) > 0 && db_pasturehay_found == false) {
		printIncompleteECDataWarning("Pasture_hay", "81", BufferNutrientsStringParams["HUC8"]);
	}
	if ((LC82) > 0 && db_cropland_found == false) {
		printIncompleteECDataWarning("Cropland", "82", BufferNutrientsStringParams["HUC8"]);
	}

	//if the cropland LC 82 data is missing in White's database, assign LC 81 pasture/hay to LC 82
	if (!db_cropland_found && db_pasturehay_found) {
		Cropland_Sediment_vec_kg_p_ha.assign(Pasture_hay_Sediment_vec_kg_p_ha.begin(), Pasture_hay_Sediment_vec_kg_p_ha.end());
		Cropland_Total_N_vec_kg_p_ha.assign(Pasture_hay_Total_N_vec_kg_p_ha.begin(), Pasture_hay_Total_N_vec_kg_p_ha.end());
		Cropland_Total_P_vec_kg_p_ha.assign(Pasture_hay_Total_P_vec_kg_p_ha.begin(), Pasture_hay_Total_P_vec_kg_p_ha.end());
		Cropland_SURQ_mm.assign(Pasture_hay_SURQ_mm.begin(), Pasture_hay_SURQ_mm.end());
		Cropland_WYLD_mm.assign(Pasture_hay_WYLD_mm.begin(), Pasture_hay_WYLD_mm.end());
	}
	if (!db_pasturehay_found && db_cropland_found) {
		Pasture_hay_Sediment_vec_kg_p_ha.assign(Cropland_Sediment_vec_kg_p_ha.begin(), Cropland_Sediment_vec_kg_p_ha.end());
		Pasture_hay_Total_N_vec_kg_p_ha.assign(Cropland_Total_N_vec_kg_p_ha.begin(), Cropland_Total_N_vec_kg_p_ha.end());
		Pasture_hay_Total_P_vec_kg_p_ha.assign(Cropland_Total_P_vec_kg_p_ha.begin(), Cropland_Total_P_vec_kg_p_ha.end());
		Pasture_hay_SURQ_mm.assign(Cropland_SURQ_mm.begin(), Cropland_SURQ_mm.end());
		Pasture_hay_WYLD_mm.assign(Cropland_WYLD_mm.begin(), Cropland_WYLD_mm.end());
	}


	readExportCoefficientTable.close();

	//in kg/ha
	EC_vec_map = {
		{"Water_Wetlands_TSS",{0,0,0,0,0,0,0}},
		{"Water_Wetlands_TN",{0,0,0,0,0,0,0}},
		{"Water_Wetlands_TP",{0,0,0,0,0,0,0}},

		{"Cropland_TSS", Cropland_Sediment_vec_kg_p_ha},
		{"Cropland_TN", Cropland_Total_N_vec_kg_p_ha},
		{"Cropland_TP", Cropland_Total_P_vec_kg_p_ha},

		{"Forest_TSS", Forest_Sediment_vec_kg_p_ha},
		{"Forest_TN", Forest_Total_N_vec_kg_p_ha},
		{"Forest_TP", Forest_Total_P_vec_kg_p_ha},

		{"Rangeland_TSS", Rangeland_Sediment_vec_kg_p_ha},
		{"Rangeland_TN", Rangeland_Total_N_vec_kg_p_ha},
		{"Rangeland_TP", Rangeland_Total_P_vec_kg_p_ha},

		{"Pasture_hay_TSS", Pasture_hay_Sediment_vec_kg_p_ha},
		{"Pasture_hay_TN", Pasture_hay_Total_N_vec_kg_p_ha},
		{"Pasture_hay_TP", Pasture_hay_Total_P_vec_kg_p_ha},

		{"Urban_TSS", Urban_Sediment_vec_kg_p_ha},
		{"Urban_TN", Urban_Total_N_vec_kg_p_ha},
		{"Urban_TP", Urban_Total_P_vec_kg_p_ha}
	};
	//print out map for debugging
	/*for (auto i = EC_vec_map.begin(); i != EC_vec_map.end(); i++) {
		cout << i->first <<": ";
		vector<double> temp = i->second;
		for (auto j : temp) {
			cout << j << " ";
		}
		cout << endl;
	}*/

	//in mm
	water_volume_vec_map = {
		{"Cropland_SurfaceRunoff", Cropland_SURQ_mm},
		{"Cropland_WaterYield", Cropland_WYLD_mm},

		{"Forest_SurfaceRunoff", Forest_SURQ_mm},
		{"Forest_WaterYield", Forest_WYLD_mm},

		{"Rangeland_SurfaceRunoff", Rangeland_SURQ_mm},
		{"Rangeland_WaterYield", Rangeland_WYLD_mm},

		{"Pasture_hay_SurfaceRunoff", Pasture_hay_SURQ_mm},
		{"Pasture_hay_WaterYield", Pasture_hay_WYLD_mm},

		{"Urban_SurfaceRunoff", Urban_SURQ_mm},
		{"Urban_WaterYield", Urban_WYLD_mm}
	};


	string ECfilename = "ExportCoefficient_Database.csv";

	// write out a EC database for this study area
	ofstream ECoutfile(SimulationStringParams["OutputFolder_Path"] + ECfilename);
	if (!ECoutfile.good())	{
		cout << "Could not create " << ECfilename << endl;
	}
	else {
		cout << "Created " << ECfilename << endl;
	}

	//vectors combination of outout row name
	vector<string> pollutant_name = { "TP","TN", "TSS" };
	vector<string> volume_name = { "SURQ", "WYLD" };
	vector<string> NLCDClass = { "NLCD_Class11", "NLCD_Class21","NLCD_Class22", "NLCD_Class23", "NLCD_Class24", "NLCD_Class41","NLCD_Class42", "NLCD_Class43", "NLCD_Class71","NLCD_Class81","NLCD_Class82","NLCD_Class90", "NLCD_Class95" };

	//vectors combination of input map key
	vector<string> LC_name = { "Urban","Forest", "Rangeland", "Pasture_hay", "Cropland", "Water_Wetlands"};
	//vector<string> pollutant_EMCvec_name_2nd = { "_Total_P_vec_mg_p_L","_Total_N_vec_mg_p_L", "_Sediment_vec_mg_p_L" };//need to match the sequence of pollutant_name
	vector<string> volumetype_name = {"SurfaceRunoff", "WaterYield" };//need to match the sequence of volume_name

	string rowname, vec_name;
	double Manningn = 0.0, Hydraulicradius = 0.0;
	//header
	ECoutfile << "Percentile," << "5," << "10," << "25," << "50," << "75," << "90," << "95," << "n," << "R_m" << endl;
	// Loop through pollutants
	for (const string& pollutant : pollutant_name) {
		// Loop through land use types
		for (const string& landuseclass : NLCDClass) {
			rowname = pollutant + "_" + landuseclass + "_kg_p_ha";

			int landuseclassCode = stoi(landuseclass.substr(landuseclass.size() - 2));
			double Manningn = getManningn(landuseclassCode);
			double Hydraulicradius = getHydraulicradius(landuseclassCode);

			if (landuseclass == "NLCD_Class21" || landuseclass == "NLCD_Class22" || landuseclass == "NLCD_Class23" || landuseclass == "NLCD_Class24") { //21 - 24 Urban
				vec_name = LC_name[0] + "_" + pollutant;
				Manningn = getManningn(21);
				Hydraulicradius = getHydraulicradius(21);
			}
			else if (landuseclass == "NLCD_Class41" || landuseclass == "NLCD_Class42" || landuseclass == "NLCD_Class43") { // 41- 43 Forest
				vec_name = LC_name[1] + "_" + pollutant;
				Manningn = getManningn(41);
				Hydraulicradius = getHydraulicradius(41);
			}
			else if (landuseclass == "NLCD_Class71") {// 71 Rangeland
				vec_name = LC_name[2] + "_" + pollutant;
				Manningn = getManningn(71);
				Hydraulicradius = getHydraulicradius(71);
			}
			else if (landuseclass == "NLCD_Class81") {// 81 Pasture_hay
				vec_name = LC_name[3] + "_" + pollutant;
				Manningn = getManningn(81);
				Hydraulicradius = getHydraulicradius(81);
			}
			else if (landuseclass == "NLCD_Class82") {//82 Cropland
				vec_name = LC_name[4]  + "_" + pollutant;
				Manningn = getManningn(82);
				Hydraulicradius = getHydraulicradius(82);
			}
			else if (landuseclass == "NLCD_Class11" || landuseclass == "NLCD_Class90" || landuseclass == "NLCD_Class95") {//11 Water, 90&95 wetlands
				vec_name = LC_name[5] + "_" + pollutant;
				if (landuseclass == "NLCD_Class11") {
					Manningn = getManningn(11);
					Hydraulicradius = getHydraulicradius(11);
				}
				else if (landuseclass == "NLCD_Class90" || landuseclass == "NLCD_Class95") {
					Manningn = getManningn(90);
					Hydraulicradius = getHydraulicradius(90);
				} 
			}
			
			//cout << rowname << ", read map[" << vec_name << "]" << endl;
			ECoutfile << rowname;
			for (int i = 0; i < 7; i++) {
				ECoutfile << "," << EC_vec_map[vec_name][i];
			}
			ECoutfile << "," << Manningn << "," << Hydraulicradius<< endl;
		}
	}
	
	//water volume loop, loop through SURQ and WYLD
	for (int volume_type = 0; volume_type < 2; volume_type++) {
		// Loop through land use types
		for (const string& landuseclass : NLCDClass) {
			//don't write EMC for water land type (water&wetlands)
			if (landuseclass != "NLCD_Class11" && landuseclass != "NLCD_Class90" && landuseclass != "NLCD_Class95") {
				rowname = volume_name[volume_type] + "_" + landuseclass + "_mm";

				if (landuseclass == "NLCD_Class21" || landuseclass == "NLCD_Class22" || landuseclass == "NLCD_Class23" || landuseclass == "NLCD_Class24") { //21 - 24 Urban
					vec_name = LC_name[0] + "_" + volumetype_name[volume_type];
				}
				else if (landuseclass == "NLCD_Class41" || landuseclass == "NLCD_Class42" || landuseclass == "NLCD_Class43") { // 41- 43 Forest
					vec_name = LC_name[1] + "_" + volumetype_name[volume_type];
				}
				else if (landuseclass == "NLCD_Class71") {// 71 Rangeland
					vec_name = LC_name[2] + "_" + volumetype_name[volume_type];
				}
				else if (landuseclass == "NLCD_Class81") {// 81 Pasture_hay
					vec_name = LC_name[3] + "_" + volumetype_name[volume_type];
				}
				else if (landuseclass == "NLCD_Class82") {//82 Cropland
					vec_name = LC_name[4] + "_" + volumetype_name[volume_type];
				}
				
				ECoutfile << rowname;
				for (int i = 0; i < 7; i++) {
					ECoutfile << "," << water_volume_vec_map[vec_name][i];
				}
				ECoutfile << endl;
			
			}
			
		}
	}
	ECoutfile.close();
	
	cout << "Created " << ECfilename << endl;



	// write out an EMC database for this study area
	double flow_75th_L, flow_50th_L, flow_25th_L;
	string flowmapkey;
	string ECmapkey, EMCmapkey;
	vector<double> EC_vec_temp;
	double min_C_mg_p_L, max_C_mg_p_L, other_C_mg_p_L;

	//EMC map created by the loops is like this:
	/*EMC_vec_map = {
		{"Cropland_Sediment_vec_mg_p_L", Cropland_Sediment_vec_mg_p_L},
		{"Cropland_Total_N_vec_mg_p_L", Cropland_Total_N_vec_mg_p_L},
		{"Cropland_Total_P_vec_mg_p_L", Cropland_Total_P_vec_mg_p_L},

		{"Forest_Sediment_vec_mg_p_L", Forest_Sediment_vec_mg_p_L},
		{"Forest_Total_N_vec_mg_p_L", Forest_Total_N_vec_mg_p_L},
		{"Forest_Total_P_vec_mg_p_L", Forest_Total_P_vec_mg_p_L},

		{"Rangeland_Sediment_vec_mg_p_L", Rangeland_Sediment_vec_mg_p_L},
		{"Rangeland_Total_N_vec_mg_p_L", Rangeland_Total_N_vec_mg_p_L},
		{"Rangeland_Total_P_vec_mg_p_L", Rangeland_Total_P_vec_mg_p_L},

		{"Pasture_hay_Sediment_vec_mg_p_L", Pasture_hay_Sediment_vec_mg_p_L},
		{"Pasture_hay_Total_N_vec_mg_p_L", Pasture_hay_Total_N_vec_mg_p_L},
		{"Pasture_hay_Total_P_vec_mg_p_L", Pasture_hay_Total_P_vec_mg_p_L},

		{"Urban_Sediment_vec_mg_p_L", Urban_Sediment_vec_mg_p_L},
		{"Urban_Total_N_vec_mg_p_L", Urban_Total_N_vec_mg_p_L},
		{"Urban_Total_P_vec_mg_p_L", Urban_Total_P_vec_mg_p_L}
	};*/

	//check the water volume type for EMC calculation
	string WaterVolume_Type = BufferNutrientsStringParams["WaterVolume_Type"];

	for (string LC : LC_name) {// for 5 type of land cover
		//don't create EMC for waters (water&wetlands)
		if (LC != "Water_Wetlands") {
			if (WaterVolume_Type == "1") {//option 1 is Water Yield
				flowmapkey = LC + "_" + "WaterYield";
			}
			if (WaterVolume_Type == "2") { //option 2 is surface runoff
					flowmapkey = LC + "_" + "SurfaceRunoff";
			}
			
			flow_75th_L = water_volume_vec_map[flowmapkey][4] * Ratio_mmHa_to_L;
			flow_50th_L = water_volume_vec_map[flowmapkey][3] * Ratio_mmHa_to_L;
			flow_25th_L = water_volume_vec_map[flowmapkey][2] * Ratio_mmHa_to_L;

			for (const string& pollutant : pollutant_name) {//loop through each pollutant
				//e.g. Urban_TP
				ECmapkey = LC + "_" + pollutant;
				EMCmapkey = LC + "_" + pollutant;

				EC_vec_temp = EC_vec_map[ECmapkey];

				for (auto it = EC_vec_temp.begin(); it != EC_vec_temp.end(); it++) {//for percentiles, 7 times

					if (it == EC_vec_temp.begin()) {//min
						min_C_mg_p_L = *it * Ratio_kg_to_mg / flow_75th_L;
						EMC_vec_map[EMCmapkey].push_back(min_C_mg_p_L);
					}
					else if (it == EC_vec_temp.end() - 1) {//max
						max_C_mg_p_L = *it * Ratio_kg_to_mg / flow_25th_L;
						EMC_vec_map[EMCmapkey].push_back(max_C_mg_p_L);
					}
					else {//others
						other_C_mg_p_L = *it * Ratio_kg_to_mg / flow_50th_L;
						EMC_vec_map[EMCmapkey].push_back(other_C_mg_p_L);
					}
				}
			}
		}
		
	}

	/*for (auto i = EMC_vec_map.begin(); i != EMC_vec_map.end(); i++) {
		vector<double> temp = i->second;
		for (auto j : temp) {
			cout << j << " ";
		}
		cout << endl;
	}*/

	string EMCfilename;
	if (WaterVolume_Type == "1") {//option 1 is Water Yield
		EMCfilename = "EventMeanConcentration_Database_WaterYield.csv";
	}
	if (WaterVolume_Type == "2") { //option 2 is surface runoff
		EMCfilename = "EventMeanConcentration_Database_SurfaceRunoff.csv";
	}

	ofstream EMCoutfile(SimulationStringParams["OutputFolder_Path"] + EMCfilename);
	if (!EMCoutfile.good()) {
		cout << "Could not create " << EMCfilename << endl;
	}
	else {
		cout << "Created " << EMCfilename << endl;
	}

	//write the header line
	EMCoutfile << "Percentile," << "Min_5th," << "Low_10th," << "25th," << "Med_50th," << "75th," << "High_90th," << "Max_95th" << endl;
	// Loop through pollutants
	for (const string& pollutant : pollutant_name) {
		// Loop through land use types
		for (const string& landuseclass : NLCDClass) {
			//don't write EMC for water land type (water&wetlands)
			if (landuseclass != "NLCD_Class11" && landuseclass != "NLCD_Class90" && landuseclass != "NLCD_Class95") {
				rowname = pollutant + "_" + landuseclass + "_mg_p_L";

				if (landuseclass == "NLCD_Class21" || landuseclass == "NLCD_Class22" || landuseclass == "NLCD_Class23" || landuseclass == "NLCD_Class24") { //21 - 24 Urban
					vec_name = LC_name[0] + "_" + pollutant;
				}
				else if (landuseclass == "NLCD_Class41" || landuseclass == "NLCD_Class42" || landuseclass == "NLCD_Class43") { // 41- 43 Forest
					vec_name = LC_name[1] + "_" + pollutant;
				}
				else if (landuseclass == "NLCD_Class71") {// 71 Rangeland
					vec_name = LC_name[2] + "_" + pollutant;
				}
				else if (landuseclass == "NLCD_Class81") {// 81 Pasture_hay
					vec_name = LC_name[3] + "_" + pollutant;
				}
				else if (landuseclass == "NLCD_Class82") {//82 Cropland
					vec_name = LC_name[4] + "_" + pollutant;
				}

				EMCoutfile << rowname;
				for (int i = 0; i < 7; i++) {
					EMCoutfile << "," << EMC_vec_map[vec_name][i];
				}
				EMCoutfile << endl;
			}
		}
	}
	EMCoutfile.close();

	cout << "Created " << EMCfilename << endl;
}

//readWaterFeaturesMap function reads water features such as rivers and lakes, typically from NHD (National Hydrography Data)
void Inputs::readWaterFeaturesMap() {
	ifstream readWaterFeaturesMap(Directory_Input_CLArg + "nhdwaters10.asc");
	if (!readWaterFeaturesMap) {
		cout << "Warning: nhdwaters10.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the nhdwaters10.asc is missing there is no tree cover data to drive the simulation." << endl;
		cout << "Correction: Create a nhdwaters10.asc file, either using DEM generated water layer by turning the Flag_FlowDirection_Source flag to DEM." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readWaterFeaturesMap >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("nhdwaters10.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
	double tempdata = 0.0;
	int count = 0;

	//while loop to read water as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readWaterFeaturesMap >> tempdata && count < nRows * nCols) {
		//if tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			//non-water area
			if (tempdata == 0) { 
				//Waters10.push_back(0);
				Waters01.push_back(1); 
				Waters0.push_back(Inputs::Length_Pixel_Side_m);
			}
			//water area
			if (tempdata == 1) {
				//Waters10.push_back(1);
				Waters01.push_back(0);
				Waters0.push_back(0);
			}
		}
		//else tempdata equals NODATA_code 
		else {
			Waters01.push_back(Inputs::NODATA_code);
			Waters0.push_back(Inputs::NODATA_code);
		}
		++count;
	}
	readWaterFeaturesMap.close();
	cout << "Finish reading nhdwaters10.asc." << endl;
}

//readNHDFlowDirectionMap function reads NHD (National Hydrography Data) Flow Direction map, which forces flow to planimetric hydrographic features 
void Inputs::readNHDFlowDirectionMap() {
	ifstream readNHDFlowDirectionMap(Directory_Input_CLArg + "nhdfdr.asc");
	if (!readNHDFlowDirectionMap) {
		cout << "Warning: nhdfdr.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: When the nhdfdr.asc is missing there is no tree cover data to drive the simulation." << endl;
		cout << "Correction: Create a nhdfdr.asc file, either using DEM generated water layer by turning the Flag_FlowDirection_Source flag to DEM." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readNHDFlowDirectionMap >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("nhdfdr.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);
	double tempdata = 0.0, code_temp;
	int count = 0;

	//while loop to read NHD flow direction as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readNHDFlowDirectionMap >> tempdata && count < nRows * nCols) {
		//if tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			//convert flow direction code from ESRI format to Whitebox format
			code_temp = flowdircode_esri_to_whitebox(tempdata);
			//push back the code (whitebox) to the fdr vector 
			NHDfdr.push_back(code_temp);
			//push back the code (whitebox) to the reveresed fdr vector 
			NHDfdr_reverse.push_back(D8_flowdir_reverse(code_temp));
		}
		//else tempdata equals NODATA_code 
		else {
			//NHDfdr defined as NODATA_code
			NHDfdr.push_back(Inputs::NODATA_code);
			NHDfdr_reverse.push_back(Inputs::NODATA_code);
		}
		++count;
	}
	readNHDFlowDirectionMap.close();
	cout << "Finish reading nhdfdr.asc." << endl;
}

//D8_flowdir_reverse function will reverse the NHD Flow Direction map from its D8 (1 of 8 directions), to provide estimates of dispersal area
double Inputs::D8_flowdir_reverse(double flowdir) {
	vector <double> dir;
	vector <double> dir_r;

	dir = { 1, 2, 4, 8, 16, 32, 64, 128 };
	dir_r = { 16, 32, 64, 128, 1, 2, 4, 8 };

	auto it_dir_r = dir_r.begin();
	for (auto it_dir = dir.begin(); it_dir != dir.end(); it_dir++) {
		if (flowdir == *it_dir) { 
			return *it_dir_r; 
			break;
		}
		else {
			it_dir_r++;
		}
	}
}

//flowdircode_esri_to_whitebox function converts flow direction from ESRI ArcGIS to Whitebox (e.g., HydroPlus)
//Note: HydroPlus uses the Whitebox flow directions nomenclature
double Inputs::flowdircode_esri_to_whitebox(double flowdir) {
	vector <double> dir_esri;//read from NHD fdr
	vector <double> dir_wb;//fdr in HydroPlus

	//dir_wb (HP) is flow direction vector that corresponds with dir_esri, each rotating clockwise, with 1st value any side of pixel
	//Note: SE=4, S=8, SW=16, W=32, NW=64, N=128, NE=1
	dir_wb = { 2, 4, 8, 16, 32, 64, 128, 1 };
	//dir_esri is flow direction vector starting with East (E=1), rotating clockwise to SE=2, S=4, SW=8, W=16, NW=32, N=64, NE=128
	dir_esri = { 1, 2, 4, 8, 16, 32, 64, 128 };

	//Note: HydroPlus WhiteBox flowDir is 1 for NE corner, 2 for E, 4 for SE, 8 for S, 16 for SW, 32 for W, 64 for NW, 128 for N
	//Whitebox FDR		Whitebox -FDR	Pairs of x,y 
	//64	128	1		4	8	16		-1,-1	0,-1	1,-1
	//32		2		2		32		-1,0			1,0
	//16	8	4		1	128	64		-1,1	0,1		1,1

	//Note: ArcGIS ESRI flowDir is 128 for NE corner, 1 for E, 2 for SE, 4 for S, 8 for SW, 16 for W, 32 for NW, 64 for N
	//ESRI FDR			ESIR -FDR		Pairs of x,y 
	//32	64	128		2	4	8		-1,-1	0,-1	1,-1
	//16		1		1		16		-1,0			1,0
	//8		4	2		128	64	32		-1,1	0,1		1,1

	for (size_t i = 0; i < dir_esri.size(); ++i) {
		if (flowdir == dir_esri[i])
			return dir_wb[i];
	}

	// Optional: warn if no match found
	//cerr << "Warning: Unknown ESRI flow direction value " << flowdir << ". Returning 0.\n";
	return flowdir; //failed to convert,  remain the original fdr
}


//flowdircode_whitebox_to_esri function converts flow direction from Whitebox (e.g., HydroPlus) to ESRI ArcGIS
//Note: HydroPlus uses the Whitebox flow directions nomenclature
double Inputs::flowdircode_whitebox_to_esri(double flowdir) {
	vector <double> dir_esri;//read from NHD fdr
	vector <double> dir_wb;//fdr in HydroPlus

	//dir_wb (HP) is flow direction vector starting with East (E=2), rotating clockwise to SE=4, S=8, SW=16, W=32, NW=64, N=128, NE=1
	dir_wb = { 2, 4, 8, 16, 32, 64, 128, 1 };
	//dir_esri is flow direction vector starting with East (E=1), rotating clockwise to SE=2, S=4, SW=8, W=16, NW=32, N=64, NE=128
	dir_esri = { 1, 2, 4, 8, 16, 32, 64, 128 };
	
	//Note: HydroPlus WhiteBox flowDir is 1 for NE corner, 2 for E, 4 for SE, 8 for S, 16 for SW, 32 for W, 64 for NW, 128 for N
	//Whitebox FDR		Whitebox -FDR	Pairs of x,y 
	//64	128	1		4	8	16		-1,-1	0,-1	1,-1
	//32		2		2		32		-1,0			1,0
	//16	8	4		1	128	64		-1,1	0,1		1,1

	//Note: ArcGIS ESRI flowDir is 128 for NE corner, 1 for E, 2 for SE, 4 for S, 8 for SW, 16 for W, 32 for NW, 64 for N
	//ESRI FDR			ESIR -FDR		Pairs of x,y 
	//32	64	128		2	4	8		-1,-1	0,-1	1,-1
	//16		1		1		16		-1,0			1,0
	//8		4	2		128	64	32		-1,1	0,1		1,1

	for (size_t i = 0; i < dir_wb.size(); ++i) {
		if (flowdir == dir_wb[i]) {
			return dir_esri[i];
		}
	}

	//cerr << "Warning: Unknown flow direction code: " << flowdir << ". Returning default ESRI code -1." << endl;
	return flowdir; // failed to convert, remain the original fdr
}


// getManningn function obtains Manning roughness n values based on NLCD Class inputs and from Table 8.1 in Wurbs and James (2001) ISBN 0-13-081293-5
double Inputs::getManningn(double nlcd_categories) {
	vector <double> nlcd;
	vector <double> Manningn;
	//nlcd is the class of nlcd pixel
	nlcd = { 11, 21, 22, 23, 24, 31, 41, 42, 43, 52, 71, 81, 82, 90, 95 };
	// Lists of Manning roughness values, n, for each NLCD LC type, from Table 8.1 in Wurbs and James (2001) ISBN 0-13-081293-5
	Manningn = { 0.01, 0.011, 0.011, 0.011, 0.011, 0.011, 0.5, 0.5, 0.5, 0.24, 0.24, 0.13, 0.13, 0.095, 0.095 };

	for (size_t i = 0; i < nlcd.size(); ++i) {
		if (nlcd_categories == nlcd[i])
			return Manningn[i];
	}

	// Optional: warn if category is not recognized
	//cerr << "Warning: Unrecognized NLCD class " << nlcd_categories << ", returning 0.\n";
	return 0.095;//median of Manningn
}

//getHydraulicradius function gets flow path hydraulic radius values, R (ft), based on NLCD Class and from Table 8.1 in Wurbs and James (2001) ISBN 0-13-081293-5
double Inputs::getHydraulicradius(double nlcd_categories) {
	vector <double> nlcd;
	vector <double> Hydraulicradius;
	//nlcd is the class of nlcd pixel
	nlcd = { 11, 21, 22, 23, 24, 31, 41, 42, 43, 52, 71, 81, 82, 90, 95 };
	//Lists of Hydraulic radius values, R (ft), for each NLCD LC type, from Table 8.1 in Wurbs and James (2001) ISBN 0-13-081293-5
	//Hydraulicradius = { 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.25, 0.25, 0.25, 0.15, 0.15, 0.04, 0.04, 1.0, 1.0 };
	//change from ft to meter
	Hydraulicradius = { 0.018288, 0.018288, 0.018288, 0.018288, 0.018288, 0.018288, 0.0762, 0.0762, 0.0762, 0.04572, 0.04572, 0.012192, 0.012192, 0.3048, 0.3048 };
	
	
	for (size_t i = 0; i < nlcd.size(); ++i) {
		if (nlcd_categories == nlcd[i])
			return Hydraulicradius[i];
	}

	// Optional: warning for unmatched NLCD category
	//cerr << "Warning: Unrecognized NLCD class " << nlcd_categories << ", returning 0.\n";
	return 0.018288; //median of Hydraulicradius
}

//get_P_release_mean function obtains Phosphorus percent released from NLCD Class based on estimates of their filtering potential
double Inputs::get_P_release_mean(double nlcd_categories) {
	vector <double> NLCD_Class;
	vector <double> TotalPhosphorus_AverageReleased_percent;
	//NLCD_Class = { 11, 21, 22, 23, 24, 31, 41, 42, 43, 52, 71, 81, 82, 90, 95 }
	//Note: NLCD Class for the pixel, using the Anderson Level 2 land cover classification described by the MRLC legend
	//Note: https://www.mrlc.gov/data/legends/national-land-cover-database-class-legend-and-description
	NLCD_Class = { 11, 21, 22, 23, 24, 31, 41, 42, 43, 52, 71, 81, 82, 90, 95 };

	//TotalPhosphorus_AverageReleased_percent = { 95, 70, 80, 95, 99, 50, 30, 25, 28, 45, 35, 85, 95, 35, 35 }
	//Note: TotalPhosphorus_AverageReleased_percent (%) represent amount released by corresponding NLCD Class 
	//Note: ... e.g., NLCD_Class = 11 (water) has TotalPhosphorus_AverageReleased_percent = 95 (% TP released)
	TotalPhosphorus_AverageReleased_percent = { 95, 70, 80, 95, 99, 50, 30, 25, 27.5, 45, 35, 85, 95, 35, 35 };
	//Note: TotalPhosphorus_AverageReleased_percent (%) based on studies synthesized by Hoffmann et al. (2009) Table 2 and Table 3
	//Note: Values were originally based on values of Endreny and Wood (2003), but were lowered based on Hoffman et al. (2009)
	//Note: Developed areas can still retain some nutrients. , e.g. reported in Li, Sisi, et al. See Phosphorus retention and delivery estimation across a large river basin: Assessing the impact of landscape patterns (2025)
	//Reference: Hoffmann, C. C., Kjaergaard, C., Uusi-Kämppä, J., Hansen, H. C. B., & Kronvang, B. (2009). Phosphorus Retention in Riparian Buffers: Review of Their Efficiency. Journal of Environmental Quality, 38(5), 1942-1955. 
	//Note: Hoffman et al. (2009) reports percent phosphorus trapped, not released, as:
	//Note: NLCD Class 11, Water 5% trapped, 95% released
	//Note: NLCD Class 21, Developed Open Space 30% trapped, 70% released
	//Note: NLCD Class 22, Developed Low Intensity 20% trapped, 80% released
	//Note: NLCD Class 23, Developed Medium Intensity 5% trapped, 95% released
	//Note: NLCD Class 24, Developed High Intensity 1% trapped, 99% released
	//Note: NLCD Class 31, Barren Land 50% trapped, 50% released 	
	//Note: NLCD Class 41, Deciduous Forest 70% trapped, 30% released
	//Note: NLCD Class 42, Evergreen Forest 75% trapped, 25% released
	//Note: NLCD Class 43, Mixed Forest 72.5% trapped, 27.5% released
	//Note: NLCD Class 52, Shrub 55% trapped, 45% released
	//Note: NLCD Class 71, Grassland 65% trapped, 35% released
	//Note: NLCD Class 81, Pasture/Hay 15% trapped, 85% released
	//Note: NLCD CLass 82, Cultivated Crops 5% trapped, 95% released 	
	//Note: NCLD Class 90, Woody Wetlands 65% trapped, 35% released
	//Note: NLCD Class 95, Emergent Herbaceous Wetlands 65% trapped, 35% released		  

	for (size_t i = 0; i < NLCD_Class.size(); ++i) {
		if (nlcd_categories == NLCD_Class[i])
			return TotalPhosphorus_AverageReleased_percent[i];
	}
	// Optionally, handle unexpected input
	//cerr << "Warning: Unrecognized NLCD class " << nlcd_categories << ", returning 0.\n";
	return 50.0;
}


// readSoilDepthMap_cm function reads soil depth from SSURGO maps in units of cm
void Inputs::readSoilDepthMap_cm() {
	string file_name = "s_depth.asc";;
	double tempdata = 0.0;
	int total_count = 0;
	double sum = 0.0;
	int count = 0;
	//min initialized to positive infinity
	double min = DBL_MAX;  

	ifstream readSoilDepthMap_cm(Directory_Input_CLArg + file_name);
	if (!readSoilDepthMap_cm) {
		cout << "Warning: s_depth.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readSoilDepthMap_cm >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("s_depth.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);

	//while loop to read NHD flow direction as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readSoilDepthMap_cm >> tempdata && total_count < nRows * nCols) {
		//if tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			s_depth.push_back(tempdata);
			// Update min to find min value
			if (tempdata < min && tempdata > 0) {
				min = tempdata;
			}
			sum += tempdata;
			++count;
		}
		//else tempdata equals NODATA_code 
		else {
			//soil depth (cm) is defined as NODATA_code
			s_depth.push_back(Inputs::NODATA_code);
		}
		++total_count;
	}
	s_depth_avg = sum / count;
	
	soildepth_min = min;
	readSoilDepthMap_cm.close();
	cout << "Finish reading soil depth from prior SSURGO analysis." << "The mean soil depth s_depth_avg is: " << s_depth_avg << " cm, the minimum soil depth is: " << soildepth_min << " cm." << endl;
}


//readHydraulicConductivityMap_umps function reads hydraulic conductivity (um/s) from SSURGO maps
void Inputs::readHydraulicConductivityMap_umps() {
	string file_name = "s_ksat.asc";
	double tempdata = 0.0;
	int count = 0;
	//min initialized to positive infinity
	double min = DBL_MAX;

	ifstream readHydraulicConductivityMap_umps(Directory_Input_CLArg + file_name);
	if (!readHydraulicConductivityMap_umps) {
		cout << "Warning: s_ksat.asc could not be opened." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	string buffer;
	readHydraulicConductivityMap_umps >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("s_ksat.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);


	//while loop to read NHD flow direction as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readHydraulicConductivityMap_umps >> tempdata && count < nRows * nCols) {
		//if tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			s_Ksat.push_back(tempdata);
			// Update min to find min value
			if (tempdata < min && tempdata > 0) {
				min = tempdata;
			}
		}
		//else tempdata equals NODATA_code 
		else {
			//s_Ksat is defined as NODATA_code
			s_Ksat.push_back(Inputs::NODATA_code);
		}
		++count;
	}
	conductivity_min = min;
	readHydraulicConductivityMap_umps.close();
	cout << "Finish reading conductivity from prior SSURGO analysis, the minimum conductivity is: " << conductivity_min << " um/s." << endl;
}

//calculateSoilTransmissivityChangeUnits function converts transmissivity to m^2/day from SSURGO units of (cm*um)/s
void Inputs::calculateSoilTransmissivityChangeUnits() {
	double s_trans_temp = 0;
	double sum = 0.0;
	int count = 0;
	//ratio_m2_p_day_cm_um_p_s_to is the ratio of m^2/day to (cm*um)/s, where (cm*um)/s was used by Gridded SSURGO Soil Data for product of sil depth and conductivity
	double ratio_m2_p_day_cm_um_p_s_to = 0.000864;
	double ratio_m_to_cm = 0.01;
	double ratio_sec_to_day = 86400.0;
	double ratio_m_to_um = 0.000001;

	//Depth_SoilTransmissivityLayer_cm (cm) is read from s_depth (cm) as units from SSURGO
	auto Depth_SoilTransmissivityLayer_cm = s_depth.begin();

	//For HydraulicConductivity_SoilLayer_um_p_s (um/s) in vector of s_Ksat (um/s) as units from SSURGO
	for (double HydraulicConductivity_SoilLayer_um_p_s : s_Ksat) {
		//If HydraulicConductivity_SoilLayer_um_p_s (um/s) is not Inputs::NODATA_code then
		if (HydraulicConductivity_SoilLayer_um_p_s != Inputs::NODATA_code) {
			//set the conductivity = 0 to be the minimum non-zero HydraulicConductivity_SoilLayer_um_p_s to avoid log(0) in buffer calculation
			if (HydraulicConductivity_SoilLayer_um_p_s <= 0) HydraulicConductivity_SoilLayer_um_p_s = conductivity_min;
			//set the soildepth = 0 to be the minimum non-zero HydraulicConductivity_SoilLayer_um_p_s to avoid log(0) in buffer calculation
			if (*Depth_SoilTransmissivityLayer_cm <= 0) *Depth_SoilTransmissivityLayer_cm = soildepth_min;

			//s_trans_temp (m2/day) is product of Depth_SoilTransmissivityLayer_cm (cm), ratio_m_to_cm, HydraulicConductivity_SoilLayer_um_p_s (um/s), ratio_sec_to_day, and ratio_m_to_um
			//Note: Product ratio_m_to_cm, ratio_sec_to_day, ratio_m_to_um = 0.000864 = (1/100)*((60*60*24)/1)*(1/10^6) = m/cm * s/day * m/um
			s_trans_temp = *Depth_SoilTransmissivityLayer_cm * ratio_m_to_cm * HydraulicConductivity_SoilLayer_um_p_s * ratio_sec_to_day * ratio_m_to_um;

			//s_trans (m/day) vector is appended with s_trans_temp (m/day)
			s_trans.push_back(s_trans_temp);

			//sum (m/day) is increased by s_trans_temp (m/day)
			sum += s_trans_temp;
			//count is increased
			++count;
		}
		//Else If HydraulicConductivity_SoilLayer_um_p_s (um/s) is Inputs::NODATA_code then
		else {
			//s_trans (um/s) is set to Inputs::NODATA_code
			s_trans.push_back(Inputs::NODATA_code);
		}
		//Depth_SoilTransmissivityLayer_cm (cm) is increased
		++Depth_SoilTransmissivityLayer_cm;
	}

	//s_trans_avg (m/day) is quotient of sum and count
	s_trans_avg = sum / count;
	//s_trans_avg = calculateMean(s_trans);
	cout << "The mean transmissivity is: " << s_trans_avg << " m^2/day." << endl;
	cout << "Finish calculating transmissivity from prior SSURGO analysis." << endl;
}

// readWatertableMap_cm function reads watertable depth from SSURGO maps in units of cm
bool Inputs::readWatertableMap_cm() {
	string file_name = "s_wtable.asc";;
	double tempdata = 0.0;
	int total_count = 0;

	ifstream readWatertable_cm(Directory_Input_CLArg + file_name);
	if (!readWatertable_cm) {
		return false;
	}
	string buffer;
	readWatertable_cm >> buffer >> nCols_temp >> buffer >> nRows_temp >> buffer >> xllcorner_m_temp >> buffer >> yllcorner_m_temp >> buffer >> cellsize_m_temp >> buffer >> NODATA_temp;
	//Check_Map_Header called to compare with dem.asc header
	Check_Map_Header("s_wtable.asc", nCols_temp, nRows_temp, NODATA_temp, cellsize_m_temp, xllcorner_m_temp, yllcorner_m_temp);

	//while loop to read NHD flow direction as tempdata (fraction) from readTreeCover while count < product of nRows and nCols
	while (readWatertable_cm >> tempdata && total_count < nRows * nCols) {
		//if tempdata not equal to NODATA_code then 
		if (tempdata != Inputs::NODATA_code) {
			s_wtable.push_back(tempdata);
		}
		//else tempdata equals NODATA_code 
		else {
			//watertable depth (cm) is defined as NODATA_code
			s_wtable.push_back(Inputs::NODATA_code);
		}
		++total_count;
	}
	
	readWatertable_cm.close();
	cout << "Finish reading watertable(cm) from prior SSURGO analysis." << endl;
	return true;
}


//calculateMean function calculates mean values
double Inputs::calculateMean(const vector<double>& data) {
	//sum initialized to zero
	double sum = 0.0;
	//count initialized to zero
	int count = 0;

	//For loop of value through all data
	for (double value : data) {
		//if value not Inputs::NODATA_code then
		if (value != Inputs::NODATA_code) {
			//sum increased by value
			sum += value;
			//count increased
			++count;
		}
	}

	//If count equals 0 then return 0
	if (count == 0) {
		cout << "No valid data to calculate mean." << endl;
		//return 0
		return 0.0; 
	}

	//return the quotient of sum and count, which is a simple mean
	return sum / count;
}

//void Inputs::writeAsciiMap function will write an ASCII Map for a given vector 
void Inputs::writeAsciiMap(const string& MapData_Name_str, const vector<double>& MapData_vec) {
	
	//outfile defined using ofstream function directed to OutputFolder_Path + MapData_Name_str + .asc
	//Note: Creates z_map_ImperviousCoverNoTreeCover_frac.asc to z_map_WaterCoverNoTC_frac.asc
	ofstream outfile(SimulationStringParams["OutputFolder_Path"] + "z_map_" + MapData_Name_str + ".asc");

	//outfile assigned header width boundary of 14 and 15, variable nCols defined within Inputs.cpp
	outfile << left << setw(14) << "ncols" << setw(15) << Inputs::nCols << endl;
	outfile << left << setw(14) << "nrows" << setw(15) << Inputs::nRows << endl;
	outfile << left << setw(14) << "xllcorner" << setw(15) << setprecision(15) << Inputs::xllcorner_m << endl;
	outfile << left << setw(14) << "yllcorner" << setw(15) << setprecision(15) << Inputs::yllcorner_m << endl;
	outfile << left << setw(14) << "cellsize" << setw(15) << setprecision(15) << Inputs::Length_Pixel_Side_m << endl;
	outfile << left << setw(14) << "NODATA_value" << setw(15) << Inputs::NODATA_code << endl;

	//mapPixel_Count = nRows * nCols
	int mapPixel_Count = nRows * nCols;
	
	//For loop of MapPixel_ID in mapPixel_Count; 
	//Note: MapData_vec.size can be larger than mapPixel_Count if reference station is outside the map, ReferenceStationOutsideMap = 1
	//Note: Conversion between MapPixel_ID and row & column pair, with MapPixel_ID starting at 0, row & col starting at 0
	//Note: Eqs: row=MapPixel_ID / Inputs::nCols; col=MapPixel_ID % Inputs::nCols; MapPixel_ID = (row * nCols) + col
	for (int MapPixel_ID = 0; MapPixel_ID < mapPixel_Count; ++MapPixel_ID) {
		//col = MapPixel_ID % nCols), extracted from vector
		int col = MapPixel_ID % nCols;
		//newLine = ((col + 1) == nCols), determined true or false for each pixel
		//Note: col + 1 shifts from 0-based to 1-based indexing for comparision with ESRI nCols
		bool newLine = ((col + 1) == nCols);

		//if (LandCover_NLCD_Class[MapPixel_ID] != Inputs::NODATA_code) then 
		if (LandCover_NLCD_Class[MapPixel_ID] != Inputs::NODATA_code) {
			//outfile writes MapData_vec[MapPixel_ID]
			outfile << fixed << setprecision(5) << MapData_vec[MapPixel_ID];
		}
		else {
			//outfile writes Inputs::NODATA_code
			outfile << fixed << setprecision(0) << Inputs::NODATA_code;

		}
		//If newLine is true, then write return with endl;
		if (newLine) {
			outfile << endl;
		}
		//Else write space for next data point
		else {
			outfile << " ";
		}
	}
	//outfile close function
	outfile.close();
}

//Inputs::ProcessSpatialBufferGIExecutionParams function reads HydroPlusConfig.xml inputs for Spatial Buffer with GI
void Inputs::ProcessSpatialBufferGIExecutionParams() {
	for (auto i : BufferSpatialWeightingStringParams) {
		/*if (i.first.find("GI_type") != string::npos) {
			GI_type = i.second;
		}*/
		if (i.first.find("GI_ulcorner") != string::npos) {
			GI_ulcorner_rows.push_back(atoi(i.second.substr(0, i.second.find(",")).c_str()) - 1);//user provided/ArcGIS row col start from 1
			GI_ulcorner_cols.push_back(atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str()) - 1);//user provided/ArcGIS row col start from 1

		}
		if (i.first.find("GI_lrcorner") != string::npos) {
			GI_lrcorner_rows.push_back(atoi(i.second.substr(0, i.second.find(",")).c_str()) - 1);//user provided/ArcGIS row col start from 1
			GI_lrcorner_cols.push_back(atoi(i.second.substr(i.second.find(",") + 1, i.second.size() - i.second.find(",")).c_str()) - 1);//user provided/ArcGIS row col start from 1
		}
	}
}

//calculate the contributing area for GIs
//need to be called after getting TerrainProcessor::FlowAccumulation_Map
//cab't be called within Buffer since the FlowAccumulation_Map has been replaced

double Inputs::BufferGIContributingAreaCalc(int GI_Index) {
	
	double total_contributing_area = 0;
	//if Flag_FlowDirection_Source is NHD, replace DEM fdr with NHD fdr and redo the flow accumulation to get the contributing cell list
	if (Inputs::BufferSpatialWeightingStringParams["Flag_FlowDirection_Source"] == "NHD") {
		cout << "Generating water layer from nhdwaters10.asc. " << endl;
		//Call ReceivingWater_ZeroValue_CreateMap function sending input, saving waters01 and waters0 layer to buffer
		BufferSpatialCalc::ReceivingWater_ZeroValue_CreateMap_from_NHD();
		cout << "Updating flow direction from nhdfdr.asc. " << endl;
		//Algorithm to replace DEM derived flow direction (FDR) with NHD FDR values, for the following flow accum	
		BufferSpatialCalc::ReplaceFlowDirectionOrganizerWithVector(Inputs::NHDfdr);
		// Re-check conditions for saving contributing/dispersal area pixels
		TerrainProcessor::setContributingDispersalFlags();
		//start flow accumulation process to get the contributing pixels
		//accumulate the non-water area
		TerrainProcessor::FlowAccum(BufferSpatialCalc::waters01, TerrainProcessor::DX_vec);
		//mark ContributingArea been saved and avoid running it again in the future accumulation
		TerrainProcessor::saveContributingDispersalArea = false;
	}
	
	//start creating contributing area 01 map, to avoid accumulating one cell several times
	BufferSpatialCalc::ContributingAreaPixels_map_writeout.clear();
	BufferSpatialCalc::DispersalAreaPixels_map_writeout.clear();
	BufferSpatialCalc::ContributingAreaPixels_map_writeout.resize(nRows * nCols, Inputs::NODATA_code);
	BufferSpatialCalc::DispersalAreaPixels_map_writeout.resize(nRows * nCols, Inputs::NODATA_code);

	int GI_ulcorner_row = Inputs::GI_ulcorner_rows[GI_Index];
	int GI_ulcorner_col = Inputs::GI_ulcorner_cols[GI_Index];
	int GI_lrcorner_row = Inputs::GI_lrcorner_rows[GI_Index];
	int GI_lrcorner_col = Inputs::GI_lrcorner_cols[GI_Index];
	
	// Iterate over each pixel in the block
	for (int gi_row = GI_ulcorner_row; gi_row <= GI_lrcorner_row; ++gi_row) {
		for (int gi_col = GI_ulcorner_col; gi_col <= GI_lrcorner_col; ++gi_col) {
			// Check if the specified row and column are within the map boundaries
			if (gi_row >= 0 && gi_row < nRows && gi_col >= 0 && gi_col < nCols) {
				//create the maps, for  multiple gi pixels, it's cumulative
				BufferSpatialCalc::Contributing_dispersal_CreateMap(gi_row, gi_col);
			}
		}
	}
	//finish creating contributing area 01 map

	//else {//won't work when using NHD flow direction since buffer has not been run yet. 
	for (int pixelIndex = 0; pixelIndex < BufferSpatialCalc::ContributingAreaPixels_map_writeout.size(); pixelIndex++) {
		//double contributingPixels = BufferDynamicCalc::ContributingAreaPixels_map_writeout[pixelIndex];
		if (BufferSpatialCalc::ContributingAreaPixels_map_writeout[pixelIndex] >= 1) {
			total_contributing_area += Inputs::Length_Pixel_Side_m * Inputs::Length_Pixel_Side_m;
		}
	}
	return total_contributing_area;
}

//BufferGIAreaCalc function calculates the area for GI units
//Note: Only called after calling TerrainProcessor::FlowAccumulation_Map
double Inputs::BufferGIAreaCalc(int GI_Index) {
	// Check if the GI index is within bounds for all vectors
	if (GI_Index >= GI_ulcorner_rows.size() ||
		GI_Index >= GI_ulcorner_cols.size() ||
		GI_Index >= GI_lrcorner_rows.size() ||
		GI_Index >= GI_lrcorner_cols.size()) {

		cout << "Warning: Mismatched GI vector lengths detected." << endl;
		cout << "Aborting: Simulation is aborted due to mismatched vector lengths." << endl;
		cout << "Explanation: The number of upper-left and lower-right corner coordinates (GI_ulcorner and GI_lrcorner) must match the number of GI folders." << endl;
		cout << "Correction: Check the GI DataFolder and ensure that the locations specified in BufferSpatialWeightingStringParams (pairs of GI_ulcorner and GI_lrcorner) have the same number of elements as the GI folders." << endl;
		return 0.0;
	}

	// Retrieve the coordinates for the current GI
	int GI_ulcorner_row = GI_ulcorner_rows[GI_Index];
	int GI_ulcorner_col = GI_ulcorner_cols[GI_Index];
	int GI_lrcorner_row = GI_lrcorner_rows[GI_Index];
	int GI_lrcorner_col = GI_lrcorner_cols[GI_Index];

	//GI_Area_m2 is ((GI_lrcorner_row - GI_ulcorner_row) + 1) * ((GI_lrcorner_col - GI_ulcorner_col) + 1) * Inputs::Length_Pixel_Side_m^2
	//Note: This takes distance across rows and columns and multiplies it by the cell area
	double GI_Area_m2 = ((GI_lrcorner_row - GI_ulcorner_row) + 1) * ((GI_lrcorner_col - GI_ulcorner_col) + 1) * Inputs::Length_Pixel_Side_m * Inputs::Length_Pixel_Side_m;

	return GI_Area_m2;
}

//Check_Map_Size_and_NoData overloaded for map vectors that are double vs double
void Inputs::Check_Map_Size_and_NoData(const vector<double>& map1, const vector<double>& map2, const string& map1Name, const string& map2Name) {

	// Check if the vectors have the same size
	if (map1.size() != map2.size()) {
		cout << "Warning: Maps " << map1Name << " and " << map2Name << " are not aligned due to size mismatch." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The HydroPlus model requires input maps to align spatially." << endl;
		cout << "Explanation: " << map1Name << " size: " << map1.size() << " vs " << map2Name << " size: " << map2.size() << endl;
		cout << "Correction: Create maps that spatially align with the dem.asc input map." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	// Compare corresponding elements of the vectors
	for (size_t MapPixel_ID = 0; MapPixel_ID < map1.size(); ++MapPixel_ID) {
		// Check if both elements are -9999 (nodata) or if they are different
		if ((map1[MapPixel_ID] == Inputs::NODATA_code && map2[MapPixel_ID] != Inputs::NODATA_code) || (map1[MapPixel_ID] != Inputs::NODATA_code && map2[MapPixel_ID] == Inputs::NODATA_code)) {
			//Note: Conversion with MapPixel_ID starting at 0, row & col starting at 0 for internal processing
			//Note: Eqs: row=MapPixel_ID / Inputs::nCols; col=MapPixel_ID % Inputs::nCols; MapPixel_ID = (row * nCols) + col
			int row = MapPixel_ID / Inputs::nCols;
			int col = MapPixel_ID % Inputs::nCols;
			cout << "Warning: Maps " << map1Name << " and " << map2Name << " have mismatched NODATA values." << endl;
			cout << "Warning: Check row: " << row << " col: " << col << ", which is MapPixel_ID: " << MapPixel_ID << "." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: The HydroPlus model requires input maps to align spatially." << endl;
			cout << "Correction: Create maps with NODATA values that spatially align with the dem.asc input map." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
	}
}

//Check_Map_Size_and_NoData overloaded for map vectors that are double vs int
void Inputs::Check_Map_Size_and_NoData(const vector<double>& map1, const vector<int>& map2, const string& map1Name, const string& map2Name)
{

	// Check if the vectors have the same size
	if (map1.size() != map2.size()) {
		cout << "Warning: Maps " << map1Name << " and " << map2Name << " are not aligned due to size mismatch." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The HydroPlus model requires input maps to align spatially." << endl;
		cout << "Explanation: " << map1Name << " size: " << map1.size() << " vs " << map2Name << " size: " << map2.size() << endl;
		cout << "Correction: Create maps that spatially align with the dem.asc input map." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	// Compare corresponding elements of the vectors
	for (size_t MapPixel_ID = 0; MapPixel_ID < map1.size(); ++MapPixel_ID) {
		// Check if both elements are -9999 (nodata) or if they are different
		if ((map1[MapPixel_ID] == Inputs::NODATA_code && map2[MapPixel_ID] != Inputs::NODATA_code) || (map1[MapPixel_ID] != Inputs::NODATA_code && map2[MapPixel_ID] == Inputs::NODATA_code)) {
			//Note: Conversion with MapPixel_ID starting at 0, row & col starting at 0 for internal processing
			//Note: Eqs: row=MapPixel_ID / Inputs::nCols; col=MapPixel_ID % Inputs::nCols; MapPixel_ID = (row * nCols) + col
			int row = MapPixel_ID / Inputs::nCols;
			int col = MapPixel_ID % Inputs::nCols;
			cout << "Warning: Maps " << map1Name << " and " << map2Name << " have mismatched NODATA values." << endl;
			cout << "Warning: Check row: " << row << " col: " << col << ", which is MapPixel_ID: " << MapPixel_ID << "." << endl;
			cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cout << "Explanation: The HydroPlus model requires input maps to align spatially." << endl;
			cout << "Correction: Create maps with NODATA values that spatially align with the dem.asc input map." << endl;
			//Call abort function, which ends the HydroPlus.exe simulation
			abort();
		}
	}
}

//Projection_Albers_to_LatitudeLongitude creates vector pair of double latitude and longitude from easting and northing for WKID 5070
//Note: Source: Snyder, J.P., (1987). Map projections: A working manual. USGS Professional Paper 1395 and EPSG documentation
//Note: Reverses spherical earth algorithm for Latitude Longitude to Albers Equal Area (AEA) projection formula
pair<double, double> Projection_Albers_to_LatitudeLongitude(double mapEasting_m, double mapNorthing_m, const ProjectionParams_Albers& ProjectionParams_Albers) {
	//rewritten using the ellipsoidal formulation, reference: https://github.com/OSGeo/PROJ/blob/master/src/projections/aea.cpp
	// Albers standard parallels and central meridian
	double phi1 = ProjectionParams_Albers.phi1;
	double phi2 = ProjectionParams_Albers.phi2;
	double phi0 = ProjectionParams_Albers.phi0;
	double lambda0 = ProjectionParams_Albers.lambda0;
	double falseEasting = ProjectionParams_Albers.falseEasting;
	double falseNorthing = ProjectionParams_Albers.falseNorthing;

	// Helper lambda for computing q(φ)
	auto calc_q = [&](double phi) {
		return (1 - pow(Earth_Eccentricity, 2)) * ((sin(phi) / (1 - pow(Earth_Eccentricity * sin(phi), 2))) - (1 / (2 * Earth_Eccentricity)) * log((1 - Earth_Eccentricity * sin(phi)) / (1 + Earth_Eccentricity * sin(phi))));
		};

	//This function appears in the Albers projection and its inverse. However, given a value of q, solving for φ analytically (i.e., directly expressing φ = f(q)) is not possible due to the transcendental nature of the logarithmic and trigonometric terms combined with φ.
	//phi_from_q function uses the Newton-Raphson method to find φ: q(ϕ) − q_target = 0
	auto solve_phi_from_q = [&](double qval) {
		double phi = asin(qval / 2.0); // initial guess
		for (int i = 0; i < 10; ++i) {
			double sin_phi = sin(phi);
			double cos_phi = cos(phi);
			double esin_phi = Earth_Eccentricity * sin_phi;
			double denom = (1 - pow(esin_phi, 2)) * (1 - pow(esin_phi, 2)) / ((1 - pow(Earth_Eccentricity, 2)) * cos_phi);
			double dq = calc_q(phi) - qval;
			phi -= dq / denom;
			if (fabs(dq) < 1e-12) break;
		}
		return phi;
	};

	double q_phi0 = calc_q(phi0);
	double q_phi1 = calc_q(phi1);
	double q_phi2 = calc_q(phi2);

	double  scaleFactor_m1 = cos(phi1) / sqrt(1 - pow(Earth_Eccentricity * sin(phi1), 2));
	double  scaleFactor_m2 = cos(phi2) / sqrt(1 - pow(Earth_Eccentricity * sin(phi2), 2));

	double projectionCone_n = (scaleFactor_m1 * scaleFactor_m1 - scaleFactor_m2 * scaleFactor_m2) / (q_phi2 - q_phi1);
	double projectionConstant_C = scaleFactor_m1 * scaleFactor_m1 + projectionCone_n * q_phi1;
	double rho0 = Earth_Radius_m * sqrt(projectionConstant_C - projectionCone_n * q_phi0) / projectionCone_n;

	double x = mapEasting_m - falseEasting;
	double y = rho0 - (mapNorthing_m - falseNorthing);
	double rho = sqrt(x * x + y * y);
	double theta = atan2(x, y);

	double q_lat = (projectionConstant_C - (rho * rho * projectionCone_n * projectionCone_n) / (Earth_Radius_m * Earth_Radius_m)) / projectionCone_n;
	double phi = solve_phi_from_q(q_lat);
	double lambda = lambda0 + theta / projectionCone_n;

	double Latitude_MapPixel_dd = phi * 180.0 / M_PI;
	double Longitude_MapPixel_dd = lambda * 180.0 / M_PI;
	return { Latitude_MapPixel_dd, Longitude_MapPixel_dd };

	
	/*//Perfect sphere (single radius) version
	//ScaleFactor_StandardParallels = (sin(ProjectionParams_Albers.phi1) + sin(ProjectionParams_Albers.phi2)) / 2.0
	double ScaleFactor_StandardParallels = (sin(ProjectionParams_Albers.phi1) + sin(ProjectionParams_Albers.phi2)) / 2.0;
	//Coef_1_ScaleFactor = pow(cos(ProjectionParams_Albers.phi1), 2) + 2 * ScaleFactor_StandardParallels * sin(ProjectionParams_Albers.phi1)
	double Coef_1_ScaleFactor = pow(cos(ProjectionParams_Albers.phi1), 2) + 2 * ScaleFactor_StandardParallels * sin(ProjectionParams_Albers.phi1);
	//Distance_to_ReferenceParallel_m = Earth_Radius_m * sqrt(Coef_1_ScaleFactor - 2 * ScaleFactor_StandardParallels * sin(ProjectionParams_Albers.phi0)) / ScaleFactor_StandardParallels
	double Distance_to_ReferenceParallel_m = Earth_Radius_m * sqrt(Coef_1_ScaleFactor - 2 * ScaleFactor_StandardParallels * sin(ProjectionParams_Albers.phi0)) / ScaleFactor_StandardParallels;

	//Distance_to_MapPixel_m = sqrt(mapEasting_m * mapEasting_m + pow(Distance_to_ReferenceParallel_m - mapNorthing_m, 2))
	double Distance_to_MapPixel_m = sqrt(mapEasting_m * mapEasting_m + pow(Distance_to_ReferenceParallel_m - mapNorthing_m, 2));
	//Angle_from_ReferenceParallel_rad = atan2(mapEasting_m, Distance_to_ReferenceParallel_m - mapNorthing_m)
	double Angle_from_ReferenceParallel_rad = atan2(mapEasting_m, Distance_to_ReferenceParallel_m - mapNorthing_m);

	//Latitude_MapPixel_rad = asin((Coef_1_ScaleFactor - (Distance_to_MapPixel_m * ScaleFactor_StandardParallels / Earth_Radius_m)) / (2 * ScaleFactor_StandardParallels))
	double Latitude_MapPixel_rad = asin((Coef_1_ScaleFactor - pow((Distance_to_MapPixel_m * ScaleFactor_StandardParallels / Earth_Radius_m),2)) / (2 * ScaleFactor_StandardParallels));

	//Longitude_MapPixel_rad = ProjectionParams_Albers.lambda0 + Angle_from_ReferenceParallel_rad / ScaleFactor_StandardParallels
	double Longitude_MapPixel_rad = ProjectionParams_Albers.lambda0 + Angle_from_ReferenceParallel_rad / ScaleFactor_StandardParallels;

	//Latitude_MapPixel_dd = Latitude_MapPixel_rad * 180.0 / M_PI, convert radian to decimal degree
	double Latitude_MapPixel_dd = Latitude_MapPixel_rad * 180.0 / M_PI;
	//Longitude_MapPixel_dd = Longitude_MapPixel_rad * 180.0 / M_PI, convert radian to decimal degree
	double Longitude_MapPixel_dd = Longitude_MapPixel_rad * 180.0 / M_PI;

	//return Latitude_MapPixel_dd and Longitude_MapPixel_dd 
	return { Latitude_MapPixel_dd, Longitude_MapPixel_dd };*/
}


//Note: Applies spherical earth algorithm for Albers Equal Area (AEA) projection from Latitude Longitude
pair<double, double> Inputs::Projection_LatitudeLongitude_to_Albers(double Latitude_MapPixel_dd, double Longitude_MapPixel_dd, const ProjectionParams_Albers& ProjectionParams_Albers) {
	//Convert decimal degrees to radians
	double Latitude_MapPixel_rad = Latitude_MapPixel_dd * M_PI / 180.0;
	double Longitude_MapPixel_rad = Longitude_MapPixel_dd * M_PI / 180.0;

	// Albers standard parallels and central meridian
	double phi1 = ProjectionParams_Albers.phi1;
	double phi2 = ProjectionParams_Albers.phi2;
	double phi0 = ProjectionParams_Albers.phi0;
	double lambda0 = ProjectionParams_Albers.lambda0;
	double falseEasting = ProjectionParams_Albers.falseEasting;
	double falseNorthing = ProjectionParams_Albers.falseNorthing;

	//Ellipsoid (GRS80 with varying radius by latitude) version, this version matches the online projection tool epsg.io result
	// Helper functions for ellipsoidal Albers
	auto calc_m = [&](double phi) {
		return cos(phi) / sqrt(1 - pow(Earth_Eccentricity * sin(phi), 2));
		};
	// Helper lambda for computing q(φ)
	auto calc_q = [&](double phi) {
		return (1 - pow(Earth_Eccentricity, 2)) * ((sin(phi) / (1 - pow(Earth_Eccentricity * sin(phi), 2))) - (1 / (2 * Earth_Eccentricity)) * log((1 - Earth_Eccentricity * sin(phi)) / (1 + Earth_Eccentricity * sin(phi))));
		};

	// Precompute for standard parallels and latitude of origin
	double m_phi1 = calc_m(phi1);
	double m_phi2 = calc_m(phi2);
	double q_phi1 = calc_q(phi1);
	double q_phi2 = calc_q(phi2);
	double q_phi0 = calc_q(phi0);

	double projectionCone_n = (m_phi1 * m_phi1 - m_phi2 * m_phi2) / (q_phi2 - q_phi1);
	double projectionConstant_C = m_phi1 * m_phi1 + projectionCone_n * q_phi1;
	double rho0 = Earth_Radius_m * sqrt(projectionConstant_C - projectionCone_n * q_phi0) / projectionCone_n;

	// Compute for map pixel latitude
	double q_lat = calc_q(Latitude_MapPixel_rad);
	double rho = Earth_Radius_m * sqrt(projectionConstant_C - projectionCone_n * q_lat) / projectionCone_n;
	double theta = projectionCone_n * (Longitude_MapPixel_rad - lambda0);

	// Compute Easting and Northing
	double easting = falseEasting + rho * sin(theta);
	double northing = falseNorthing + rho0 - rho * cos(theta);

	return { easting, northing };

	/*
	//Perfect sphere (single radius) version
	//ScaleFactor_StandardParallels = (sin(phi1) + sin(phi2)) / 2.0
	double ScaleFactor_StandardParallels = (sin(phi1) + sin(phi2)) / 2.0;
	//Coef_1_ScaleFactor = cos²(phi1) + 2n * sin(phi1)
	double Coef_1_ScaleFactor = pow(cos(phi1), 2) + 2 * ScaleFactor_StandardParallels * sin(phi1);
	//Distance_to_ReferenceParallel_m = R * sqrt(C - 2n * sin(phi0)) / n
	double Distance_to_ReferenceParallel_m = Earth_Radius_m / ScaleFactor_StandardParallels * sqrt(Coef_1_ScaleFactor - 2 * ScaleFactor_StandardParallels * sin(phi0));
	//Distance_to_MapPixel_m = R * sqrt(C - 2n * sin(phi)) / n
	double Distance_to_MapPixel_m = Earth_Radius_m / ScaleFactor_StandardParallels * sqrt(Coef_1_ScaleFactor - 2 * ScaleFactor_StandardParallels * sin(Latitude_MapPixel_rad));
	//Angle_from_ReferenceParallel_rad = n * (λ - λ₀)
	double Angle_from_ReferenceParallel_rad = ScaleFactor_StandardParallels * (Longitude_MapPixel_rad - lambda0);
	//mapEasting_m = ρ * sin(θ)
	double mapEasting_m = Distance_to_MapPixel_m * sin(Angle_from_ReferenceParallel_rad);
	//mapNorthing_m = ρ₀ - ρ * cos(θ)
	double mapNorthing_m = Distance_to_ReferenceParallel_m - Distance_to_MapPixel_m * cos(Angle_from_ReferenceParallel_rad);

	return { mapEasting_m, mapNorthing_m };
	*/
}


//Projection_Lambert_to_LatitudeLongitude creates vector pair of double latitude and longitude from easting and northing for WKID 3034
//Note: Source: Snyder, J.P., (1987). Map projections: A working manual. USGS Professional Paper 1395 and EPSG documentation
//Note: Reverses spherical earth algorithm for Latitude Longitude to Lambert Conformal Conic (LCC) projection formula
pair<double, double> Projection_Lambert_to_LatitudeLongitude(double mapEasting_m, double mapNorthing_m, const ProjectionParams_Lambert& ProjectionParams_Lambert) {
	// Ellipsoid version
	// Extract projection parameters
	double phi1 = ProjectionParams_Lambert.phi1;
	double phi2 = ProjectionParams_Lambert.phi2;
	double phi0 = ProjectionParams_Lambert.phi0;
	double lambda0 = ProjectionParams_Lambert.lambda0;
	double falseEasting = ProjectionParams_Lambert.falseEasting;
	double falseNorthing = ProjectionParams_Lambert.falseNorthing;
	
	auto tsfn = [&](double phi) {
		return tan(M_PI / 4.0 - phi / 2.0) /
			pow((1 - Earth_Eccentricity * sin(phi)) / (1 + Earth_Eccentricity * sin(phi)), Earth_Eccentricity / 2.0);
		};

	auto solve_phi_from_t = [&](double t) {
		double phi = M_PI / 2 - 2 * atan(t);
		for (int i = 0; i < 10; ++i) {
			double esinphi = Earth_Eccentricity * sin(phi);
			double phi_next = M_PI / 2 - 2 * atan(t * pow((1 - esinphi) / (1 + esinphi), Earth_Eccentricity / 2.0));
			if (fabs(phi - phi_next) < 1e-12) break;
			phi = phi_next;
		}
		return phi;
		};
	
	double m_phi1 = cos(phi1) / sqrt(1 - pow(Earth_Eccentricity * sin(phi1), 2));
	double m_phi2 = cos(phi2) / sqrt(1 - pow(Earth_Eccentricity * sin(phi2), 2));
	double tsfn_phi1 = tsfn(phi1);
	double tsfn_phi2 = tsfn(phi2);
	double tsfn_phi0 = tsfn(phi0);

	double projectionCone_n = (log(m_phi1) - log(m_phi2)) / (log(tsfn_phi1) - log(tsfn_phi2));
	double F = m_phi1 / (projectionCone_n * pow(tsfn_phi1, projectionCone_n));
	double rho0 = Earth_Radius_m * F * pow(tsfn_phi0, projectionCone_n);

	double x = mapEasting_m - falseEasting;
	double y = rho0 - (mapNorthing_m - falseNorthing);
	double rho = sqrt(x * x + y * y);
	double theta = atan2(x, y);

	double t = pow(rho / (Earth_Radius_m * F), 1 / projectionCone_n);
	double phi = solve_phi_from_t(t);
	double lambda = lambda0 + theta / projectionCone_n;

	double Latitude_MapPixel_dd = phi * 180.0 / M_PI;
	double Longitude_MapPixel_dd = lambda * 180.0 / M_PI;
	return { Latitude_MapPixel_dd, Longitude_MapPixel_dd };

	/*
	//Perfect sphere (single radius) version// Define constants
	const double ScaleFactor_StandardParallels = (log(cos(ProjectionParams_Lambert.phi1) / cos(ProjectionParams_Lambert.phi2))) /
		(log(tan(M_PI / 4 + ProjectionParams_Lambert.phi2 / 2) / tan(M_PI / 4 + ProjectionParams_Lambert.phi1 / 2)));
	const double Coef_1_ScaleFactor = cos(ProjectionParams_Lambert.phi1) * pow(tan(M_PI / 4 + ProjectionParams_Lambert.phi1 / 2), ScaleFactor_StandardParallels) / ScaleFactor_StandardParallels;
	const double rho0 = Earth_Radius_m * Coef_1_ScaleFactor / pow(tan(M_PI / 4 + ProjectionParams_Lambert.phi0 / 2), ScaleFactor_StandardParallels);

	// Calculate Distance_to_CentralParallel_m and Angle_from_CentralMeridian_rad
	const double Delta_Easting_m = mapEasting_m - ProjectionParams_Lambert.falseEasting;
	const double Delta_Northing_m = rho0 - (mapNorthing_m - ProjectionParams_Lambert.falseNorthing);
	const double Distance_to_CentralParallel_m = sqrt(Delta_Easting_m * Delta_Easting_m + Delta_Northing_m * Delta_Northing_m);
	const double Angle_from_CentralMeridian_rad = atan2(Delta_Easting_m, Delta_Northing_m);

	// Calculate Latitude and Longitude
	const double Latitude_MapPixel_rad = 2 * atan(pow(Earth_Radius_m * Coef_1_ScaleFactor / Distance_to_CentralParallel_m, 1 / ScaleFactor_StandardParallels)) - M_PI / 2;
	const double Longitude_MapPixel_rad = ProjectionParams_Lambert.lambda0 + Angle_from_CentralMeridian_rad / ScaleFactor_StandardParallels;

	// Convert to decimal degrees
	double Latitude_MapPixel_dd = Latitude_MapPixel_rad * 180.0 / M_PI;
	double Longitude_MapPixel_dd = Longitude_MapPixel_rad * 180.0 / M_PI;

	// Return the pair of latitude and longitude
	return { Latitude_MapPixel_dd, Longitude_MapPixel_dd };*/
}


pair<double, double> Inputs::Projection_LatitudeLongitude_to_Lambert(double Latitude_MapPixel_dd, double Longitude_MapPixel_dd, const ProjectionParams_Lambert& ProjectionParams_Lambert) {
	// Convert degrees to radians
	double Latitude_MapPixel_rad = Latitude_MapPixel_dd * M_PI / 180.0;
	double Longitude_MapPixel_rad = Longitude_MapPixel_dd * M_PI / 180.0;

	// Extract projection parameters
	double phi1 = ProjectionParams_Lambert.phi1;
	double phi2 = ProjectionParams_Lambert.phi2;
	double phi0 = ProjectionParams_Lambert.phi0;
	double lambda0 = ProjectionParams_Lambert.lambda0;
	double falseEasting = ProjectionParams_Lambert.falseEasting;
	double falseNorthing = ProjectionParams_Lambert.falseNorthing;

	//Ellipsoid version
	// Helper lambda for computing q(φ)
	auto calc_m = [&](double phi) {
		return cos(phi) / sqrt(1 - pow(Earth_Eccentricity * sin(phi), 2));
		};
	auto calc_q = [&](double phi) {
		return tan(M_PI / 4 - phi / 2) * pow((1 + Earth_Eccentricity * sin(phi)) / (1 - Earth_Eccentricity * sin(phi)), Earth_Eccentricity / 2);
		};


	double m_phi1 = calc_m(phi1);
	double m_phi2 = calc_m(phi2);
	double q_phi0 = calc_q(phi0);
	double q_phi1 = calc_q(phi1);
	double q_phi2 = calc_q(phi2);

	double n = (log(m_phi1) - log(m_phi2)) / (log(q_phi1) - log(q_phi2));
	double F = m_phi1 / pow(q_phi1, n) / n;

	double q_lat = calc_q(Latitude_MapPixel_rad);
	double rho0 = Earth_Radius_m * F * pow(q_phi0, n);
	double rho = Earth_Radius_m * F * pow(q_lat, n);
	double theta = n * (Longitude_MapPixel_rad - lambda0);

	double easting = falseEasting + rho * sin(theta);
	double northing = falseNorthing + rho0 - rho * cos(theta);

	return { easting, northing };

	/*//Perfect sphere (single radius) version
	// Compute projection constants
	double n = log(cos(phi1) / cos(phi2)) / log(tan(M_PI / 4 + phi2 / 2) / tan(M_PI / 4 + phi1 / 2));
	double F = cos(phi1) * pow(tan(M_PI / 4 + phi1 / 2), n) / n;
	double rho = Earth_Radius_m * F / pow(tan(M_PI / 4 + phi / 2), n);
	double rho0 = Earth_Radius_m * F / pow(tan(M_PI / 4 + phi0 / 2), n);

	// Compute projected coordinates
	double theta = n * (lambda - lambda0);
	double easting = falseEasting + rho * sin(theta);
	double northing = falseNorthing + rho0 - rho * cos(theta);

	return { easting, northing };*/
}


//Projection_UTM_to_LatitudeLongitude creates vector pair of double latitude and longitude from easting and northing for WKID 32600 or 32700
//Note: Source: Snyder, J.P., (1987). Map projections: A working manual. USGS Professional Paper 1395 and EPSG documentation
//Note: Reverses spherical earth algorithm for Latitude Longitude to Universal Transverse Mercator (UTM) projection formula
pair<double, double> Projection_UTM_to_LatitudeLongitude(double mapEasting_m, double mapNorthing_m, const ProjectionParams_UTM& projectionParams) {
	const double Earth_Eccentricity_squared = pow(Earth_Eccentricity, 2);
	// Second eccentricity squared
	const double Coeff_1_Eccentricity = Earth_Eccentricity_squared / (1 - Earth_Eccentricity_squared);
	const double ScaleFactor_UTM = projectionParams.scaleFactor;

	// Adjust easting and northing by false origins
	double Delta_Easting_m = mapEasting_m - projectionParams.falseEasting;
	double Delta_Northing_m = mapNorthing_m;

	// Adjust for southern hemisphere
	if (Delta_Northing_m < 0) {
		Delta_Northing_m += projectionParams.falseNorthing;
	}

	// Calculate the meridional arc
	double Meridional_Arc_m = Delta_Northing_m / ScaleFactor_UTM;

	// Calculate the footpoint latitude
	double Latitude_Footprint_m = Meridional_Arc_m / (Earth_Radius_m * (1 - Earth_Eccentricity_squared / 4 - 3 * Earth_Eccentricity_squared * Earth_Eccentricity_squared / 64 - 5 * Earth_Eccentricity_squared * Earth_Eccentricity_squared * Earth_Eccentricity_squared / 256));
	double Coeff_2_Eccentricity = (1 - sqrt(1 - Earth_Eccentricity_squared)) / (1 + sqrt(1 - Earth_Eccentricity_squared));

	double Latitude_CentralMeridian_rad = Latitude_Footprint_m
		+ (3 * Coeff_2_Eccentricity / 2 - 27 * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity / 32) * sin(2 * Latitude_Footprint_m)
		+ (21 * Coeff_2_Eccentricity * Coeff_2_Eccentricity / 16 - 55 * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity / 32) * sin(4 * Latitude_Footprint_m)
		+ (151 * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity / 96) * sin(6 * Latitude_Footprint_m)
		+ (1097 * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity * Coeff_2_Eccentricity / 512) * sin(8 * Latitude_Footprint_m);

	// Calculate latitude and longitude
	double Coeff_3_Eccentricity = Coeff_1_Eccentricity * pow(cos(Latitude_CentralMeridian_rad), 2);
	double Tangent_Latitude_squared = pow(tan(Latitude_CentralMeridian_rad), 2);
	double Radius_PrimeVerticalCurvature = Earth_Radius_m / sqrt(1 - Earth_Eccentricity_squared * pow(sin(Latitude_CentralMeridian_rad), 2));
	double Radius_MeridianCurvature = Earth_Radius_m * (1 - Earth_Eccentricity_squared) / pow(1 - Earth_Eccentricity_squared * pow(sin(Latitude_CentralMeridian_rad), 2), 1.5);
	double Delta_Easting_Normalized_m = Delta_Easting_m / (Radius_PrimeVerticalCurvature * ScaleFactor_UTM);

	double Latitude_MapPixel_rad = Latitude_CentralMeridian_rad
		- (Radius_PrimeVerticalCurvature * tan(Latitude_CentralMeridian_rad) / Radius_MeridianCurvature)
		* (Delta_Easting_Normalized_m * Delta_Easting_Normalized_m / 2
			- (5 + 3 * Tangent_Latitude_squared + 10 * Coeff_3_Eccentricity - 4 * Coeff_3_Eccentricity * Coeff_3_Eccentricity - 9 * Coeff_1_Eccentricity) * pow(Delta_Easting_Normalized_m, 4) / 24
			+ (61 + 90 * Tangent_Latitude_squared + 298 * Coeff_3_Eccentricity + 45 * Tangent_Latitude_squared * Tangent_Latitude_squared - 252 * Coeff_1_Eccentricity - 3 * Coeff_3_Eccentricity * Coeff_3_Eccentricity) * pow(Delta_Easting_Normalized_m, 6) / 720);

	double Longitude_MapPixel_rad = projectionParams.centralMeridian
		+ (Delta_Easting_Normalized_m
			- (1 + 2 * Tangent_Latitude_squared + Coeff_3_Eccentricity) * pow(Delta_Easting_Normalized_m, 3) / 6
			+ (5 - 2 * Coeff_3_Eccentricity + 28 * Tangent_Latitude_squared - 3 * Coeff_3_Eccentricity * Coeff_3_Eccentricity + 8 * Coeff_1_Eccentricity + 24 * Tangent_Latitude_squared * Tangent_Latitude_squared) * pow(Delta_Easting_Normalized_m, 5) / 120)
		/ cos(Latitude_CentralMeridian_rad);

	// Convert radians to degrees
	double Latitude_MapPixel_dd = Latitude_MapPixel_rad * 180.0 / M_PI;
	double Longitude_MapPixel_dd = Longitude_MapPixel_rad * 180.0 / M_PI;

	return { Latitude_MapPixel_dd, Longitude_MapPixel_dd };
}

pair<double, double>  Inputs::Projection_LatitudeLongitude_to_UTM(double latitude_deg, double longitude_deg, const ProjectionParams_UTM& utmParams) {
	const double Earth_Eccentricity_squared = pow(Earth_Eccentricity, 2);
	//Convert latitude/longitude to radians
	double lat_rad = latitude_deg * M_PI / 180.0;
	double lon_rad = longitude_deg * M_PI / 180.0;
	
	// Extract projection parameters
	const double k0 = utmParams.scaleFactor;
	double centralMeridian_rad = utmParams.centralMeridian;

	//Precompute trigonometric terms
	double sinLat = sin(lat_rad);
	double cosLat = cos(lat_rad);
	double tanLat = tan(lat_rad);

	//Radius of curvature in the prime vertical (ν)
	double nu_m = Earth_Radius_m / sqrt(1.0 - Earth_Eccentricity_squared * sinLat * sinLat);

	//Compute the meridional arc distance from the equator to the latitude
	double A0 = 1 - Earth_Eccentricity_squared / 4 - 3 * pow(Earth_Eccentricity_squared, 2) / 64 - 5 * pow(Earth_Eccentricity_squared, 3) / 256;
	double A2 = 3.0 / 8.0 * (Earth_Eccentricity_squared + pow(Earth_Eccentricity_squared, 2) / 4 + 15 * pow(Earth_Eccentricity_squared, 3) / 128);
	double A4 = 15.0 / 256.0 * (pow(Earth_Eccentricity_squared, 2) + 3 * pow(Earth_Eccentricity_squared, 3) / 4);
	double A6 = 35.0 / 3072.0 * pow(Earth_Eccentricity_squared, 3);

	double meridionalArc_m = Earth_Radius_m * ( A0 * lat_rad - A2 * sin(2.0 * lat_rad) + A4 * sin(4.0 * lat_rad) - A6 * sin(6.0 * lat_rad));

	//Compute central angle difference from central meridian
	double deltaLambda_rad = lon_rad - centralMeridian_rad;

	//Precompute additional parameters
	double etaSquared = Earth_Eccentricity_squared / (1 - Earth_Eccentricity_squared) * cosLat * cosLat;
	double tanLat_2 = tanLat * tanLat;
	double cosLatdeltadeltaLambda = cosLat * deltaLambda_rad;

	//Compute Easting (X) in meters
	double easting_m = utmParams.falseEasting + k0 * nu_m * (
		cosLatdeltadeltaLambda
		+ (1 - tanLat_2 + etaSquared) * pow(cosLatdeltadeltaLambda, 3) / 6
		+ (5 - 18 * tanLat_2 + tanLat_2 * tanLat_2 + 72 * etaSquared - 58 * Earth_Eccentricity_squared / (1 - Earth_Eccentricity_squared)) * pow(cosLatdeltadeltaLambda, 5) / 120);

	//Compute Northing (Y) in meters
	double northing_m = k0 * (
		meridionalArc_m
		+ nu_m * tanLat * (
			cosLatdeltadeltaLambda * cosLatdeltadeltaLambda / 2
			+ (5 - tanLat_2 + 9 * etaSquared + 4 * etaSquared * etaSquared) * pow(cosLatdeltadeltaLambda, 4) / 24
			+ (61 - 58 * tanLat_2 + tanLat_2 * tanLat_2 + 600 * etaSquared - 330 * Earth_Eccentricity_squared / (1 - Earth_Eccentricity_squared)) * pow(cosLatdeltadeltaLambda, 6) / 720
			)
		);

	// Add false northing for southern hemisphere
	if (utmParams.falseNorthing > 0.0) {
		northing_m += utmParams.falseNorthing;
	}

	return { easting_m, northing_m };
}


//ProjectionParams_UTM initializeUTMParams function convertes MapProjection_WKID into params values
ProjectionParams_UTM Inputs::initializeUTMParams(int MapProjection_WKID) {
	ProjectionParams_UTM params;
	params.zone = MapProjection_WKID % 100;// Extract the last two digits for the zone
	params.centralMeridian = (params.zone * 6 - 183) * M_PI / 180.0;// Central meridian in radians
	params.falseEasting = 500000.0;// UTM always uses 500,000m false easting
	if ( MapProjection_WKID / 100 == 326) {
		params.falseNorthing = 0.0;           // Northern Hemisphere
	}
	else if (MapProjection_WKID / 100 == 327) {
		params.falseNorthing = 10000000.0;    // Southern Hemisphere
	}
	else {
		cout << "Unsupported UTM WKID: " + to_string(MapProjection_WKID) << endl;	// Invalid or unsupported UTM code
	}
	params.scaleFactor = 0.9996;// UTM scale factor

	return params;
}

//LatitudeLongitude_Projection_Manager function will call Projection specific functions to compute Latitude_Longitude_vectorPair_dd
void Inputs::LatitudeLongitude_Projection_Manager(vector<vector<pair<double, double>>>& Latitude_Longitude_vectorPair_dd) {
	//MapMetadata struct initializes instance mapMetadata
	MapMetadata mapMetadata;
	//mapMetadata.lowerLeftX_m = Inputs::xllcorner_m
	mapMetadata.lowerLeftX_m = Inputs::xllcorner_m;
	//mapMetadata.lowerLeftY_m = Inputs::yllcorner_m
	mapMetadata.lowerLeftY_m = Inputs::yllcorner_m;
	//mapMetadata.cellSize_m = Inputs::Length_Pixel_Side_m
	mapMetadata.cellSize_m = Inputs::Length_Pixel_Side_m;
	//mapMetadata.nCols = Inputs::nCols
	mapMetadata.nCols = Inputs::nCols;
	//mapMetadata.nRows = Inputs::nRows
	mapMetadata.nRows = Inputs::nRows;

	//MapProjection_WKID defined as SimulationNumericalParams["MapProjection_WKID"] from HydroPlusConfig.xml
	int MapProjection_WKID = SimulationNumericalParams["MapProjection_WKID"];
	//function<pair<double, double>(double, double)> projection_to_LatitudeLongitude_Function 
	//Note: Define a general-purpose function pointer to convert projected coordinates (Easting, Northing) to ...
	//Note: ... geographic coordinates (Latitude, Longitude) in decimal degrees (dd)
	function<pair<double, double>(double, double)> projection_to_LatitudeLongitude_Function;
	
	// Look up projection parameters from supported WKID map
	if (AlbersParamMap.find(MapProjection_WKID) != AlbersParamMap.end()) {
		//ProjectionParams_Albers projectionParams; create Albers-specific parameters
		ProjectionParams_Albers projectionParams = AlbersParamMap[MapProjection_WKID];
		//projection_to_LatitudeLongitude_Function = [projectionParams](double easting, double northing); 
		//Note: Rather than a refernce use a local copy of projectionParams, otherwise once outside the block it is out of scope and destroyed
		//Note: Assign a lambda function that captures the parameters by reference and wraps the actual Albers projection function
		projection_to_LatitudeLongitude_Function = [projectionParams](double easting, double northing) {
			//returns the output from Projection_Albers_to_LatitudeLongitude(easting, northing, projectionParams)
			return Projection_Albers_to_LatitudeLongitude(easting, northing, projectionParams);
		};
	}
	//If MapProjection_WKID >= 32601 && MapProjection_WKID <= 32660 ... then Universal Transverse Mercator
	else if ((MapProjection_WKID >= 32601 && MapProjection_WKID <= 32660) || (MapProjection_WKID >= 32701 && MapProjection_WKID <= 32760)) { 
		//ProjectionParams_UTM projectionParams = initializeUTMParams(MapProjection_WKID); create UTM-specific parameters
		ProjectionParams_UTM projectionParams = initializeUTMParams(MapProjection_WKID);
		//projection_to_LatitudeLongitude_Function = [projectionParams](double easting, double northing)
		//Note: Rather than a refernce use a local copy of projectionParams, otherwise once outside the block it is out of scope and destroyed
		//Note: Assign a lambda function that captures the parameters by reference and wraps the actual UTM projection function
		projection_to_LatitudeLongitude_Function = [projectionParams](double easting, double northing) {
			//returns the output from Projection_UTM_to_LatitudeLongitude(easting, northing, projectionParams)
			return Projection_UTM_to_LatitudeLongitude(easting, northing, projectionParams);
		};
	}
	//Look up projection parameters from supported WKID map, used to only have option of If MapProjection_WKID == 3034 
	else if (Inputs::LambertParamMap.find(MapProjection_WKID) != Inputs::LambertParamMap.end()) {
		// Retrieve Lambert projection parameters from map
		ProjectionParams_Lambert projectionParams = Inputs::LambertParamMap[MapProjection_WKID];
		//projection_to_LatitudeLongitude_Function = [&projectionParams](double easting, double northing)
		//Note: Rather than a refernce use a local copy of projectionParams, otherwise once outside the block it is out of scope and destroyed
		//Note: Assign a lambda function that captures the parameters by reference and wraps the actual Lambert projection function
		projection_to_LatitudeLongitude_Function = [projectionParams](double easting, double northing) {
			//returns the output from Projection_Lambert_to_LatitudeLongitude(easting, northing, projectionParams)
			return Projection_Lambert_to_LatitudeLongitude(easting, northing, projectionParams);
		};
	}

	//nRows = mapMetadata.nRows and nCols = mapMetadata.nCols when ReferenceStationOutsideMap is 0 or false
	int nRows = mapMetadata.nRows;
	int nCols = mapMetadata.nCols;

	//Latitude_Longitude_vectorPair_dd resize as the latitude-longitude vector
	Latitude_Longitude_vectorPair_dd.resize(nRows, vector<pair<double, double>>(nCols));

	//For loop row to mapMetadata.nRows
	for (int row = 0; row < mapMetadata.nRows; ++row) {
		//For loop col to mapMetadata.nRows
		for (int col = 0; col < mapMetadata.nCols; ++col) {
			//mapEasting_m = mapMetadata.lowerleftX + col * mapMetadata.cellsize
			double mapEasting_m = mapMetadata.lowerLeftX_m + col * mapMetadata.cellSize_m;
			//mapNorthing_m = mapMetadata.lowerLeftY_m + (mapMetadata.nRows - row - 1) * mapMetadata.cellSize_m
			double mapNorthing_m = mapMetadata.lowerLeftY_m + (mapMetadata.nRows - row - 1) * mapMetadata.cellSize_m;
			//Latitude_Longitude_vectorPair_dd[row][col] = projection_to_LatitudeLongitude_Function(mapEasting_m, mapNorthing_m)
			//Note: projection_to_LatitudeLongitude_Function has pointer to projectionParams and Projection_Albers_to_LatitudeLongitude or similar vector
			Latitude_Longitude_vectorPair_dd[row][col] = projection_to_LatitudeLongitude_Function(mapEasting_m, mapNorthing_m);
			
			//Debugging
			/*if (row < 5 && col < 5) {
				cout << "MapProjection_WKID= " << MapProjection_WKID << endl;
				cout << "MapEasting_m is " << to_string(mapEasting_m) << ", mapNorthing_m is " << to_string(mapNorthing_m) << endl;
				cout << "Latitude_dd= " << Latitude_Longitude_vectorPair_dd[row][col].first << " Longitude_dd= " << Latitude_Longitude_vectorPair_dd[row][col].second << endl;
			}*/
			
		}
	}

	//If ReferenceStationOutsideMap is 1 and Flag_MultipleStations not 1 then extra index is needed
	if (SimulationStringParams["ReferenceStationOutsideMap"] == "1" && SimulationNumericalParams["Flag_MultipleStations"] != 1) {
		//ref_MapPixel_ID = nRows * nCols
		int ref_MapPixel_ID = nRows * nCols;
		//If Latitude_Longitude_vectorPair_dd.size() <= ref_MapPixel_ID then resize for 1 station outside map
		if (Latitude_Longitude_vectorPair_dd.size() <= ref_MapPixel_ID) {
			// Resize to exactly nRows*nCols + 1
			Latitude_Longitude_vectorPair_dd.resize(ref_MapPixel_ID + 1);
		}
		//mapEasting_m = mapMetadata.lowerLeftX_m; initially assigned a default location in lower left corner of map as easiest guess
		//Note: mapEasting_m assigned default value to begin but is replaced if data were provided
		double mapEasting_m= mapMetadata.lowerLeftX_m;
		///mapNorthing_m = mapMetadata.lowerLeftY_m; initially assigned a default location in lower left corner of map as easiest guess
		double mapNorthing_m = mapMetadata.lowerLeftY_m;

		// Read station coordinates based on RefWeatherLocationCoordinateFormat
		string ref_coord_format_flag = "PCS";  // Default to PCS
		// Check if user provided the flag
		if (SimulationStringParams.find("RefWeatherLocationCoordinateFormat") != SimulationStringParams.end()) {
			ref_coord_format_flag = SimulationStringParams["RefWeatherLocationCoordinateFormat"];
		}
		//else cout the warning message
		else {
			cout << "Warning: RefWeatherLocationCoordinateFormat not found in HydroPlusConfig.xml. Defaulting to PCS (Easting/Northing in meters)." << endl;
		}

		//missingOrEmptyEasting = TemperatureExecutionParams.count("RefWeatherLocationEasting_m_or_Longitude_dd") == 0 || TemperatureExecutionParams["RefWeatherLocationEasting_m_or_Longitude_dd"].empty() to check if HydroPlusConfig.xml element is missing or empty
		bool missingOrEmptyEasting = TemperatureExecutionParams.count("RefWeatherLocationEasting_m_or_Longitude_dd") == 0 || TemperatureExecutionParams["RefWeatherLocationEasting_m_or_Longitude_dd"].empty();
		//missingOrEmptyNorthing = TemperatureExecutionParams.count("RefWeatherLocationNorthing_m_or_Latitude_dd") == 0 || TemperatureExecutionParams["RefWeatherLocationNorthing_m_or_Latitude_dd"].empty() to check if HydroPlusConfig.xml element is missing or empty
		bool missingOrEmptyNorthing = TemperatureExecutionParams.count("RefWeatherLocationNorthing_m_or_Latitude_dd") == 0 || TemperatureExecutionParams["RefWeatherLocationNorthing_m_or_Latitude_dd"].empty();

		//If !missingOrEmptyEasting then not missing HydroPlusConfig.xml element and assigned intended location
		if (!missingOrEmptyEasting && !missingOrEmptyNorthing) {
			if (ref_coord_format_flag == "GCS") {
				double RefStation_Latitude_dd;
				// User provided Lat/Lon in decimal degrees
				double Station_Longitude_dd = stod(TemperatureExecutionParams["RefWeatherLocationEasting_m_or_Longitude_dd"]);
				double Station_Latitude_dd = stod(TemperatureExecutionParams["RefWeatherLocationNorthing_m_or_Latitude_dd"]);
				//MapProjection_WKID is read from HydroPlusConfig.xml as SimulationNumericalParams["MapProjection_WKID"]
				int MapProjection_WKID = SimulationNumericalParams["MapProjection_WKID"];

				pair<double, double> projected_EastingNorthing;
				// Look up projection parameters from supported WKID map
				if (AlbersParamMap.find(MapProjection_WKID) != AlbersParamMap.end()) {
					//Call the projection function to convert Lat/Lon (dd) to Easting/Northing (meters)
					projected_EastingNorthing = Projection_LatitudeLongitude_to_Albers(Station_Latitude_dd, Station_Longitude_dd, AlbersParamMap[MapProjection_WKID]);
				}
				else if (LambertParamMap.find(MapProjection_WKID) != LambertParamMap.end()) {
					//Call the projection function to convert Lat/Lon (dd) to Easting/Northing (meters)
					projected_EastingNorthing = Projection_LatitudeLongitude_to_Lambert(Station_Latitude_dd, Station_Longitude_dd, LambertParamMap[MapProjection_WKID]);
				}
				else if (((MapProjection_WKID >= 32601 && MapProjection_WKID <= 32660) || (MapProjection_WKID >= 32701 && MapProjection_WKID <= 32760))) {
					//Call the projection function to convert Lat/Lon (dd) to Easting/Northing (meters)
					projected_EastingNorthing = Projection_LatitudeLongitude_to_UTM(Station_Latitude_dd, Station_Longitude_dd, initializeUTMParams(MapProjection_WKID));
				}
				else {
					cout << "Warning: Unsupported MapProjection_WKID for coordinate conversion." << endl;
				}
				//Assign projected_EastingNorthing to mapEasting_m and mapNorthing_m
				mapEasting_m = projected_EastingNorthing.first;
				mapNorthing_m = projected_EastingNorthing.second;
				//print out the convertion result for debugging
				cout << "Reference station (GCS): Longitude = " << Station_Longitude_dd
					<< ", Latitude = " << Station_Latitude_dd
					<< " -> Projected (PCS): Easting = " << int(round(mapEasting_m))
					<< " m, Northing = " << int(round(mapNorthing_m)) << " m" << endl;
			}
			else {
				// PCS: read directly
				mapEasting_m = stod(TemperatureExecutionParams["RefWeatherLocationEasting_m_or_Longitude_dd"]);
				mapNorthing_m = stod(TemperatureExecutionParams["RefWeatherLocationNorthing_m_or_Latitude_dd"]);
			}
		}
		else {
			if (missingOrEmptyEasting) {
				cout << "Warning: RefWeatherLocationEasting_m_or_Longitude_dd element missing in HydroPlusConfig.xml TemperatureExecutionParams." << endl;
				cout << "Note: If element RefWeatherLocationRowCol has a value < 0, then Easting value is expected." << endl;
				cout << "Note: The Easting value was replaced with the dem.asc input file header value for xllcorner." << endl;
			}
			if (missingOrEmptyNorthing) {
				cout << "Warning: RefWeatherLocationNorthing_m_or_Latitude_dd element missing in HydroPlusConfig.xml TemperatureExecutionParams." << endl;
				cout << "Note: If element RefWeatherLocationRowCol has a value < 0, then station Northing value is expected." << endl;
				cout << "Note: The Northing value was replaced with the dem.asc input file header value for yllcorner." << endl;
			}
		}

		//Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID] = vector<pair<double, double>>(nCols); resize vector
		Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID] = vector<pair<double, double>>(nCols);
		//Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID][0] = projection_to_LatitudeLongitude_Function(mapEasting_m, mapNorthing_m)
		//Note: projection_to_LatitudeLongitude_Function has pointer to projectionParams and Projection_Albers_to_LatitudeLongitude
		Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID][0] = projection_to_LatitudeLongitude_Function(mapEasting_m, mapNorthing_m);
		//Latitude_Longitude_vectorPair_dd.push_back(vector<pair<double, double>>(nCols)); adds one new pairmapNorthing_m);
	}
	
	//If ReferenceStationOutsideMap is 1 and Flag_MultipleStations is 1 then loop through all reference stations
	if (SimulationStringParams["ReferenceStationOutsideMap"] == "1" && SimulationNumericalParams["Flag_MultipleStations"] == 1) {
		//Latitude_Longitude_vectorPair_dd resized for Station_OBJECTID_vec
		Latitude_Longitude_vectorPair_dd.resize(nRows* nCols + Station_OBJECTID_vec.size());
		//Loop through each reference station
		for (size_t i = 0; i < Station_Easting_m_vec.size(); ++i) {
			//Extract the easting and northing for the reference station
			double mapEasting_m = Station_Easting_m_vec[i];
			double mapNorthing_m = Station_Northing_m_vec[i];

			int ref_MapPixel_ID = nRows * nCols + static_cast<int>(i);
			//Push a new row with 1 col into the latitude/longitude grid and assign projected value
			Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID].resize(1);
			Latitude_Longitude_vectorPair_dd[ref_MapPixel_ID][0] = projection_to_LatitudeLongitude_Function(mapEasting_m, mapNorthing_m);
		}
	}
}

//ValidateAndExtractDrawerFolderID function checks validity of HydroPlusConfig.xml DataDrawer and DataFolder location
void Inputs::ValidateAndExtractDrawerFolderID(const string& DataDrawer_DataFolder_str, const string& SimulationStringParams_DataDrawer_DataFolder, const vector<vector<unordered_map<string, double>>>& DataDrawers, const vector<vector<unordered_map<string, string>>>& StringDataDrawers, int& outDrawerID, int& outFolderID) {
	//size_t commaPos = SimulationStringParams_DataDrawer_DataFolder.find(","); defines comma position
	size_t commaPos = SimulationStringParams_DataDrawer_DataFolder.find(",");
	//If (commaPos == string::npos) not present then send error
	if (commaPos == string::npos) {
		cout << "Error: " << DataDrawer_DataFolder_str << " is missing a comma (expected format: DrawerID,FolderID)." << endl;
		cout << "DataDrawers.size()= " << DataDrawers.size() << " StringDataDrawers.size()= " << StringDataDrawers.size() << endl;
		exit(0);
	}

	//outDrawerID = atoi(SimulationStringParams_DataDrawer_DataFolder.substr(0, commaPos).c_str()) reads DataDrawer left of comma
	outDrawerID = atoi(SimulationStringParams_DataDrawer_DataFolder.substr(0, commaPos).c_str());
	//outFolderID = atoi(SimulationStringParams_DataDrawer_DataFolder.substr(commaPos + 1).c_str()) reads DataFolder right of comma
	outFolderID = atoi(SimulationStringParams_DataDrawer_DataFolder.substr(commaPos + 1).c_str());
	//if (outDrawerID < 0 OR outDrawerID >= int(DataDrawers.size()) OR outDrawerID >= int(StringDataDrawers.size())) then DataDrawer out of bounds
	if (outDrawerID < 0 || outDrawerID >= int(DataDrawers.size()) || outDrawerID >= int(StringDataDrawers.size())) {
		cout << "Warning: HydroPlusConfig.xml element " << DataDrawer_DataFolder_str << " has problematic DataDrawer value " << outDrawerID << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The 1st " << DataDrawer_DataFolder_str << " value must correspond to a DataDrawer in the DataOrganizer." << endl;
		cout << "Correction: Assign a " << DataDrawer_DataFolder_str << " value to correspond with a DataDrawer, where 0 is the 1st instance." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	//if (outFolderID < 0 OR outFolderID >= int(DataDrawers[outDrawerID].size()) OR outFolderID >= int(StringDataDrawers[outDrawerID].size())) then DataFolder out of bounds
	if (outFolderID < 0 || outFolderID >= int(DataDrawers[outDrawerID].size()) || outFolderID >= int(StringDataDrawers[outDrawerID].size())) {
		cout << "Warning: HydroPlusConfig.xml element " << DataDrawer_DataFolder_str << " has problematic DataFolder value " << outFolderID << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Explanation: The 2nd " << DataDrawer_DataFolder_str << " value must correspond to a DataFolder in the DataOrganizer." << endl;
		cout << "Correction: Assign a " << DataDrawer_DataFolder_str << " value to correspond with a DataFolder, where 0 is the 1st instance." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
}

void Inputs::LoadScenarioParametersFromSimulationScenarios()
{
	//If SimulationScenarios map contains the key Flag_Scenario_CoolAir_LandCover then enter
	//Note: Flag_Scenario_CoolAir_LandCover (flag) is initialized to 0, which will result in no change to land cover
	if (SimulationScenarios.count("Flag_Scenario_CoolAir_LandCover")) {

		//SimulationScenarios["Flag_Scenario_CoolAir_LandCover"] defined in HydroPlusConfig.xml for element TreeCanopyCover_overImpervious_frac
		//Note: All adjustments maintain the ranges for fractional cover between 0 and 1
		//Note: If Flag_Scenario_CoolAir_LandCover = 0 then TreeCanopyCover_overImpervious_frac causes no change in imperviouscover.asc values ...
		//Note: ... with IC = IC, TCI = TC * TreeCanopyCover_overImpervious_frac, TCP = TC - TCI, IC_underT = TCI, IC_noT = IC - TCI
		//Note: If Flag_Scenario_CoolAir_LandCover = 1 then TreeCanopyCover_overImpervious_frac causes increase in imperviouscover.asc values ...
		//Note: ... with IC = IC + TCI, TCI = TC * TreeCanopyCover_overImpervious_frac, TCP = TC - TCI, IC_underT = TCI, IC_noT = IC - TCI
		//Note: If Flag_Scenario_CoolAir_LandCover = 2 then comparison is made with base case that used Flag_Scenario_CoolAir_LandCover = 0 ...
		//Note: ... with TC increased by Scenario_CoolAir_Alternative_TC, distributed by Scenario_CoolAir_Base_TCI_to_TC ...
		//Note: ... without increasing IC to include TCI and perhaps decreasing IC to implement Scenario_CoolAir_Base_TCI_to_TC 
		//Note: If Flag_Scenario_CoolAir_LandCover = 3 then comparison is made with base case that used Flag_Scenario_CoolAir_LandCover = 1 ...
		//Note: ... with TC increased by Scenario_CoolAir_Alternative_TC, distributed by Scenario_CoolAir_Base_TCI_to_TC ...
		//Note: ... with increasing IC to include TCI and perhaps decreasing IC to implement Scenario_CoolAir_Base_TCI_to_TC 
		//Note: If Flag_Scenario_CoolAir_LandCover = 4 then comparison is made with base case that used Flag_Scenario_CoolAir_LandCover = 1 ...
		//Note: ... with TC increased by Scenario_CoolAir_Alternative_TC, distributed by TreeCanopyCover_overImpervious_frac ...
		//Note: ... with increasing Permeable Pavement to include TCI suggested in implement Scenario_CoolAir_Base_TCI_to_TC 
		Flag_Scenario_CoolAir_LandCover = SimulationScenarios["Flag_Scenario_CoolAir_LandCover"];
		//If Flag_Scenario_CoolAir_LandCover not 0, 1, 2, 3, or 4 then set to 0
		if (Flag_Scenario_CoolAir_LandCover != 0 && Flag_Scenario_CoolAir_LandCover != 1 && 
			Flag_Scenario_CoolAir_LandCover != 2 && Flag_Scenario_CoolAir_LandCover != 3 && 
			Flag_Scenario_CoolAir_LandCover != 4) {
			//Flag_Scenario_CoolAir_LandCover default setting is 0, no change
			Flag_Scenario_CoolAir_LandCover = 0;
		}
	}

	//If SimulationScenarios map contains the key Scenario_CoolAir_Base_TCI_to_TC_frac then enter
	//Note: Scenario_CoolAir_Base_TCI_to_TC (frac) is initialized to -1, which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Base_TCI_to_TC_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Base_TCI_to_TC_frac"] defined in HydroPlusConfig.xml for scenarios
		Scenario_CoolAir_Base_TCI_to_TC_frac = SimulationScenarios["Scenario_CoolAir_Base_TCI_to_TC_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Base_TCI_to_TC_frac = clamp(Scenario_CoolAir_Base_TCI_to_TC_frac, 0.0, 1.0);
	}
	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_TC_Min_frac then enter
	//Note: Scenario_CoolAir_Alternative_TC_Min_frac (frac) is initialized to 0, which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Min_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_TC_Min_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_TC_Min_frac (frac) represents minimum treecover (frac)
		Scenario_CoolAir_Alternative_TC_Min_frac = SimulationScenarios["Scenario_CoolAir_Alternative_TC_Min_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Alternative_TC_Min_frac = clamp(Scenario_CoolAir_Alternative_TC_Min_frac, 0.0, 1.0);
	}
	//if SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Max_frac") then enter
	//Note: Scenario_CoolAir_Alternative_TC_Max_frac (frac) is initialized to 1, which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Max_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_TC_Max_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_TC_Max_frac (frac) represents maximum treecover (frac)
		Scenario_CoolAir_Alternative_TC_Max_frac = SimulationScenarios["Scenario_CoolAir_Alternative_TC_Max_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Alternative_TC_Max_frac = clamp(Scenario_CoolAir_Alternative_TC_Max_frac, 0.0, 1.0);
	}
	//If Flag_Scenario_CoolAir_LandCover > 1 & Scenario_CoolAir_Alternative_TC_Min_frac > Scenario_CoolAir_Alternative_TC_Max_frac then abort
	if (Flag_Scenario_CoolAir_LandCover > 1 && Scenario_CoolAir_Alternative_TC_Min_frac > Scenario_CoolAir_Alternative_TC_Max_frac) {
		cout << "Warning: HydroPlusConfig.xml Scenario_CoolAir_Alternative_TC_Min_frac > Scenario_CoolAir_Alternative_TC_Max_frac." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Correction: Set Scenario_CoolAir_Alternative_TC_Min_frac <= Scenario_CoolAir_Alternative_TC_Max_frac." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	//If (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_replacing_TC_frac") then enter
	//Note: Scenario_CoolAir_Alternative_IC_replacing_TC_frac (frac) is initialized to 0, which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_replacing_TC_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_IC_replacing_TC_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Scenario_CoolAir_Alternative_TC_Max_frac<1 then Scenario_CoolAir_Alternative_IC_replacing_TC_frac represents replacement cover
		Scenario_CoolAir_Alternative_IC_replacing_TC_frac = SimulationScenarios["Scenario_CoolAir_Alternative_IC_replacing_TC_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Alternative_IC_replacing_TC_frac = clamp(Scenario_CoolAir_Alternative_IC_replacing_TC_frac, 0.0, 1.0);
	}
	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_IC_Max_frac then enter
	//Note: Scenario_CoolAir_Alternative_IC_Max_frac (frac) is initialized to 1, which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Max_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_IC_Max_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_IC_Max_frac (frac) represents maximum imperviouscover (frac)
		Scenario_CoolAir_Alternative_IC_Max_frac = SimulationScenarios["Scenario_CoolAir_Alternative_IC_Max_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Alternative_IC_Max_frac = clamp(Scenario_CoolAir_Alternative_IC_Max_frac, 0.0, 1.0);
	}
	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_IC_Min_frac then enter
	//Note: Scenario_CoolAir_Alternative_IC_Min_frac (frac) is initialized to 0 which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Min_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_IC_Min_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_IC_Min_frac (frac) represents minimum imperviouscover (frac)
		Scenario_CoolAir_Alternative_IC_Min_frac = SimulationScenarios["Scenario_CoolAir_Alternative_IC_Min_frac"];
		//Clamp to ensure variable is between 0 and 1 
		Scenario_CoolAir_Alternative_IC_Min_frac = clamp(Scenario_CoolAir_Alternative_IC_Min_frac, 0.0, 1.0);
	}
	//If Flag_Scenario_CoolAir_LandCover > 1 & Scenario_CoolAir_Alternative_IC_Min_frac > Scenario_CoolAir_Alternative_IC_Max_frac then abort
	if (Flag_Scenario_CoolAir_LandCover > 1 && Scenario_CoolAir_Alternative_IC_Min_frac > Scenario_CoolAir_Alternative_IC_Max_frac) {
		cout << "Warning: HydroPlusConfig.xml Scenario_CoolAir_Alternative_IC_Min_frac > Scenario_CoolAir_Alternative_IC_Max_frac." << endl;
		cout << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
		cout << "Correction: Set Scenario_CoolAir_Alternative_IC_Min_frac <= Scenario_CoolAir_Alternative_IC_Max_frac." << endl;
		//Call abort function, which ends the HydroPlus.exe simulation
		abort();
	}
	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_TC_Change_frac then enter
	//Note: Scenario_CoolAir_Alternative_TC_Change_frac (frac) is initialized to 0 which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_TC_Change_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_TC_Change_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_TC_Change_frac (frac) represents change in tree cover (frac)
		Scenario_CoolAir_Alternative_TC_Change_frac = SimulationScenarios["Scenario_CoolAir_Alternative_TC_Change_frac"];
		//Clamp to ensure variable is between -1 and 1 
		Scenario_CoolAir_Alternative_TC_Change_frac = clamp(Scenario_CoolAir_Alternative_TC_Change_frac, -1.0, 1.0);
	}
	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_IC_Change_frac then enter
	//Note: Scenario_CoolAir_Alternative_IC_Change_frac (frac) is initialized to 0 which will result in no change to land cover
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_IC_Change_frac")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_IC_Change_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_IC_Change_frac (frac) represents change in impervious cover (frac)
		Scenario_CoolAir_Alternative_IC_Change_frac = SimulationScenarios["Scenario_CoolAir_Alternative_IC_Change_frac"];
		//Clamp to ensure variable is between -1 and 1 
		Scenario_CoolAir_Alternative_IC_Change_frac = clamp(Scenario_CoolAir_Alternative_IC_Change_frac, -1.0, 1.0);
	}

	//If SimulationScenarios map contains the key Scenario_CoolAir_Alternative_Change_type then enter
	//Note: Scenario_CoolAir_Alternative_Change_type initialized to 0 for Absolute, 1 for Relative
	//Note: Absolute adds a fixed fraction to the existing value; Relative adds a fraction of the existing value to the existing value
	if (SimulationScenarios.count("Scenario_CoolAir_Alternative_Change_type")) {
		//SimulationScenarios["Scenario_CoolAir_Alternative_IC_Change_frac"] defined in HydroPlusConfig.xml for scenarios
		//Note: If Flag_Scenario_CoolAir_LandCover>1 then Scenario_CoolAir_Alternative_IC_Change_frac (frac) represents change in impervious cover (frac)
		Scenario_CoolAir_Alternative_Change_type = SimulationScenarios["Scenario_CoolAir_Alternative_Change_type"];
		//If Scenario_CoolAir_Alternative_Change_type not 0 or 1, then set to 0 for Absolute
		if (Scenario_CoolAir_Alternative_Change_type != 0 && Scenario_CoolAir_Alternative_Change_type != 1) {
			Scenario_CoolAir_Alternative_Change_type = 0;
		}
	}

}

//IsNearUrban will search the neighbors around a pixel to determine if they are an urban class
//Note: Allows call with only MapPixel_ID, using default searhRadius of 1
bool Inputs::IsNearUrban(int mapPixel_ID, int searchRadius) {

	//row is mapPixel_ID / nCols
	//Note: Conversion with MapPixel_ID starting at 0, row & col starting at 0 for internal processing
	//Note: Eqs: row=MapPixel_ID / Inputs::nCols; col=MapPixel_ID % Inputs::nCols; MapPixel_ID = (row * nCols) + col
	int row = mapPixel_ID / nCols;
	//col is mapPixel_ID % nCols
	int col = mapPixel_ID % nCols;

	//For loop from delta_row -searchRadius to delta_row <= searchRadius; looping around all neighbor rows
	for (int delta_row = -searchRadius; delta_row <= searchRadius; ++delta_row) {
		//For loop from delta_column -searchRadius to delta_column <= searchRadius; looping around all neighbor columns
		for (int delta_column = -searchRadius; delta_column <= searchRadius; ++delta_column) {
			//If delta_row == 0 && delta_column == 0 then at MapPixel_ID or center pixel, and continue to next item in for loop
			if (delta_row == 0 && delta_column == 0) {
				continue;
			}
			//neighbor_row = row + delta_row
			int neighbor_row = row + delta_row;
			//neighbor_col = col + delta_column
			int neighbor_col = col + delta_column;
			//If neighbor_row < 0 || neighbor_col < 0 || neighbor_row >= nRows || neighbor_col >= nCols then continue to next item in for loop
			//Note: This is a neighbor pixel that is out of the map array
			//Note: This avoids acting on reference stations outside of map
			if (neighbor_row < 0 || neighbor_col < 0 || neighbor_row >= nRows || neighbor_col >= nCols)
				continue;
			//neighbor_MapPixel_ID = neighbor_row * nCols + neighbor_col
			int neighbor_MapPixel_ID = neighbor_row * nCols + neighbor_col;
			//neighbor_NLCD_Class = int(LandCover_NLCD_Class[neighbor_MapPixel_ID])
			int neighbor_NLCD_Class = int(LandCover_NLCD_Class[neighbor_MapPixel_ID]);
			//If neighbor_NLCD_Class >= 21 && neighbor_NLCD_Class <= 24 then urban pixel based on NLCD Class
			if (neighbor_NLCD_Class >= 21 && neighbor_NLCD_Class <= 24)
				//return true
				return true;
		}
	}
	//return false
	return false;
}

//IsAppropriateLandCoverForScenario will return true if MapPixel_ID is appropriate for scenario, e.g., irrigation, no transpiration
bool Inputs::IsAppropriateLandCoverForScenario(int MapPixel_ID, int Scenario_CoolAir_LandCover_Class) {
	//static bool warning_printed_this_timestep = false; 
	//Note: Flag persists across calls within one timestep
	static bool Flag_warning_printed_this_timestep = false;

	//If cenario_CoolAir_LandCover_Class == 0 then all land cover is appropriate
	if (Scenario_CoolAir_LandCover_Class == 0) {
		//return true
		return true;
	}
	//Else If Scenario_CoolAir_LandCover_Class == 1 then only urban land cover is appropriate
	else if (Scenario_CoolAir_LandCover_Class == 1) {
		//return result of (LandCover_NLCD_Class[MapPixel_ID] >= 21 && LandCover_NLCD_Class[MapPixel_ID] <= 24); true or false
		return (LandCover_NLCD_Class[MapPixel_ID] >= 21 && LandCover_NLCD_Class[MapPixel_ID] <= 24);
	}
	//Else if Scenario_CoolAir_LandCover_Class > 1 then look to see if neighboring pixels are urban land cover
	else if (Scenario_CoolAir_LandCover_Class > 1) {
		//search_radius_pixels is Scenario_CoolAir_LandCover_Class - 1
		int search_radius_pixels = Scenario_CoolAir_LandCover_Class - 1;

		//if search_radius_pixels >= 10 && !Flag_warning_printed_this_timestep then alert user of time constraint
		if (search_radius_pixels >= 10 && !Flag_warning_printed_this_timestep) {
			cout << "Note: Scenario_CoolAir_Irrigate_LandCover_Class or Scenario_CoolAir_NoTreeTranspiration_LandCover_Class ..." << endl; 
			cout << "... are > 10 in HydroPlusconfig.xml SimulationScenarios key, which slows the simulation." << endl;
			Flag_warning_printed_this_timestep = true;
		}

		//return result of IsNearUrban(MapPixel_ID, search_radius_pixels) function, true or false
		//Note: IsNearUrban function searches if MapPixel_ID is neighbored by urban land cover within a radius of search_radius_pixels
		return IsNearUrban(MapPixel_ID, search_radius_pixels);
	}
	//return false otherwise
	return false;
}

//IsAppropriateDay_or_NightForScenario will return true if ElevationAngle_Solar_rad is appropriate for scenario, 0=day and night; 1=day; 2=night
bool Inputs::IsAppropriateDay_or_NightForScenario(int timeStep, double ElevationAngle_Solar_rad, int Scenario_CoolAir_Irrigate_Day_or_Night) {

	//if (timeStep == 0 && ElevationAngle_Solar_rad == 0 && stoi(SimulationTime_HMS[timeStep].substr(0, 2)) >= 6) then guess yes
	//Note: On 1st timestep ElevationAngle_Solar_rad is not computed before this function is called, so guess daytime by 6 am as simple fix
	if (timeStep == 0 && ElevationAngle_Solar_rad == 0 && stoi(SimulationTime_HMS[timeStep].substr(0, 2)) >= 6) {
		//Diurnal_Hour_HH of 0 to 23 hours from SimulationTime_HMS[] vector w stoi and substr functions, starting at 0, selecting 2 places	
		ElevationAngle_Solar_rad = 0.1;
	}

	//If Scenario_CoolAir_Irrigate_Day_or_Night == 0 then both day and night are appropriate
	if (Scenario_CoolAir_Irrigate_Day_or_Night == 0) {
		//return true
		return true;
	}
	//Else If cenario_CoolAir_LandCover_Class == 1 && isGreaterThan(ElevationAngle_Solar_rad,0) then only day is appropriate and occuring
	//Note: If ElevationAngle_Solar_rad > 0 then day is active and it is daytime
	else if (Scenario_CoolAir_Irrigate_Day_or_Night == 1 && isGreaterThan(ElevationAngle_Solar_rad,0)) {
		//return true
		return true;
	}
	//Else If cenario_CoolAir_LandCover_Class == 2 && isLessThan(ElevationAngle_Solar_rad,0) then only night is appropriate and occuring
	//Note: If ElevationAngle_Solar_rad < 0 then night is active and it is nighttime
	else if (Scenario_CoolAir_Irrigate_Day_or_Night == 2 && isLessThan(ElevationAngle_Solar_rad, 0)) {
		//return true
		return true;
	}
	//Else, return false
	else {
		//return false
		return false;
	}
}

//IsAppropriateTimesPerDay will return true if IrrigationCountByDay <= Scenario_CoolAir_Irrigate_MaxPerDay, or Scenario_CoolAir_Irrigate_MaxPerDay is 0
bool Inputs::IsAppropriateTimesPerDay(int MapPixel_ID, int currentDate, const string& landCoverType, int Scenario_CoolAir_Irrigate_MaxPerDay) {
	//int& irrigationsToday = IrrigationCountByDay[MapPixel_ID][currentDate]; automatically creates entry
	//Note: IrrigationCountByDay map contains the MapPixel_ID, CurrentDate, LandCover_Type and then the count
	int& irrigationsToday = IrrigationCountByDay[MapPixel_ID][currentDate][landCoverType];

	//If (Scenario_CoolAir_Irrigate_MaxPerDay == 0 || irrigationsToday < Scenario_CoolAir_Irrigate_MaxPerDay) then irrigation is appropriate
	//Note: If Scenario_CoolAir_Irrigate_MaxPerDay is 0, then unlimited irrigation
	if (Scenario_CoolAir_Irrigate_MaxPerDay == 0 || isLessThan(irrigationsToday, Scenario_CoolAir_Irrigate_MaxPerDay)) {
		return true;
	}
	//Else, return false
	else {
		return false;
	}
}

//IsAppropriateTimesPerDay will return true if IrrigationCountByDay <= Scenario_CoolAir_Irrigate_MaxPerDay, or Scenario_CoolAir_Irrigate_MaxPerDay is 0
void Inputs::IncreaseTimesPerDay(int MapPixel_ID, int currentDate, const string& landCoverType) {
	//IrrigationCountByDay[MapPixel_ID][currentDate][landCoverType] += 1; increment with irrigation
	//Note: IrrigationCountByDay map contains the MapPixel_ID, CurrentDate, LandCover_Type and then the count
	IrrigationCountByDay[MapPixel_ID][currentDate][landCoverType] += 1;
}

//CleanTimesPerDayMap function will clear map if current date is not the map date for maps such as IrrigationCountByDay
//Note: Can hangle any map that contains the Integer, CurrentDate, String and then the count
void Inputs::CleanTimesPerDayMap(map<int, map<int, map<string, int>>>& MapCountByDay, int currentDate) {
	//For (auto& pixelEntry : IrrigationCountByDay) loop through 
	for (auto& pixelEntry : MapCountByDay) {
		//auto& dateMap = pixelEntry.second; gets date from .second position
		auto& dateMap = pixelEntry.second;
		//(auto map_item = dateMap.begin(); map_item != dateMap.end(); ); 
		for (auto map_item = dateMap.begin(); map_item != dateMap.end(); ) {
			//If (map_item->first < currentDate) then erase map_item
			if (map_item->first < currentDate) {
				//map_item = dateMap.erase(map_item)
				map_item = dateMap.erase(map_item);
			}
			//Else increment map_item and check next
			else {
				++map_item;
			}
		}
	}
}

//find discrepancy and force all input maps to use the same map projection header information as the dem.asc defined in DEM_Read_and_Prepare
void Inputs::Check_Map_Header(string map_name, int nCols_test, int nRows_test, double NODATA_test, double cellsize_m_test, double xllcorner_m_test, double yllcorner_m_test) {
	bool discrepancy_found = false;

	//If Model is SpatialTemperatureHydro, SpatialBuffer, SpatialBufferGI, or CoolBuilding then test agreement with HydroPlusConfig.xml parameters
	if ((SimulationStringParams["Model_Selection"] == "SpatialTemperatureHydro") || (SimulationStringParams["Model_Selection"] == "SpatialBuffer") || (SimulationStringParams["Model_Selection"] == "SpatialBufferwGI") || (SimulationStringParams["Model_Selection"] == "CoolBuilding")) {
		// Abort on nCols/nRows mismatch — critical for map consistency
		if (static_cast<int>(Inputs::nCols) != nCols_test) {
			cerr << "Warning: " << map_name << " header nCols value is different from dem.asc header nCols." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
		if (static_cast <int>(Inputs::nRows) != nRows_test) {
			cerr << "Warning: " << map_name << " header nRows value is different from dem.asc header nRows." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
		// Non-critical: override other values silently if present
		if (Inputs::NODATA_code != NODATA_test) {
			cerr << "Warning: " << map_name << " header NODATA_value is different from dem.asc header NODATA_value." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
		if (Inputs::Length_Pixel_Side_m != cellsize_m_test) {
			cerr << "Warning: " << map_name << " header cellsize value is different from dem.asc header cellsize." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
		if (Inputs::xllcorner_m != xllcorner_m_test) {
			cerr << "Warning: " << map_name << " header xllcorner value is different from dem.asc header xllcorner." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
		if (Inputs::yllcorner_m != yllcorner_m_test) {
			cerr << "Warning: " << map_name << " header yllcorner value is different from dem.asc header yllcorner." << endl;
			cerr << "Aborting: This warning triggers the HydroPlus simulation to abort." << endl;
			cerr << "Explanation: Hydroplus expects all input maps to share the same properties." << endl;
			cerr << "Correction: Create a " << map_name << " input map that agrees with dem.asc file properties." << endl;
			abort();
		}
	}
	//Else other model type and HydroPlusConfig.xml parameters need definition
	else {

		Inputs::nCols = nCols_test;
		Inputs::nRows = nRows_test;
		Inputs::NODATA_code = NODATA_test;
		Inputs::Length_Pixel_Side_m = cellsize_m_test;
		Inputs::xllcorner_m = xllcorner_m_test;
		Inputs::yllcorner_m = yllcorner_m_test;
	}
}

//isReferenceStationPixel will return true or false if MapPixel_ID is the reference station
bool Inputs::isReferenceStationPixel(int MapPixel_ID) const {
	//return find(MapPixel_ReferenceStation_IDs.begin(), MapPixel_ReferenceStation_IDs.end(), MapPixel_ID) != MapPixel_ReferenceStation_IDs.end()
	//Note: This is true if it finds MapPixel_ID within the MapPixel_ReferenceStation_IDs vector
	return find(MapPixel_ReferenceStation_IDs.begin(), MapPixel_ReferenceStation_IDs.end(), MapPixel_ID) != MapPixel_ReferenceStation_IDs.end();
}

//getReferenceStationIndex will return the index value of the reference station within the MapPixel_ID vector
//Note: MapPixel_ReferenceStation_IDs puts reference station within nRows * nCols, or appends at end if ReferenceStationOutsideMap is true
//Note: NumberOfLocations contains nCols * nRows + reference station(s) if ReferenceStationOutsideMap is true
int Inputs::getReferenceStationIndex(int MapPixel_ID) {
	//For (size_t i = 0; i < MapPixel_ReferenceStation_IDs.size(); ++i); loop through MapPixel_ReferenceStation_IDs indices
	for (size_t i = 0; i < MapPixel_ReferenceStation_IDs.size(); ++i) {
		//if MapPixel_ReferenceStation_IDs[i] == MapPixel_ID then index is found
		if (MapPixel_ReferenceStation_IDs[i] == MapPixel_ID) {
			//return static_cast<int>(i); index
			return static_cast<int>(i);
		}
	}
	//return -1; use as indication it is not a reference station
	return -1;
}
//Build the BG index after reading blockgroup.asc and know nCols/nRows. Initializing beBG happens here too
void Inputs::buildBlockGroupIndex(double NODATA_code) {

	//Reset previous BG index data, clear and (re)build
	Unique_BlockGroup_IDs.clear(); //will store unique block group IDs
	bg_to_drawer.clear(); // maps a BG ID to its drawer index

	// Scan all pixels and collect unique BG IDs
	//For (int pixelIndex = 0; pixelIndex < BlockGroup_ID.size(); ++pixelIndex) {
	for (int pixelIndex = 0; pixelIndex < BlockGroup_ID.size(); ++pixelIndex) {
		int blockGroupValue = BlockGroup_ID[pixelIndex];
		 // Skip NODATA entries
		if (blockGroupValue == NODATA_code) continue;
		// If this BG ID has not been seen yet, add it
		if (!bg_to_drawer.count(blockGroupValue)) {
			// Assign a drawer index for this newly discovered BlockGroup ID. The drawer index is equal to the next available index
			int newDrawerIndex = static_cast<int>(Unique_BlockGroup_IDs.size());
			// Record this BlockGroup ID in the list of unique BG IDs
			Unique_BlockGroup_IDs.push_back(blockGroupValue);
			// Store the mapping between the BlockGroup ID and drawer index.
			// Example: bg_to_drawer[310912501001] = 4
			bg_to_drawer.emplace(blockGroupValue, newDrawerIndex);
		}
	}
	// One folder per BG (folder 0). Start with 0 variables.
	vector<int> folders_per_bg(Unique_BlockGroup_IDs.size(), 1);
	beBG.init_ragged(folders_per_bg, /*nvars=*/0, /*init=*/0.0);
}

//bool EnsureFileExists function to check if a file exists using <fstream>
bool EnsureFileExists(const string& filePath) {
	ifstream file(filePath);
	return file.good();
}

//Convert_String_to_Lower_Case function converts strings to lower case, so if user types ALL or All it is read is all
string Inputs::Convert_String_to_Lower_Case(const string& string_Unknown_Case) {
	string string_Lower_Case = string_Unknown_Case;
	transform(string_Lower_Case.begin(), string_Lower_Case.end(), string_Lower_Case.begin(), ::tolower);
	return string_Lower_Case;
}

//SafeDivide function avoids division by 0 and returns 0 rather than nan; relies on .h version containing Eq_defaultValue = 0.0
//Note: Function only receives Eq_numerator and Eq_denominator, but can return its quotient or Eq_defaultValue
double Inputs::SafeDivide(double Eq_numerator, double Eq_denominator, double Eq_defaultValue) {
	//returns (Eq_denominator != 0.0) ? (Eq_numerator / Eq_denominator) : Eq_defaultValue
	//Note: Ternary to return actual division if denominator is not equal to 0, otherwise Eq_defaultValue
	return (Eq_denominator != 0.0) ? (Eq_numerator / Eq_denominator) : Eq_defaultValue;
}

//EnsureTrailingBackslash function to ensure it has no parenthesis and ends with backslash
string EnsureTrailingBackslash(const string& directoryPath) {
	// For now, assuming the users giving Linux style filepaths are always formatting the paths as required.
	// defined(__linux__) || defined(__gnu_linux__)
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN16)

	//cleanedPath defined as path to remove any quotation marks
	string cleanedPath = directoryPath;
	//cleanedPath uses .erase with remove(cleanedPath.begin() and cleanedPath.end(), cleanedPath.end() to emove quotation marks
	cleanedPath.erase(remove(cleanedPath.begin(), cleanedPath.end(), '\"'), cleanedPath.end());

	//If not cleanedPath.empty and not cleanedPath.back = backslash then add a trailing backslash
	//Note: backslash \ is an escape character, so 2 backslashes are needed to reference only 1 backslash, e.g., "\\" = "\"
	if (!cleanedPath.empty() && cleanedPath.back() != '\\') {
		return cleanedPath + "\\";
	}
	return cleanedPath;

#else

	//Return new copy.
	string cleanedPath = directoryPath;
	return cleanedPath;

#endif
}

//isGreaterThanOrAlmostEqual function returns true if Variable_a > Variable_b + eps or if fabs(Variable_a - Variable_b) < eps
//Note: Uses eps (e.g., Epsilon_Tolerance_1E_negative15) tolerance for peformance agreement between Linux and Windows builds
bool Inputs::isGreaterThanOrAlmostEqual(double Variable_a, double Variable_b, double eps) {
	//return True if (Variable_a > Variable_b + eps) || (fabs(Variable_a - Variable_b) < eps)
	return (Variable_a > Variable_b + eps) || (fabs(Variable_a - Variable_b) < eps);
}

//isLessThanOrAlmostEqual function returns true if Variable_a < Variable_b + eps or if fabs(Variable_a - Variable_b) < eps
//Note: Uses eps (e.g., Epsilon_Tolerance_1E_negative15) tolerance for peformance agreement between Linux and Windows builds
bool Inputs::isLessThanOrAlmostEqual(double Variable_a, double Variable_b, double eps) {
	//return True if (Variable_a < Variable_b - eps) || (fabs(Variable_a - Variable_b) < eps)
	return (Variable_a < Variable_b - eps) || (fabs(Variable_a - Variable_b) < eps);
}

//isGreaterThan function returns true if Variable_a > Variable_b + eps
//Note: Uses eps (e.g., Epsilon_Tolerance_1E_negative15) tolerance for strict inequality with consistent performance across platforms
bool Inputs::isGreaterThan(double Variable_a, double Variable_b, double eps) {
	//return True if (Variable_a > Variable_b + eps)
	return (Variable_a > Variable_b + eps);
}

//isLessThan function returns true if Variable_a < Variable_b - eps
//Note: Uses eps (e.g., Epsilon_Tolerance_1E_negative15) tolerance for strict inequality with consistent performance across platforms
bool Inputs::isLessThan(double Variable_a, double Variable_b, double eps) {
	//return True if (Variable_a < Variable_b - eps)
	return (Variable_a < Variable_b - eps);
}


//forceZeroIfLessThanOrAlmostEqualZero function returns 0 or Variable_value; converts Variable_value to 0 if Variable_value < eps
//Note: Uses eps (e.g., Epsilon_Tolerance_1E_negative15) tolerance for peformance agreement between Linux and Windows builds
double Inputs::forceZeroIfLessThanOrAlmostEqualZero(double Variable_value, double eps) {
	//If (isLessThanOrAlmostEqual(Variable_value, 0.0, eps) then return 0.0
	if (isLessThanOrAlmostEqual(Variable_value, 0.0, eps)) {
		return 0.0;
	}
	//Else, return Variable_value
	else {
		return Variable_value;
	}
}

//findIndex_Meteorological_Min_or_Max function finds index of max or min value within vector<double>& Meteo_values
//Note: size_t allows for large integer values
size_t Inputs::findIndex_Meteorological_Min_or_Max(const vector<double>& Meteo_values, string Max_or_Min) {
	//Error handling for empty vector
	if (Meteo_values.empty()) {
		throw runtime_error("findMeteorological_Min_or_Max: input vector is empty");
	}
	//if (Max_or_Min == "Max") then use max_element function to find index of max value
	if (Max_or_Min == "Max") {
		auto it = max_element(Meteo_values.begin(), Meteo_values.end());
		return static_cast<size_t>(distance(Meteo_values.begin(), it));
	}
	//if (Max_or_Min == "Min") then use min_element function to find index of min value
	else {
		auto it = min_element(Meteo_values.begin(), Meteo_values.end());
		return static_cast<size_t>(distance(Meteo_values.begin(), it));
	}
}

//findIndex_Min_in_Window function finds index of min value within range of sent index for Meteo_values
size_t Inputs::findIndex_Min_or_Max_in_Window(const vector<double>& Meteo_values, size_t Index_Vector, string Max_or_Min, int window_hours, double timestep_hours) {
	//Error handling for empty vector
	if (Meteo_values.empty()) {
		throw runtime_error("findMeteorological_Min_or_Max: input vector is empty");
	}
	//half_span = static_cast<int>(floor(window_hours / timestep_hours)); e.g., typically 24 / 1
	int half_span = static_cast<int>(floor(window_hours / timestep_hours));

	//Left_part = (Index_Vector > static_cast<size_t>(half_span)) ? Index_Vector - static_cast<size_t>(half_span) : 0;
	//Note: Left_part is determined with ternary, if false then use 0, lowest value, or use half_span from received Index_vector
	size_t Left_part = (Index_Vector > static_cast<size_t>(half_span)) ? Index_Vector - static_cast<size_t>(half_span) : 0;
	//Right_part = min<size_t>(Meteo_values.size() - 1, Index_Vector + static_cast<size_t>(half_span));
	//Note: Right_part is min of the Meteo_values vector size, or it searches the half_span 
	size_t Right_part = min<size_t>(Meteo_values.size() - 1, Index_Vector + static_cast<size_t>(half_span));

	//if (Max_or_Min == "Max") then use max_element function to find index of max value
	if (Max_or_Min == "Max") {
		//Index_of_Min = min_element(Meteo_values.begin() + Left_part, Meteo_values.begin() + Right_part + 1)
		auto Index_of_Max = max_element(Meteo_values.begin() + Left_part, Meteo_values.begin() + Right_part + 1);
		return static_cast<size_t>(distance(Meteo_values.begin(), Index_of_Max));
	}
	//if (Max_or_Min == "Min") then use min_element function to find index of min value
	else {
		//Index_of_Min = min_element(Meteo_values.begin() + Left_part, Meteo_values.begin() + Right_part + 1)
		auto Index_of_Min = min_element(Meteo_values.begin() + Left_part, Meteo_values.begin() + Right_part + 1);
		return static_cast<size_t>(distance(Meteo_values.begin(), Index_of_Min));
	}
}

//roundToDecimalPlaces function to round a double to 2,3,or 4 decimal places
double Inputs::roundToDecimalPlaces(double Variable_value, int decimal_places) {
	double Precision_adjuster = pow(10.0, decimal_places);
	return round(Variable_value * Precision_adjuster) / Precision_adjuster;
}

// ==========================================================================================
// Projection Parameter Maps (Albers and Lambert) – WKID to Projection Parameter Lookup
//struct containing the following projection parameters (in radians and meters):
//
//   phi0      - Latitude of origin (radians)
//   lambda0   - Central meridian (radians)
//   phi1      - Standard parallel 1 (radians)
//   phi2      - Standard parallel 2 (radians)
//   falseEasting  - X offset in meters (typically 0, but some use offsets like 4,000,000)
//   falseNorthing - Y offset in meters (typically 0, but varies by region)
// Example usage: 
//   ProjectionParams_Albers params = Inputs::AlbersParamMap[5070];
//   pair<double, double> projected = Projection_LatitudeLongitude_to_Albers(lat_dd, lon_dd, params);

unordered_map<int, ProjectionParams_Albers>Inputs::AlbersParamMap = {
	{5070, {23 * M_PI / 180.0, -96.0 * M_PI / 180.0, 29.5 * M_PI / 180.0, 45.5 * M_PI / 180.0,  0.0, 0.0}},  // NAD 1983 Contiguous USA Albers WKID 5070 reference: https://epsg.io/5070
	{102003, {37.5 * M_PI / 180.0, -96.0 * M_PI / 180.0, 29.5 * M_PI / 180.0, 45.5 * M_PI / 180.0,  0.0, 0.0}}, // USA Contiguous Albers Equal Area Conic WKID 102003 reference: https://epsg.io/102003
	{102008, {40.0 * M_PI / 180.0, -96.0 * M_PI / 180.0, 20.0 * M_PI / 180.0, 60.0 * M_PI / 180.0,  0.0, 0.0}}, // North America Albers Equal Area Conic WKID 102008 reference: https://epsg.io/102008
	{102589, {25.0 * M_PI / 180.0, -86.0 * M_PI / 180.0, 31.0 * M_PI / 180.0, 41.0 * M_PI / 180.0,  0.0, 0.0}}, //  USA Panhandle Energy Albers WKID 102589 reference: https://epsg.io/102589
	{5069, {23 * M_PI / 180.0, -96.0 * M_PI / 180.0, 29.5 * M_PI / 180.0, 45.5 * M_PI / 180.0,  0.0, 0.0}},     // NAD 1927 Contiguous USA Albers WKID 5069 reference: https://epsg.io/5069
	{6350, {23 * M_PI / 180.0, -96.0 * M_PI / 180.0, 29.5 * M_PI / 180.0, 45.5 * M_PI / 180.0,  0.0, 0.0}},     // NAD 1983 Contiguous USA Albers WKID 6350 reference: https://epsg.io/6350
	{102039, {23 * M_PI / 180.0, -96.0 * M_PI / 180.0, 29.5 * M_PI / 180.0, 45.5 * M_PI / 180.0,  0.0, 0.0}}      //USA Contiguous Albers Equal Area Conic USGS reference : https://epsg.io/102039
};
//
unordered_map<int, ProjectionParams_Lambert> Inputs::LambertParamMap = {
	{102004, {39.0 * M_PI / 180.0, -96.0 * M_PI / 180.0, 33.0 * M_PI / 180.0, 45.0 * M_PI / 180.0,  0.0, 0.0}},// USA Contiguous Lambert Conformal Conic (Esri: 102004)
	{102009, {40.0 * M_PI / 180.0, -96.0 * M_PI / 180.0, 20.0 * M_PI / 180.0, 60.0 * M_PI / 180.0,  0.0, 0.0}},// North America Lambert Conformal Conic (Esri: 102009)
	{102014, {30.0 * M_PI / 180.0, 10.0 * M_PI / 180.0, 43.0 * M_PI / 180.0, 62.0 * M_PI / 180.0,  0.0, 0.0}},// Europe Lambert Conformal Conic (Esri: 102014)
	{3034, {52.0 * M_PI / 180.0, 10.0 * M_PI / 180.0, 35.0 * M_PI / 180.0, 65.0 * M_PI / 180.0,  4000000.0, 2800000.0}}//ProjectionParams_Lambert struct contains WKID 3034 European Lambert Conformal Conic projection parameters
	// Source: Snyder, J.P., (1987). Map projections: A working manual. USGS Professional Paper 1395, https://pubs.usgs.gov/publication/pp1395
};

// ==========================================================================================