Skip to content

Commit

Permalink
Do much polishing on HighReGrader. Also, bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
FranzBangar committed Oct 23, 2024
1 parent 5c21dbb commit 2c0edc1
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.wordBasedSuggestions": "off",
"editor.codeActionsOnSave": {
"source.organizeImports.ruff": "explicit"
"source.organizeImports.isort": "explicit"
},
},
"python.analysis.typeCheckingMode": "basic",
Expand Down
12 changes: 6 additions & 6 deletions examples/advanced/autograding_highre.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os

import classy_blocks as cb
from classy_blocks.grading.autograding.grader import HighReGrader, SimpleGrader
from classy_blocks.grading.autograding.params import HighReChopParams, SimpleChopParams
from classy_blocks.grading.autograding.grader import HighReGrader
from classy_blocks.grading.autograding.params import HighReChopParams

mesh = cb.Mesh()

Expand All @@ -25,12 +25,12 @@
mesh.set_default_patch("walls", "wall")


params = HighReChopParams(0.075)
params = HighReChopParams(0.05)
grader = HighReGrader(mesh, params)
grader.grade(take="max")
grader.grade()

params = SimpleChopParams(0.075)
grader = SimpleGrader(mesh, params)
# params = SimpleChopParams(0.05)
# grader = SimpleGrader(mesh, params)
# grader.grade(take="max")

mesh.write(os.path.join("..", "case", "system", "blockMeshDict"), debug_path="debug.vtk")
18 changes: 14 additions & 4 deletions examples/shape/cylinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import classy_blocks as cb
from classy_blocks.construct.flat.sketches.disk import DiskBase
from classy_blocks.grading.autograding.grader import HighReGrader
from classy_blocks.grading.autograding.params import HighReChopParams

DiskBase.core_ratio = 0.7 # Default is 0.8
DiskBase.core_ratio = 0.4 # Default is 0.8

mesh = cb.Mesh()

Expand All @@ -24,10 +26,18 @@
bl_thickness = 0.05
core_size = 0.2

cylinder.chop_axial(count=30)
cylinder.chop_radial(start_size=core_size, end_size=bl_thickness)
cylinder.chop_tangential(start_size=core_size)
# cylinder.chop_axial(count=30)
# cylinder.chop_radial(start_size=core_size, end_size=bl_thickness)
# cylinder.chop_tangential(start_size=core_size)

mesh.add(cylinder)

mesh.assemble()
mesh.block_list.update()

params = HighReChopParams(0.1)
grader = HighReGrader(mesh, params)
grader.grade()


mesh.write(os.path.join("..", "case", "system", "blockMeshDict"), debug_path="debug.vtk")
32 changes: 32 additions & 0 deletions examples/shape/quarter_cylinder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os

import classy_blocks as cb
from classy_blocks.construct.flat.sketches.disk import QuarterDisk
from classy_blocks.grading.autograding.grader import HighReGrader
from classy_blocks.grading.autograding.params import HighReChopParams
from classy_blocks.util import functions as f

mesh = cb.Mesh()

axis_point_1 = f.vector(0.0, 0.0, 0.0)
axis_point_2 = f.vector(5.0, 5.0, 0.0)
radius_point_1 = f.vector(0.0, 0.0, 2.0)

# make a disk with small core block - yields particularly bad cell sizes with normal chops
QuarterDisk.core_ratio = 0.4

quarter_disk = QuarterDisk(axis_point_1, radius_point_1, axis_point_1 - axis_point_2)
quarter_cylinder = cb.ExtrudedShape(quarter_disk, f.norm(axis_point_2 - axis_point_1))

mesh.add(quarter_cylinder)

mesh.assemble()
# TODO: automate or something
mesh.block_list.update()

params = HighReChopParams(0.05)
grader = HighReGrader(mesh, params)
grader.grade()


mesh.write(os.path.join("..", "case", "system", "blockMeshDict"), debug_path="debug.vtk")
103 changes: 37 additions & 66 deletions src/classy_blocks/grading/autograding/grader.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,75 @@
from typing import Optional, Set, get_args
import abc
from typing import get_args

from classy_blocks.grading.autograding.params import ChopParams, FixedCountParams, HighReChopParams, SimpleChopParams
from classy_blocks.grading.autograding.probe import Probe
from classy_blocks.grading.chop import Chop
from classy_blocks.items.wires.wire import Wire
from classy_blocks.grading.autograding.probe import Probe, Row
from classy_blocks.mesh import Mesh
from classy_blocks.types import ChopTakeType, DirectionType


class GraderBase:
class GraderBase(abc.ABC):
stages: int

def __init__(self, mesh: Mesh, params: ChopParams):
self.mesh = mesh
self.params = params

self.mesh.assemble()
self.probe = Probe(self.mesh)

def _get_end_size(self, wires: Set[Wire]) -> Optional[float]:
"""Returns average size of wires' last cell"""
if len(wires) == 0:
return None
def get_count(self, row: Row, take: ChopTakeType) -> int:
count = row.get_count()

return sum(wire.grading.end_size for wire in wires) / len(wires)
if count is None:
# take length from a row, as requested by 'take'
length = row.get_length(take)
# and set count from it
count = self.params.get_count(length)

def _get_start_size(self, wires: Set[Wire]) -> Optional[float]:
"""Returns average size of wires' first cell"""
if len(wires) == 0:
return None
return count

return sum(wire.grading.start_size for wire in wires) / len(wires)
def grade_axis(self, axis: DirectionType, take: ChopTakeType, stage: int) -> None:
handled_wires = set()

def grade_axis(self, axis: DirectionType, take: ChopTakeType) -> None:
for row in self.probe.get_rows(axis):
# determine count
wires = row.get_wires()

for wire in wires:
if wire.is_defined:
# there's a wire with a defined count already, use that
count = wire.grading.count
break
else:
# take length from a row, as requested
length = row.get_length(take)
# and set count from it
count = self.params.get_count(length)
count = self.get_count(row, take)

for wire in row.get_wires():
if wire in handled_wires:
continue

# don't touch defined wires
# TODO! don't touch wires, defined by USER
# if wire.is_defined:
# # TODO: test
# continue

size_before = self._get_end_size(wire.before)
size_after = self._get_start_size(wire.after)
chops = self.params.get_chops(count, wire.length, size_before, size_after)
size_before = wire.size_before
size_after = wire.size_after

chops = self.params.get_chops(stage, count, wire.length, size_before, size_after)

wire.grading.clear()
for chop in chops:
wire.grading.add_chop(chop)

wire.copy_to_coincidents()

handled_wires.add(wire)
handled_wires.update(wire.coincidents)

def grade(self, take: ChopTakeType = "avg") -> None:
for axis in get_args(DirectionType):
self.grade_axis(axis, take)
for stage in range(self.stages):
self.grade_axis(axis, take, stage)


class FixedCountGrader(GraderBase):
"""The simplest possible mesh grading: use a constant cell count for all axes on all blocks;
useful during mesh building and some tutorial cases"""

stages = 1

def __init__(self, mesh: Mesh, params: FixedCountParams):
super().__init__(mesh, params)

Expand All @@ -79,6 +79,8 @@ class SimpleGrader(GraderBase):
A single chop is used that sets cell count based on size.
Cell sizes between blocks differ as blocks' sizes change."""

stages = 1

def __init__(self, mesh: Mesh, params: SimpleChopParams):
super().__init__(mesh, params)

Expand All @@ -89,38 +91,7 @@ class HighReGrader(GraderBase):
are utilized to keep cell sizes between blocks consistent
(as much as possible)"""

stages = 3

def __init__(self, mesh: Mesh, params: HighReChopParams):
super().__init__(mesh, params)

def grade_axis(self, axis, take) -> None:
for row in self.probe.get_rows(axis):
# determine count
wires = row.get_wires()

for wire in reversed(wires):
if wire.is_defined:
# there's a wire with a defined count already, use that
count = wire.grading.count
break
else:
# take length from a row, as requested
length = row.get_length(take)
# and set count from it
count = self.params.get_count(length)

for wire in row.get_wires():
# don't touch defined wires
# TODO! don't touch wires, defined by USER
# if wire.is_defined:
# # TODO: test
# continue

# make a rudimentary chop first, then adjust
# in subsequent passes
chops = [Chop(length_ratio=0.5, count=count // 2), Chop(length_ratio=0.5, count=count // 2)]

for chop in chops:
wire.grading.add_chop(chop)

super().grade_axis(axis, take)
super().grade_axis(axis, take)
42 changes: 27 additions & 15 deletions src/classy_blocks/grading/autograding/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ def get_count(self, length: float) -> int:
"""Calculates count based on given length - used once only"""

@abc.abstractmethod
def get_chops(self, count: int, length: float, size_before: CellSizeType, size_after: CellSizeType) -> List[Chop]:
def get_chops(
self, stage: int, count: int, length: float, size_before: CellSizeType, size_after: CellSizeType
) -> List[Chop]:
"""Fixes cell count but modifies chops so that proper cell sizing will be obeyed"""
# That depends on inherited classes' philosophy

Expand All @@ -42,7 +44,7 @@ class FixedCountParams(ChopParams):
def get_count(self, _length):
return self.count

def get_chops(self, count, _length, _size_before=0, _size_after=0) -> List[Chop]:
def get_chops(self, _stage, count, _length, _size_before=0, _size_after=0) -> List[Chop]:
return [Chop(count=count)]


Expand All @@ -53,7 +55,7 @@ class SimpleChopParams(ChopParams):
def get_count(self, length: float):
return int(length / self.cell_size)

def get_chops(self, count, _length, _size_before=0, _size_after=0):
def get_chops(self, _stage, count, _length, _size_before=0, _size_after=0):
return [Chop(count=count)]


Expand All @@ -73,7 +75,9 @@ def get_count(self, length: float):
def define_sizes(
self, count: int, length: float, size_before: CellSizeType, size_after: CellSizeType
) -> Tuple[float, float]:
"""Defines start and end cell size with respect to given circumstances"""
"""Defines start and end cell size.
size_before and size_after are taken from preceding/following wires;
when a size is None, this is the last/first wire."""
if size_before == 0 or size_after == 0:
# until all counts/sizes are defined
# (the first pass with uniform grading),
Expand All @@ -82,28 +86,36 @@ def define_sizes(

# not enough room for all cells?
cramped = self.cell_size * count > length
base_size = length / count

if cramped:
return base_size, base_size

if size_before is None:
if cramped:
size_before = length / count
else:
size_before = self.cell_size
size_before = self.cell_size

if size_after is None:
if cramped:
size_after = length / count
else:
size_after = self.cell_size
size_after = self.cell_size

return size_before, size_after

def get_chops(self, count, length, size_before=CellSizeType, size_after=CellSizeType):
def get_chops(self, stage, count, length, size_before, size_after):
halfcount = count // 2

uniform_chops = [
Chop(length_ratio=0.5, count=halfcount),
Chop(length_ratio=0.5, count=halfcount),
]

if stage == 0:
# start with simple uniformly graded chops first
return uniform_chops

size_before, size_after = self.define_sizes(count, length, size_before, size_after)

# choose length ratio so that cells at the middle of blocks
# (between the two chops) have the same size
def fobj(lratio):
halfcount = count // 2
chop_1 = Chop(length_ratio=lratio, count=halfcount, start_size=size_before)
data_1 = chop_1.calculate(length)

Expand Down Expand Up @@ -228,5 +240,5 @@ def get_count(self, length: float):
# return chops
return 1

def get_chops(self, count, length, size_before=0, size_after=0) -> List[Chop]:
def get_chops(self, stage, count, length, size_before=0, size_after=0) -> List[Chop]:
raise NotImplementedError("TODO!")
11 changes: 9 additions & 2 deletions src/classy_blocks/grading/autograding/probe.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import functools
from typing import Dict, List, get_args
from typing import Dict, List, Optional, get_args

from classy_blocks.items.block import Block
from classy_blocks.items.wires.axis import Axis
Expand Down Expand Up @@ -43,7 +43,7 @@ class Row:
def __init__(self, direction: DirectionType):
self.direction = direction

# block of different orientations can belong to the same row;
# blocks of different orientations can belong to the same row;
# remember how they are oriented
self.blocks: List[Block] = []
self.headings: List[DirectionType] = []
Expand Down Expand Up @@ -80,6 +80,13 @@ def get_wires(self) -> List[Wire]:

return wires

def get_count(self) -> Optional[int]:
for wire in self.get_wires():
if wire.is_defined:
return wire.grading.count

return None


class Catalogue:
"""A collection of rows on a specified axis"""
Expand Down
Loading

0 comments on commit 2c0edc1

Please sign in to comment.