Skip to content

Input guard for flagging used input (JSON entries), raising exception when some remain unused #395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Mar 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5435793
Add: Basic InputGuard implementation
Griger5 Jan 3, 2025
11fcd1c
Merge branch 'main' of github.com:Griger5/PyPartMC into input-flagging
Griger5 Jan 3, 2025
68fb60d
Merge branch 'open-atmos:main' into input-flagging
Griger5 Jan 3, 2025
3dc6951
Add: Integrated InputGuard into the library's basic inner workflow
Griger5 Jan 3, 2025
3507e94
Merge branch 'input-flagging' of github.com:Griger5/PyPartMC into inp…
Griger5 Jan 3, 2025
3c5f50f
Fix: Updated tests, so they don't have unused parameters
Griger5 Jan 20, 2025
9fcf0b2
Add: InputGuard now processes reading of integers and arrays
Griger5 Jan 20, 2025
99d0acf
Add&fix: InputGuard now catches boolean values. Fixed typo in TestCon…
Griger5 Jan 21, 2025
99a8525
Fix: Deleted 'do_optical' from an initializing loop in RunPartOpt obj…
Griger5 Jan 21, 2025
0cb419d
Updated: Updated InputGuards inner logic, so it works properly with m…
Griger5 Feb 11, 2025
3024ee6
Update: Updated function calls for the new InputGuard
Griger5 Feb 11, 2025
6876613
Updated code style of InputGuard, so it matches the rest of the codebase
Griger5 Feb 11, 2025
7b539f6
Update: Moved InputGuard definition to seperate file
Griger5 Feb 27, 2025
6afd364
Update: Added try-catch statement to InputGuard's destructor, which p…
Griger5 Feb 27, 2025
be669ae
Add: Tests for InputGuard
Griger5 Feb 27, 2025
95b6066
Update: removed check_used_inputs from the InputGuard destructor, a J…
Griger5 Mar 13, 2025
e13cddd
Add: objects accepting JSONs now check if all inputs were parsed
Griger5 Mar 13, 2025
4afebb5
Add: added tests for all objects that check if inputs were used
Griger5 Mar 13, 2025
131060f
Add: add missing check_parameters calls
Griger5 Mar 13, 2025
9f387e7
Fix: pre-commit hooks
Griger5 Mar 13, 2025
f4d8504
Fix: added missing includes
Griger5 Mar 13, 2025
0f9de0d
Merge branch 'main' into input-flagging
slayoo Mar 13, 2025
413cb48
Fix: got rid of unused parameters in the running of PyPartMC model
Griger5 Mar 14, 2025
5625880
Merge branch 'main' into input-flagging
slayoo Mar 14, 2025
eaedb09
skip exception tests on arm64 + mark with a TODO label
slayoo Mar 15, 2025
6f8d09d
add missing import
slayoo Mar 15, 2025
5f4ab07
rerun additive intercomparison notebook to bring back Droplets.jl + a…
slayoo Mar 15, 2025
ed7006d
bring back a fishy pylint disable
slayoo Mar 15, 2025
6239aa5
add Gracjan to list of contributors in .zenodo.json
slayoo Mar 15, 2025
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: 4 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
"affiliation": "University of California, Davis, CA, USA",
"name": "Ware, Emma"
},
{
"affiliation": "AGH University of Krakow, Kraków, Poland",
"name": "Adamus, Gracjan"
},
{
"affiliation": "University of Illinois Urbana-Champaign",
"name": "Riemer, Nicole",
Expand Down
114 changes: 72 additions & 42 deletions examples/additive_coag_comparison.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/aero_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct AeroData {

JSONResourceGuard<InputJSONResource> guard(json);
f_aero_data_from_json(this->ptr.f_arg());
guard.check_parameters();
}

AeroData() :
Expand Down
1 change: 1 addition & 0 deletions src/aero_dist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct AeroDist {

JSONResourceGuard<InputJSONResource> guard(json, "", "mode_name", 1);
f_aero_dist_from_json(ptr.f_arg_non_const(), aero_data->ptr.f_arg_non_const());
guard.check_parameters();
}

AeroDist() :
Expand Down
1 change: 1 addition & 0 deletions src/aero_mode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct AeroMode {
check_mode_json(json.begin().value());
JSONResourceGuard<InputJSONResource> guard(json, "", "mode_name");
f_aero_mode_from_json(ptr.f_arg_non_const(), aero_data.ptr.f_arg_non_const());
guard.check_parameters();
}

static void check_mode_json(const nlohmann::json &mode) {
Expand Down
1 change: 1 addition & 0 deletions src/env_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct EnvState {
{
JSONResourceGuard<InputJSONResource> guard(json);
f_env_state_from_json(this->ptr.f_arg());
guard.check_parameters();
}

EnvState() :
Expand Down
1 change: 1 addition & 0 deletions src/gas_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct GasData {

JSONResourceGuard<InputJSONResource> guard(json_array);
f_gas_data_from_json(this->ptr.f_arg());
guard.check_parameters();
}

GasData() :
Expand Down
1 change: 1 addition & 0 deletions src/gas_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,6 @@ struct GasState {
self.ptr.f_arg(),
self.gas_data->ptr.f_arg()
);
guard.check_parameters();
}
};
155 changes: 155 additions & 0 deletions src/input_guard.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <sstream>
#include "pybind11_json/pybind11_json.hpp"
#include "nlohmann/json.hpp"

struct InputGuard {
public:
InputGuard(const nlohmann::json &j) {
process_json(j);

this->dict_key_present = false;
}

~InputGuard() {}

void check_used_inputs() {
for (auto item : this->used_inputs) {
if (!item.second) {
std::string err = std::string("WARNING: \"") + item.first + std::string("\" parameter remains unused.");
throw std::runtime_error(err);
}
}
}

void mark_used_input(const std::string &input_name) {
if (this->prefixes.find(input_name) == this->prefixes.end()) {
std::string prefix = combine_str_vec(this->curr_prefix);

if (!prefix.empty()) {
this->used_inputs[prefix + "/" + input_name] = true;
}
else {
this->used_inputs[input_name] = true;
}
}
}

void update_dict_key(std::string dict_key) {
this->curr_dict_key = dict_key;
}

void check_read_line(std::string line) {
if (line == "mode_name") {
if (this->dict_key_present) {
this->curr_prefix.pop_back();
}
this->curr_prefix.push_back(this->curr_dict_key);
this->dict_key_present = true;
}
else if (line.empty()) {
this->curr_prefix.pop_back();
this->dict_key_present = false;
}
}

void open_spec_file(std::string spec_file_name) {
this->curr_prefix.push_back(spec_file_name);
}

void close_spec_file() {
this->curr_prefix.pop_back();
}

private:
std::map<std::string, bool> used_inputs;

std::set<std::string> prefixes;
std::string curr_dict_key;
std::vector<std::string> curr_prefix;

bool dict_key_present;

void process_json(const nlohmann::json &j) {
nlohmann::json flat = j.flatten();

// JSON Pointer, as in a string syntax for identifying a specific value in JSON
std::vector<std::string> json_pointers;

for (auto f : flat.items()) {
json_pointers.push_back(clean_string(f.key()));
}

std::set<std::string> json_pointers_set(json_pointers.begin(), json_pointers.end());

for (auto s : json_pointers_set) {
this->used_inputs[s] = false;
}

get_prefixes(json_pointers_set);
}

std::string clean_string(std::string str) {
bool after_slash = false;

for (size_t i = 0; i < str.size(); i++) {
if (str.at(i) == '/' && i+1 < str.size()) {
if (isdigit(str.at(i+1))) {
after_slash = true;
str.erase(i, 1);
i -= 1;
}
}
else if (isdigit(str.at(i)) && after_slash) {
str.erase(i, 1);
i -= 1;
}
else {
after_slash = false;
}
}

str.erase(0, 1);

return str;
}

std::string combine_str_vec(std::vector<std::string> vec) {
if (vec.size() == 0) return "";

std::string temp = vec[0];

for (size_t i = 1; i < vec.size(); i++) {
temp += "/";
temp += vec[i];
}

return temp;
}

void get_prefixes(std::set<std::string> json_pointers_set) {
std::string temp;
std::vector<std::string> temp_vec;

for (auto s : json_pointers_set) {
std::stringstream line(s);

while(getline(line, temp, '/')) {
temp_vec.push_back(temp);
}

if (temp_vec.size() > 1) {
temp_vec.pop_back();

for (auto v : temp_vec) {
this->prefixes.insert(v);
}
}

temp_vec.clear();
}
}
};
17 changes: 17 additions & 0 deletions src/json_resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@
#include <sstream>
#include <set>
#include <stack>
#include <map>
#include "nlohmann/json.hpp"
#include <tcb/span.hpp>
#include <bpstd/string_view.hpp>
#include "input_guard.hpp"

struct JSONResource {
private:
std::set<std::string> vars;
const nlohmann::json *json;
std::stack<const nlohmann::json*> json_parent;

std::unique_ptr<InputGuard> input_guard_ptr;

void warn(const std::exception &exception) {
std::cerr << "WARN: " << exception.what() << std::endl;
// assert(false);
Expand All @@ -35,6 +39,8 @@ struct JSONResource {
for (auto &entry : this->json->items()) {
this->vars.insert(entry.key());
}

input_guard_ptr = std::make_unique<InputGuard>(json);
};

void set_current_json_ptr(const nlohmann::json *ptr) {
Expand Down Expand Up @@ -65,6 +71,9 @@ struct JSONResource {
key = item.key();
}
}

input_guard_ptr->update_dict_key(key);

return key;
}

Expand Down Expand Up @@ -212,6 +221,10 @@ struct JSONResource {
return it;
}

InputGuard *get_input_guard_ptr() {
return input_guard_ptr.get();
}

virtual std::string str() const = 0;

virtual bool read_line(std::string &name, std::string &data) = 0;
Expand Down Expand Up @@ -342,5 +355,9 @@ struct JSONResourceGuard {
~JSONResourceGuard() {
json_resource_ptr().reset();
}

void check_parameters() {
json_resource_ptr()->get_input_guard_ptr()->check_used_inputs();
}
};

3 changes: 2 additions & 1 deletion src/run_part_opt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct RunPartOpt {
json_copy["do_parallel"] = false;

for (auto key : std::set<std::string>({
"do_mosaic", "do_camp_chem", "do_condensation", "do_optical", "do_nucleation",
"do_mosaic", "do_camp_chem", "do_condensation", "do_nucleation",
}))
if (json_copy.find(key) == json_copy.end())
json_copy[key] = false;
Expand All @@ -51,6 +51,7 @@ struct RunPartOpt {

JSONResourceGuard<InputJSONResource> guard(json_copy);
f_run_part_opt_from_json(this->ptr.f_arg());
guard.check_parameters();
}

static auto t_max(const RunPartOpt &self){
Expand Down
1 change: 1 addition & 0 deletions src/scenario.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct Scenario {
aero_data.ptr.f_arg(),
this->ptr.f_arg()
);
guard.check_parameters();
}

static auto __str__(const Scenario &self) {
Expand Down
15 changes: 14 additions & 1 deletion src/spec_file_pypartmc.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*##################################################################################################
/*##################################################################################################
# This file is a part of PyPartMC licensed under the GNU General Public License v3 (LICENSE file) #
# Copyright (C) 2022 University of Illinois Urbana-Champaign #
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
Expand All @@ -17,6 +17,7 @@ void c_spec_file_read_real(
const char *name_data, const int *name_size, double *var
) noexcept {
json_resource_ptr()->read_value(bpstd::string_view(name_data, *name_size), var);
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(bpstd::string_view(name_data, *name_size)));
}

/*********************************************************************************/
Expand All @@ -26,6 +27,7 @@ void c_spec_file_read_integer(
const char *name_data, const int *name_size, int *var
) noexcept {
json_resource_ptr()->read_value(bpstd::string_view(name_data, *name_size), var);
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(bpstd::string_view(name_data, *name_size)));
}

/*********************************************************************************/
Expand All @@ -35,6 +37,7 @@ void c_spec_file_read_logical(
const char *name_data, const int *name_size, bool *var
) noexcept {
json_resource_ptr()->read_value(bpstd::string_view(name_data, *name_size), var);
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(bpstd::string_view(name_data, *name_size)));
}

/*********************************************************************************/
Expand All @@ -45,6 +48,7 @@ void spec_file_read_string(
int *var_size
) noexcept {
json_resource_ptr()->read_str(name, var_data, var_size);
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(name));
}

extern "C"
Expand All @@ -64,6 +68,7 @@ void c_spec_file_read_string(

void spec_file_open(const bpstd::string_view &filename) noexcept {
json_resource_ptr()->zoom_in(filename);
json_resource_ptr()->get_input_guard_ptr()->open_spec_file(static_cast<std::string>(filename));
}

extern "C"
Expand All @@ -80,6 +85,7 @@ void c_spec_file_open(

void spec_file_close() noexcept {
json_resource_ptr()->zoom_out();
json_resource_ptr()->get_input_guard_ptr()->close_spec_file();
}

extern "C"
Expand Down Expand Up @@ -121,6 +127,8 @@ void spec_file_read_timed_real_array_data(
) noexcept {
json_resource_ptr()->read_arr("time", times);
json_resource_ptr()->read_arr(name, vals);
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(name));
json_resource_ptr()->get_input_guard_ptr()->mark_used_input("time");
}

extern "C"
Expand Down Expand Up @@ -184,6 +192,9 @@ void spec_file_read_real_named_array_data(
for (auto idx=0u; idx < entry.value().size(); ++idx) {
vals[idx] = entry.value().at(idx).get<double>();
}

json_resource_ptr()->get_input_guard_ptr()->mark_used_input(entry.key());

break;
}
}
Expand Down Expand Up @@ -239,4 +250,6 @@ void c_spec_file_read_line(
}
*data0_size = i;
}

json_resource_ptr()->get_input_guard_ptr()->check_read_line(std::string(name_data, *name_size));
}
Loading
Loading