-
Notifications
You must be signed in to change notification settings - Fork 500
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
Cylindrical prism with optional filleted edges #2309
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a useful addition @yardasol. I have a few suggestions related mostly to documentation and code reuse/maintainability.
openmc/model/funcs.py
Outdated
boundary_type='transmission', | ||
upper_edge_radius=0., | ||
lower_edge_radius=0.): | ||
"""Get a finite cylindrical prism. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps more information here about the rounded edges. From reviewing the code, it seems that this option adds what I would call a fillet at each end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for fillet terminology
openmc/model/funcs.py
Outdated
args['a'] = r - upper_edge_radius | ||
args['b'] = upper_edge_radius | ||
args['c'] = upper_edge_radius | ||
args[axis + '0'] = origin[axcoord] + height/2 - upper_edge_radius | ||
tor_upper_max = tor(name='{} max'.format(axis), **args) | ||
|
||
axis_upper_min = plane(axis, 'upper min', height/2 + origin[axcoord] - upper_edge_radius) | ||
cyl_upper_min = cylinder(axis, 'upper min', x1, origin[axcoord1], x2, origin[axcoord2], r - upper_edge_radius) | ||
|
||
corners_upper = (+cyl_upper_min & +tor_upper_max & + axis_upper_min) | ||
|
||
if lower_edge_radius > 0.: | ||
args['a'] = r - lower_edge_radius | ||
args['b'] = lower_edge_radius | ||
args['c'] = lower_edge_radius | ||
args[axis + '0'] = origin[axcoord] - height/2 + lower_edge_radius | ||
tor_lower_min = tor(name='{} min'.format(axis), **args) | ||
|
||
axis_lower_max = plane(axis, 'lower max', origin[axcoord] - height/2 + lower_edge_radius) | ||
cyl_lower_min = cylinder(axis, 'lower min', x1, origin[axcoord1], x2, origin[axcoord2], r - lower_edge_radius) | ||
|
||
corners_lower = (+cyl_lower_min & +tor_lower_min & - axis_lower_max) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems as though there is an opportunity to reuse at least some of this code in a modular way (e.g. a function or loop) rather than cut/paste for the upper/lower. The benefit of this kind of reuse is for consistency/maintainability and not for performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this contribution @yardasol! In addition to the line comments, can you also add this function to the documentation in docs/source/pythonapi/model.rst
?
openmc/model/funcs.py
Outdated
cls = globals()['{}Plane'.format(axis.upper())] | ||
return cls(name='{} {}'.format(name, axis), | ||
boundary_type=boundary_type, | ||
**{axis + '0': value}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of globals() feels a little hacky. I'd rather we import openmc
and then get the object from that. Also, if you put the value of the axis first when creating the plane, it will simplify it a bit:
cls = globals()['{}Plane'.format(axis.upper())] | |
return cls(name='{} {}'.format(name, axis), | |
boundary_type=boundary_type, | |
**{axis + '0': value}) | |
cls = getattr(openmc, f'{axis.upper()}Plane') | |
return cls(value, name=f'{name} {axis}', | |
boundary_type=boundary_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The globals()
approach is used in rectangular_prism
and hexagonal_prism
so these should probably get changed as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few smalll comments from me for now, @yardasol. Sorry it's taken me a bit to get to.
One thought that came to mind is the case where the fillet radius is larger than the cylinder radius. This would result in a degenerate torus (a minor radius larger than the major radius). I think we can do transport on those (@paulromano would know for sure), but I'm curious about your thoughts on what the behavior of this function should be in that case. My initial feeling is that if the corner radius is greater than the cylinder radius, we should limit the fillet radius to the radius of the cylinder and display a warning.
tests/unit_tests/test_geometry.py
Outdated
5, | ||
origin=(-1,1,3)) | ||
# clear checks | ||
assert (-1, 1, 3) in cyl_prism # center |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be beneficial to use the openmc.lib.find_cell
method to check that the C++ code is also getting the correct result too since it's what will affect transport.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea!
openmc/model/funcs.py
Outdated
boundary_type='transmission', | ||
upper_edge_radius=0., | ||
lower_edge_radius=0.): | ||
"""Get a finite cylindrical prism. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for fillet terminology
Co-authored-by: Paul Romano <[email protected]>
typo fix in comment Co-authored-by: Patrick Shriwise <[email protected]>
Thanks for the reviews everyone! I believe I have addressed everyone's comments. @pshriwise I added the cell check via
I believe that I enforce the fillet radii must be less than the cylinder radius using the |
Ah, yeah so you do. Missed it. Sorry about that. |
tests/unit_tests/test_geometry.py
Outdated
(-2.4, 1, 0.51)]] | ||
|
||
openmc.lib.init() | ||
for prism, inside_points, outside_points in zip(prisms, inside_points, outside_points): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few things to address here:
- When
openmc.lib.init
is called, it will read the XML files and initialize OpenMC in memory, so any subsequent XML files that are generated will be ignored. Theinit
andfinalize
calls should be inside of the loop so OpenMC is started and shutdown for each prism being tested here. I'd even look into theopenmc.lib.run_in_memory()
context manager too to simplify this further. - The name
cell
is going to be undefined in the loop here. Might be good to return the cell created from themake_geometry
function. - Going to need a boundary condition on at least one surface in the geometry.
- Need to wrap the string passed to
match
inpytest.raises
in anre.escape
call to ask the regex matching not to treat the parentheses characters as a pattern. I've been hit by that one before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, got it working!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more ideas for streamlining the code. I confess some are based on speculation of how things work and incomplete expertise on my part, but looked like opportunities for simplifying things.
openmc/model/funcs.py
Outdated
# Handle rounded corners if given | ||
if upper_fillet_radius > 0. or lower_fillet_radius > 0.: | ||
# Get torus class corresponding to given axis | ||
tor = globals()['{}Torus'.format(axis.upper())] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note: this is outside my expertise, but based on design patterns....) don't you want to access this the same way you access planes and cylinders through the OpenMC
object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, can this be moved into your create_fillet
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and yes!
openmc/model/funcs.py
Outdated
cyl_name = 'upper_min' | ||
axial_bound = +plane(axis, 'upper min', coord) | ||
else: | ||
coord = origin[axcoord] - height / 2 + lower_fillet_radius |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
coord = origin[axcoord] - height / 2 + lower_fillet_radius | |
coord = origin[axcoord] - height / 2 + fillet_radius |
openmc/model/funcs.py
Outdated
if fillet_lower is not None and fillet_upper is not None: | ||
fillet = fillet_lower | fillet_upper | ||
elif fillet_lower is None: | ||
fillet = fillet_upper | ||
else: | ||
fillet = fillet_lower | ||
|
||
prism = prism & ~fillet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Speculation?) If neither fillet is defined then fillet
will be None
in this last operation. Is that geometry operation well defined if fillet is None
?
If it is.... can you just use the fillet_upper
and fillet_lower
without the above logic to see if they are defined?
if fillet_lower is not None and fillet_upper is not None: | |
fillet = fillet_lower | fillet_upper | |
elif fillet_lower is None: | |
fillet = fillet_upper | |
else: | |
fillet = fillet_lower | |
prism = prism & ~fillet | |
prism = prism & ~(fillet_upper | fillet_lower) |
Are all those operation well defined if one or both are None
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great question. My guess is that it doesn't work that way based on the documentation for the |
operator:
class Union(Region, MutableSequence):
r"""Union of two or more regions.
Instances of Union are generally created via the | operator applied to two
instances of :class:`openmc.Region`. This is illustrated in the following
example:
>>> s1 = openmc.ZPlane(z0=0.0)
>>> s2 = openmc.Sphere(r=637.1e6)
>>> type(-s2 | +s1)
<class 'openmc.region.Union'>
Instances of this class behave like a mutable sequence, e.g., they can be
indexed and have an append() method.
Parameters
----------
nodes : iterable of openmc.Region
but maybe @paulromano or @pshriwise can verify?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If neither fillet is defined then
fillet
will beNone
in this last operation. Is that geometry operation well defined iffillet is None
?
At least one fillet is guaranteed to exist because this code block is contained within an if statement that requires at least one of uppet_fillet_radius
and lower_fillet_radius
to be nonzero.
6715de2
to
e1d27a9
Compare
Co-authored-by: Paul Wilson <[email protected]>
@yardasol Now I'm realizing that we already have |
This is a good idea. I've been busy doing other things, which is why I added the "on hold" tag, but I'll be able to get back to this |
f450152
to
84d35a7
Compare
Finally got around to dealing with this one. @paulromano @gonuke @pshriwise |
@paulromano bump |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the long delay @yardasol! Thanks for refactoring this into the RightCircularCylinder class -- it's looking really good
Co-authored-by: Paul Romano <[email protected]>
080e742
to
e9cb99c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the quick updates @yardasol. Good to go!
@pshriwise do you have any further requests on this PR? |
No further requests from me 🚀 |
@pshriwise Thanks! need you to hit the approve button for this one to land |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @yardasol!
Co-authored-by: Paul Romano <[email protected]> Co-authored-by: Patrick Shriwise <[email protected]> Co-authored-by: Paul Wilson <[email protected]>
This PR adds a new function,
cylindrical_prism
, that creates a finite cylindrical prism region with the option to fillet the cylinder's edges