From 7980792edaa473037da6e856f83cf5923882ea0c Mon Sep 17 00:00:00 2001 From: Nejc Jurkovic Date: Sun, 17 Nov 2024 22:55:36 +0100 Subject: [PATCH] Fix WireInfo.starts_at_wall (and ends) --- examples/advanced/inflation_grader.py | 2 +- .../grading/autograding/params/inflation.py | 17 ++--- .../grading/autograding/probe.py | 32 +++++++--- src/classy_blocks/items/block.py | 5 +- src/classy_blocks/items/wires/wire.py | 2 +- src/classy_blocks/util/constants.py | 10 ++- tests/test_grading/test_autograde.py | 4 ++ tests/test_grading/test_probe.py | 62 +++++++++++++++++++ 8 files changed, 112 insertions(+), 22 deletions(-) diff --git a/examples/advanced/inflation_grader.py b/examples/advanced/inflation_grader.py index f1f6b783..be91f037 100644 --- a/examples/advanced/inflation_grader.py +++ b/examples/advanced/inflation_grader.py @@ -35,7 +35,7 @@ # TODO: Hack! mesh.assemble() won't work here but wires et. al. must be updated mesh.block_list.update() -grader = InflationGrader(mesh, 1e-3, 0.1) +grader = InflationGrader(mesh, 1e-2, 0.1) grader.grade() mesh.write(os.path.join("..", "case", "system", "blockMeshDict"), debug_path="debug.vtk") diff --git a/src/classy_blocks/grading/autograding/params/inflation.py b/src/classy_blocks/grading/autograding/params/inflation.py index b04120f5..e45f95b6 100644 --- a/src/classy_blocks/grading/autograding/params/inflation.py +++ b/src/classy_blocks/grading/autograding/params/inflation.py @@ -35,10 +35,6 @@ def end_size(self) -> float: """Size of the last cell in this layer""" return self.start_size * self.c2c_expansion**self.count - # @abc.abstractmethod - # def get_chop(self, invert: bool) -> Chop: - # """Prepare a Chop for grader params""" - @property def is_final(self) -> bool: """Returns True if this layer is the last (no more space for additional ones)""" @@ -53,14 +49,14 @@ def get_chop(self, total_count: int, invert: bool) -> Chop: length_ratio=self.length, end_size=self.end_size, c2c_expansion=1 / self.c2c_expansion, - count=max(self.count, total_count), + count=min(self.count, total_count), ) return Chop( length_ratio=self.length, start_size=self.start_size, c2c_expansion=self.c2c_expansion, - count=max(self.count, total_count), + count=min(self.count, total_count), ) def __repr__(self): @@ -89,16 +85,17 @@ def __init__(self, start_size: float, c2c_expansion: float, bulk_size: float, ma self.total_expansion = self.bulk_size / self.start_size # manually sum up those few cells that lead from start to bulk size - count = 1 + count = 0 size = self.start_size length = 0.0 while size <= self.bulk_size: + length += size + count += 1 + if length > max_length: break - length += size - count += 1 size *= self.c2c_expansion self._count = count @@ -236,8 +233,6 @@ def get_stack(self, length: float) -> LayerStack: return stack def get_count(self, length: float, starts_at_wall: bool, ends_at_wall: bool): - print(starts_at_wall, ends_at_wall) - if not (starts_at_wall or ends_at_wall): return super().get_count(length, False, False) diff --git a/src/classy_blocks/grading/autograding/probe.py b/src/classy_blocks/grading/autograding/probe.py index 230bd38f..8528b061 100644 --- a/src/classy_blocks/grading/autograding/probe.py +++ b/src/classy_blocks/grading/autograding/probe.py @@ -1,6 +1,6 @@ import dataclasses import functools -from typing import List, Optional, Set +from typing import List, Optional, Set, Tuple from classy_blocks.base.exceptions import PatchNotFoundError from classy_blocks.grading.autograding.catalogue import Catalogue @@ -11,7 +11,7 @@ from classy_blocks.mesh import Mesh from classy_blocks.optimize.grid import HexGrid from classy_blocks.types import DirectionType, OrientType -from classy_blocks.util.constants import FACE_MAP +from classy_blocks.util.constants import DIRECTION_MAP @functools.lru_cache(maxsize=2) @@ -130,7 +130,7 @@ def get_default_wall_vertices(self, block: Block) -> Set[Vertex]: ] for orient in boundaries: - side_vertices = {block.vertices[i] for i in FACE_MAP[orient]} + side_vertices = set(block.get_side_vertices(orient)) # check if they are defined elsewhere try: self.mesh.patch_list.find(side_vertices) @@ -143,10 +143,28 @@ def get_default_wall_vertices(self, block: Block) -> Set[Vertex]: def get_wall_vertices(self, block: Block) -> Set[Vertex]: """Returns vertices that are on the 'wall' patches""" + # TODO: delete if not needed return self.get_explicit_wall_vertices(block).union(self.get_default_wall_vertices(block)) - def get_wire_info(self, wire: Wire, block: Block) -> WireInfo: - # TODO: test - wall_vertices = self.get_wall_vertices(block) + def get_wire_boundaries(self, wire: Wire, block: Block) -> Tuple[bool, bool]: + """Finds out whether a Wire starts or ends on a wall patch""" + start_orient = DIRECTION_MAP[wire.direction][0] + end_orient = DIRECTION_MAP[wire.direction][1] + + def find_patch(orient: OrientType) -> bool: + vertices = set(block.get_side_vertices(orient)) + + try: + patch = self.mesh.patch_list.find(vertices) + if patch.kind == "wall": + return True + except PatchNotFoundError: + if self.mesh.patch_list.default.get("kind") == "wall": + return True - return WireInfo(wire, wire.vertices[0] in wall_vertices, wire.vertices[1] in wall_vertices) + return False + + return (find_patch(start_orient), find_patch(end_orient)) + + def get_wire_info(self, wire: Wire, block: Block) -> WireInfo: + return WireInfo(wire, *self.get_wire_boundaries(wire, block)) diff --git a/src/classy_blocks/items/block.py b/src/classy_blocks/items/block.py index 33d28e89..9226a859 100644 --- a/src/classy_blocks/items/block.py +++ b/src/classy_blocks/items/block.py @@ -5,7 +5,7 @@ from classy_blocks.items.vertex import Vertex from classy_blocks.items.wires.axis import Axis from classy_blocks.items.wires.wire import Wire -from classy_blocks.types import DirectionType, IndexType +from classy_blocks.types import DirectionType, IndexType, OrientType from classy_blocks.util import constants from classy_blocks.util.frame import Frame @@ -117,6 +117,9 @@ def check_consistency(self) -> None: def indexes(self) -> IndexType: return [vertex.index for vertex in self.vertices] + def get_side_vertices(self, orient: OrientType) -> List[Vertex]: + return [self.vertices[i] for i in constants.FACE_MAP[orient]] + def format_grading(self) -> str: """Returns the simple/edgeGrading string""" if all(axis.is_simple for axis in self.axes): # is_simple diff --git a/src/classy_blocks/items/wires/wire.py b/src/classy_blocks/items/wires/wire.py index 068cbdea..ab9b3f11 100644 --- a/src/classy_blocks/items/wires/wire.py +++ b/src/classy_blocks/items/wires/wire.py @@ -30,7 +30,7 @@ def __init__(self, vertices: List[Vertex], direction: DirectionType, corner_1: i self.corners = [corner_1, corner_2] self.vertices = [vertices[corner_1], vertices[corner_2]] - self.direction = direction + self.direction: DirectionType = direction # the default edge is 'line' but will be replaced if the user wishes so # (that is, not included in edge.factory.registry) diff --git a/src/classy_blocks/util/constants.py b/src/classy_blocks/util/constants.py index 21937be7..39b73e89 100644 --- a/src/classy_blocks/util/constants.py +++ b/src/classy_blocks/util/constants.py @@ -1,6 +1,6 @@ from typing import Dict, List, Tuple -from classy_blocks.types import OrientType +from classy_blocks.types import DirectionType, OrientType # data type DTYPE = "float" # dtype as taken by np.array() @@ -32,6 +32,14 @@ "left", ] +# Connects block axis (direction) and orients +# (read: Direction 0 goes from right to left, etc. +DIRECTION_MAP: Dict[DirectionType, Tuple[OrientType, OrientType]] = { + 0: ("left", "right"), + 1: ("front", "back"), + 2: ("bottom", "top"), +} + # pairs of corner indexes along axes AXIS_PAIRS = ( ((0, 1), (3, 2), (7, 6), (4, 5)), # x diff --git a/tests/test_grading/test_autograde.py b/tests/test_grading/test_autograde.py index cd7604ec..95ea7f2a 100644 --- a/tests/test_grading/test_autograde.py +++ b/tests/test_grading/test_autograde.py @@ -3,6 +3,7 @@ import numpy as np from classy_blocks.construct.flat.sketches.grid import Grid +from classy_blocks.construct.operations.box import Box from classy_blocks.construct.shapes.cylinder import Cylinder from classy_blocks.construct.shapes.frustum import Frustum from classy_blocks.construct.stack import ExtrudedStack @@ -30,6 +31,9 @@ def get_cylinder(self) -> Cylinder: def get_frustum(self) -> Frustum: return Frustum([0, 0, 0], [1, 0, 0], [0, 1, 0], 0.3) + def get_box(self) -> Box: + return Box([0, 0, 0], [1, 1, 1]) + def setUp(self): self.mesh = Mesh() diff --git a/tests/test_grading/test_probe.py b/tests/test_grading/test_probe.py index be8d90ad..ae084228 100644 --- a/tests/test_grading/test_probe.py +++ b/tests/test_grading/test_probe.py @@ -205,3 +205,65 @@ def test_flipped_shape(self, flip_indexes, check_row, check_index): probe = Probe(self.mesh) self.assertTrue(probe.get_rows(1)[check_row].entries[check_index].flipped) + + @parameterized.expand( + ( + (0, 4, True), + (1, 5, True), + (2, 6, True), + (3, 7, True), + (0, 1, True), + (3, 2, True), + (4, 5, True), + (7, 6, True), + (0, 3, False), + (1, 2, False), + (4, 7, False), + (5, 6, False), + ) + ) + def test_wire_boundaries_explicit(self, wire_start, wire_end, starts_at_wall): + box = self.get_box() + box.set_patch(["bottom", "left", "right"], "wallPatch") + self.mesh.add(box) + + self.mesh.modify_patch("wallPatch", "wall") + self.mesh.assemble() + + probe = Probe(self.mesh) + block = self.mesh.blocks[0] + info = probe.get_wire_info(block.wires[wire_start][wire_end], block) + + self.assertEqual(info.starts_at_wall, starts_at_wall) + + @parameterized.expand( + ( + (0, 4, True), + (1, 5, True), + (2, 6, True), + (3, 7, True), + (0, 1, True), + (3, 2, True), + (4, 5, True), + (7, 6, True), + (0, 3, False), + (1, 2, False), + (4, 7, False), + (5, 6, False), + ) + ) + def test_wire_boundaries_default(self, wire_start, wire_end, starts_at_wall): + # same situation as the 'explicit' test but 'patch' patch types are defined + # and walls are the default + box = self.get_box() + box.set_patch(["top", "front", "back"], "patchPatch") + self.mesh.add(box) + + self.mesh.set_default_patch("defaultFaces", "wall") + self.mesh.assemble() + + probe = Probe(self.mesh) + block = self.mesh.blocks[0] + info = probe.get_wire_info(block.wires[wire_start][wire_end], block) + + self.assertEqual(info.starts_at_wall, starts_at_wall)