Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create an ab initio parse method to quickly make new objects from an MCNP string. #595

Merged
merged 46 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3883295
Made all objects parsable from a bare string.
MicahGale Nov 22, 2024
013d15a
Updated tests to test using a str init.
MicahGale Nov 22, 2024
2c5350b
Fixed case of overriding subclass var.
MicahGale Nov 22, 2024
1772873
Added number parameter to all numbered mcnp objects.
MicahGale Nov 22, 2024
e2a7079
added universal parse method.
MicahGale Nov 22, 2024
0009c78
Test number init.
MicahGale Nov 22, 2024
d3d1acc
Fixed various bugs with how default numbers are loaded.
MicahGale Nov 22, 2024
350accd
Added #88 to changelog.
MicahGale Nov 22, 2024
c8b5187
Tested parse.
MicahGale Nov 22, 2024
a2d6081
Actually appended the objects to self.
MicahGale Nov 22, 2024
c8dd2e4
Updated starting guide to use number constructor.
MicahGale Nov 22, 2024
085cc3f
test type enforcement.
MicahGale Nov 22, 2024
fb68afb
Documented how to use parse.
MicahGale Nov 22, 2024
cb17eac
Formatted with black.
MicahGale Nov 22, 2024
a194a45
Fixed typo in doctest.
MicahGale Nov 22, 2024
5fb2b83
Merge branch 'alpha-test-dev' into parse_method
MicahGale Dec 5, 2024
813d35f
Post-merge black formatting.
MicahGale Dec 5, 2024
eba0d1d
Fixed name errors.
MicahGale Dec 5, 2024
403fa45
Made lazy pretty print, and abandoned parens.
MicahGale Dec 6, 2024
a1897ba
Made sure mat number is never padded.
MicahGale Dec 6, 2024
624cbd8
Switched positional to keyword arg.
MicahGale Dec 6, 2024
2cf7f2a
Tested pretty str.
MicahGale Dec 6, 2024
eca2d48
Handled more pretty str edge cases.
MicahGale Dec 6, 2024
c54e5bb
Merge branch 'alpha-test-dev' into parse_method
MicahGale Dec 12, 2024
2d92f36
Updated changelog to point to issue and not PR.
MicahGale Dec 14, 2024
7dcbbd7
Removed errant doc string.
MicahGale Dec 14, 2024
5f83b97
Updated all object init to use a type alias hint.
MicahGale Dec 14, 2024
e855ca2
Updated typeerror with more guidance.
MicahGale Dec 14, 2024
09cf9f9
Py39 can't check isisntance of a Union type.
MicahGale Dec 15, 2024
39bdd02
Merge branch 'develop' into parse_method
MicahGale Dec 15, 2024
736851a
Fixed typo with pyproject from merge.
MicahGale Dec 15, 2024
74f9af9
Merge branch 'alpha-test-dev' into parse_method
MicahGale Dec 16, 2024
0c21e86
Merge branch 'alpha-test-dev' into parse_method
MicahGale Dec 16, 2024
52ae985
Merge branch 'alpha-test-dev' into parse_method
MicahGale Jan 6, 2025
f418865
Merge branch 'alpha-test-dev' into parse_method
MicahGale Jan 9, 2025
9556f84
Fixed circular import.
MicahGale Jan 11, 2025
7f18ee7
Added append option to MCNP_Problem.parse.
MicahGale Jan 11, 2025
ffcb68d
Tested parse append.
MicahGale Jan 11, 2025
6b9baf7
Promoted parsing functions to be top level functions.
MicahGale Jan 11, 2025
565d19c
Made parse_surface function name more pythonic.
MicahGale Jan 11, 2025
e76db62
hid pretty_str as it's not ready yet.
MicahGale Jan 11, 2025
2663481
Added demo of parse functions.
MicahGale Jan 11, 2025
843780d
Added demo of append option.
MicahGale Jan 11, 2025
8b184de
Updated tests for pretty_str change.
MicahGale Jan 11, 2025
c8abb71
Updated references to deprecated surface_builder.
MicahGale Jan 11, 2025
4838a86
Removed all version change markers for 0.2.0
MicahGale Jan 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
MontePy Changelog
*****************

1.0.0 releases
==============
1.0 releases
============

#Next Version#
--------------

**Features Added**

* Redesigned how Materials hold Material_Components. See :ref:`migrate 0 1` (:pull:`507`).

* Made it easier to create an Isotope, or now Nuclide: ``montepy.Nuclide("H-1.80c")`` (:issue:`505`).
* When a typo in an object attribute is made an Error is raised rather than silently having no effect (:issue:`508`).
* Improved material printing to avoid very long lists of components (:issue:`144`).
Expand All @@ -22,6 +21,8 @@ MontePy Changelog
* Made ``Material.is_atom_fraction`` settable (:issue:`511`).
* Made NumberedObjectCollections act like a set (:issue:`138`).
* Automatically added children objects, e.g., the surfaces in a cell, to the problem when the cell is added to the problem (:issue:`63`).
* Added ability to parse all MCNP objects from a string (:pull:`595`).
* Added function: :func:`~montepy.mcnp_problem.MCNP_Problem.parse` to parse arbitrary MCNP object (:pull:`595`).
MicahGale marked this conversation as resolved.
Show resolved Hide resolved

**Bugs Fixed**

Expand Down
85 changes: 54 additions & 31 deletions doc/source/starting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,7 @@ The ``NumberedObjectCollection`` has various mechanisms internally to avoid numb

import montepy
prob = montepy.read_input("tests/inputs/test.imcnp")
cell = montepy.Cell()
cell.number = 2
cell = montepy.Cell(number = 2)
prob.cells.append(cell)

.. testoutput::
Expand Down Expand Up @@ -334,21 +333,23 @@ Using the generators in this way does not cause any issues, but there are ways t
by making "stale" information.
This can be done by making a copy of it with ``list()``.

>>> for num in problem.cells.numbers:
... print(num)
1
2
3
99
5
>>> numbers = list(problem.cells.numbers)
>>> numbers
[1, 2, 3, 99, 5]
>>> problem.cells[1].number = 1000
>>> 1000 in problem.cells.numbers
True
>>> 1000 in numbers
False
.. doctest::

>>> for num in problem.cells.numbers:
... print(num)
1
2
3
99
5
>>> numbers = list(problem.cells.numbers)
>>> numbers
[1, 2, 3, 99, 5]
>>> problem.cells[1].number = 1000
>>> 1000 in problem.cells.numbers
True
>>> 1000 in numbers
False

Oh no! When we made a list of the numbers we broke the link, and the new list won't update when the numbers of the cells change,
and you can cause issues this way.
Expand All @@ -364,6 +365,35 @@ If a ``Cell`` or a group of ``Cells`` are cloned their numbers will be to change
However, if a whole :class:`~montepy.mcnp_problem.MCNP_Problem` is cloned these objects will not have their numbers changed.
For an example for how to clone a numbered object see :ref:`Cloning a Cell`.

Creating Objects from a String
MicahGale marked this conversation as resolved.
Show resolved Hide resolved
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Sometimes its more convenient to create an MCNP object from its input string for MCNP, rather than setting a lot of properties,
or the object you need isn't supported by MontePy yet.
In this case there are a few ways to generate this object.
First all :class:`~montepy.mcnp_object.MCNP_Object` constructors can take a string:

.. doctest::

>>> cell = montepy.Cell("1 0 -2 imp:n=1")
>>> cell.number
1
>>> cell.importance[montepy.Particle.NEUTRON]
1.0

This object is still unlinked from other objects, and won't be kept with a problem.
So there is also :func:`~montepy.mcnp_problem.MCNP_Problem.parse`.
This takes a string, and then creates the MCNP object,
links it to the problem,
links it to its other objects (e.g., surfaces, materials, etc.),
and appends it to necessary collections:

.. testcode::

cell = problem.parse("123 0 -1005")
assert cell in problem.cells
assert cell.surfaces[1005] is problem.surfaces[1005]

Surfaces
--------

Expand Down Expand Up @@ -590,24 +620,18 @@ Order of precedence and grouping is automatically handled by Python so you can e
.. testcode::

# build blank surfaces
bottom_plane = montepy.AxisPlane()
bottom_plane = montepy.AxisPlane(number=1)
bottom_plane.location = 0.0
top_plane = montepy.AxisPlane()
top_plane = montepy.AxisPlane(number=2)
top_plane.location = 10.0
fuel_cylinder = montepy.CylinderOnAxis()
fuel_cylinder = montepy.CylinderOnAxis(number=3)
fuel_cylinder.radius = 1.26 / 2
clad_cylinder = montepy.CylinderOnAxis()
clad_cylinder = montepy.CylinderOnAxis( number=4)
clad_cylinder.radius = (1.26 / 2) + 1e-3 # fuel, gap, cladding
clad_od = montepy.surfaces.CylinderOnAxis()
clad_od = montepy.CylinderOnAxis(number=5)
clad_od.radius = clad_cylinder.radius + 0.1 # add thickness
other_fuel = montepy.surfaces.CylinderOnAxis()
other_fuel = montepy.CylinderOnAxis(number=6)
other_fuel.radius = 3.0
other_fuel.number = 10
bottom_plane.number = 1
top_plane.number = 2
fuel_cylinder.number = 3
clad_cylinder.number = 4
clad_od.number = 5

#make weird truncated fuel sample
slug_half_space = +bottom_plane & -top_plane & -fuel_cylinder
Expand Down Expand Up @@ -802,8 +826,7 @@ You can also easy apply a transform to the filling universe with:
.. testcode::

import numpy as np
transform = montepy.data_inputs.transform.Transform()
transform.number = 5
transform = montepy.data_inputs.transform.Transform(number=5)
transform.displacement_vector = np.array([1, 2, 0])
cell.fill.transform = transform

Expand Down
1 change: 1 addition & 0 deletions montepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
You will receive an MCNP_Problem object that you will interact with.
"""

from . import data_inputs
from . import input_parser
from . import constants
import importlib.metadata
Expand Down
32 changes: 21 additions & 11 deletions montepy/cell.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# 2024, Battelle Energy Alliance, LLC All Rights Reserved.
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
from __future__ import annotations

import copy
import itertools
import numbers
from typing import Union

import montepy
from montepy.cells import Cells
from montepy.constants import BLANK_SPACE_CONTINUE
from montepy.data_inputs import importance, fill, lattice_input, universe_input, volume
Expand Down Expand Up @@ -64,15 +65,20 @@ class Cell(Numbered_MCNP_Object):

complement = ~cell


.. seealso::

* :manual63sec:`5.2`
* :manual62:`55`

:param input: the input for the cell definition
:type input: Input
.. versionchanged:: 1.0.0

Added number parameter


:param input: The Input syntax object this will wrap and parse.
:type input: Union[Input, str]
:param number: The number to set for this object.
:type number: int
"""

_ALLOWED_KEYWORDS = {
Expand Down Expand Up @@ -105,7 +111,12 @@ class Cell(Numbered_MCNP_Object):

_parser = CellParser()

def __init__(self, input: montepy.input_parser.mcnp_input.Input = None):
def __init__(
self,
input: Union[montepy.input_parser.mcnp_input.Input, str] = None,
number: int = None,
):
self._BLOCK_TYPE = montepy.input_parser.block_type.BlockType.CELL
self._CHILD_OBJ_MAP = {
"material": Material,
"surfaces": Surface,
Expand All @@ -119,10 +130,9 @@ def __init__(self, input: montepy.input_parser.mcnp_input.Input = None):
self._density_node = self._generate_default_node(float, None)
self._surfaces = Surfaces()
self._complements = Cells()
self._number = self._generate_default_node(int, -1)
super().__init__(input, self._parser)
super().__init__(input, self._parser, number)
if not input:
self._generate_default_tree()
self._generate_default_tree(number)
self._old_number = copy.deepcopy(self._tree["cell_num"])
self._number = self._tree["cell_num"]
mat_tree = self._tree["material"]
Expand Down Expand Up @@ -607,7 +617,7 @@ def _update_values(self):
for input_class, (attr, _) in self._INPUTS_TO_PROPERTY.items():
getattr(self, attr)._update_values()

def _generate_default_tree(self):
def _generate_default_tree(self, number: int = None):
material = syntax_node.SyntaxNode(
"material",
{
Expand All @@ -619,7 +629,7 @@ def _generate_default_tree(self):
self._tree = syntax_node.SyntaxNode(
"cell",
{
"cell_num": self._generate_default_node(int, None),
"cell_num": self._generate_default_node(int, number),
"material": material,
"geometry": None,
"parameters": syntax_node.ParametersNode(),
Expand Down
24 changes: 19 additions & 5 deletions montepy/data_inputs/data_input.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
from __future__ import annotations
from abc import abstractmethod
import copy

Expand All @@ -14,6 +15,7 @@
from montepy.mcnp_object import MCNP_Object

import re
from typing import Union


class _ClassifierInput(Input):
Expand Down Expand Up @@ -43,7 +45,7 @@ class DataInputAbstract(MCNP_Object):
Parent class to describe all MCNP data inputs.

:param input: the Input object representing this data input
:type input: Input
:type input: Union[Input, str]
:param fast_parse: Whether or not to only parse the first word for the type of data.
:type fast_parse: bool
"""
Expand All @@ -52,17 +54,29 @@ class DataInputAbstract(MCNP_Object):

_classifier_parser = ClassifierParser()

def __init__(self, input=None, fast_parse=False):
def __init__(
self,
input: Union[montepy.input_parser.mcnp_input.Input, str] = None,
MicahGale marked this conversation as resolved.
Show resolved Hide resolved
fast_parse=False,
):
self._particles = None
if not fast_parse:
super().__init__(input, self._parser)
if input:
self.__split_name(input)
else:
input = copy.copy(input)
input.__class__ = _ClassifierInput
if input:
if isinstance(input, str):
input = _ClassifierInput(
input.split("\n"),
montepy.input_parser.block_type.BlockType.DATA,
)
else:
input = copy.copy(input)
input.__class__ = _ClassifierInput
super().__init__(input, self._classifier_parser)
self.__split_name(input)
if input:
self.__split_name(input)

@staticmethod
@abstractmethod
Expand Down
30 changes: 20 additions & 10 deletions montepy/data_inputs/material.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
from __future__ import annotations

import copy
import collections as co
import copy
import itertools
import math
import re
from typing import Generator, Union
import warnings
import weakref

import montepy
from montepy.data_inputs import data_input, thermal_scattering
from montepy.data_inputs.nuclide import Library, Nucleus, Nuclide
from montepy.data_inputs.element import Element
Expand All @@ -19,10 +21,6 @@
from montepy.errors import *
from montepy.utilities import *
from montepy.particle import LibraryType
import montepy

import re
import warnings


MAX_PRINT_ELEMENTS: int = 5
Expand Down Expand Up @@ -265,6 +263,7 @@ class Material(data_input.DataInputAbstract, Numbered_MCNP_Object):
80p
00c

.. versionchanged:: 1.0.0

.. seealso::

Expand All @@ -273,15 +272,24 @@ class Material(data_input.DataInputAbstract, Numbered_MCNP_Object):

.. versionchanged:: 1.0.0

This was the primary change for this release. For more details on what changed see :ref:`migrate 0 1`.
* Added number parameter
* This was the primary change for this release. For more details on what changed see :ref:`migrate 0 1`.

:param input: the input that contains the data for this material
:type input: Input
:param input: The Input syntax object this will wrap and parse.
:type input: Union[Input, str]
:param parser: The parser object to parse the input with.
:type parser: MCNP_Parser
:param number: The number to set for this object.
:type number: int
"""

_parser = MaterialParser()

def __init__(self, input: montepy.input_parser.mcnp_input.Input = None):
def __init__(
self,
input: Union[montepy.input_parser.mcnp_input.Input, str] = None,
number: int = None,
):
self._components = []
self._thermal_scattering = None
self._is_atom_fraction = True
Expand All @@ -291,6 +299,7 @@ def __init__(self, input: montepy.input_parser.mcnp_input.Input = None):
self._nuclei = set()
self._default_libs = _DefaultLibraries(self)
super().__init__(input)
self._load_init_num(number)
if input:
num = self._input_number
self._old_number = copy.deepcopy(num)
Expand Down Expand Up @@ -342,6 +351,7 @@ def _grab_default(self, param: syntax_node.SyntaxNode):
def _create_default_tree(self):
classifier = syntax_node.ClassifierNode()
classifier.number = self._number
classifier.number.never_pad = True
classifier.prefix = syntax_node.ValueNode("M", str, never_pad=True)
classifier.padding = syntax_node.PaddingNode(" ")
mats = syntax_node.MaterialsNode("mat stuff")
Expand Down
Loading
Loading