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
2 changes: 2 additions & 0 deletions docs/guide/code/case.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The goal is to have PSC cases to be self-contained, ie., everything related to p

* One cannot just change a parameter in the case and run it -- the code needs to be recompiled first.

Alternatively, use an InputParams object to load parameters from an input file at runtime. See ``src/include/input_params.hxx``.

Changing / adding a case
========================

Expand Down
190 changes: 190 additions & 0 deletions src/include/input_params.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#pragma once

#include <unordered_map>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

// TODO in c++20, use std::format
// TODO use LOG_WARNING and LOG_ERROR, but ideally only on 1 proc

/// @brief A parser that reads a dict-like map of parameter names to
/// parameter values from a single input file. The input file syntax is
/// extremely simple:
/// ```txt
/// param1 val1
/// param2 val2 thiswordisignored andsoisthis
/// ```
/// The first (space-separated) word in each line is interpreted as a parameter
/// name, and the second word is interpreted as a value. Words after the first
/// two are silently ignored, allowing for comments. If a parameter is
/// duplicated, all values but the last are silently ignored.
///
/// This class has no knowledge about what parameters should or shouldn't be
/// present, nor does it know what types its values should have. A value isn't
/// parsed to a specific type (e.g. `double`) until it is actually accessed as
/// that type by a user.
class InputParams
{
private:
std::unordered_map<std::string, std::string> params;

public:
InputParams(const std::string file_path)
{
// iterate over each line
std::ifstream ifs(file_path);

if (ifs.is_open()) {
for (std::string line; std::getline(ifs, line);) {

// parse first two words within line
std::istringstream iss(line);
std::string paramName, paramVal;
if (iss >> paramName >> paramVal)
params[paramName] = paramVal;
}

ifs.close();
} else {
std::cout << "Failed to open params file: " << file_path << "\n";
exit(EXIT_FAILURE);
}
}

/// @brief Check if the parameter is present.
/// @param paramName name of parameter
/// @return whether or not the parameter is present
bool has(const std::string paramName) { return params.count(paramName) == 1; }

/// @brief Get a parameter, parsing it to the given type.
/// @tparam T type of parameter
/// @param paramName name of parameter
/// @return the parameter
template <typename T>
T get(const std::string paramName)
{
try {
return _getParsed<T>(paramName);
} catch (const std::invalid_argument& e) {
std::string unparsed = _getUnparsed(paramName);
// TODO ensure human-readable type name
std::cerr << "ERROR Unable to parse parameter '" << paramName
<< "', which has value '" << unparsed << "', to type "
<< typeid(T).name() << "\n";
abort();
}
}

/// @brief Get a parameter if it's there, otherwise
/// return the given default value.
/// @tparam T type of parameter
/// @param paramName name of parameter
/// @param deflt default value of parameter
/// @return the parameter
template <typename T>
T getOrDefault(const std::string paramName, T deflt)
{
if (has(paramName)) {
return get<T>(paramName);
}

std::cout << "Warning: using default value for parameter '" << paramName
<< "': " << deflt << "\n";
return deflt;
}

/// @brief Get a parameter and display a warning if it's there, otherwise
/// return the given default value.
/// @tparam T type of parameter
/// @param paramName name of parameter
/// @param deflt default value of parameter
/// @return the parameter
template <typename T>
T getAndWarnOrDefault(const std::string paramName, T deflt)
{
if (!has(paramName)) {
return deflt;
}

T val = get<T>(paramName);
std::cout << "Warning: using non-default value for parameter '" << paramName
<< "': " << val << "\nDefault value of '" << deflt
<< "' is recommended.\n";
return val;
}

/// @brief Display a warning if a parameter is present.
/// @param paramName name of parameter
/// @param advice user-friendly instructions on what to do instead
/// @return whether or not the parameter was present
bool warnIfPresent(const std::string paramName, const std::string advice)
{
if (!has(paramName)) {
return false;
}

std::cout << "Warning: parameter " << paramName << " is deprecated.\n"
<< advice << "\n";
return true;
}

private:
/// Retrieves an unparsed value, throwing a helpful error if the parameter
/// is missing.
std::string _getUnparsed(const std::string paramName)
{
if (has(paramName)) {
return params.at(paramName);
}

throw std::out_of_range("missing required input parameter: " + paramName);
}

/// Retrieve and parse a value, possibly throwing std::invalid_argument
template <typename T>
T _getParsed(const std::string paramName);
};

// get implementations

template <>
bool InputParams::_getParsed<bool>(const std::string paramName)
{
auto lowercase = _getUnparsed(paramName);
std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(),
[](unsigned char c) { return std::tolower(c); });

if (lowercase == "true") {
return true;
} else if (lowercase == "false") {
return false;
} else {
throw std::invalid_argument(_getUnparsed(paramName));
}
}

template <>
double InputParams::_getParsed<double>(const std::string paramName)
{
return std::stod(_getUnparsed(paramName));
}

template <>
int InputParams::_getParsed<int>(const std::string paramName)
{
return std::stoi(_getUnparsed(paramName));
}

template <>
float InputParams::_getParsed<float>(const std::string paramName)
{
return std::stof(_getUnparsed(paramName));
}

template <>
std::string InputParams::_getParsed<std::string>(const std::string paramName)
{
return _getUnparsed(paramName);
}
4 changes: 2 additions & 2 deletions src/psc_bgk.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
#include "DiagnosticsDefault.h"
#include "OutputFieldsDefault.h"
#include "psc_config.hxx"
#include "input_params.hxx"

#include "psc_bgk_util/bgk_params.hxx"
#include "psc_bgk_util/table.hxx"
#include "psc_bgk_util/params_parser.hxx"

// ======================================================================
// PSC configuration
Expand Down Expand Up @@ -66,7 +66,7 @@ void setupParameters(int argc, char** argv)
exit(1);
}
std::string path_to_params(argv[1]);
ParsedParams parsedParams(path_to_params);
InputParams parsedParams(path_to_params);
ic_table = new Table(parsedParams.get<std::string>("path_to_data"));
g.loadParams(parsedParams, *ic_table);

Expand Down
6 changes: 4 additions & 2 deletions src/psc_bgk_util/bgk_params.hxx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include <cmath>
#include "params_parser.hxx"

#include "../include/input_params.hxx"

#include "table.hxx"

// ======================================================================
Expand Down Expand Up @@ -106,7 +108,7 @@ struct PscBgkParams
double rel_box_size_3; // length of 3rd dimension in calculated units
int n_patches_3; // number of patches in 3rd dimension

void loadParams(ParsedParams parsedParams, Table& ic_table)
void loadParams(InputParams parsedParams, Table& ic_table)
{
box_size = parsedParams.getAndWarnOrDefault<double>("box_size", -1);
rel_box_size = parsedParams.getOrDefault<double>("rel_box_size", 1);
Expand Down
139 changes: 0 additions & 139 deletions src/psc_bgk_util/params_parser.hxx

This file was deleted.

Loading