diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 28d9dc30..b92223c9 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -23,6 +23,7 @@ MontePy Changelog * Added ability to parse all MCNP objects from a string (:issue:`88`). * Added function: :func:`~montepy.mcnp_problem.MCNP_Problem.parse` to parse arbitrary MCNP object (:issue:`88`). * An error is now raised when typos in object attributes are used, e.g., ``cell.nubmer`` (:issue:`508`). +* Particle type exceptions are now warnings, not errors (:issue:`381`). **Bugs Fixed** diff --git a/montepy/__init__.py b/montepy/__init__.py index 129a6d62..7f9d02a6 100644 --- a/montepy/__init__.py +++ b/montepy/__init__.py @@ -1,5 +1,5 @@ # Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved. -""" MontePy is a library for reading, editing, and writing MCNP input files. +"""MontePy is a library for reading, editing, and writing MCNP input files. This creates a semantic understanding of the MCNP input file. start by running montepy.read_input(). diff --git a/montepy/cells.py b/montepy/cells.py index 2be3e1bb..a6f77993 100644 --- a/montepy/cells.py +++ b/montepy/cells.py @@ -170,8 +170,6 @@ def handle_error(e): except ( BrokenObjectLinkError, MalformedInputError, - ParticleTypeNotInProblem, - ParticleTypeNotInCell, ) as e: handle_error(e) continue diff --git a/montepy/data_inputs/importance.py b/montepy/data_inputs/importance.py index 74bbcab0..635dbeba 100644 --- a/montepy/data_inputs/importance.py +++ b/montepy/data_inputs/importance.py @@ -2,6 +2,7 @@ import collections import copy import math +import warnings from montepy.data_inputs.cell_modifier import CellModifierInput, InitInput from montepy.errors import * from montepy.constants import DEFAULT_VERSION, rel_tol, abs_tol @@ -279,8 +280,9 @@ def _clear_data(self): def _check_particle_in_problem(self, particle_type): if self._problem: if particle_type not in self._problem.mode: - raise ParticleTypeNotInProblem( - f"Particle type: {particle_type} not included in problem mode." + warnings.warn( + f"Particle type: {particle_type} not included in problem mode.", + ParticleTypeNotInProblem, ) def _collect_new_values(self): @@ -291,9 +293,10 @@ def _collect_new_values(self): try: tree = cell.importance._particle_importances[particle] except KeyError: - raise ParticleTypeNotInCell( + raise NotImplementedError( f"Importance data not available for cell {cell.number} for particle: " - f"{particle}, though it is in the problem" + f"{particle}, though it is in the problem, and default importance logic " + "is not yet implemented in MontePy." ) new_vals[particle].append(tree["data"][0]) if len(particle_pairings[particle]) == 0: @@ -483,7 +486,6 @@ def __create_particle_imp_doc(particle_type): :type importnace: float :returns: the importance for the particle type. If not set, defaults to 0. :rtype: float -:raises ParticleTypeNotInProblem: raised if this particle is accessed while not in the problem mode. """ diff --git a/montepy/errors.py b/montepy/errors.py index 75554649..abf39cac 100644 --- a/montepy/errors.py +++ b/montepy/errors.py @@ -153,10 +153,10 @@ def __init__(self, key, new_value): super().__init__(self.message) -class ParticleTypeNotInProblem(ValueError): +class ParticleTypeWarning(Warning): """ - Raised when data are set for a particle type not in - the problem's mode. + Base class for incongruencies between particle types + in problem mode, cell importances, and tallies """ def __init__(self, message): @@ -164,15 +164,22 @@ def __init__(self, message): super().__init__(message) -class ParticleTypeNotInCell(ValueError): +class ParticleTypeNotInProblem(ParticleTypeWarning): + """ + Raised when data, such as cell importance or tally type, + are set for a particle type not in the problem's mode. + """ + + pass + + +class ParticleTypeNotInCell(ParticleTypeWarning): """ Raised when data for importance data for a particle in the problem is not provided for a cell. """ - def __init__(self, message): - self.message = message - super().__init__(message) + pass class UnsupportedFeature(NotImplementedError): diff --git a/montepy/mcnp_problem.py b/montepy/mcnp_problem.py index 247a199a..3a03aa6e 100644 --- a/montepy/mcnp_problem.py +++ b/montepy/mcnp_problem.py @@ -427,11 +427,7 @@ def handle_error(e): for surface in self._surfaces: try: surface.update_pointers(self.surfaces, self._data_inputs) - except ( - BrokenObjectLinkError, - ParticleTypeNotInProblem, - ParticleTypeNotInCell, - ) as e: + except (BrokenObjectLinkError,) as e: handle_error(e) to_delete = [] for data_index, data_input in enumerate(self._data_inputs): @@ -441,8 +437,6 @@ def handle_error(e): except ( BrokenObjectLinkError, MalformedInputError, - ParticleTypeNotInProblem, - ParticleTypeNotInCell, ) as e: handle_error(e) continue diff --git a/tests/inputs/test_not_imp.imcnp b/tests/inputs/test_not_imp.imcnp new file mode 100644 index 00000000..62da55c0 --- /dev/null +++ b/tests/inputs/test_not_imp.imcnp @@ -0,0 +1,48 @@ +A test with a default importance (Not Implemented) +C cells +c +1 1 20 + -1000 $ dollar comment + U=350 trcl=5 + imp:n,p=1 $ imp:e should default to 1 +2 2 8 + -1005 + imp:n,p=1 $ imp:e should default to 1 +3 3 -1 + 1000 1005 -1010 + imp:n=3 $ imp:e and imp:p should default to 1 +99 0 + 1010 + imp:n=9 $ imp:e should default to 1 + imp:p=0 +5 0 + #99 + imp:n=0 $ imp:e should default to 0 + imp:p=0 +c foo end comment + +C surfaces +1000 SO 1 +1005 RCC 0 1.5 -0.5 0 0 1 0.25 +1010 SO 3 + +C data +C materials +C UO2 5 atpt enriched +m1 92235.80c 5 & +92238.80c 95 +C Iron +m2 26054.80c 5.85 + 26056.80c 91.75 + 26057.80c 2.12 + 26058.80c 0.28 +C water +m3 1001.80c 2 + 8016.80c 1 +MT3 lwtr.23t +C execution +ksrc 0 0 0 +kcode 100000 1.000 50 1050 +phys:p j 1 2j 1 +mode n p e + diff --git a/tests/test_importance.py b/tests/test_importance.py index e7b5f4ea..fa4313e2 100644 --- a/tests/test_importance.py +++ b/tests/test_importance.py @@ -1,5 +1,4 @@ # Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved. -from unittest import TestCase import montepy from montepy.cell import Cell from montepy.particle import Particle @@ -7,178 +6,184 @@ from montepy.errors import * from montepy.input_parser import mcnp_input, block_type import os +import io +import pytest -class TestImportance(TestCase): - def test_importance_init_cell(self): - # test_normal cell init - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) +def test_importance_init_cell(): + # test_normal cell init + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + assert cell.importance.neutron == 1.0 + assert cell.importance.photon == 1.0 + assert cell.importance.alpha_particle == 0.0 + assert cell.importance.all is None + assert cell.importance.in_cell_block + # test non-number imp + in_str = "1 0 -1 IMP:N,P=h" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(ValueError): cell = Cell(card) - self.assertEqual(cell.importance.neutron, 1.0) - self.assertEqual(cell.importance.photon, 1.0) - self.assertEqual(cell.importance.alpha_particle, 0.0) - self.assertIsNone(cell.importance.all) - self.assertTrue(cell.importance.in_cell_block) - # test non-number imp - in_str = "1 0 -1 IMP:N,P=h" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(ValueError): - cell = Cell(card) - # test negative imp - in_str = "1 0 -1 IMP:N,P=-2" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(ValueError): - cell = Cell(card) - - def test_importance_init_data(self): - in_str = "IMP:N,P 1 0" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - imp = Importance(card) - self.assertEqual( - [val.value for val in imp._particle_importances[Particle.NEUTRON]["data"]], - [1.0, 0.0], - ) - self.assertEqual( - [val.value for val in imp._particle_importances[Particle.PHOTON]["data"]], - [1.0, 0.0], - ) - # test non-number imp - in_str = "IMP:N,P 1 h" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(ValueError): - imp = Importance(card) - # test negative - in_str = "IMP:N,P 1 -2" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(ValueError): - imp = Importance(card) - # test bad in_cell_block - in_str = "IMP:N,P 1 2" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(TypeError): - imp = Importance(card, in_cell_block=1) - # test bad key - in_str = "IMP:N,P 1 2" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(TypeError): - imp = Importance(card, key=1) - # test bad value - in_str = "IMP:N,P 1 2" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - with self.assertRaises(TypeError): - imp = Importance(card, value=1) - - def test_importance_iter_getter_in(self): - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - cell = Cell(card) - imp = cell.importance - particles = [ - montepy.particle.Particle.NEUTRON, - montepy.particle.Particle.PHOTON, - ] - for particle in imp: - self.assertIn(particle, particles) - self.assertAlmostEqual(imp[particle], 1.0) - for particle in particles: - self.assertIn(particle, imp) - with self.assertRaises(TypeError): - imp["hi"] - - def test_importance_all_setter(self): - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - cell = Cell(card) - problem = montepy.mcnp_problem.MCNP_Problem("foo") - problem.mode.add(montepy.particle.Particle.NEUTRON) - problem.mode.add(montepy.particle.Particle.PHOTON) - imp = cell.importance - cell.link_to_problem(problem) - imp.all = 2.0 - self.assertAlmostEqual(imp.neutron, 2.0) - self.assertAlmostEqual(imp.photon, 2.0) - # try wrong type - with self.assertRaises(TypeError): - imp.all = "h" - # try negative type - with self.assertRaises(ValueError): - imp.all = -2.0 - - def test_importance_setter(self): - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + # test negative imp + in_str = "1 0 -1 IMP:N,P=-2" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(ValueError): cell = Cell(card) - cell.importance.neutron = 2.5 - self.assertEqual(cell.importance.neutron, 2.5) - problem = montepy.mcnp_problem.MCNP_Problem("foo") - cell.link_to_problem(problem) - # test problem mode enforcement - with self.assertRaises(ValueError): - cell.importance.photon = 1.0 - # test wrong type - with self.assertRaises(TypeError): - cell.importance.neutron = "h" - # test negative - with self.assertRaises(ValueError): - cell.importance.neutron = -0.5 - - cell.importance[Particle.NEUTRON] = 3 - self.assertEqual(cell.importance.neutron, 3.0) - with self.assertRaises(TypeError): - cell.importance[""] = 5 - with self.assertRaises(TypeError): - cell.importance[Particle.NEUTRON] = "" - with self.assertRaises(ValueError): - cell.importance[Particle.NEUTRON] = -1.0 - - def test_importance_deleter(self): - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - cell = Cell(card) - del cell.importance.neutron - self.assertAlmostEqual(cell.importance.neutron, 0.0) - del cell.importance[Particle.PHOTON] - self.assertAlmostEqual(cell.importance.photon, 0.0) - with self.assertRaises(TypeError): - del cell.importance[""] - - def test_importance_merge(self): - in_str = "IMP:N,P 1 0" - card = mcnp_input.Input([in_str], block_type.BlockType.DATA) - imp1 = Importance(card) - in_str = "IMP:E 0 0" - card = mcnp_input.Input([in_str], block_type.BlockType.DATA) - imp2 = Importance(card) + + +def test_importance_init_data(): + in_str = "IMP:N,P 1 0" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + imp = Importance(card) + assert [ + val.value for val in imp._particle_importances[Particle.NEUTRON]["data"] + ] == [1.0, 0.0] + assert [ + val.value for val in imp._particle_importances[Particle.PHOTON]["data"] + ] == [1.0, 0.0] + # test non-number imp + in_str = "IMP:N,P 1 h" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(ValueError): + imp = Importance(card) + # test negative + in_str = "IMP:N,P 1 -2" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(ValueError): + imp = Importance(card) + # test bad in_cell_block + in_str = "IMP:N,P 1 2" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(TypeError): + imp = Importance(card, in_cell_block=1) + # test bad key + in_str = "IMP:N,P 1 2" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(TypeError): + imp = Importance(card, key=1) + # test bad value + in_str = "IMP:N,P 1 2" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + with pytest.raises(TypeError): + imp = Importance(card, value=1) + + +def test_importance_iter_getter_in(): + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + imp = cell.importance + particles = [ + montepy.particle.Particle.NEUTRON, + montepy.particle.Particle.PHOTON, + ] + for particle in imp: + assert particle in particles + assert imp[particle] == 1.0 + for particle in particles: + assert particle in imp + with pytest.raises(TypeError): + imp["hi"] + + +def test_importance_all_setter(): + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + problem = montepy.mcnp_problem.MCNP_Problem("foo") + problem.mode.add(montepy.particle.Particle.NEUTRON) + problem.mode.add(montepy.particle.Particle.PHOTON) + imp = cell.importance + cell.link_to_problem(problem) + imp.all = 2.0 + assert imp.neutron == 2.0 + assert imp.photon == 2.0 + # try wrong type + with pytest.raises(TypeError): + imp.all = "h" + # try negative type + with pytest.raises(ValueError): + imp.all = -2.0 + + +def test_importance_setter(): + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + cell.importance.neutron = 2.5 + assert cell.importance.neutron == 2.5 + problem = montepy.mcnp_problem.MCNP_Problem("foo") + cell.link_to_problem(problem) + # test problem mode enforcement + with pytest.warns(ParticleTypeNotInProblem): + cell.importance.photon = 1.0 + # test wrong type + with pytest.raises(TypeError): + cell.importance.neutron = "h" + # test negative + with pytest.raises(ValueError): + cell.importance.neutron = -0.5 + + cell.importance[Particle.NEUTRON] = 3 + assert cell.importance.neutron == 3.0 + with pytest.raises(TypeError): + cell.importance[""] = 5 + with pytest.raises(TypeError): + cell.importance[Particle.NEUTRON] = "" + with pytest.raises(ValueError): + cell.importance[Particle.NEUTRON] = -1.0 + + +def test_importance_deleter(): + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + del cell.importance.neutron + assert cell.importance.neutron == 0.0 + del cell.importance[Particle.PHOTON] + assert cell.importance.photon == 0.0 + with pytest.raises(TypeError): + del cell.importance[""] + + +def test_importance_merge(): + in_str = "IMP:N,P 1 0" + card = mcnp_input.Input([in_str], block_type.BlockType.DATA) + imp1 = Importance(card) + in_str = "IMP:E 0 0" + card = mcnp_input.Input([in_str], block_type.BlockType.DATA) + imp2 = Importance(card) + imp1.merge(imp2) + assert [ + val.value for val in imp1._particle_importances[Particle.NEUTRON]["data"] + ] == [1.0, 0.0] + assert [ + val.value for val in imp1._particle_importances[Particle.ELECTRON]["data"] + ] == [0.0, 0.0] + # test bad type + with pytest.raises(TypeError): + imp1.merge("hi") + # test bad block type + in_str = "1 0 -1 IMP:N,P=1" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + cell = Cell(card) + with pytest.raises(ValueError): + imp1.merge(cell.importance) + in_str = "IMP:P 0 0" + card = mcnp_input.Input([in_str], block_type.BlockType.CELL) + imp2 = Importance(card) + with pytest.raises(MalformedInputError): imp1.merge(imp2) - self.assertEqual( - [val.value for val in imp1._particle_importances[Particle.NEUTRON]["data"]], - [1.0, 0.0], - ) - self.assertEqual( - [ - val.value - for val in imp1._particle_importances[Particle.ELECTRON]["data"] - ], - [0.0, 0.0], - ) - # test bad type - with self.assertRaises(TypeError): - imp1.merge("hi") - # test bad block type - in_str = "1 0 -1 IMP:N,P=1" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - cell = Cell(card) - with self.assertRaises(ValueError): - imp1.merge(cell.importance) - in_str = "IMP:P 0 0" - card = mcnp_input.Input([in_str], block_type.BlockType.CELL) - imp2 = Importance(card) - with self.assertRaises(MalformedInputError): - imp1.merge(imp2) - - def tests_redundant_importance(self): - with self.assertRaises(MalformedInputError): - montepy.read_input( - os.path.join("tests", "inputs", "test_imp_redundant.imcnp") - ) + + +def test_redundant_importance(): + with pytest.raises(MalformedInputError): + montepy.read_input(os.path.join("tests", "inputs", "test_imp_redundant.imcnp")) + + +def test_default_importance_not_implemented(): + prob = montepy.read_input(os.path.join("tests", "inputs", "test_not_imp.imcnp")) + prob.print_in_data_block["imp"] = True + with pytest.raises(NotImplementedError): + prob.write_problem(io.StringIO())