Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
38ba56a
added model for testing stitching many faces
jacobrkerstetter May 1, 2025
ade4c4a
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] May 1, 2025
c9bc1c1
chore: adding changelog file 1953.added.md [dependabot-skip]
pyansys-ci-bot May 1, 2025
0547a59
wip - discrepancy in scripting and core service result of find small …
jacobrkerstetter May 1, 2025
065c075
adding max distance to standard find method
jacobrkerstetter May 1, 2025
e70a95c
put default max distance for find stitch to 0.0001
jacobrkerstetter May 1, 2025
785233a
Merge branch 'feat/find_fix_stitch_faces_enhancement' of https://gith…
jacobrkerstetter May 1, 2025
83aebb6
adding options for find missing faces
jacobrkerstetter May 1, 2025
cad60df
Merge branch 'feat/find_missing_faces_enhancement' of https://github.…
jacobrkerstetter May 1, 2025
d54cc0c
changing all angles/distances to proper classes and defaults
jacobrkerstetter May 1, 2025
1edcbc7
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] May 1, 2025
ae6aedf
chore: adding changelog file 1953.added.md [dependabot-skip]
pyansys-ci-bot May 1, 2025
d9baa72
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
jacobrkerstetter May 6, 2025
8006873
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter May 7, 2025
6960d45
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter May 7, 2025
7034aaf
wip refactoring
jacobrkerstetter May 7, 2025
b8e7251
refactor to match new grpc architecture
jacobrkerstetter May 7, 2025
571fe02
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] May 7, 2025
e00d092
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
jacobrkerstetter May 7, 2025
981890b
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
RobPasMue May 8, 2025
750f9a0
chore: general improvements
RobPasMue May 8, 2025
9c98e1b
chore: refactoring
RobPasMue May 8, 2025
dd9e74c
fix: enhancements
RobPasMue May 8, 2025
93290e6
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter May 14, 2025
13c885f
wip, changing signature
jacobrkerstetter May 14, 2025
cf45567
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter May 14, 2025
f9e2cdb
Merge branch 'feat/find_small_faces_enhancements' of https://github.c…
jacobrkerstetter May 14, 2025
45f8d6f
adding small faces options
jacobrkerstetter May 15, 2025
f321646
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] May 15, 2025
66d8ff5
Merge branch 'main' of https://github.com/ansys/pyansys-geometry into…
jacobrkerstetter May 20, 2025
14080a7
all tests enabled for core service
jacobrkerstetter May 20, 2025
5956c38
Merge branch 'feat/find_fix_stitch_faces_enhancement' of https://gith…
jacobrkerstetter May 20, 2025
519e0ba
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
RobPasMue Jun 9, 2025
6ee104b
chore: adding changelog file 1953.added.md [dependabot-skip]
pyansys-ci-bot Jun 9, 2025
ea4291e
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
RobPasMue Jun 9, 2025
b62bdcf
chore: adding changelog file 1953.added.md [dependabot-skip]
pyansys-ci-bot Jun 12, 2025
9baab31
add test
RyanJWard Jun 12, 2025
78435d2
Merge branch 'feat/find_fix_stitch_faces_enhancement' of https://gith…
RyanJWard Jun 12, 2025
2bc2c79
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
RobPasMue Jun 13, 2025
ad5bd1d
added Area subclass of measurement
jacobrkerstetter Jun 13, 2025
bb867c1
Merge branch 'feat/find_fix_stitch_faces_enhancement' of https://gith…
jacobrkerstetter Jun 13, 2025
14417c9
expand test coverage
RyanJWard Jun 16, 2025
9032a54
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 16, 2025
a95118f
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
jacobrkerstetter Jun 16, 2025
147f978
Update comment
RyanJWard Jun 16, 2025
2717ad5
Merge branch 'feat/find_fix_stitch_faces_enhancement' of https://gith…
RyanJWard Jun 16, 2025
377d2a0
resolving comments on PR
jacobrkerstetter Jun 16, 2025
09525d9
chore: auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 16, 2025
ef6346a
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
jacobrkerstetter Jun 16, 2025
e4daa0e
Merge branch 'main' into feat/find_fix_stitch_faces_enhancement
RobPasMue Jun 16, 2025
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/1953.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Find and fix stitch/missing/small faces enhancements
16 changes: 16 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/base/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ def from_measurement_to_server_angle(input: Measurement) -> float:
return input.value.m_as(DEFAULT_UNITS.SERVER_ANGLE)


def from_measurement_to_server_area(input: Measurement) -> float:
"""Convert a measurement to an area value.

Parameters
----------
value : Measurement
Measurement value.

Returns
-------
float
Area value in server-defined units. By default, square meters.
"""
return input.value.m_as(DEFAULT_UNITS.SERVER_AREA)


def to_distance(value: float | int) -> Distance:
"""Convert a server value to a Distance object.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class GRPCRepairToolsService(ABC): # pragma: no cover

def __init__(self, channel: grpc.Channel):
"""Initialize the gRPC repair tools service."""
pass

@abstractmethod
def find_split_edges(self, **kwargs) -> dict:
Expand Down Expand Up @@ -113,6 +114,11 @@ def find_and_fix_simplify(self, **kwargs) -> dict:
"""Identify and simplify areas in the geometry."""
pass

@abstractmethod
def find_and_fix_stitch_faces(self, **kwargs) -> dict:
"""Identify and stitch faces in the geometry."""
pass

@abstractmethod
def inspect_geometry(self, **kwargs) -> dict:
"""Inspect the geometry for issues."""
Expand Down
145 changes: 137 additions & 8 deletions src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def find_split_edges(self, **kwargs) -> dict: # noqa: D102

from ansys.api.geometry.v0.repairtools_pb2 import FindSplitEdgesRequest

# Create the gRPC request
# Create the request - assumes all inputs are valid and of the proper type
request = FindSplitEdgesRequest(
bodies_or_faces=kwargs["bodies_or_faces"],
angle=DoubleValue(value=float(kwargs["angle"])),
Expand Down Expand Up @@ -104,6 +104,7 @@ def find_extra_edges(self, **kwargs) -> dict: # noqa: D102
def find_inexact_edges(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import FindInexactEdgesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindInexactEdgesRequest(selection=kwargs["selection"])

# Call the gRPC service
Expand Down Expand Up @@ -150,6 +151,7 @@ def find_short_edges(self, **kwargs) -> dict: # noqa: D102
def find_duplicate_faces(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import FindDuplicateFacesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindDuplicateFacesRequest(faces=kwargs["faces"])

# Call the gRPC service
Expand All @@ -168,9 +170,41 @@ def find_duplicate_faces(self, **kwargs) -> dict: # noqa: D102

@protect_grpc
def find_missing_faces(self, **kwargs) -> dict: # noqa: D102
from google.protobuf.wrappers_pb2 import DoubleValue

from ansys.api.geometry.v0.repairtools_pb2 import FindMissingFacesRequest
from ansys.geometry.core.logger import LOG

from ..base.conversions import (
from_measurement_to_server_angle,
from_measurement_to_server_length,
)

# Check the backend version to set optional parameters
if kwargs["backend_version"] < (26, 1, 0) and (
kwargs["angle"] is not None or kwargs["distance"] is not None
):
# If the backend version is less than 26.1.0, set angle and distance to None
kwargs["angle"] = None
kwargs["distance"] = None

# Log a warning
LOG.warning(
"The backend version is less than 26.1.0, so angle and distance parameters will be"
"ignored. Please update the backend to use these parameters."
)

# Create the request - assumes all inputs are valid and of the proper type
request = FindMissingFacesRequest(
faces=kwargs["faces"],
angle=DoubleValue(value=from_measurement_to_server_angle(kwargs["angle"]))
if kwargs["angle"] is not None
else None,
distance=DoubleValue(value=from_measurement_to_server_length(kwargs["distance"]))
if kwargs["distance"] is not None
else None,
)

request = FindMissingFacesRequest(faces=kwargs["faces"])
# Call the gRPC service
response = self.stub.FindMissingFaces(request)

Expand All @@ -187,9 +221,41 @@ def find_missing_faces(self, **kwargs) -> dict: # noqa: D102

@protect_grpc
def find_small_faces(self, **kwargs) -> dict: # noqa: D102
from google.protobuf.wrappers_pb2 import DoubleValue

from ansys.api.geometry.v0.repairtools_pb2 import FindSmallFacesRequest
from ansys.geometry.core.logger import LOG

from ..base.conversions import (
from_measurement_to_server_area,
from_measurement_to_server_length,
)

# Check the backend version to set optional parameters
if kwargs["backend_version"] < (26, 1, 0) and (
kwargs["area"] is not None or kwargs["width"] is not None
):
# If the backend version is less than 26.1.0, set area and width to None
kwargs["area"] = None
kwargs["width"] = None

# Log a warning
LOG.warning(
"The backend version is less than 26.1.0, so area and width parameters will be"
"ignored. Please update the backend to use these parameters."
)

# Create the request - assumes all inputs are valid and of the proper type
request = FindSmallFacesRequest(
selection=kwargs["selection"],
area=DoubleValue(value=from_measurement_to_server_area(kwargs["area"]))
if kwargs["area"] is not None
else None,
width=DoubleValue(value=from_measurement_to_server_length(kwargs["width"]))
if kwargs["width"] is not None
else None,
)

request = FindSmallFacesRequest(selection=kwargs["selection"])
# Call the gRPC service
response = self.stub.FindSmallFaces(request)

Expand All @@ -206,11 +272,33 @@ def find_small_faces(self, **kwargs) -> dict: # noqa: D102

@protect_grpc
def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102
from google.protobuf.wrappers_pb2 import DoubleValue

from ansys.api.geometry.v0.repairtools_pb2 import FindStitchFacesRequest
from ansys.geometry.core.logger import LOG

from ..base.conversions import from_measurement_to_server_length

if kwargs["backend_version"] < (26, 1, 0) and kwargs["distance"] is not None:
# If the backend version is less than 26.1.0, set distance to None and log warning
kwargs["distance"] = None
LOG.warning(
"The backend version is less than 26.1.0, so distance parameter will be ignored. "
)

# Create the request - assumes all inputs are valid and of the proper type
request = FindStitchFacesRequest(
faces=kwargs["faces"],
maximum_distance=DoubleValue(
value=from_measurement_to_server_length(kwargs["distance"])
)
if kwargs["distance"] is not None
else None,
)

request = FindStitchFacesRequest(faces=kwargs["faces"])
# Call the gRPC service
response = self.stub.FindStitchFaces(request)

# Return the response - formatted as a dictionary
return {
"problems": [
Expand All @@ -226,10 +314,12 @@ def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102
def find_simplify(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import FindAdjustSimplifyRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindAdjustSimplifyRequest(selection=kwargs["selection"])

# Call the gRPC service
response = self.stub.FindAdjustSimplify(request)

# Return the response - formatted as a dictionary
return {
"problems": [
Expand All @@ -245,12 +335,15 @@ def find_simplify(self, **kwargs) -> dict: # noqa: D102
def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import FindAdjustSimplifyRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindAdjustSimplifyRequest(
selection=kwargs["selection"],
comprehensive=kwargs["comprehensive_result"],
)

# Call the gRPC service
response = self.stub.FindAndSimplify(request)

# Return the response - formatted as a dictionary
return {
"success": response.success,
Expand All @@ -260,23 +353,54 @@ def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
"modified_bodies_monikers": [],
}

@protect_grpc
def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
from google.protobuf.wrappers_pb2 import BoolValue, DoubleValue

from ansys.api.geometry.v0.repairtools_pb2 import FindStitchFacesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindStitchFacesRequest(
faces=kwargs["body_ids"],
maximum_distance=DoubleValue(value=kwargs["max_distance"])
if kwargs["max_distance"] is not None
else None,
allow_multiple_bodies=BoolValue(value=kwargs["allow_multiple_bodies"]),
maintain_components=BoolValue(value=kwargs["maintain_components"]),
check_for_coincidence=BoolValue(value=kwargs["check_for_coincidence"]),
comprehensive=kwargs["comprehensive_result"],
)

# Call the gRPC service
response = self.stub.FindAndFixStitchFaces(request)

# Return the response - formatted as a dictionary
return {
"success": response.success,
"created_bodies_monikers": response.created_bodies_monikers,
"modified_bodies_monikers": response.modified_bodies_monikers,
"found": response.found,
"repaired": response.repaired,
}

@protect_grpc
def inspect_geometry(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import InspectGeometryRequest

# Create the gRPC request
# Create the request - assumes all inputs are valid and of the proper type
request = InspectGeometryRequest(bodies=kwargs.get("bodies", []))

# Call the gRPC service
inspect_result_response = self.stub.InspectGeometry(request)

# Serialize and return the response
return self.serialize_inspect_result_response(inspect_result_response)
return self.__serialize_inspect_result_response(inspect_result_response)

@protect_grpc
def repair_geometry(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import RepairGeometryRequest

# Create the request - assumes all inputs are valid and of the proper type
request = RepairGeometryRequest(bodies=kwargs.get("bodies", []))

# Call the gRPC service
Expand All @@ -293,6 +417,7 @@ def find_interferences(self, **kwargs) -> dict: # noqa: D102

from ansys.api.geometry.v0.repairtools_pb2 import FindInterferenceRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindInterferenceRequest(
bodies=kwargs["bodies"],
cut_smaller_body=BoolValue(value=kwargs["cut_smaller_body"]),
Expand All @@ -318,11 +443,13 @@ def find_and_fix_short_edges(self, **kwargs): # noqa: D102

from ansys.api.geometry.v0.repairtools_pb2 import FindShortEdgesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindShortEdgesRequest(
selection=kwargs["selection"],
max_edge_length=DoubleValue(value=kwargs["length"]),
comprehensive=kwargs["comprehensive_result"],
)

# Call the gRPC service
response = self.stub.FindAndFixShortEdges(request)

Expand All @@ -339,10 +466,12 @@ def find_and_fix_short_edges(self, **kwargs): # noqa: D102
def find_and_fix_extra_edges(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.repairtools_pb2 import FindExtraEdgesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindExtraEdgesRequest(
selection=kwargs["selection"],
comprehensive=kwargs["comprehensive_result"],
)

# Call the gRPC service
response = self.stub.FindAndFixExtraEdges(request)

Expand All @@ -361,6 +490,7 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102

from ansys.api.geometry.v0.repairtools_pb2 import FindSplitEdgesRequest

# Create the request - assumes all inputs are valid and of the proper type
request = FindSplitEdgesRequest(
bodies_or_faces=kwargs["bodies_or_faces"],
angle=DoubleValue(value=float(kwargs["angle"])),
Expand All @@ -380,8 +510,7 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102
"modified_bodies_monikers": [],
}

@staticmethod
def serialize_inspect_result_response(response) -> dict: # noqa: D102
def __serialize_inspect_result_response(self, response) -> dict: # noqa: D102
def serialize_body(body):
return {
"id": body.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Module containing the repair tools service implementation."""
"""Module containing the repair tools service implementation for v1."""

import grpc

Expand Down Expand Up @@ -99,6 +99,10 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102
def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError

@protect_grpc
def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError

@protect_grpc
def inspect_geometry(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError
Expand Down
23 changes: 23 additions & 0 deletions src/ansys/geometry/core/misc/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def ANGLE(self, value: Unit) -> None: # noqa: N802
check_pint_unit_compatibility(value, self._angle)
self._angle = value

@property
def AREA(self) -> Unit: # noqa: N802
"""Default area unit for PyAnsys Geometry."""
return self._length * self._length

@property
def SERVER_LENGTH(self) -> Unit: # noqa: N802
"""Default length unit for gRPC messages.
Expand Down Expand Up @@ -228,3 +233,21 @@ def __init__(self, value: Real | Quantity, unit: Unit | None = None):
# Delegates in Measurement ctor. forcing expected dimensions.
unit = unit if unit else DEFAULT_UNITS.ANGLE
super().__init__(value, unit, DEFAULT_UNITS.ANGLE)


class Area(Measurement):
"""Provides the ``Measurement`` subclass for holding an area.

Parameters
----------
value : Real | ~pint.Quantity
Value of the area.
unit : ~pint.Unit, default: DEFAULT_UNITS.AREA
Units for the area.
"""

def __init__(self, value: Real | Quantity, unit: Unit | None = None):
"""Initialize the ``Area`` class."""
# Delegates in Measurement ctor. forcing expected dimensions.
unit = unit if unit else DEFAULT_UNITS.AREA
super().__init__(value, unit, DEFAULT_UNITS.AREA)
Loading
Loading