Skip to content

Commit a358aca

Browse files
committed
addressing issue #83, ensuring there are sensible checks in place for the input parameters
1 parent 1794f68 commit a358aca

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed

CodeEntropy/config/arg_config_manager.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,62 @@ def merge_configs(self, args, run_config):
185185
handler.setLevel(logging.INFO)
186186

187187
return args
188+
189+
def input_parameters_validation(self, u, args):
190+
"""Check the validity of the user inputs against sensible values"""
191+
192+
self._check_input_start(u, args)
193+
self._check_input_end(u, args)
194+
self._check_input_step(args)
195+
self._check_input_bin_width(args)
196+
self._check_input_temperature(args)
197+
self._check_input_force_partitioning(args)
198+
199+
def _check_input_start(self, u, args):
200+
"""Check that the input does not exceed the length of the trajectory."""
201+
if args.start > len(u.trajectory):
202+
raise ValueError(
203+
f"Invalid 'start' value: {args.start}. It exceeds the trajectory length"
204+
" of {len(u.trajectory)}."
205+
)
206+
207+
def _check_input_end(self, u, args):
208+
"""Check that the end index does not exceed the trajectory length."""
209+
if args.end > len(u.trajectory):
210+
raise ValueError(
211+
f"Invalid 'end' value: {args.end}. It exceeds the trajectory length of"
212+
" {len(u.trajectory)}."
213+
)
214+
215+
def _check_input_step(self, args):
216+
"""Check that the step value is non-negative."""
217+
if args.step < 0:
218+
logger.warning(
219+
f"Negative 'step' value provided: {args.step}. This may lead to"
220+
" unexpected behavior."
221+
)
222+
223+
def _check_input_bin_width(self, args):
224+
"""Check that the bin width is within the valid range [0, 360]."""
225+
if args.bin_width < 0 or args.bin_width > 360:
226+
raise ValueError(
227+
f"Invalid 'bin_width': {args.bin_width}. It must be between 0 and 360"
228+
" degrees."
229+
)
230+
231+
def _check_input_temperature(self, args):
232+
"""Check that the temperature is non-negative."""
233+
if args.temperature < 0:
234+
raise ValueError(
235+
f"Invalid 'temperature': {args.temperature}. Temperature cannot be"
236+
" below 0."
237+
)
238+
239+
def _check_input_force_partitioning(self, args):
240+
"""Warn if force partitioning is not set to the default value."""
241+
default_value = arg_map["force_partitioning"]["default"]
242+
if args.force_partitioning != default_value:
243+
logger.warning(
244+
f"'force_partitioning' is set to {args.force_partitioning},"
245+
" which differs from the default ({default_value})."
246+
)

CodeEntropy/run.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ def run_entropy_workflow(self):
135135
logger.debug(f"Loading Universe with {tprfile} and {trrfile}")
136136
u = mda.Universe(tprfile, trrfile)
137137

138+
self._config_manager.input_parameters_validation(u, args)
139+
138140
# Create LevelManager instance
139141
level_manager = LevelManager()
140142

tests/test_CodeEntropy/test_arg_config_manager.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,103 @@ def test_empty_yaml_config(self, mock_exists, mock_file):
494494
self.assertIsInstance(config, dict)
495495
self.assertEqual(config, {})
496496

497+
def test_input_parameters_validation_all_valid(self):
498+
"""Test that input_parameters_validation passes with all valid inputs."""
499+
manager = ConfigManager()
500+
u = MagicMock()
501+
u.trajectory = [0] * 100
502+
503+
args = MagicMock(
504+
start=10,
505+
end=90,
506+
step=1,
507+
bin_width=30,
508+
temperature=298.0,
509+
force_partitioning=0.5,
510+
)
511+
512+
with patch.dict(
513+
"CodeEntropy.config.arg_config_manager.arg_map",
514+
{"force_partitioning": {"default": 0.5}},
515+
):
516+
manager.input_parameters_validation(u, args)
517+
518+
def test_check_input_start_valid(self):
519+
"""Test that a valid 'start' value does not raise an error."""
520+
args = MagicMock(start=50)
521+
u = MagicMock()
522+
u.trajectory = [0] * 100
523+
ConfigManager()._check_input_start(u, args)
524+
525+
def test_check_input_start_invalid(self):
526+
"""Test that an invalid 'start' value raises a ValueError."""
527+
args = MagicMock(start=150)
528+
u = MagicMock()
529+
u.trajectory = [0] * 100
530+
with self.assertRaises(ValueError):
531+
ConfigManager()._check_input_start(u, args)
532+
533+
def test_check_input_end_valid(self):
534+
"""Test that a valid 'end' value does not raise an error."""
535+
args = MagicMock(end=100)
536+
u = MagicMock()
537+
u.trajectory = [0] * 100
538+
ConfigManager()._check_input_end(u, args)
539+
540+
def test_check_input_end_invalid(self):
541+
"""Test that an 'end' value exceeding trajectory length raises a ValueError."""
542+
args = MagicMock(end=101)
543+
u = MagicMock()
544+
u.trajectory = [0] * 100
545+
with self.assertRaises(ValueError):
546+
ConfigManager()._check_input_end(u, args)
547+
548+
@patch("CodeEntropy.config.arg_config_manager.logger")
549+
def test_check_input_step_negative(self, mock_logger):
550+
"""Test that a negative 'step' value triggers a warning."""
551+
args = MagicMock(step=-1)
552+
ConfigManager()._check_input_step(args)
553+
mock_logger.warning.assert_called_once()
554+
555+
def test_check_input_bin_width_valid(self):
556+
"""Test that a valid 'bin_width' value does not raise an error."""
557+
args = MagicMock(bin_width=180)
558+
ConfigManager()._check_input_bin_width(args)
559+
560+
def test_check_input_bin_width_invalid_low(self):
561+
"""Test that a negative 'bin_width' value raises a ValueError."""
562+
args = MagicMock(bin_width=-10)
563+
with self.assertRaises(ValueError):
564+
ConfigManager()._check_input_bin_width(args)
565+
566+
def test_check_input_bin_width_invalid_high(self):
567+
"""Test that a 'bin_width' value above 360 raises a ValueError."""
568+
args = MagicMock(bin_width=400)
569+
with self.assertRaises(ValueError):
570+
ConfigManager()._check_input_bin_width(args)
571+
572+
def test_check_input_temperature_valid(self):
573+
"""Test that a valid 'temperature' value does not raise an error."""
574+
args = MagicMock(temperature=298.0)
575+
ConfigManager()._check_input_temperature(args)
576+
577+
def test_check_input_temperature_invalid(self):
578+
"""Test that a negative 'temperature' value raises a ValueError."""
579+
args = MagicMock(temperature=-5)
580+
with self.assertRaises(ValueError):
581+
ConfigManager()._check_input_temperature(args)
582+
583+
@patch("CodeEntropy.config.arg_config_manager.logger")
584+
def test_check_input_force_partitioning_warning(self, mock_logger):
585+
"""Test that a non-default 'force_partitioning' value triggers a warning."""
586+
args = MagicMock(force_partitioning=0.7)
587+
with patch.dict(
588+
"CodeEntropy.config.arg_config_manager.arg_map",
589+
{"force_partitioning": {"default": 0.5}},
590+
):
591+
ConfigManager()._check_input_force_partitioning(args)
592+
mock_logger.warning.assert_called_once()
593+
497594

498595
if __name__ == "__main__":
499596
unittest.main()

0 commit comments

Comments
 (0)