Skip to content

Commit

Permalink
raise exception for non-unique mass_frac species in AeroMode and Aero…
Browse files Browse the repository at this point in the history
…Dist ctors. closes #240 (#321)
  • Loading branch information
slayoo authored Dec 3, 2023
1 parent 87839e8 commit a35f543
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/aero_dist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct AeroDist {
{
if (!InputGimmick::unique_keys(json))
throw std::runtime_error("Mode names must be unique");
for (const auto &mode : json)
AeroMode::check_mode_json(mode.begin().value());

GimmickGuard<InputGimmick> guard(json, "", "mode_name", 1);
f_aero_dist_from_json(ptr.f_arg_non_const(), aero_data->ptr.f_arg_non_const());
Expand Down
18 changes: 15 additions & 3 deletions src/aero_mode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,25 @@ struct AeroMode {
AeroMode(AeroData &aero_data, const nlohmann::json &json) :
ptr(f_aero_mode_ctor, f_aero_mode_dtor)
{
if (json.size() != 1)
throw std::runtime_error("Single element expected");

if (json.size() != 1 || !json.is_object() || !json.begin().value().is_object())
throw std::runtime_error("Single-element dict expected with mode name as key and mode params dict as value");
check_mode_json(json.begin().value());
GimmickGuard<InputGimmick> guard(json, "", "mode_name");
f_aero_mode_from_json(ptr.f_arg_non_const(), aero_data.ptr.f_arg_non_const());
}

static void check_mode_json(const nlohmann::json &mode) {
for (auto key : std::set<std::string>({"mass_frac", "mode_type"})) // TODO #320: more...
if (mode.find(key) == mode.end())
throw std::runtime_error("mode parameters dict must include key '" + key + "'");
auto mass_frac = mode["mass_frac"];

if (!mass_frac.is_array()) // TODO #320: check if all are single-element dicts
throw std::runtime_error("mass_frac value must be a list of single-element dicts");
if (!InputGimmick::unique_keys(mass_frac))
throw std::runtime_error("mass_frac keys must be unique");
}

static auto get_num_conc(const AeroMode &self){
double val;
f_aero_mode_get_num_conc(self.ptr.f_arg(), &val);
Expand Down
16 changes: 16 additions & 0 deletions tests/test_aero_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,19 @@ def test_ctor_multimode_error_on_repeated_mode_names():

# assert
assert str(exc_info.value) == "Mode names must be unique"

@staticmethod
def test_ctor_error_on_repeated_massfrac_keys():
# arrange
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
fishy_ctor_arg = copy.deepcopy(AERO_DIST_CTOR_ARG_MINIMAL)
fishy_ctor_arg[0]["test_mode"]["mass_frac"].append(
fishy_ctor_arg[0]["test_mode"]["mass_frac"][0]
)

# act
with pytest.raises(Exception) as exc_info:
ppmc.AeroDist(aero_data, fishy_ctor_arg)

# assert
assert str(exc_info.value) == "mass_frac keys must be unique"
29 changes: 22 additions & 7 deletions tests/test_aero_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
####################################################################################################

import copy

import numpy as np
import pytest

Expand Down Expand Up @@ -242,30 +244,43 @@ def test_set_name():
def test_ctor_fails_with_multiple_modes():
# arrange
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
fishy_ctor_arg["xxx"] = fishy_ctor_arg["test_mode"]

# act
with pytest.raises(Exception) as exc_info:
ppmc.AeroMode(aero_data, fishy_ctor_arg)

# assert
assert str(exc_info.value) == "Single element expected"
assert (
str(exc_info.value)
== "Single-element dict expected with mode name as key and mode params dict as value"
)

@staticmethod
def test_ctor_fails_with_nonunique_mass_fracs():
pytest.skip("TODO #240")

# arrange
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
fishy_ctor_arg["test_mode"]["mass_frac"].append(
fishy_ctor_arg["test_mode"]["mass_frac"]
AERO_MODE_CTOR_LOG_NORMAL["test_mode"]["mass_frac"][0]
)

# act
with pytest.raises(Exception) as exc_info:
ppmc.AeroMode(aero_data, fishy_ctor_arg)

# assert
assert str(exc_info.value) == ""
assert str(exc_info.value) == "mass_frac keys must be unique"

@staticmethod
def test_segfault_case(): # TODO #319
pytest.skip()

aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
fishy_ctor_arg["test_mode"]["mass_frac"].append(
fishy_ctor_arg["test_mode"]["mass_frac"]
)
print(fishy_ctor_arg)
ppmc.AeroMode(aero_data, fishy_ctor_arg)

0 comments on commit a35f543

Please sign in to comment.