From 543579384ebc7ad09ed463eaff686f783d7653ce Mon Sep 17 00:00:00 2001 From: Griger5 Date: Fri, 3 Jan 2025 20:11:16 +0100 Subject: [PATCH 1/9] Add: Basic InputGuard implementation --- src/json_resource.hpp | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/json_resource.hpp b/src/json_resource.hpp index 91e46219..f904316c 100644 --- a/src/json_resource.hpp +++ b/src/json_resource.hpp @@ -10,10 +10,56 @@ #include #include #include +#include #include "nlohmann/json.hpp" #include #include +struct InputGuard { + public: + InputGuard(const nlohmann::json &j) { + map_inputs_recursive(j); + } + + ~InputGuard() { + check_used_inputs(); + } + + void mark_used_input(const std::string &input_name) { + this->used_inputs[input_name] = true; + } + + private: + std::map used_inputs; + + void map_inputs_recursive(const nlohmann::json &j) { + if (j.is_array()) { + for (auto item : j) { + map_inputs_recursive(item); + } + return; + } + + for(auto [key, value] : j.items()) + { + if (value.is_structured()) { + map_inputs_recursive(value); + } + + if (key.find_first_not_of(" \t\n\v\f\r") != std::string::npos) + this->used_inputs[key] = false; + } + } + + void check_used_inputs() { + for (auto item : used_inputs) { + if (!item.second) { + throw std::logic_error(std::string("Failed: \"") + item.first + std::string("\" parameter remains unused.")); + } + } + } +}; + struct JSONResource { private: std::set vars; From 3dc69515a877bebf459ebbb0a1b4d80b49afad78 Mon Sep 17 00:00:00 2001 From: Griger5 Date: Fri, 3 Jan 2025 20:30:43 +0100 Subject: [PATCH 2/9] Add: Integrated InputGuard into the library's basic inner workflow --- src/json_resource.hpp | 11 +++++++++++ src/spec_file_pypartmc.cpp | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/json_resource.hpp b/src/json_resource.hpp index f904316c..98da30d1 100644 --- a/src/json_resource.hpp +++ b/src/json_resource.hpp @@ -66,6 +66,8 @@ struct JSONResource { const nlohmann::json *json; std::stack json_parent; + std::unique_ptr input_guard_ptr; + void warn(const std::exception &exception) { std::cerr << "WARN: " << exception.what() << std::endl; // assert(false); @@ -81,6 +83,8 @@ struct JSONResource { for (auto &entry : this->json->items()) { this->vars.insert(entry.key()); } + + input_guard_ptr = std::make_unique(json); }; void set_current_json_ptr(const nlohmann::json *ptr) { @@ -111,6 +115,9 @@ struct JSONResource { key = item.key(); } } + + input_guard_ptr->mark_used_input(key); + return key; } @@ -258,6 +265,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; diff --git a/src/spec_file_pypartmc.cpp b/src/spec_file_pypartmc.cpp index 214d94f0..d9e2719f 100644 --- a/src/spec_file_pypartmc.cpp +++ b/src/spec_file_pypartmc.cpp @@ -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 # @@ -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(bpstd::string_view(name_data, *name_size))); } /*********************************************************************************/ @@ -45,6 +46,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(name)); } extern "C" @@ -184,6 +186,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(); } + + json_resource_ptr()->get_input_guard_ptr()->mark_used_input(entry.key()); + break; } } From 3c5f50ff341d50461e5e1fc0310202269cccafb3 Mon Sep 17 00:00:00 2001 From: Griger5 Date: Mon, 20 Jan 2025 18:33:41 +0100 Subject: [PATCH 3/9] Fix: Updated tests, so they don't have unused parameters --- tests/test_aero_dist.py | 8 ++------ tests/test_aero_mode.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/test_aero_dist.py b/tests/test_aero_dist.py index 94e2d6eb..a6b3a284 100644 --- a/tests/test_aero_dist.py +++ b/tests/test_aero_dist.py @@ -19,6 +19,7 @@ AERO_MODE_CTOR_LOG_NORMAL, AERO_MODE_CTOR_LOG_NORMAL_COAGULATION, AERO_MODE_CTOR_LOG_NORMAL_FULL, + AERO_MODE_CTOR_SAMPLED, ) AERO_DIST_CTOR_ARG_MINIMAL = [ @@ -222,12 +223,7 @@ def test_ctor_error_on_repeated_massfrac_keys(): def test_ctor_sampled_mode(): # arrange aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL) - ctor_arg = copy.deepcopy(AERO_DIST_CTOR_ARG_MINIMAL) - ctor_arg[0]["test_mode"]["mode_type"] = "sampled" - ctor_arg[0]["test_mode"]["size_dist"] = [ - {"diam": [1, 2, 3, 4]}, - {"num_conc": [1, 2, 3]}, - ] + ctor_arg = [AERO_MODE_CTOR_SAMPLED] # act sut = ppmc.AeroDist(aero_data, ctor_arg) diff --git a/tests/test_aero_mode.py b/tests/test_aero_mode.py index e8526640..0130a607 100644 --- a/tests/test_aero_mode.py +++ b/tests/test_aero_mode.py @@ -26,6 +26,7 @@ } } + AERO_MODE_CTOR_LOG_NORMAL_FULL = { "test_mode": { "mass_frac": [{"SO4": [1]}], @@ -199,7 +200,6 @@ def test_set_sample_invalid(): "mode_type": "exp", "num_conc": 100 / si.m**3, "diam_at_mean_vol": 2 * si.um, - "temp": 300 * si.K, } }, ), From 9fcf0b20dc997f511ac277e8c46deb993273e83c Mon Sep 17 00:00:00 2001 From: Griger5 Date: Mon, 20 Jan 2025 18:44:42 +0100 Subject: [PATCH 4/9] Add: InputGuard now processes reading of integers and arrays --- src/spec_file_pypartmc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/spec_file_pypartmc.cpp b/src/spec_file_pypartmc.cpp index d9e2719f..a1c3c438 100644 --- a/src/spec_file_pypartmc.cpp +++ b/src/spec_file_pypartmc.cpp @@ -27,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(bpstd::string_view(name_data, *name_size))); } /*********************************************************************************/ @@ -123,6 +124,7 @@ 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(name)); } extern "C" From 99d0acf33cff643607081ec338f5e48331e68f65 Mon Sep 17 00:00:00 2001 From: Griger5 Date: Tue, 21 Jan 2025 17:33:25 +0100 Subject: [PATCH 5/9] Add&fix: InputGuard now catches boolean values. Fixed typo in TestCondense::test_equilib_particle --- src/spec_file_pypartmc.cpp | 1 + tests/test_condense.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spec_file_pypartmc.cpp b/src/spec_file_pypartmc.cpp index a1c3c438..2b61a932 100644 --- a/src/spec_file_pypartmc.cpp +++ b/src/spec_file_pypartmc.cpp @@ -37,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(bpstd::string_view(name_data, *name_size))); } /*********************************************************************************/ diff --git a/tests/test_condense.py b/tests/test_condense.py index 04417946..8fba6e66 100644 --- a/tests/test_condense.py +++ b/tests/test_condense.py @@ -42,7 +42,7 @@ def test_equilib_particles(): def test_equilib_particle(aero_data_params: dict): # arrange env_state_ctor_arg = ENV_STATE_CTOR_ARG_MINIMAL - env_state_ctor_arg["rel_humid"] = 0.99 + env_state_ctor_arg["rel_humidity"] = 0.99 env_state = ppmc.EnvState(env_state_ctor_arg) env_state.set_temperature(300) aero_data = ppmc.AeroData( From 99a8525edad04127d633ab701843e6693e57c04d Mon Sep 17 00:00:00 2001 From: Griger5 Date: Tue, 21 Jan 2025 19:05:11 +0100 Subject: [PATCH 6/9] Fix: Deleted 'do_optical' from an initializing loop in RunPartOpt object. Deleted 'do_optical' from RUN_PART_OPT_CTOR_ARG_SIMULATION test parameter config. All pytest tests are passing --- src/run_part_opt.hpp | 2 +- tests/test_run_part_opt.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/run_part_opt.hpp b/src/run_part_opt.hpp index f657dbe6..cdf3426f 100644 --- a/src/run_part_opt.hpp +++ b/src/run_part_opt.hpp @@ -30,7 +30,7 @@ struct RunPartOpt { json_copy["do_parallel"] = false; for (auto key : std::set({ - "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; diff --git a/tests/test_run_part_opt.py b/tests/test_run_part_opt.py index e17f3132..0ddbd1bd 100644 --- a/tests/test_run_part_opt.py +++ b/tests/test_run_part_opt.py @@ -25,7 +25,6 @@ "do_parallel": False, "do_nucleation": False, "do_mosaic": False, - "do_optical": False, "do_condensation": False, "do_camp_chem": False, "t_max": 86400.0, From 0cb419d2a609e73972a158a5912b750e2b30e3a8 Mon Sep 17 00:00:00 2001 From: Griger5 Date: Tue, 11 Feb 2025 16:40:05 +0100 Subject: [PATCH 7/9] Updated: Updated InputGuards inner logic, so it works properly with more compliacted JSONs --- src/json_resource.hpp | 142 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 19 deletions(-) diff --git a/src/json_resource.hpp b/src/json_resource.hpp index 98da30d1..08047066 100644 --- a/src/json_resource.hpp +++ b/src/json_resource.hpp @@ -18,7 +18,9 @@ struct InputGuard { public: InputGuard(const nlohmann::json &j) { - map_inputs_recursive(j); + process_json(j); + + dict_key_present = false; } ~InputGuard() { @@ -26,31 +28,53 @@ struct InputGuard { } void mark_used_input(const std::string &input_name) { - this->used_inputs[input_name] = true; - } + if (prefixes.find(input_name) == prefixes.end()) { + std::string prefix = combine_str_vec(curr_prefix); - private: - std::map used_inputs; - - void map_inputs_recursive(const nlohmann::json &j) { - if (j.is_array()) { - for (auto item : j) { - map_inputs_recursive(item); + if (!prefix.empty()) { + used_inputs[prefix + "/" + input_name] = true; + } + else { + used_inputs[input_name] = true; } - return; } + } - for(auto [key, value] : j.items()) - { - if (value.is_structured()) { - map_inputs_recursive(value); + void update_dict_key(std::string dict_key) { + curr_dict_key = dict_key; + } + + void check_read_line(std::string line) { + if (line == "mode_name") { + if (dict_key_present) { + curr_prefix.pop_back(); } - - if (key.find_first_not_of(" \t\n\v\f\r") != std::string::npos) - this->used_inputs[key] = false; + curr_prefix.push_back(curr_dict_key); + dict_key_present = true; + } + else if (line.empty()) { + curr_prefix.pop_back(); + dict_key_present = false; } } + void open_spec_file(std::string spec_file_name) { + curr_prefix.push_back(spec_file_name); + } + + void close_spec_file() { + curr_prefix.pop_back(); + } + + private: + std::map used_inputs; + + std::set prefixes; + std::string curr_dict_key; + std::vector curr_prefix; + + bool dict_key_present; + void check_used_inputs() { for (auto item : used_inputs) { if (!item.second) { @@ -58,6 +82,86 @@ struct InputGuard { } } } + + 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 json_pointers; + + for (auto f : flat.items()) { + json_pointers.push_back(clean_string(f.key())); + } + + std::set json_pointers_set(json_pointers.begin(), json_pointers.end()); + + for (auto s : json_pointers_set) { + 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 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 json_pointers_set) { + std::string temp; + std::vector 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) { + prefixes.insert(v); + } + } + + temp_vec.clear(); + } + } }; struct JSONResource { @@ -116,7 +220,7 @@ struct JSONResource { } } - input_guard_ptr->mark_used_input(key); + input_guard_ptr->update_dict_key(key); return key; } From 3024ee6d7abb2f2c5833361982d9cfa7fe98b3ba Mon Sep 17 00:00:00 2001 From: Griger5 Date: Tue, 11 Feb 2025 16:41:11 +0100 Subject: [PATCH 8/9] Update: Updated function calls for the new InputGuard --- src/spec_file_pypartmc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/spec_file_pypartmc.cpp b/src/spec_file_pypartmc.cpp index 2b61a932..5c3a9909 100644 --- a/src/spec_file_pypartmc.cpp +++ b/src/spec_file_pypartmc.cpp @@ -68,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(filename)); } extern "C" @@ -84,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" @@ -126,6 +128,7 @@ void spec_file_read_timed_real_array_data( 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(name)); + json_resource_ptr()->get_input_guard_ptr()->mark_used_input("time"); } extern "C" @@ -247,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)); } From 6876613adf687c3c4bd6714736d62ebc26d0e324 Mon Sep 17 00:00:00 2001 From: Griger5 Date: Tue, 11 Feb 2025 16:53:37 +0100 Subject: [PATCH 9/9] Updated code style of InputGuard, so it matches the rest of the codebase --- src/json_resource.hpp | 210 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/src/json_resource.hpp b/src/json_resource.hpp index 08047066..0d5070f7 100644 --- a/src/json_resource.hpp +++ b/src/json_resource.hpp @@ -16,152 +16,152 @@ #include struct InputGuard { - public: - InputGuard(const nlohmann::json &j) { - process_json(j); + public: + InputGuard(const nlohmann::json &j) { + process_json(j); - dict_key_present = false; - } + this->dict_key_present = false; + } - ~InputGuard() { - check_used_inputs(); - } + ~InputGuard() { + check_used_inputs(); + } - void mark_used_input(const std::string &input_name) { - if (prefixes.find(input_name) == prefixes.end()) { - std::string prefix = combine_str_vec(curr_prefix); + 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()) { - used_inputs[prefix + "/" + input_name] = true; - } - else { - used_inputs[input_name] = true; - } + 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) { - curr_dict_key = dict_key; - } + 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 (dict_key_present) { - curr_prefix.pop_back(); - } - curr_prefix.push_back(curr_dict_key); - dict_key_present = true; - } - else if (line.empty()) { - curr_prefix.pop_back(); - dict_key_present = false; + 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; } - - void open_spec_file(std::string spec_file_name) { - curr_prefix.push_back(spec_file_name); + else if (line.empty()) { + this->curr_prefix.pop_back(); + this->dict_key_present = false; } + } - void close_spec_file() { - curr_prefix.pop_back(); - } + 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 used_inputs; + private: + std::map used_inputs; - std::set prefixes; - std::string curr_dict_key; - std::vector curr_prefix; + std::set prefixes; + std::string curr_dict_key; + std::vector curr_prefix; - bool dict_key_present; + bool dict_key_present; - void check_used_inputs() { - for (auto item : used_inputs) { - if (!item.second) { - throw std::logic_error(std::string("Failed: \"") + item.first + std::string("\" parameter remains unused.")); - } + void check_used_inputs() { + for (auto item : this->used_inputs) { + if (!item.second) { + throw std::logic_error(std::string("Failed: \"") + item.first + std::string("\" parameter remains unused.")); } } - - 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 json_pointers; + } + + void process_json(const nlohmann::json &j) { + nlohmann::json flat = j.flatten(); - for (auto f : flat.items()) { - json_pointers.push_back(clean_string(f.key())); - } + // JSON Pointer, as in a string syntax for identifying a specific value in JSON + std::vector json_pointers; - std::set json_pointers_set(json_pointers.begin(), json_pointers.end()); + for (auto f : flat.items()) { + json_pointers.push_back(clean_string(f.key())); + } - for (auto s : json_pointers_set) { - used_inputs[s] = false; - } + std::set json_pointers_set(json_pointers.begin(), json_pointers.end()); - get_prefixes(json_pointers_set); + for (auto s : json_pointers_set) { + this->used_inputs[s] = false; } - std::string clean_string(std::string str) { - bool after_slash = 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) { + 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 { - after_slash = false; - } } - - str.erase(0, 1); - - return str; + else if (isdigit(str.at(i)) && after_slash) { + str.erase(i, 1); + i -= 1; + } + else { + after_slash = false; + } } - std::string combine_str_vec(std::vector vec) { - if (vec.size() == 0) return ""; - - std::string temp = vec[0]; + str.erase(0, 1); - for (size_t i = 1; i < vec.size(); i++) { - temp += "/"; - temp += vec[i]; - } + return str; + } + + std::string combine_str_vec(std::vector vec) { + if (vec.size() == 0) return ""; + + std::string temp = vec[0]; - return temp; + for (size_t i = 1; i < vec.size(); i++) { + temp += "/"; + temp += vec[i]; } - void get_prefixes(std::set json_pointers_set) { - std::string temp; - std::vector temp_vec; - - for (auto s : json_pointers_set) { - std::stringstream line(s); + return temp; + } - while(getline(line, temp, '/')) { - temp_vec.push_back(temp); - } + void get_prefixes(std::set json_pointers_set) { + std::string temp; + std::vector temp_vec; - if (temp_vec.size() > 1) { - temp_vec.pop_back(); + for (auto s : json_pointers_set) { + std::stringstream line(s); - for (auto v : temp_vec) { - prefixes.insert(v); - } - } + while(getline(line, temp, '/')) { + temp_vec.push_back(temp); + } + + if (temp_vec.size() > 1) { + temp_vec.pop_back(); - temp_vec.clear(); + for (auto v : temp_vec) { + this->prefixes.insert(v); + } } + + temp_vec.clear(); } + } }; struct JSONResource {