diff --git a/.gitignore b/.gitignore index b0e5a8f4..28191207 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ examples/tmp*.pdf examples/tmp*.svg examples/tmp*.gif +**/__pycache__/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 40e419c2..9af83fe7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -56,5 +56,5 @@ shallow = true [submodule "nanobind_json"] path = gitmodules/nanobind_json - url = https://github.com/Griger5/nanobind_json + url = https://github.com/Griger5/nanobind_json.git shallow = true diff --git a/gitmodules/nanobind_json b/gitmodules/nanobind_json index 6e9e15fe..cb8a8565 160000 --- a/gitmodules/nanobind_json +++ b/gitmodules/nanobind_json @@ -1 +1 @@ -Subproject commit 6e9e15fee7fee798ec3259cb1fa1829c3991b567 +Subproject commit cb8a8565cc3bc7510b448cea6e6ec9cff98a599b diff --git a/src/aero_data.hpp b/src/aero_data.hpp index 1bf1d279..b57a22f2 100644 --- a/src/aero_data.hpp +++ b/src/aero_data.hpp @@ -48,7 +48,7 @@ struct AeroData { f_aero_data_from_camp(this->ptr.f_arg(), camp_core.ptr.f_arg()); } - AeroData(const nlohmann::json &json) : + AeroData(const nlohmann::ordered_json &json) : ptr(f_aero_data_ctor, f_aero_data_dtor) { if (!InputJSONResource::unique_keys(json)) diff --git a/src/aero_dist.hpp b/src/aero_dist.hpp index fe867561..a1f6375f 100644 --- a/src/aero_dist.hpp +++ b/src/aero_dist.hpp @@ -44,7 +44,7 @@ struct AeroDist { AeroDist( std::shared_ptr aero_data, - const nlohmann::json &json + const nlohmann::ordered_json &json ): ptr(f_aero_dist_ctor, f_aero_dist_dtor), aero_data(aero_data) diff --git a/src/aero_mode.hpp b/src/aero_mode.hpp index 51549eca..46f98cbc 100644 --- a/src/aero_mode.hpp +++ b/src/aero_mode.hpp @@ -138,7 +138,7 @@ struct AeroMode { ptr(f_aero_mode_ctor, f_aero_mode_dtor) {} - AeroMode(AeroData &aero_data, const nlohmann::json &json) : + AeroMode(AeroData &aero_data, const nlohmann::ordered_json &json) : ptr(f_aero_mode_ctor, f_aero_mode_dtor) { if (json.size() != 1 || !json.is_object() || !json.begin().value().is_object()) @@ -149,7 +149,7 @@ struct AeroMode { guard.check_parameters(); } - static void check_mode_json(const nlohmann::json &mode) { + static void check_mode_json(const nlohmann::ordered_json &mode) { for (auto key : std::set({"mass_frac", "mode_type"})) // TODO #320: more... if (mode.find(key) == mode.end()) throw std::runtime_error("mode parameters dict must include key '" + key + "'"); diff --git a/src/env_state.hpp b/src/env_state.hpp index 990742d5..4fa1bd8c 100644 --- a/src/env_state.hpp +++ b/src/env_state.hpp @@ -29,7 +29,7 @@ extern "C" void f_env_state_air_dens(const void *ptr, double *air_density) noexc struct EnvState { PMCResource ptr; - EnvState(const nlohmann::json &json) : + EnvState(const nlohmann::ordered_json &json) : ptr(f_env_state_ctor, f_env_state_dtor) { JSONResourceGuard guard(json); diff --git a/src/gas_data.hpp b/src/gas_data.hpp index cecadaca..58af81a2 100644 --- a/src/gas_data.hpp +++ b/src/gas_data.hpp @@ -11,7 +11,7 @@ #include "gas_data_parameters.hpp" #include "camp_core.hpp" #include "nanobind/nanobind.h" -#include "nanobind_json/nanobind_json.hpp" +#include "nanobind_json/nanobind_json.h" extern "C" void f_gas_data_ctor(void *ptr) noexcept; extern "C" void f_gas_data_dtor(void *ptr) noexcept; @@ -26,7 +26,7 @@ extern "C" void f_gas_data_spec_name_by_index(const void *ptr, const int *i_spec struct GasData { PMCResource ptr; - const nlohmann::json json; + const nlohmann::ordered_json json; GasData(const CampCore &CampCore) : ptr(f_gas_data_ctor, f_gas_data_dtor) @@ -38,11 +38,11 @@ struct GasData { ptr(f_gas_data_ctor, f_gas_data_dtor), json(tpl) { - auto json_array = nlohmann::json::array(); + auto json_array = nlohmann::ordered_json::array(); for (const auto item : tpl) - json_array.push_back(nlohmann::json::object({{ + json_array.push_back(nlohmann::ordered_json::object({{ nanobind::cast(item), - nlohmann::json::array() + nlohmann::ordered_json::array() }})); JSONResourceGuard guard(json_array); diff --git a/src/gas_state.hpp b/src/gas_state.hpp index 23dcc390..6b1ad688 100644 --- a/src/gas_state.hpp +++ b/src/gas_state.hpp @@ -115,7 +115,7 @@ struct GasState { return data; } - static void set_mix_rats(const GasState &self, const nlohmann::json &json) { + static void set_mix_rats(const GasState &self, const nlohmann::ordered_json &json) { if (json.size() == 0) throw std::runtime_error("Non-empty sequence of mixing ratios expected"); diff --git a/src/input_guard.hpp b/src/input_guard.hpp index 0bb238c9..0b638bd0 100644 --- a/src/input_guard.hpp +++ b/src/input_guard.hpp @@ -6,7 +6,7 @@ struct InputGuard { public: - InputGuard(const nlohmann::json &j) { + InputGuard(const nlohmann::ordered_json &j) { process_json(j); this->dict_key_present = false; @@ -71,8 +71,8 @@ struct InputGuard { bool dict_key_present; - void process_json(const nlohmann::json &j) { - nlohmann::json flat = j.flatten(); + void process_json(const nlohmann::ordered_json &j) { + nlohmann::ordered_json flat = j.flatten(); // JSON Pointer, as in a string syntax for identifying a specific value in JSON std::vector json_pointers; diff --git a/src/json_resource.hpp b/src/json_resource.hpp index dd558e4c..76b99409 100644 --- a/src/json_resource.hpp +++ b/src/json_resource.hpp @@ -19,8 +19,8 @@ struct JSONResource { private: std::set vars; - const nlohmann::json *json; - std::stack json_parent; + const nlohmann::ordered_json *json; + std::stack json_parent; std::unique_ptr input_guard_ptr; @@ -34,7 +34,7 @@ struct JSONResource { JSONResource() {} - JSONResource(const nlohmann::json &json) { + JSONResource(const nlohmann::ordered_json &json) { this->set_current_json_ptr(&json); for (auto &entry : this->json->items()) { this->vars.insert(entry.key()); @@ -43,7 +43,7 @@ struct JSONResource { input_guard_ptr = std::make_unique(json); }; - void set_current_json_ptr(const nlohmann::json *ptr) { + void set_current_json_ptr(const nlohmann::ordered_json *ptr) { this->json = ptr; } @@ -86,8 +86,8 @@ struct JSONResource { void zoom_in(const bpstd::string_view &sub) noexcept { auto it = this->json->is_array() - ? this->json->at(this->json->size()-1).find(sub) - : this->json->find(sub); + ? this->json->at(this->json->size()-1).find(sub.to_string()) + : this->json->find(sub.to_string()); assert(*it != NULL); this->json_parent.push(this->json); @@ -157,7 +157,7 @@ struct JSONResource { const bpstd::string_view name, T *var ) { - *var = this->find(name)->get(); + *var = this->find(name.to_string())->get(); } void read_str( @@ -165,7 +165,7 @@ struct JSONResource { char* var_data, int* var_size ) noexcept { - auto it = this->find(name); + auto it = this->find(name.to_string()); if (it == this->json->end()) { assert(false); @@ -240,7 +240,7 @@ struct InputJSONResource: JSONResource { public: InputJSONResource( - const nlohmann::json &json, + const nlohmann::ordered_json &json, const std::string key_cond = "", const std::string key_name = "", const std::size_t max_zoom_level = 3 @@ -290,7 +290,7 @@ struct InputJSONResource: JSONResource { return 1; } - static bool unique_keys(const nlohmann::json &json) { + static bool unique_keys(const nlohmann::ordered_json &json) { std::set keys; for (auto i=0u; i guard; + std::unique_ptr guard; - OutputJSONResource() : guard(std::make_unique()) { + OutputJSONResource() : guard(std::make_unique()) { this->set_current_json_ptr(this->guard.get()); } @@ -331,12 +331,12 @@ struct JSONResourceGuard { json_resource_ptr() = std::make_unique(); } - JSONResourceGuard(const nlohmann::json & json) { + JSONResourceGuard(const nlohmann::ordered_json & json) { json_resource_ptr() = std::make_unique(json); } JSONResourceGuard( - const nlohmann::json & json, + const nlohmann::ordered_json & json, const std::string key_cond, const std::string key_name ) { @@ -344,7 +344,7 @@ struct JSONResourceGuard { } JSONResourceGuard( - const nlohmann::json & json, + const nlohmann::ordered_json & json, const std::string key_cond, const std::string key_name, const int max_zoom_level diff --git a/src/pypartmc.cpp b/src/pypartmc.cpp index c758a2a0..9f532729 100644 --- a/src/pypartmc.cpp +++ b/src/pypartmc.cpp @@ -14,7 +14,7 @@ #include "nanobind/ndarray.h" #undef snprintf // required to fix an issue with std::snprintf in nlohmann::json #include "nlohmann/json.hpp" -#include "nanobind_json/nanobind_json.hpp" +#include "nanobind_json/nanobind_json.h" #include "sundials/sundials_config.h" #include "camp/version.h" #include "tl/optional.hpp" @@ -174,7 +174,7 @@ NB_MODULE(_PyPartMC, m) { )pbdoc" ) .def(nb::init()) - .def(nb::init()) + .def(nb::init()) .def("spec_by_name", AeroData::spec_by_name, "Return the number of the species in AeroData with the given name.") .def("__len__", AeroData::__len__, "Return number of aerosol species.") @@ -404,7 +404,7 @@ NB_MODULE(_PyPartMC, m) { scenario_t. )pbdoc" ) - .def(nb::init()) + .def(nb::init()) .def("set_temperature", EnvState::set_temperature, "Set the temperature of the environment state.") .def_prop_ro("temp", EnvState::temp, @@ -463,7 +463,7 @@ NB_MODULE(_PyPartMC, m) { nb::init< const GasData&, const AeroData&, - const nlohmann::json& + const nlohmann::ordered_json& >(), "instantiates and initializes from a JSON object" ) @@ -525,7 +525,7 @@ NB_MODULE(_PyPartMC, m) { "RunPartOpt", "Options controlling the execution of run_part()." ) - .def(nb::init()) + .def(nb::init()) .def_prop_ro("t_max", RunPartOpt::t_max, "Total simulation time.") .def_prop_ro("del_t", RunPartOpt::del_t, "Time step.") ; @@ -534,7 +534,7 @@ NB_MODULE(_PyPartMC, m) { "RunSectOpt", "Options controlling the execution of run_sect()." ) - .def(nb::init()) + .def(nb::init()) .def_prop_ro("t_max", RunSectOpt::t_max, "Total simulation time.") .def_prop_ro("del_t", RunSectOpt::del_t, "Time step.") ; @@ -543,7 +543,7 @@ NB_MODULE(_PyPartMC, m) { "RunExactOpt", "Options controlling the execution of run_exact()." ) - .def(nb::init()) + .def(nb::init()) .def_prop_ro("t_max", RunExactOpt::t_max, "Total simulation time.") ; @@ -556,9 +556,8 @@ NB_MODULE(_PyPartMC, m) { .def_prop_ro("widths", BinGrid::widths, "Return bin widths.") ; - nb::class_(m,"AeroMode", - "An aerosol size distribution mode.") - .def(nb::init()) + nb::class_(m,"AeroMode") + .def(nb::init()) .def_prop_rw("num_conc", &AeroMode::get_num_conc, &AeroMode::set_num_conc, "Provide access (read or write) to the total number concentration of a mode.") .def("num_dist", &AeroMode::num_dist, @@ -584,7 +583,7 @@ NB_MODULE(_PyPartMC, m) { ; nb::class_(m,"AeroDist") - .def(nb::init, const nlohmann::json&>()) + .def(nb::init, const nlohmann::ordered_json&>()) .def_prop_ro("n_mode", &AeroDist::get_n_mode, "Number of aerosol modes in the distribution.") .def_prop_ro("num_conc", &AeroDist::get_total_num_conc, diff --git a/src/run_exact_opt.hpp b/src/run_exact_opt.hpp index fbf22a76..fdede19c 100644 --- a/src/run_exact_opt.hpp +++ b/src/run_exact_opt.hpp @@ -18,10 +18,10 @@ extern "C" void f_run_exact_opt_t_max(const void *ptr, double *t_max) noexcept; struct RunExactOpt { PMCResource ptr; - RunExactOpt(const nlohmann::json &json, EnvState &env_state) : + RunExactOpt(const nlohmann::ordered_json &json, EnvState &env_state) : ptr(f_run_exact_opt_ctor, f_run_exact_opt_dtor) { - nlohmann::json json_copy(json); + nlohmann::ordered_json json_copy(json); for (auto key : std::set({ "t_output" diff --git a/src/run_part_opt.hpp b/src/run_part_opt.hpp index 423f1f55..279d72da 100644 --- a/src/run_part_opt.hpp +++ b/src/run_part_opt.hpp @@ -19,10 +19,10 @@ struct RunPartOpt { PMCResource ptr; bool allow_halving, allow_doubling; - RunPartOpt(const nlohmann::json &json) : + RunPartOpt(const nlohmann::ordered_json &json) : ptr(f_run_part_opt_ctor, f_run_part_opt_dtor) { - nlohmann::json json_copy(json); + nlohmann::ordered_json json_copy(json); if (json_copy.find("do_parallel") != json_copy.end() && json_copy["do_parallel"]) throw std::runtime_error("setting do_parallel=true not supported in PyPartMC"); diff --git a/src/run_sect_opt.hpp b/src/run_sect_opt.hpp index cab6a187..175a80a4 100644 --- a/src/run_sect_opt.hpp +++ b/src/run_sect_opt.hpp @@ -19,10 +19,10 @@ extern "C" void f_run_sect_opt_del_t(const void *ptr, double *del_t) noexcept; struct RunSectOpt { PMCResource ptr; - RunSectOpt(const nlohmann::json &json, EnvState &env_state) : + RunSectOpt(const nlohmann::ordered_json &json, EnvState &env_state) : ptr(f_run_sect_opt_ctor, f_run_sect_opt_dtor) { - nlohmann::json json_copy(json); + nlohmann::ordered_json json_copy(json); for (auto key : std::set({ "t_output", "t_progress" diff --git a/src/scenario.hpp b/src/scenario.hpp index 8ba25b69..893f661e 100644 --- a/src/scenario.hpp +++ b/src/scenario.hpp @@ -85,12 +85,12 @@ extern "C" void f_scenario_aero_background_time( struct Scenario { PMCResource ptr; - const nlohmann::json json; + const nlohmann::ordered_json json; Scenario( const GasData &gas_data, const AeroData &aero_data, - const nlohmann::json &json + const nlohmann::ordered_json &json ) : ptr(f_scenario_ctor, f_scenario_dtor), json(json) diff --git a/tests/test_aero_dist.py b/tests/test_aero_dist.py index c6af191c..3cb9a09e 100644 --- a/tests/test_aero_dist.py +++ b/tests/test_aero_dist.py @@ -100,10 +100,7 @@ def test_ctor(): "order", ( range, - pytest.param( - lambda n_modes: reversed(range(n_modes)), - marks=(pytest.mark.xfail(strict=True),), - ), # TODO #213 + lambda n_modes: reversed(range(n_modes)), ), ) def test_ctor_multimode(n_modes, order): @@ -124,11 +121,14 @@ def test_ctor_multimode(n_modes, order): # assert assert sut.n_mode == n_modes - assert sut.num_conc == np.sum(num_concs) + np.testing.assert_approx_equal( + actual=sut.num_conc, desired=np.sum(num_concs), significant=10 + ) for i in range(sut.n_mode): - assert sut.mode(i).type == modes[i]["mode_type"] - assert sut.mode(i).num_conc == modes[i]["num_conc"] - assert sut.mode(i).name == f"mode_{tuple(order(n_modes))[i]}" + j = tuple(order(n_modes))[i] + assert sut.mode(i).type == modes[j]["mode_type"] + assert sut.mode(i).num_conc == modes[j]["num_conc"] + assert sut.mode(i).name == f"mode_{j}" @staticmethod def test_ctor_modes_in_order(n_modes=4): diff --git a/tests/test_aero_mode.py b/tests/test_aero_mode.py index 29c5dee9..a2f649a6 100644 --- a/tests/test_aero_mode.py +++ b/tests/test_aero_mode.py @@ -323,11 +323,11 @@ def test_fixed_segfault_case_on_circular_reference(): ) # act - with pytest.raises(TypeError) as exc_info: + with pytest.raises(RuntimeError) as exc_info: ppmc.AeroMode(aero_data, fishy_ctor_arg) # assert - assert "incompatible function arguments" in str(exc_info.value) + assert "Circular reference detected" in str(exc_info.value) @staticmethod @pytest.mark.skipif(platform.machine() == "arm64", reason="TODO #348") diff --git a/tests/test_scenario.py b/tests/test_scenario.py index 98701044..ad8b75b1 100644 --- a/tests/test_scenario.py +++ b/tests/test_scenario.py @@ -170,12 +170,7 @@ def test_ctor_fails_with_no_values_in_time_array(): @staticmethod @pytest.mark.parametrize( "mode_names", - ( - ("A", "B"), - pytest.param( - ("B", "A"), marks=(pytest.mark.xfail(strict=True),) - ), # TODO #213 - ), + (("A", "B"), pytest.param(("B", "A"))), ) def test_multi_mode(mode_names): # arrange @@ -207,12 +202,7 @@ def test_multi_mode(mode_names): @pytest.mark.parametrize("key", ("aero_emissions", "aero_background")) @pytest.mark.parametrize( "mode_names", - ( - ("A", "B"), - pytest.param( - ("B", "A"), marks=(pytest.mark.xfail(strict=True),) - ), # TODO #213 - ), + (("A", "B"), pytest.param(("B", "A"))), ) def test_time_varying_aero_multimode(key, mode_names): # arrange