Fix bug when objects start in a positive halo

Objects with a thickness (triangles and circular sectors) can no longer
extend beyond the grid in the dimension they are aligned to.

Building these objects where the lower extent is aligned with a positive
halo, no longer corrupts memory due to out of bounds memory access
这个提交包含在:
Nathan Mannall
2025-02-21 18:29:11 +00:00
父节点 aecf08f269
当前提交 e9bc7d3978
共有 4 个文件被更改,包括 89 次插入57 次删除

查看文件

@@ -140,6 +140,9 @@ class UserInput(Generic[GridType]):
""" """
return self.discretise_point(point) * self.grid.dl return self.discretise_point(point) * self.grid.dl
def discrete_to_continuous(self, point: npt.NDArray[np.int32]) -> npt.NDArray[np.float64]:
return point * self.grid.dl
class MainGridUserInput(UserInput[GridType]): class MainGridUserInput(UserInput[GridType]):
"""Handles (x, y, z) points supplied by the user in the main grid.""" """Handles (x, y, z) points supplied by the user in the main grid."""
@@ -199,30 +202,29 @@ class MainGridUserInput(UserInput[GridType]):
p2: Tuple[float, float, float], p2: Tuple[float, float, float],
p3: Tuple[float, float, float], p3: Tuple[float, float, float],
cmd_str: str, cmd_str: str,
) -> Tuple[bool, npt.NDArray[np.int32], npt.NDArray[np.int32], npt.NDArray[np.int32]]: ) -> Tuple[npt.NDArray[np.int32], npt.NDArray[np.int32], npt.NDArray[np.int32]]:
p1_within_grid, p1_checked = self.check_point(p1, cmd_str, name="vertex_1") # We only care if the point are in the global grid (an error
p2_within_grid, p2_checked = self.check_point(p2, cmd_str, name="vertex_2") # will be thrown if that is not the case).
p3_within_grid, p3_checked = self.check_point(p3, cmd_str, name="vertex_3") _, p1_checked = self.check_point(p1, cmd_str, name="vertex_1")
_, p2_checked = self.check_point(p2, cmd_str, name="vertex_2")
_, p3_checked = self.check_point(p3, cmd_str, name="vertex_3")
return ( return p1_checked, p2_checked, p3_checked
p1_within_grid and p2_within_grid and p3_within_grid,
p1_checked,
p2_checked,
p3_checked,
)
def check_thickness( def check_thickness(
self, dimension: str, start: float, thickness: float self,
dimension: str,
lower_extent: float,
thickness: float,
cmd_str: str,
) -> Tuple[bool, float, float]: ) -> Tuple[bool, float, float]:
"""Check the thickness of an object in a specified dimension. """Check the thickness of an object in a specified dimension.
Args: Args:
dimension: Dimension to check the thickness value for. dimension: Dimension to check the thickness value for.
This must have value x, y, or z. This must have value x, y, or z.
start: Start coordinate of the object in the specified lower_extent: Lower extent of the object in the specified
dimension. This must be in the local grid coordinate dimension.
system - i.e. previously translated using the
round_to_grid function.
thickness: Thickness of the object. thickness: Thickness of the object.
Raises: Raises:
@@ -231,35 +233,71 @@ class MainGridUserInput(UserInput[GridType]):
Returns: Returns:
within_grid: True if part of the object is within the within_grid: True if part of the object is within the
current grid. False otherwise. current grid. False otherwise.
start: Start value limited to the bounds of the grid. lower_extent: Lower extent limited to the bounds of the
thickness: Thickness value such that start + thickness is grid.
within the bounds of the grid. thickness: Thickness value such that lower_extent +
thickness is within the bounds of the grid.
""" """
# TODO: This should throw an error if the bounds are outside the if thickness < 0:
# global grid. raise ValueError(f"'{cmd_str}' requires a non negative thickness")
if lower_extent < 0:
raise ValueError(
f"'{cmd_str}' lower extent should be non negative in the {dimension} dimension"
)
upper_extent = lower_extent + thickness
if dimension == "x": if dimension == "x":
grid_size = self.grid.nx * self.grid.dx lower_point = self.discretise_point((lower_extent, 0, 0))
upper_point = self.discretise_point((upper_extent, 0, 0))
thickness_point = self.discretise_static_point((thickness, 0, 0))
index = 0
elif dimension == "y": elif dimension == "y":
grid_size = self.grid.ny * self.grid.dy lower_point = self.discretise_point((0, lower_extent, 0))
upper_point = self.discretise_point((0, upper_extent, 0))
thickness_point = self.discretise_static_point((0, thickness, 0))
index = 1
elif dimension == "z": elif dimension == "z":
grid_size = self.grid.nz * self.grid.dz lower_point = self.discretise_point((0, 0, lower_extent))
upper_point = self.discretise_point((0, 0, upper_extent))
thickness_point = self.discretise_static_point((0, 0, thickness))
index = 2
else: else:
raise ValueError("Dimension should have value x, y, or z") raise ValueError("Dimension should have value x, y, or z")
end = start + thickness try:
self.grid.within_bounds(upper_point)
except ValueError:
raise ValueError(
f"'{cmd_str}' extends beyond the size of the model in the {dimension} dimension"
)
if start > grid_size: # Work with discretised (int) values as reduces imprecision due
return False, start, thickness # to floating point calculations
elif end < 0: size = self.grid.size[index]
return False, start, thickness lower_extent = lower_point[index]
upper_extent = upper_point[index]
thickness = thickness_point[index]
if start < 0: # These should only trigger for MPIGrids.
thickness += start # TODO: Can this be structured so these checks happen in the
start = 0 # MPIGridUserInput object?
if end > grid_size: if lower_extent < 0:
thickness -= end - grid_size thickness += lower_extent
lower_extent = 0
if upper_extent > size:
thickness -= upper_extent - size
return True, start, thickness dl = self.grid.dl[index]
return (
lower_extent <= size
and upper_extent >= 0
and not (upper_extent > size and thickness <= 0),
lower_extent * dl,
thickness * dl,
)
class MPIUserInput(MainGridUserInput[MPIGrid]): class MPIUserInput(MainGridUserInput[MPIGrid]):

查看文件

@@ -90,7 +90,9 @@ class CylindricalSector(GeometryUserObject):
elif normal == "z": elif normal == "z":
ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1)) ctr1, ctr2, level = uip.round_to_grid((ctr1, ctr2, extent1))
sector_within_grid, level, thickness = uip.check_thickness(normal, level, thickness) sector_within_grid, level, thickness = uip.check_thickness(
normal, extent1, thickness, self.__str__()
)
# Exit early if none of the cylindrical sector is in this grid # Exit early if none of the cylindrical sector is in this grid
# as there is nothing else to do. # as there is nothing else to do.

查看文件

@@ -96,37 +96,35 @@ class Triangle(RotatableMixin, GeometryUserObject):
logger.exception(f"{self.__str__()} no materials have been specified") logger.exception(f"{self.__str__()} no materials have been specified")
raise raise
if thickness < 0:
logger.exception(f"{self.__str__()} requires a positive value for thickness")
raise ValueError
uip = self._create_uip(grid) uip = self._create_uip(grid)
# Check whether points are valid against grid # Check whether points are valid against grid
uip.check_tri_points(up1, up2, up3, self.__str__()) dp1, dp2, dp3 = uip.check_tri_points(up1, up2, up3, self.__str__())
# Convert points to metres # Convert points to metres
x1, y1, z1 = uip.round_to_grid(up1) x1, y1, z1 = uip.discrete_to_continuous(dp1)
x2, y2, z2 = uip.round_to_grid(up2) x2, y2, z2 = uip.discrete_to_continuous(dp2)
x3, y3, z3 = uip.round_to_grid(up3) x3, y3, z3 = uip.discrete_to_continuous(dp3)
# Check for valid orientations # Check for valid orientations
# yz-plane triangle # yz-plane triangle
if x1 == x2 == x3: if x1 == x2 == x3:
normal = "x" normal = "x"
start = x1 lower_extent = up1[0]
# xz-plane triangle # xz-plane triangle
elif y1 == y2 == y3: elif y1 == y2 == y3:
normal = "y" normal = "y"
start = y1 lower_extent = up1[1]
# xy-plane triangle # xy-plane triangle
elif z1 == z2 == z3: elif z1 == z2 == z3:
normal = "z" normal = "z"
start = z1 lower_extent = up1[2]
else: else:
logger.exception(f"{self.__str__()} the triangle is not specified correctly") logger.exception(f"{self.__str__()} the triangle is not specified correctly")
raise ValueError raise ValueError
triangle_within_grid, start, thickness = uip.check_thickness(normal, start, thickness) triangle_within_grid, lower_extent, thickness = uip.check_thickness(
normal, lower_extent, thickness, self.__str__()
)
# Exit early if none of the triangle is in this grid as there is # Exit early if none of the triangle is in this grid as there is
# nothing else to do. # nothing else to do.
@@ -136,19 +134,13 @@ class Triangle(RotatableMixin, GeometryUserObject):
# Update start bound of the triangle # Update start bound of the triangle
# yz-plane triangle # yz-plane triangle
if normal == "x": if normal == "x":
x1 = start x1 = x2 = x3 = lower_extent
x2 = start
x3 = start
# xz-plane triangle # xz-plane triangle
elif normal == "y": elif normal == "y":
y1 = start y1 = y2 = y3 = lower_extent
y2 = start
y3 = start
# xy-plane triangle # xy-plane triangle
elif normal == "z": elif normal == "z":
z1 = start z1 = z2 = z3 = lower_extent
z2 = start
z3 = start
# Look up requested materials in existing list of material instances # Look up requested materials in existing list of material instances
materials = [y for x in materialsrequested for y in grid.materials if y.ID == x] materials = [y for x in materialsrequested for y in grid.materials if y.ID == x]

查看文件

@@ -8,6 +8,6 @@
#rx: 0.080 0.080 0.080 #rx: 0.080 0.080 0.080
#material: 8 0 1 0 half_space #material: 8 0 1 0 half_space
#cylindrical_sector: z 0.07 0.06 0.04 0.12 0.05 30 240 half_space #cylindrical_sector: z 0.07 0.06 0.04 0.1 0.05 30 240 half_space
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume #geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume