Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Code.v05-00/defaults/input.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
SIMULATION MENU:
OpenMP Num Threads (positive int): 8
OUTPUT SUBMENU:
Output folder (string): APCEMM_out/
Output folder (string): =MISSING=
Overwrite if folder exists (T/F): T
# FFT options (for spectral solver)
Use threaded FFT (T/F): F
FFTW WISDOM SUBMENU:
Use FFTW WISDOM (T/F): T
Dir w/ write permission (string): ./
Dir w/ write permission (string): =MISSING=
# This mostly contains information on background aerosol concentration; Not too relevant for contrail behavior
Input background condition (string): =DEFAULT=
# All parameters here are overwritten in EMISSION INDICES SUBMENU
Expand Down
4 changes: 2 additions & 2 deletions Code.v05-00/include/Defaults/Input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ R"====(
SIMULATION MENU:
OpenMP Num Threads (positive int): 8
OUTPUT SUBMENU:
Output folder (string): APCEMM_out/
Output folder (string): =MISSING=
Overwrite if folder exists (T/F): T
# FFT options (for spectral solver)
Use threaded FFT (T/F): F
FFTW WISDOM SUBMENU:
Use FFTW WISDOM (T/F): T
Dir w/ write permission (string): ./
Dir w/ write permission (string): =MISSING=
# This mostly contains information on background aerosol concentration; Not too relevant for contrail behavior
Input background condition (string): =DEFAULT=
# All parameters here are overwritten in EMISSION INDICES SUBMENU
Expand Down
4 changes: 4 additions & 0 deletions Code.v05-00/include/Util/YamlUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ const std::string getScalarKey(const YAML::Node& key);

const YAML::Node mergeYamlNodes(const YAML::Node& defaults, const YAML::Node& overrides);

inline const std::string buildKeyPath(const std::string& currentPath, const std::string& key){
return currentPath.empty() ? key : currentPath + " -> " + key;
}

#endif // YAMLUTILS_H_
4 changes: 2 additions & 2 deletions Code.v05-00/include/YamlInputReader/YamlInputReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#define YAMLINPUTREADER_H

#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <string_view>
#include <yaml-cpp/yaml.h>
#include <filesystem>
#include "Core/Input.hpp"
Expand All @@ -20,7 +22,6 @@ namespace YamlInputReader{
"mode were removed, so every PARAMETER MENU entry takes exactly one value. To vary a "
"parameter, write one input file per value and start one APCEMM process for each.";

static std::filesystem::path INPUT_FILE_PATH;

// Merge default and user provided files, returns a merged YAML::Node
YAML::Node mergeYamlInputFiles(const vector<string>& filenames);
Expand Down Expand Up @@ -122,6 +123,5 @@ namespace YamlInputReader{
}

vector<int> parseVectorIntString(const string paramString, const string paramLocation = "");
std::string parseFileSystemPath(std::string str);
}
#endif
21 changes: 21 additions & 0 deletions Code.v05-00/include/YamlInputReader/YamlPathUtils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef YAMLPATHUTILS_H_
#define YAMLPATHUTILS_H_

#include <filesystem>
#include <string>
#include <yaml-cpp/yaml.h>

/*
See description in YamlPathUtils.cpp
*/
namespace YamlInputReader {
bool isPathSentinel(const std::string& value);

void resolvePathsInPlace(YAML::Node node, const std::filesystem::path& baseDir);

void checkDefaultPaths(const YAML::Node& defaultData);

std::string readPath(const YAML::Node& node, const std::string& key);
}

#endif // YAMLPATHUTILS_H_
10 changes: 6 additions & 4 deletions Code.v05-00/src/Core/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include "Util/PhysConstant.hpp"
#include "Core/Engine.hpp"

Expand Down Expand Up @@ -47,10 +48,11 @@ Engine::Engine(std::string engineName, std::string engineFileName,
} else {
std::ifstream engineFile(engineFileName);
if (!engineFile) {
std::string const currFunc("Engine::Engine");
std::cout << "ERROR: In " << currFunc << ": Cannot read ("
<< engineFileName << ")" << std::endl;
return;
throw std::runtime_error(
"Cannot open the engine emissions file '" + engineFileName +
"'. It is set by 'Input engine emissions (string)' under SIMULATION MENU. "
"A relative path is resolved against the directory of the input file that "
"sets it. Use '=DEFAULT=' for the emissions data compiled into APCEMM.");
}

foundEngine =
Expand Down
9 changes: 9 additions & 0 deletions Code.v05-00/src/EPM/EPM.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <cctype>
#include <filesystem>
#include <netcdf>
#include <stdexcept>

#include "Core/Input_Mod.hpp"
#include "EPM/EPM.hpp"
Expand Down Expand Up @@ -68,6 +70,13 @@ void Output::write(std::string filename) const {
}

void Output::read(std::string filename) {
if (!std::filesystem::exists(filename)) {
throw std::runtime_error(
"External EPM NetCDF file does not exist: '" + filename +
"'. It is set by 'External EPM NetCDF file' under SIMULATION MENU. A "
"relative path is resolved against the directory of the input file that "
"sets it.");
}
NcFile nc(filename, NcFile::read);

// Scalar variables.
Expand Down
10 changes: 7 additions & 3 deletions Code.v05-00/src/EPM/Solution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

#include <fstream>
#include <stdexcept>

#include "KPP/KPP.hpp"
#include "KPP/KPP_Parameters.h"
Expand Down Expand Up @@ -209,9 +210,12 @@ void Solution::readInputBackgroundConditions(const Input& input, Vector_1D& amb_
} else {
std::ifstream file(fileName);
if (!file) {
std::cout << "ERROR: In Structure::Initialize: Can't read (" << fileName
<< ")" << std::endl;
exit(-1);
throw std::runtime_error(
"Cannot open the background condition file '" + fileName +
"'. It is set by 'Input background condition (string)' under SIMULATION "
"MENU. A relative path is resolved against the directory of the input "
"file that sets it. Use '=DEFAULT=' for the background data compiled "
"into APCEMM.");
}

processInputBackgroundLine(file, amb_Value, aer_Value);
Expand Down
3 changes: 2 additions & 1 deletion Code.v05-00/src/YamlInputReader/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(SRCS
YamlInputReader.cpp)
YamlInputReader.cpp
YamlPathUtils.cpp)
add_library(YamlInputReader STATIC ${SRCS})
target_link_libraries(YamlInputReader yaml-cpp::yaml-cpp Core)
43 changes: 22 additions & 21 deletions Code.v05-00/src/YamlInputReader/YamlInputReader.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
#include "YamlInputReader/YamlInputReader.hpp"
#include "YamlInputReader/YamlPathUtils.hpp"
#include "APCEMM.h"
#include "Util/ForwardDecl.hpp"
#include "Util/YamlUtils.hpp"
#include <algorithm> // std::equal
#include <cctype> // std::tolower
#include <fstream>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string_view> // std::string_view
#include <set>
#include <string>
#include <vector>
Expand Down Expand Up @@ -57,7 +55,7 @@ namespace YamlInputReader{
const std::string key = getScalarKey(it.first);
const bool isNewKey = seenKeys.insert(key).second;
if (!isNewKey) {
std::string errorPath = currentPath.empty() ? key : currentPath + " -> " + key;
std::string errorPath = buildKeyPath(currentPath, key);
throw std::runtime_error("Duplicate key found in " + source + ": '" + errorPath + "'. Each key must appear only once. Group all the entries of a menu under a single heading.");
}
}
Expand All @@ -74,7 +72,7 @@ namespace YamlInputReader{
checkNoDuplicateKeys(node, source, currentPath);
for (const auto& it : node) {
const std::string key = getScalarKey(it.first);
const std::string nextPath = currentPath.empty() ? key : currentPath + " -> " + key;
const std::string nextPath = buildKeyPath(currentPath, key);
checkNoDuplicateKeysRecursive(it.second, source, nextPath);
}
}
Expand Down Expand Up @@ -145,7 +143,7 @@ namespace YamlInputReader{
for (const auto& key : userKeys) {
if (!defaultKeys.contains(key)) {
// The key from the user's YAML does not exist in the default YAML.
std::string errorPath = currentPath.empty() ? key : currentPath + " -> " + key;
std::string errorPath = buildKeyPath(currentPath, key);
if (checkDeprecatedKey(key, errorPath)) {
continue;
}
Expand All @@ -156,7 +154,7 @@ namespace YamlInputReader{
// Recurse into every map/value to check their validity
const YAML::Node nextUserNode = userNode[key];
const YAML::Node nextDefaultNode = defaultNode[key];
std::string nextPath = currentPath.empty() ? key : currentPath + " -> " + key;
std::string nextPath = buildKeyPath(currentPath, key);

validateYamlKeys(nextDefaultNode, nextUserNode, nextPath);
}
Expand All @@ -172,6 +170,7 @@ namespace YamlInputReader{
// errors in it, but this is cheap to verify.
try {
checkNoDuplicateKeysRecursive(defaultData, "the default input.yaml");
checkDefaultPaths(defaultData);
} catch (const std::runtime_error& e) {
throw std::runtime_error(std::string(e.what()) + " This is not a problem with your input file: check the state of your APCEMM repository.");
}
Expand All @@ -185,7 +184,8 @@ namespace YamlInputReader{
} catch (const std::runtime_error& e) {
throw std::runtime_error("Invalid field in YAML input file '" + filename + "': " + e.what());
}
INPUT_FILE_PATH = std::filesystem::path(filename);
// Resolve relative paths against the location of the current input file before merging
resolvePathsInPlace(userData, std::filesystem::path(filename).parent_path());
mergedData = mergeYamlNodes(mergedData, userData);
}

Expand Down Expand Up @@ -284,7 +284,14 @@ namespace YamlInputReader{
}

YAML::Node outputSubmenu = simNode["OUTPUT SUBMENU"];
std::string outputFolder = parseFileSystemPath(outputSubmenu["Output folder (string)"].as<string>());
std::string outputFolder = readPath(outputSubmenu, "Output folder (string)");
// Ensure that outputFolder has a real value set by the user
if (isPathSentinel(outputFolder)) {
throw std::invalid_argument("No output folder set: 'Output folder (string)' under SIMULATION MENU -> "
"OUTPUT SUBMENU is '" + outputFolder + "'. APCEMM has no default output "
"folder, so set one in your input file. A relative path resolves against "
"the directory of the file that sets it.");
}
// Ensure path to save directory is terminated by "/"
if ( outputFolder.back() != '/' ) {outputFolder = outputFolder + "/";}
input.SIMULATION_OUTPUT_FOLDER = outputFolder;
Expand All @@ -293,9 +300,9 @@ namespace YamlInputReader{

YAML::Node fftwWisdomSubmenu = simNode["FFTW WISDOM SUBMENU"];
input.SIMULATION_USE_FFTW_WISDOM = parseBoolString(fftwWisdomSubmenu["Use FFTW WISDOM (T/F)"].as<string>(), "Use FFTW WISDOM (T/F)");
input.SIMULATION_DIRECTORY_W_WRITE_PERMISSION = parseFileSystemPath(fftwWisdomSubmenu["Dir w/ write permission (string)"].as<string>());
input.SIMULATION_INPUT_BACKG_COND = parseFileSystemPath(simNode["Input background condition (string)"].as<string>());
input.SIMULATION_INPUT_ENG_EI = parseFileSystemPath(simNode["Input engine emissions (string)"].as<string>());
input.SIMULATION_DIRECTORY_W_WRITE_PERMISSION = readPath(fftwWisdomSubmenu, "Dir w/ write permission (string)");
input.SIMULATION_INPUT_BACKG_COND = readPath(simNode, "Input background condition (string)");
input.SIMULATION_INPUT_ENG_EI = readPath(simNode, "Input engine emissions (string)");

YAML::Node saveForwardSubmenu = simNode["SAVE FORWARD RESULTS SUBMENU"];
input.SIMULATION_SAVE_FORWARD = parseBoolString(saveForwardSubmenu["Save forward results (T/F)"].as<string>(), "Save forward results (T/F)");
Expand Down Expand Up @@ -328,7 +335,7 @@ namespace YamlInputReader{
throw std::invalid_argument("Invalid EPM type specified in SIMULATION MENU: " + epm);
}

input.SIMULATION_EXTERNAL_EPM_NETCDF_FILENAME = simNode["External EPM NetCDF file"].as<string>();
input.SIMULATION_EXTERNAL_EPM_NETCDF_FILENAME = readPath(simNode, "External EPM NetCDF file");
}
void readParamMenu(Input& scenario, const YAML::Node& paramNode){

Expand Down Expand Up @@ -386,7 +393,7 @@ namespace YamlInputReader{
void readChemMenu(OptInput& input, const YAML::Node& chemNode){
input.CHEMISTRY_CHEMISTRY = parseBoolString(chemNode["Turn on Chemistry (T/F)"].as<string>(), "Turn on Chemistry (T/F)");
input.CHEMISTRY_HETCHEM = parseBoolString(chemNode["Perform hetero. chem. (T/F)"].as<string>(), "Perform hetero. chem. (T/F)");
input.CHEMISTRY_JRATE_FOLDER = parseFileSystemPath(chemNode["Photolysis rates folder (string)"].as<string>());
input.CHEMISTRY_JRATE_FOLDER = readPath(chemNode, "Photolysis rates folder (string)");
}
void readAeroMenu(OptInput& input, const YAML::Node& aeroNode){
input.AEROSOL_GRAVSETTLING = parseBoolString(aeroNode["Turn on grav. settling (T/F)"].as<string>(), "Turn on grav. settling (T/F)");
Expand All @@ -398,7 +405,7 @@ namespace YamlInputReader{
void readMetMenu(OptInput& input, const YAML::Node& metNode){
YAML::Node metInputSubmenu = metNode["METEOROLOGICAL INPUT SUBMENU"];
input.MET_LOADMET = parseBoolString(metInputSubmenu["Use met. input (T/F)"].as<string>(), "Use met. input (T/F)");
input.MET_FILENAME = parseFileSystemPath(metInputSubmenu["Met input file path (string)"].as<string>());
input.MET_FILENAME = readPath(metInputSubmenu, "Met input file path (string)");
input.MET_DT = parseDoubleString(metInputSubmenu["Time series data timestep [hr] (double)"].as<string>(), "Time series data timestep [hr] (double)");
input.MET_LOADTEMP = parseBoolString(metInputSubmenu["Init temp. from met. (T/F)"].as<string>(), "Init temp. from met. (T/F)");
input.MET_TEMPTIMESERIES = parseBoolString(metInputSubmenu["Temp. time series input (T/F)"].as<string>(), "Temp. time series input (T/F)");
Expand Down Expand Up @@ -530,10 +537,4 @@ namespace YamlInputReader{
}
return vecint;
}

std::string parseFileSystemPath(std::string str){
if (str == "=MISSING=" || str == "=DEFAULT=") return str;
std::filesystem::path p(str);
return p.is_absolute() ? str : std::filesystem::weakly_canonical(INPUT_FILE_PATH.parent_path() / str).generic_string();
}
}
Loading