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

HexBlock.rotate updates child spatial locators #1943

Merged
merged 66 commits into from
Oct 25, 2024

Conversation

drewj-tp
Copy link
Contributor

@drewj-tp drewj-tp commented Oct 10, 2024

What is the change?

Calls to HexBlock.rotate update the spatial locator on all child components. This is necessary because you'd expect rotation to move things in space.

Tests are added showing

  1. pin coordinates, as reported by Block.getPinCoordinates reflect the rotation
  2. location objects, reported by Component.spatialLocator reflect the rotation

Other changes were made in support of this functionality.

What about pinLocation

It still exists but is not modified at all during calls to HexBlock.rotate. Routines that were using pinLocation for a pre/post mapping should be okay, as evidenced by the lack of changes to any pinLocation related tests (e.g., setPinPowers). The mapping is transparent because it remains a list of one-indexed "pin locations" such that data[pinLocations[i] - 1] is still data[i] since pinLocations[i] == i + 1.

HexGrid.rotateIndex

To facilitate the rotation, I added HexGrid.rotateIndex to take in an index location, rotate it some number of 60-degree rotations, and return the new location. I drew a lot of information and support from https://www.redblobgames.com/grids/hexagons/ (hereafter RBG) that I'll try to synthesize here.

ARMI's hex grid indexing is very similar to the "axial" or "trapezoidal" coordinate system described in RBG. Some key differences

  1. We use i,j to define location whereas RBG uses q,r.
  2. In the RBG examples, their global Y direction is opposite ours, i.e., our grid is flipped along the x-axis. This means a clockwise rotation in our coordinate system is a counterclockwise rotation in theirs, and vice-versa.

Rotation of items on a hex grid is almost trivial using what RBG refers to as a cubic coordinate system, or where a point on the 2-D grid can be defined by three coordinates: q, r, s. The conversion between axial and cube just means computing s = -q - r.

60-degree rotation in this system is created by shuffling and negating the points. Following their examples, starting at location (q, r, s), increasing CW rotations would place a cell at

  1. (-r, -s, -q)
  2. (s, q, r)
  3. (-q, -r, -s)
  4. and so on.

After three rotations, we have gone 180 degrees so it tracks that our locations is essentially the same coordinates but opposite sign.

This is tested by rotating a point some amount, and comparing the XY coordinates in the cartesian plane according to a rotation matrix

[[ cos(theta), -sin(theta)]
 [ sin(theta),   cos(theta)]]

Block.getPinLocations for obtaining index locations

Might want to change the name to not use "location" because that feels confusing w/ getPinCoordinates. Maybe getPinIndices? getPinIndexLocations?

Anyway.  Some talk with internal stakeholders implied, to me, that mapping between ARMI's pin ordering and ordering in other applications would be more suited with integer indices rather than Cartesian XY coordinates. So Block.getPinLocations will produce all the IndexLocation objects for pins.

Block.getPinCoordinates returns numpy array

It felt odd to get a list of arrays? This should be back compatible for things expecting list of arrays, as the getPinCoordinates test was not updated to use (N, 3) array features until later

I guess not fully back compatible if you're doing pop/append/index operations on the coordinates... I can revert this change if we want to save it for later. Just made sense as a code-cleanup operation.

Why is the change being made?

Closes #1939

Personal Todo

  • Docs on rotation, and how to find where data like linPowByPin exists in space
  • [ ] Remove usage of pinLocation in pin data setters like setPinPowers (could be follow on since it still works, it's just unnecessary) Not necessary at this time

Checklist

  • The release notes have been updated if necessary.
  • The documentation is still up-to-date in the doc folder.
  • The dependencies are still up-to-date in pyproject.toml.

Add requirement test and implementation crumbs
Most methods seem to have their own copying in place so let them
do that per test. But create the entire block once, then let deepcopy create the unique copies.
Otherwise an error is raised trying to get local coordinates
on a pin-like component when the grid is auto-created
Maybe it's left out to avoid pre-populating sites that aren't needed?
But when we're creating a spatial grid on the assumption we have a full
grid of things, why not pass in the number of rings we expect
automatically?
There were some errors raised in the invocation of _evaluateMesh
only expecting a single entry, errors like
```
    result[self._stepDims] = stepCoords
    ValueError: shape mismatch: value array of shape (3,3) could not be broadcast to indexing result of shape (3,)
```
Now, `MultiIndex.getLocationCoordinates` will produce the local coords for everything
contained in the index.

And a test is added. so that's good!
Rely on a cubic coordinate system for rotation. Yes, that's confusing
because we're hexagons. It's really a 3-d indexing such that
``(q, r, s) = (i, j, -(i + j))``. Rotation is much simpler here because
we can shift these values left once and negate them for each counter clockwise
rotation. So, starting at ``(q, r, s)``, the new "cubic" coordinates
after increasing rotations would be

1. ``(-r, -s, -q)``
2. ``(s, q, r)``
3. ``(-q, -r, -s)``

and so on.

This draws on the glorious https://www.redblobgames.com/grids/hexagons/
page for guidance. There are minor deviations that are worth calling
out here

1. ARMI orientation is referred to as axial coorinates (aka trapezoidal
   or oblique or skewed)
2. ARMI orientation is flipped across the x-axis in their examples. So
   our +y global cartesian vector is their -y global cartesian vector.
   The real difference is our CCW rotation is their CW rotation because
   of this flip.
Compare the local coordinates with a translation matrix to show
we get similar results.
Some applications may find integer indices easier to work with than
xyz coordinates.

getPinCoords is an array because a list of arrays is inefficient.
Need to specially consider CoordinateLocation objects but should be nearly there.
@drewj-tp drewj-tp self-assigned this Oct 10, 2024
It doesn't matter how many times we rotate the center location, we
should get the center
Unsure if we should then override the grid and set the grid of the new
object to be `self`. For now, let it ride
* origin/main:
  Updating cluster settings (#1958)
  Reducing warnings while building the docs (#1959)
  Moving anl-afci-177 test files to their own directory (#1957)
@drewj-tp
Copy link
Contributor Author

Had some downstream differences that I couldn't quite square until I removed

  1. Changes to HexBlock.autoCreateSpatialGrids
  2. Implementation of MultiIndexLocation.getLocalCoordinates
    • Maybe the "bug" I thought I found was a product of a bad test configuration? For whatever reason, I'm not hitting a potentially related problem that caused me to look at this method

@drewj-tp drewj-tp marked this pull request as ready for review October 21, 2024 20:52
* main:
  Revert "Updating cluster settings (#1958)" (#1965)
drewj-tp and others added 2 commits October 22, 2024 12:48
Co-authored-by: John Stilley <1831479+john-science@users.noreply.github.com>
drewj-tp and others added 4 commits October 23, 2024 09:40
* main:
  Changing default of syncDbAfterWrite to True (#1968)
  Removing upper bound pin on HDF5 (#1932)
* main:
  Renaming Database3 to Database, but preserving the API (#1961)
* origin/main:
  Ensuring reaction rate parameters are set after calc (#1971)
@john-science john-science merged commit 2743df2 into main Oct 25, 2024
19 checks passed
@john-science john-science deleted the drewj/block-rotate/1939 branch October 25, 2024 20:17
drewj-tp added a commit that referenced this pull request Oct 29, 2024
* main:
  Fixing HexBlock docstrings (#1981)
  Removing a duplicate hex rotation impl tag (#1979)
  Avoiding closing plots that are meant to be interactive (#1978)
  Ensuring HexBlock.rotate updates child spatial locators (#1943)
  Add new memory runLog info to memoryProfiler.py  (#1970)
  Fixing various doc build issues (#1974)
  Hiding sphinx-needs warnings during doc build (#1973)
  Fixing warnings in CLI startup (#1972)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update spatial locations after HexBlock.rotate
3 participants