Skip to content

Commit a4e5377

Browse files
Griger5slayoo
andauthored
Input guard for flagging used input (JSON entries), raising exception when some remain unused (#395)
Co-authored-by: Sylwester Arabas <[email protected]>
1 parent 0144d1a commit a4e5377

18 files changed

+400
-53
lines changed

.zenodo.json

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
"affiliation": "University of California, Davis, CA, USA",
2626
"name": "Ware, Emma"
2727
},
28+
{
29+
"affiliation": "AGH University of Krakow, Kraków, Poland",
30+
"name": "Adamus, Gracjan"
31+
},
2832
{
2933
"affiliation": "University of Illinois Urbana-Champaign",
3034
"name": "Riemer, Nicole",

examples/additive_coag_comparison.ipynb

+72-42
Large diffs are not rendered by default.

src/aero_data.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ struct AeroData {
4646

4747
JSONResourceGuard<InputJSONResource> guard(json);
4848
f_aero_data_from_json(this->ptr.f_arg());
49+
guard.check_parameters();
4950
}
5051

5152
AeroData() :

src/aero_dist.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct AeroDist {
5454

5555
JSONResourceGuard<InputJSONResource> guard(json, "", "mode_name", 1);
5656
f_aero_dist_from_json(ptr.f_arg_non_const(), aero_data->ptr.f_arg_non_const());
57+
guard.check_parameters();
5758
}
5859

5960
AeroDist() :

src/aero_mode.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ struct AeroMode {
147147
check_mode_json(json.begin().value());
148148
JSONResourceGuard<InputJSONResource> guard(json, "", "mode_name");
149149
f_aero_mode_from_json(ptr.f_arg_non_const(), aero_data.ptr.f_arg_non_const());
150+
guard.check_parameters();
150151
}
151152

152153
static void check_mode_json(const nlohmann::json &mode) {

src/env_state.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct EnvState {
3434
{
3535
JSONResourceGuard<InputJSONResource> guard(json);
3636
f_env_state_from_json(this->ptr.f_arg());
37+
guard.check_parameters();
3738
}
3839

3940
EnvState() :

src/gas_data.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct GasData {
3939

4040
JSONResourceGuard<InputJSONResource> guard(json_array);
4141
f_gas_data_from_json(this->ptr.f_arg());
42+
guard.check_parameters();
4243
}
4344

4445
GasData() :

src/gas_state.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,6 @@ struct GasState {
125125
self.ptr.f_arg(),
126126
self.gas_data->ptr.f_arg()
127127
);
128+
guard.check_parameters();
128129
}
129130
};

src/input_guard.hpp

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#include <iostream>
2+
#include <set>
3+
#include <map>
4+
#include <string>
5+
#include <sstream>
6+
#include "pybind11_json/pybind11_json.hpp"
7+
#include "nlohmann/json.hpp"
8+
9+
struct InputGuard {
10+
public:
11+
InputGuard(const nlohmann::json &j) {
12+
process_json(j);
13+
14+
this->dict_key_present = false;
15+
}
16+
17+
~InputGuard() {}
18+
19+
void check_used_inputs() {
20+
for (auto item : this->used_inputs) {
21+
if (!item.second) {
22+
std::string err = std::string("WARNING: \"") + item.first + std::string("\" parameter remains unused.");
23+
throw std::runtime_error(err);
24+
}
25+
}
26+
}
27+
28+
void mark_used_input(const std::string &input_name) {
29+
if (this->prefixes.find(input_name) == this->prefixes.end()) {
30+
std::string prefix = combine_str_vec(this->curr_prefix);
31+
32+
if (!prefix.empty()) {
33+
this->used_inputs[prefix + "/" + input_name] = true;
34+
}
35+
else {
36+
this->used_inputs[input_name] = true;
37+
}
38+
}
39+
}
40+
41+
void update_dict_key(std::string dict_key) {
42+
this->curr_dict_key = dict_key;
43+
}
44+
45+
void check_read_line(std::string line) {
46+
if (line == "mode_name") {
47+
if (this->dict_key_present) {
48+
this->curr_prefix.pop_back();
49+
}
50+
this->curr_prefix.push_back(this->curr_dict_key);
51+
this->dict_key_present = true;
52+
}
53+
else if (line.empty()) {
54+
this->curr_prefix.pop_back();
55+
this->dict_key_present = false;
56+
}
57+
}
58+
59+
void open_spec_file(std::string spec_file_name) {
60+
this->curr_prefix.push_back(spec_file_name);
61+
}
62+
63+
void close_spec_file() {
64+
this->curr_prefix.pop_back();
65+
}
66+
67+
private:
68+
std::map<std::string, bool> used_inputs;
69+
70+
std::set<std::string> prefixes;
71+
std::string curr_dict_key;
72+
std::vector<std::string> curr_prefix;
73+
74+
bool dict_key_present;
75+
76+
void process_json(const nlohmann::json &j) {
77+
nlohmann::json flat = j.flatten();
78+
79+
// JSON Pointer, as in a string syntax for identifying a specific value in JSON
80+
std::vector<std::string> json_pointers;
81+
82+
for (auto f : flat.items()) {
83+
json_pointers.push_back(clean_string(f.key()));
84+
}
85+
86+
std::set<std::string> json_pointers_set(json_pointers.begin(), json_pointers.end());
87+
88+
for (auto s : json_pointers_set) {
89+
this->used_inputs[s] = false;
90+
}
91+
92+
get_prefixes(json_pointers_set);
93+
}
94+
95+
std::string clean_string(std::string str) {
96+
bool after_slash = false;
97+
98+
for (size_t i = 0; i < str.size(); i++) {
99+
if (str.at(i) == '/' && i+1 < str.size()) {
100+
if (isdigit(str.at(i+1))) {
101+
after_slash = true;
102+
str.erase(i, 1);
103+
i -= 1;
104+
}
105+
}
106+
else if (isdigit(str.at(i)) && after_slash) {
107+
str.erase(i, 1);
108+
i -= 1;
109+
}
110+
else {
111+
after_slash = false;
112+
}
113+
}
114+
115+
str.erase(0, 1);
116+
117+
return str;
118+
}
119+
120+
std::string combine_str_vec(std::vector<std::string> vec) {
121+
if (vec.size() == 0) return "";
122+
123+
std::string temp = vec[0];
124+
125+
for (size_t i = 1; i < vec.size(); i++) {
126+
temp += "/";
127+
temp += vec[i];
128+
}
129+
130+
return temp;
131+
}
132+
133+
void get_prefixes(std::set<std::string> json_pointers_set) {
134+
std::string temp;
135+
std::vector<std::string> temp_vec;
136+
137+
for (auto s : json_pointers_set) {
138+
std::stringstream line(s);
139+
140+
while(getline(line, temp, '/')) {
141+
temp_vec.push_back(temp);
142+
}
143+
144+
if (temp_vec.size() > 1) {
145+
temp_vec.pop_back();
146+
147+
for (auto v : temp_vec) {
148+
this->prefixes.insert(v);
149+
}
150+
}
151+
152+
temp_vec.clear();
153+
}
154+
}
155+
};

src/json_resource.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,20 @@
1010
#include <sstream>
1111
#include <set>
1212
#include <stack>
13+
#include <map>
1314
#include "nlohmann/json.hpp"
1415
#include <tcb/span.hpp>
1516
#include <bpstd/string_view.hpp>
17+
#include "input_guard.hpp"
1618

1719
struct JSONResource {
1820
private:
1921
std::set<std::string> vars;
2022
const nlohmann::json *json;
2123
std::stack<const nlohmann::json*> json_parent;
2224

25+
std::unique_ptr<InputGuard> input_guard_ptr;
26+
2327
void warn(const std::exception &exception) {
2428
std::cerr << "WARN: " << exception.what() << std::endl;
2529
// assert(false);
@@ -35,6 +39,8 @@ struct JSONResource {
3539
for (auto &entry : this->json->items()) {
3640
this->vars.insert(entry.key());
3741
}
42+
43+
input_guard_ptr = std::make_unique<InputGuard>(json);
3844
};
3945

4046
void set_current_json_ptr(const nlohmann::json *ptr) {
@@ -65,6 +71,9 @@ struct JSONResource {
6571
key = item.key();
6672
}
6773
}
74+
75+
input_guard_ptr->update_dict_key(key);
76+
6877
return key;
6978
}
7079

@@ -212,6 +221,10 @@ struct JSONResource {
212221
return it;
213222
}
214223

224+
InputGuard *get_input_guard_ptr() {
225+
return input_guard_ptr.get();
226+
}
227+
215228
virtual std::string str() const = 0;
216229

217230
virtual bool read_line(std::string &name, std::string &data) = 0;
@@ -342,5 +355,9 @@ struct JSONResourceGuard {
342355
~JSONResourceGuard() {
343356
json_resource_ptr().reset();
344357
}
358+
359+
void check_parameters() {
360+
json_resource_ptr()->get_input_guard_ptr()->check_used_inputs();
361+
}
345362
};
346363

src/run_part_opt.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct RunPartOpt {
3030
json_copy["do_parallel"] = false;
3131

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

5252
JSONResourceGuard<InputJSONResource> guard(json_copy);
5353
f_run_part_opt_from_json(this->ptr.f_arg());
54+
guard.check_parameters();
5455
}
5556

5657
static auto t_max(const RunPartOpt &self){

src/scenario.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct Scenario {
129129
aero_data.ptr.f_arg(),
130130
this->ptr.f_arg()
131131
);
132+
guard.check_parameters();
132133
}
133134

134135
static auto __str__(const Scenario &self) {

src/spec_file_pypartmc.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*##################################################################################################
1+
/*##################################################################################################
22
# This file is a part of PyPartMC licensed under the GNU General Public License v3 (LICENSE file) #
33
# Copyright (C) 2022 University of Illinois Urbana-Champaign #
44
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
@@ -17,6 +17,7 @@ void c_spec_file_read_real(
1717
const char *name_data, const int *name_size, double *var
1818
) noexcept {
1919
json_resource_ptr()->read_value(bpstd::string_view(name_data, *name_size), var);
20+
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(bpstd::string_view(name_data, *name_size)));
2021
}
2122

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

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

4043
/*********************************************************************************/
@@ -45,6 +48,7 @@ void spec_file_read_string(
4548
int *var_size
4649
) noexcept {
4750
json_resource_ptr()->read_str(name, var_data, var_size);
51+
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(name));
4852
}
4953

5054
extern "C"
@@ -64,6 +68,7 @@ void c_spec_file_read_string(
6468

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

6974
extern "C"
@@ -80,6 +85,7 @@ void c_spec_file_open(
8085

8186
void spec_file_close() noexcept {
8287
json_resource_ptr()->zoom_out();
88+
json_resource_ptr()->get_input_guard_ptr()->close_spec_file();
8389
}
8490

8591
extern "C"
@@ -121,6 +127,8 @@ void spec_file_read_timed_real_array_data(
121127
) noexcept {
122128
json_resource_ptr()->read_arr("time", times);
123129
json_resource_ptr()->read_arr(name, vals);
130+
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(static_cast<std::string>(name));
131+
json_resource_ptr()->get_input_guard_ptr()->mark_used_input("time");
124132
}
125133

126134
extern "C"
@@ -184,6 +192,9 @@ void spec_file_read_real_named_array_data(
184192
for (auto idx=0u; idx < entry.value().size(); ++idx) {
185193
vals[idx] = entry.value().at(idx).get<double>();
186194
}
195+
196+
json_resource_ptr()->get_input_guard_ptr()->mark_used_input(entry.key());
197+
187198
break;
188199
}
189200
}
@@ -239,4 +250,6 @@ void c_spec_file_read_line(
239250
}
240251
*data0_size = i;
241252
}
253+
254+
json_resource_ptr()->get_input_guard_ptr()->check_read_line(std::string(name_data, *name_size));
242255
}

0 commit comments

Comments
 (0)