Skip to content

Make axial linking aware of block grids for axial expansion #2145

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

Open
wants to merge 48 commits into
base: mixed-pin-assemblies
Choose a base branch
from

Conversation

albeanth
Copy link
Member

@albeanth albeanth commented May 23, 2025

What is the change? Why is it being made?

This is the replacement for #1376.

What: This change enables the axial linking class supporting axial expansion to be aware of the grid blocks. This enables users to have assembly definitions that contain multiple pin-components (e.g., mixing fuel and reflector pins).

Why: Increases the capabilities of ARMI by allowing more assembly types to be modeled.

SCR Information

Change Type: features

One-Sentence Description: Enable multi-pin component support for axial expansion.

One-line Impact on Requirements: N/A


Checklist

@albeanth albeanth requested a review from drewj-tp May 23, 2025 19:47
Copy link
Member Author

@albeanth albeanth May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relevant changes to this file:

  1. simplified some of the dimensions and consistency with component temps.
  2. replaced the lead test fuel assembly with a mixed pin assembly, multi pin fuel.
  3. removed the feed fuel and lead test fuel b assemblies (I can't recall which, but some weren't used in the lattice map).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the lattice map to be consistent with the new assembly defs and added the block-grid.

Comment on lines +85 to +86
for newB in newAssem:
sourceB = sourceAssem.getBlockAtElevation(newB.p.z)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was needed because with the new blueprints def, the newAssem didn't have the same number of blocks as the sourceAssem.

Copy link
Member Author

@albeanth albeanth May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would LOVE to clean this up and split it into multiple files.... But I didn't want to make the changes so drastic is was harder to review than necessary

EDIT: I did split out axial linking testing into its own file. Was easier to manage.

@@ -117,8 +152,7 @@ class AxialLink(typing.Generic[Comp]):
* :attr:`AxialAssemblyLinkage.linkedComponents`
"""

lower: typing.Optional[Comp] = dataclasses.field(default=None)
upper: typing.Optional[Comp] = dataclasses.field(default=None)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So turns out, we never use upper and having it complicated the new linking. We can bring it back if you feel strongly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we weren't using it, probably seems we don't need it. I guess you would want to use it to understand what this component is pushing into and/or sending information into. But we still understand the overall linking based on every component knowing what's below it. Kind of list a singly linked list vs. doubly linked list

elif isinstance(componentA.spatialLocator, MultiIndexLocation) and isinstance(
componentB.spatialLocator, MultiIndexLocation
):
## Case 2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bet the stuff under this conditional could be made more elegant/pythonic... open to suggestions!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use a Set[tuple[int, int]] where each tuple is the ij location for that index? Then we can compare the sets directly. Maybe

fromA = set((index.i, index.j) for index in componentA.spatialLocator.indices)
fromB = set((index.i, index.j) for index in componentB.spatialLocator.indices)
if fromA == fromB:
    linked = _checkOverlap(componentA, componentB)
else:
    linked = False

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e40089a
much simpler actually matches what is intended -- all indices must mach. I should have looked closer here! I must have been trying to get fancy when I originally wrote this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sooooo I was actually mistaken. we do not want to always require a perfect match. I will make updates and ping you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very curious to find out why

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below is a snippet from the docs addition. If we want to keep this ability, then we need to be able to query for a partial match.
image

Put another way
image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conversely, we could not allow this and require exact matches. This might better represent reality as the pin stacks for fuel 1 and fuel 2 are likely different designs, so it makes less sense to say they share the same shield pin component.

Copy link
Contributor

@drewj-tp drewj-tp Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm okay, I would want to bring in people who'd be making the inputs. But I feel like a partial match is going to be more trouble than it's worth.

Especially as we look at updating things between linked components. That feels more prone to stumbling than if we enforce 1:1

Copy link
Member Author

@albeanth albeanth Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chatted with @mgjarrett and we will enforce 1:1 and split the shield component into two different components.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@albeanth
Copy link
Member Author

albeanth commented May 23, 2025

So requirements: I don't think this work has any effect on existing requirements. Nor do I necessarily think we need to add any new ones.

.. ## axialExpansionChanger ######################
.. req:: The axial expansion changer shall perform axial thermal expansion and contraction on solid components within a compatible ARMI assembly according to a given axial temperature distribution.
:id: R_ARMI_AXIAL_EXP_THERM
:subtype: functional
:basis: Axial expansion is used to conserve mass and appropriately capture the reactor state under temperature changes.
:acceptance_criteria: Perform thermal expansion due to an applied axial temperature distribution.
:status: accepted
.. req:: The axial expansion changer shall perform axial expansion/contraction given a list of components and corresponding expansion coefficients.
:id: R_ARMI_AXIAL_EXP_PRESC
:subtype: functional
:basis: Axial expansion is used to conserve mass and appropriately capture the reactor state under temperature changes.
:acceptance_criteria: Perform axial expansion given a list of components from an assembly and corresponding expansion coefficients.
:status: accepted
.. req:: The axial expansion changer shall perform expansion during core construction based on block heights at a user-specified temperature.
:id: R_ARMI_INP_COLD_HEIGHT
:subtype: functional
:basis: The typical workflow in ARMI applications is to transcribe component dimensions, which are generally given at room temperatures.
:acceptance_criteria: Perform axial expansion during core construction based on block heights at user-specified temperature.
:status: accepted
.. req:: The axial expansion changer shall allow user-specified target axial expansion components on a given block.
:id: R_ARMI_MANUAL_TARG_COMP
:subtype: functional
:basis: The target axial expansion component influences the conservation of mass in a block.
:acceptance_criteria: Set a target component and verify it was set correctly.
:status: accepted
.. req:: The axial expansion changer shall preserve the total height of a compatible ARMI assembly.
:id: R_ARMI_ASSEM_HEIGHT_PRES
:subtype: functional
:basis: Many physics solvers require that the total height of each assembly in the core is consistent.
:acceptance_criteria: Perform axial expansion and confirm that the height of the compatible ARMI assembly is preserved.
:status: accepted

We could possibly add unit tests to the acceptance testing, but I don't think it's necessary. Am open to discussion though.

@albeanth albeanth deleted the branch terrapower:mixed-pin-assemblies June 2, 2025 18:51
@albeanth albeanth closed this Jun 2, 2025
Copy link
Contributor

@drewj-tp drewj-tp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review. The implementation side of things give a lot of confidence. There's one big thing I think to resolve and test: exact match in indices or are we okay with a subset.

I am partially through reviewing the changes to testing and I'm liking what I'm seeing

Comment on lines 316 to 317
plenumVolume += math.pi * (cladId / 2.0) ** 2.0 * length * 1e-6
return plenumVolume
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Micro-opt: increment the squared radii for each clad component and then scale by pi * length * 1e-6 at the end

Copy link
Member Author

@albeanth albeanth Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

met ya halfway. I thought keeping the volume calc together (pi r^2 h) was nice for readability but did pull the unit conversion out to the end
386b8df

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the changes here seem to be related to #2142

Does the base branch need to be updated to track main?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be good now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the changes here seem to be f-string related changes. Possibly from #2146 // #2141

Can you update the base branch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be good now

"""Case 4; component type mismatch."""
compDims = ("test", "FakeMat", 25.0, 25.0) # name, material, Tinput, Thot
comp1 = Circle(*compDims, od=1.0, id=0.0)
comp2 = UnshapedComponent(*compDims, area=1.0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we do special logic for unshaped components, I would recommend making the second component a hexagon or something. Just so we don't have any overlap (pun intended) with case 1: unshaped components

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0080e09 good call

comp2.p.mult = 2
self.assertFalse(AssemblyAxialLinkage.areAxiallyLinked(comp1, comp2))

def test_multiIndexLocation(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request for a test that shows either

  1. Locations can be a partial match, or
  2. Locations cannot be a partial match

I'm inclined to think we want the latter (exact match in locations) but other comments/parts of the code have me unsure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are correct, we want the latter. They have to match exactly.

Comment on lines 191 to 196
if assertionBool:
self.assertTrue(_checkOverlap(typeA, typeB), msg=msg)
self.assertTrue(_checkOverlap(typeB, typeA), msg=msg)
else:
self.assertFalse(_checkOverlap(typeA, typeB), msg=msg)
self.assertFalse(_checkOverlap(typeB, typeA), msg=msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we made the assertion function an argument to the test? e.g., self.runTest(..., self.assertTrue)? Non-blocking discussion not really a request but it would collapse six lines to two without sacrificing readability in the later tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1241895 agreed totally


def runTest(
self,
componentsToTest: dict,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(sarcastic) come on, we can do better w/ the types!

I think dict[Type[Component], dict[str, float]] would be what we want

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 577a88a

@albeanth albeanth reopened this Jun 2, 2025
@albeanth albeanth requested a review from drewj-tp June 3, 2025 20:32
@albeanth albeanth force-pushed the newAxialExpLinking branch from c6c382c to e84c018 Compare June 5, 2025 15:20
@albeanth albeanth force-pushed the newAxialExpLinking branch from 64d91b5 to e694041 Compare June 5, 2025 16:26
@albeanth albeanth force-pushed the newAxialExpLinking branch 2 times, most recently from 45b9092 to ea6348b Compare June 5, 2025 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Smaller user request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants