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

feat: workflow enhancements for better tool results #1723

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1723.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
workflow enhancements for better tool results
53 changes: 53 additions & 0 deletions src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ExtrudeFacesUpToRequest,
FilletRequest,
FullFilletRequest,
ModifyCircularPatternRequest,
ModifyLinearPatternRequest,
PatternRequest,
RenameObjectRequest,
Expand Down Expand Up @@ -743,6 +744,58 @@ def create_circular_pattern(

return result.result.success

@protect_grpc
@min_backend_version(25, 2, 0)
def modify_circular_pattern(
self,
selection: Union["Face", list["Face"]],
circular_count: int = 0,
linear_count: int = 0,
step_angle: Real = 0.0,
step_linear: Real = 0.0,
) -> bool:
"""Modify a circular pattern. Leave an argument at 0 for it to remain unchanged.

Parameters
----------
selection : Face | list[Face]
Faces that belong to the pattern.
circular_count : int, default: 0
How many members are in the circular pattern.
linear_count : int, default: 0
How many times the circular pattern repeats along the radial lines for a
two-dimensional pattern.
step_angle : Real, default: 0.0
Defines the circular angle.
step_linear : Real, default: 0.0
Defines the step, along the radial lines, for a pattern dimension greater than 1.

Returns
-------
bool
``True`` when successful, ``False`` when failed.
"""
from ansys.geometry.core.designer.face import Face

selection: list[Face] = selection if isinstance(selection, list) else [selection]

check_type_all_elements_in_iterable(selection, Face)

for object in selection:
object.body._reset_tessellation_cache()

result = self._commands_stub.ModifyCircularPattern(
ModifyCircularPatternRequest(
selection=[object._grpc_id for object in selection],
circular_count=circular_count,
linear_count=linear_count,
step_angle=step_angle,
step_linear=step_linear,
)
)

return result.result.success

@protect_grpc
@min_backend_version(25, 2, 0)
def create_fill_pattern(
Expand Down
47 changes: 47 additions & 0 deletions src/ansys/geometry/core/tools/prepare_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
get_design_from_face,
)
from ansys.geometry.core.misc.checks import check_type_all_elements_in_iterable, min_backend_version
from ansys.geometry.core.tools.repair_tool_message import RepairToolMessage
from ansys.geometry.core.typing import Real

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -204,3 +205,49 @@ def share_topology(
)
)
return share_topo_response.result

@protect_grpc
@min_backend_version(25, 2, 0)
def enhanced_share_topology(
self, bodies: list["Body"], tol: Real = 0.0, preserve_instances: bool = False
) -> RepairToolMessage:
"""Share topology between the chosen bodies.

Parameters
----------
bodies : list[Body]
List of bodies to share topology between.
tol : Real
Maximum distance between bodies.
preserve_instances : bool
Whether instances are preserved.

Returns
-------
RepairToolMessage
Message containing number of problem areas found/fixed, created and/or modified bodies.
"""
from ansys.geometry.core.designer.body import Body

if not bodies:
return RepairToolMessage(False, [], [], 0, 0)

# Verify inputs
check_type_all_elements_in_iterable(bodies, Body)

share_topo_response = self._prepare_stub.EnhancedShareTopology(
ShareTopologyRequest(
selection=[GRPCBody(id=body.id) for body in bodies],
tolerance=DoubleValue(value=tol),
preserve_instances=BoolValue(value=preserve_instances),
)
)

message = RepairToolMessage(
share_topo_response.success,
share_topo_response.created_bodies_monikers,
share_topo_response.modified_bodies_monikers,
share_topo_response.found,
share_topo_response.repaired,
)
return message
29 changes: 28 additions & 1 deletion src/ansys/geometry/core/tools/repair_tool_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@
class RepairToolMessage:
"""Provides return message for the repair tool methods."""

def __init__(self, success: bool, created_bodies: list[str], modified_bodies: list[str]):
def __init__(
self,
success: bool,
created_bodies: list[str],
modified_bodies: list[str],
found: int = -1,
repaired: int = -1,
):
"""Initialize a new instance of the extra edge problem area class.

Parameters
Expand All @@ -36,10 +43,20 @@ def __init__(self, success: bool, created_bodies: list[str], modified_bodies: li
List of bodies created after the repair operation.
modified_bodies: list[str]
List of bodies modified after the repair operation.
found: int, default: -1
Number of problem areas found for the repair operation.
If default, the operation does not provide the number of found problem areas.
repaired: int, default: -1
Number of problem areas repaired during the repair operation.
If default, the operation does not provide the number of fixed problem areas.


"""
self._success = success
self._created_bodies = created_bodies
self._modified_bodies = modified_bodies
self._found = found
self._repaired = repaired

@property
def success(self) -> bool:
Expand All @@ -55,3 +72,13 @@ def created_bodies(self) -> list[str]:
def modified_bodies(self) -> list[str]:
"""The list of the modified bodies after the repair operation."""
return self._modified_bodies

@property
def found(self) -> int:
"""Number of problem areas found for the repair operation."""
return self._found

@property
def repaired(self) -> int:
"""Number of problem areas repaired during the repair operation."""
return self._repaired
51 changes: 40 additions & 11 deletions src/ansys/geometry/core/tools/repair_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def find_interferences(
@protect_grpc
@min_backend_version(25, 2, 0)
def find_and_fix_short_edges(
self, bodies: list["Body"], length: Real = 0.0
self, bodies: list["Body"], length: Real = 0.0, comprehensive_result: bool = False
) -> RepairToolMessage:
"""Find and fix the short edge problem areas.

Expand All @@ -473,24 +473,29 @@ def find_and_fix_short_edges(
List of bodies that short edges are investigated on.
length : Real, optional
The maximum length of the edges. By default, 0.0.
comprehensive_result : bool, optional
Determines whether the repair tool will attempt to fix all problem areas at once (false, default), or each individually (true).
Fixing each area individually may take longer, but gives a more comprehensive result about the repair operation's success.

Returns
-------
RepairToolMessage
Message containing created and/or modified bodies.
Message containing number of problem areas found/fixed, created and/or modified bodies.
"""
from ansys.geometry.core.designer.body import Body

check_type_all_elements_in_iterable(bodies, Body)
check_type(length, Real)
check_type(comprehensive_result, bool)

if not bodies:
return RepairToolMessage(False, [], [])
return RepairToolMessage(False, [], [], 0, 0)

response = self._repair_stub.FindAndFixShortEdges(
FindShortEdgesRequest(
selection=[body.id for body in bodies],
max_edge_length=DoubleValue(value=length),
comprehensive=comprehensive_result,
)
)

Expand All @@ -500,12 +505,16 @@ def find_and_fix_short_edges(
response.success,
response.created_bodies_monikers,
response.modified_bodies_monikers,
response.found,
response.repaired,
)
return message

@protect_grpc
@min_backend_version(25, 2, 0)
def find_and_fix_extra_edges(self, bodies: list["Body"]) -> RepairToolMessage:
def find_and_fix_extra_edges(
self, bodies: list["Body"], comprehensive_result: bool = False
) -> RepairToolMessage:
"""Find and fix the extra edge problem areas.

Notes
Expand All @@ -518,22 +527,26 @@ def find_and_fix_extra_edges(self, bodies: list["Body"]) -> RepairToolMessage:
List of bodies that short edges are investigated on.
length : Real
The maximum length of the edges.
comprehensive_result : bool, optional
Determines whether the repair tool will attempt to fix all problem areas at once (false, default), or each individually (true).
Fixing each area individually may take longer, but gives a more comprehensive result about the repair operation's success.

Returns
-------
RepairToolMessage
Message containing created and/or modified bodies.
Message containing number of problem areas found/fixed, created and/or modified bodies.
"""
from ansys.geometry.core.designer.body import Body

check_type_all_elements_in_iterable(bodies, Body)
check_type(comprehensive_result, bool)

if not bodies:
return RepairToolMessage(False, [], [])
return RepairToolMessage(False, [], [], 0, 0)

response = self._repair_stub.FindAndFixExtraEdges(
FindExtraEdgesRequest(
selection=[body.id for body in bodies],
selection=[body.id for body in bodies], comprehensive=comprehensive_result
)
)

Expand All @@ -543,13 +556,19 @@ def find_and_fix_extra_edges(self, bodies: list["Body"]) -> RepairToolMessage:
response.success,
response.created_bodies_monikers,
response.modified_bodies_monikers,
response.found,
response.repaired,
)
return message

@protect_grpc
@min_backend_version(25, 2, 0)
def find_and_fix_split_edges(
self, bodies: list["Body"], angle: Real = 0.0, length: Real = 0.0
self,
bodies: list["Body"],
angle: Real = 0.0,
length: Real = 0.0,
comprehensive_result: bool = False,
) -> RepairToolMessage:
"""Find and fix the split edge problem areas.

Expand All @@ -565,28 +584,36 @@ def find_and_fix_split_edges(
The maximum angle between edges. By default, 0.0.
length : Real, optional
The maximum length of the edges. By default, 0.0.
comprehensive_result : bool, optional
Determines whether the repair tool will attempt to fix all problem areas at once (false, default), or each individually (true).
Fixing each area individually may take longer, but gives a more comprehensive result about the repair operation's success.


Returns
-------
RepairToolMessage
Message containing created and/or modified bodies.
Message containing number of problem areas found/fixed, created and/or modified bodies.
"""
from ansys.geometry.core.designer.body import Body

check_type_all_elements_in_iterable(bodies, Body)
check_type(angle, Real)
check_type(length, Real)
check_type(comprehensive_result, bool)

if not bodies:
return RepairToolMessage(False, [], [])
return RepairToolMessage(False, [], [], 0, 0)

angle_value = DoubleValue(value=float(angle))
length_value = DoubleValue(value=float(length))
body_ids = [body.id for body in bodies]

response = self._repair_stub.FindAndFixSplitEdges(
FindSplitEdgesRequest(
bodies_or_faces=body_ids, angle=angle_value, distance=length_value
bodies_or_faces=body_ids,
angle=angle_value,
distance=length_value,
comprehensive=comprehensive_result,
)
)

Expand All @@ -596,5 +623,7 @@ def find_and_fix_split_edges(
response.success,
response.created_bodies_monikers,
response.modified_bodies_monikers,
response.found,
response.repaired,
)
return message
42 changes: 42 additions & 0 deletions tests/integration/test_geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,48 @@ def test_circular_pattern_on_imported_geometry_faces(modeler: Modeler):
)


def test_circular_pattern_on_imported_geometry_faces_modify(modeler: Modeler):
"""Test creating a circular pattern out of imported geometry and modifying it"""
design = modeler.open_file(FILES_DIR / "Fan_OneBlade_CircularPatter.scdocx")
assert len(design.bodies) == 1
assert len(design.bodies[0].faces) == 13
assert design.bodies[0].volume.m == pytest.approx(
Quantity(0.00019496, UNITS.m**3).m, rel=1e-6, abs=1e-8
)
success = modeler.geometry_commands.create_circular_pattern(
[
design.bodies[0].faces[10],
design.bodies[0].faces[11],
design.bodies[0].faces[7],
design.bodies[0].faces[9],
design.bodies[0].faces[8],
design.bodies[0].faces[12],
],
design.bodies[0].edges[3],
8,
np.pi * 2,
False,
None,
None,
None,
)
assert len(design.bodies) == 1
assert len(design.bodies[0].faces) == 55
assert success
assert design.bodies[0].volume.m == pytest.approx(
Quantity(0.0002373, UNITS.m**3).m, rel=1e-6, abs=1e-8
)
success = modeler.geometry_commands.modify_circular_pattern(
[design.bodies[0].faces[30]], 12, 0, 0.523598775598, None
)
assert len(design.bodies) == 1
assert len(design.bodies[0].faces) == 79
assert success
assert design.bodies[0].volume.m == pytest.approx(
Quantity(0.00026159, UNITS.m**3).m, rel=1e-6, abs=1e-8
)


def test_fill_pattern_on_imported_geometry_faces(modeler: Modeler):
"""Test create a fill pattern on imported geometry"""
design = modeler.open_file(FILES_DIR / "FillPattern.scdocx")
Expand Down
Loading
Loading