Skip to content
Merged
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/2169.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Option to write body facets on save
20 changes: 20 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v0/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1161,3 +1161,23 @@ def _nurbs_curves_compatibility(backend_version: "semver.Version", grpc_geometri
+ "26.1.0, but the current version used is "
+ f"{backend_version}."
)


def _check_write_body_facets_input(backend_version: "semver.Version", write_body_facets: bool):
"""Check if the backend version is compatible with NURBS curves in sketches.

Parameters
----------
backend_version : semver.Version
The version of the backend.
write_body_facets : bool
Option to write out body facets.
"""
if write_body_facets and backend_version < (26, 1, 0):
from ansys.geometry.core.logger import LOG

LOG.warning(
"The usage of write_body_facets requires a minimum Ansys release version of "
+ "26.1.0, but the current version used is "
+ f"{backend_version}."
)
22 changes: 18 additions & 4 deletions src/ansys/geometry/core/_grpc/_services/v0/designs.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
from ansys.geometry.core.errors import protect_grpc

from ..base.designs import GRPCDesignsService
from .conversions import build_grpc_id, from_design_file_format_to_grpc_part_export_format
from .conversions import (
_check_write_body_facets_input,
build_grpc_id,
from_design_file_format_to_grpc_part_export_format,
)


class GRPCDesignsServiceV0(GRPCDesignsService): # pragma: no cover
Expand Down Expand Up @@ -106,8 +110,12 @@ def put_active(self, **kwargs) -> dict: # noqa: D102
def save_as(self, **kwargs) -> dict: # noqa: D102
from ansys.api.dbu.v0.designs_pb2 import SaveAsRequest

_check_write_body_facets_input(kwargs["backend_version"], kwargs["write_body_facets"])

# Create the request - assumes all inputs are valid and of the proper type
request = SaveAsRequest(filepath=kwargs["filepath"])
request = SaveAsRequest(
filepath=kwargs["filepath"], write_body_facets=kwargs["write_body_facets"]
)

# Call the gRPC service
_ = self.stub.SaveAs(request)
Expand All @@ -119,9 +127,12 @@ def save_as(self, **kwargs) -> dict: # noqa: D102
def download_export(self, **kwargs) -> dict: # noqa: D102
from ansys.api.dbu.v0.designs_pb2 import DownloadExportFileRequest

_check_write_body_facets_input(kwargs["backend_version"], kwargs["write_body_facets"])

# Create the request - assumes all inputs are valid and of the proper type
request = DownloadExportFileRequest(
format=from_design_file_format_to_grpc_part_export_format(kwargs["format"])
format=from_design_file_format_to_grpc_part_export_format(kwargs["format"]),
write_body_facets=kwargs["write_body_facets"],
)

# Call the gRPC service
Expand All @@ -136,9 +147,12 @@ def download_export(self, **kwargs) -> dict: # noqa: D102
def stream_download_export(self, **kwargs) -> dict: # noqa: D102
from ansys.api.dbu.v0.designs_pb2 import DownloadExportFileRequest

_check_write_body_facets_input(kwargs["backend_version"], kwargs["write_body_facets"])

# Create the request - assumes all inputs are valid and of the proper type
request = DownloadExportFileRequest(
format=from_design_file_format_to_grpc_part_export_format(kwargs["format"])
format=from_design_file_format_to_grpc_part_export_format(kwargs["format"]),
write_body_facets=kwargs["write_body_facets"],
)

# Call the gRPC service
Expand Down
35 changes: 29 additions & 6 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,25 @@ def add_material(self, material: Material) -> None:

@check_input_types
@ensure_design_is_active
def save(self, file_location: Path | str) -> None:
def save(self, file_location: Path | str, write_body_facets: bool = False) -> None:
"""Save a design to disk on the active Geometry server instance.

Parameters
----------
file_location : ~pathlib.Path | str
Location on disk to save the file to.
write_body_facets : bool, default: False
Option to write body facets into the saved file. 26R1 and later.
"""
# Sanity checks on inputs
if isinstance(file_location, Path):
file_location = str(file_location)

self._grpc_client.services.designs.save_as(filepath=file_location)
self._grpc_client.services.designs.save_as(
filepath=file_location,
write_body_facets=write_body_facets,
backend_version=self._grpc_client.backend_version,
)
self._grpc_client.log.debug(f"Design successfully saved at location {file_location}.")

@protect_grpc
Expand All @@ -251,6 +257,7 @@ def download(
self,
file_location: Path | str,
format: DesignFileFormat = DesignFileFormat.SCDOCX,
write_body_facets: bool = False,
) -> None:
"""Export and download the design from the server.

Expand All @@ -260,6 +267,8 @@ def download(
Location on disk to save the file to.
format : DesignFileFormat, default: DesignFileFormat.SCDOCX
Format for the file to save to.
write_body_facets : bool, default: False
Option to write body facets into the saved file. SCDOCX only, 26R1 and later.
"""
# Sanity checks on inputs
if isinstance(file_location, str):
Expand All @@ -275,7 +284,9 @@ def download(
if self._modeler.client.backend_version < (25, 2, 0):
received_bytes = self.__export_and_download_legacy(format=format)
else:
received_bytes = self.__export_and_download(format=format)
received_bytes = self.__export_and_download(
format=format, write_body_facets=write_body_facets
)

# Write to file
file_location.write_bytes(received_bytes)
Expand Down Expand Up @@ -323,7 +334,11 @@ def __export_and_download_legacy(self, format: DesignFileFormat) -> bytes:

return received_bytes

def __export_and_download(self, format: DesignFileFormat) -> bytes:
def __export_and_download(
self,
format: DesignFileFormat,
write_body_facets: bool = False,
) -> bytes:
"""Export and download the design from the server.

Parameters
Expand Down Expand Up @@ -351,14 +366,22 @@ def __export_and_download(self, format: DesignFileFormat) -> bytes:
DesignFileFormat.STRIDE,
]:
try:
response = self._grpc_client.services.designs.download_export(format=format)
response = self._grpc_client.services.designs.download_export(
format=format,
write_body_facets=write_body_facets,
backend_version=self._grpc_client.backend_version,
)
except Exception:
self._grpc_client.log.warning(
f"Failed to download the file in {format} format."
" Attempting to stream download."
)
# Attempt to download the file via streaming
response = self._grpc_client.services.designs.stream_download_export(format=format)
response = self._grpc_client.services.designs.stream_download_export(
format=format,
write_body_facets=write_body_facets,
backend_version=self._grpc_client.backend_version,
)
else:
self._grpc_client.log.warning(
f"{format} format requested is not supported. Ignoring download request."
Expand Down
8 changes: 8 additions & 0 deletions tests/_incompatible_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ backends:
# Bug fix included from 26.1 onwards
- tests/integration/test_design_import.py::test_named_selections_after_file_insert
- tests/integration/test_design_import.py::test_named_selections_after_file_open
# Export body facets add in 26.1
- tests/integration/test_design.py::test_write_body_facets_on_save

- version: "24.2"
incompatible_tests:
Expand Down Expand Up @@ -198,6 +200,8 @@ backends:
# Bug fix included from 26.1 onwards
- tests/integration/test_design_import.py::test_named_selections_after_file_insert
- tests/integration/test_design_import.py::test_named_selections_after_file_open
# Export body facets add in 26.1
- tests/integration/test_design.py::test_write_body_facets_on_save

- version: "25.1"
incompatible_tests:
Expand Down Expand Up @@ -263,6 +267,8 @@ backends:
# Bug fix included from 26.1 onwards
- tests/integration/test_design_import.py::test_named_selections_after_file_insert
- tests/integration/test_design_import.py::test_named_selections_after_file_open
# Export body facets add in 26.1
- tests/integration/test_design.py::test_write_body_facets_on_save

- version: "25.2"
incompatible_tests:
Expand Down Expand Up @@ -292,3 +298,5 @@ backends:
# Bug fix included from 26.1 onwards
- tests/integration/test_design_import.py::test_named_selections_after_file_insert
- tests/integration/test_design_import.py::test_named_selections_after_file_open
# Export body facets add in 26.1
- tests/integration/test_design.py::test_write_body_facets_on_save
31 changes: 31 additions & 0 deletions tests/integration/test_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import os
from pathlib import Path
import zipfile

import matplotlib.colors as mcolors
import numpy as np
Expand Down Expand Up @@ -3579,3 +3580,33 @@ def test_component_make_independent(modeler: Modeler):
comp = design.components[0].components[-1].components[-1] # stale from update-design-in-place

assert not Accuracy.length_is_equal(comp.bodies[0].volume.m, face.body.volume.m)


def test_write_body_facets_on_save(modeler: Modeler, tmp_path_factory: pytest.TempPathFactory):
design = modeler.open_file(Path(FILES_DIR, "cars.scdocx"))

# First file without body facets
filepath_no_facets = tmp_path_factory.mktemp("test_design") / "cars_no_facets.scdocx"
design.download(filepath_no_facets)

# Second file with body facets
filepath_with_facets = tmp_path_factory.mktemp("test_design") / "cars_with_facets.scdocx"
design.download(filepath_with_facets, write_body_facets=True)

# Compare file sizes
size_no_facets = filepath_no_facets.stat().st_size
size_with_facets = filepath_with_facets.stat().st_size

assert size_with_facets > size_no_facets

# Ensure facets.bin and renderlist.xml files exist
with zipfile.ZipFile(filepath_with_facets, "r") as zip_ref:
namelist = set(zip_ref.namelist())

expected_files = {
"SpaceClaim/Graphics/facets.bin",
"SpaceClaim/Graphics/renderlist.xml",
}

missing = expected_files - namelist
assert not missing
Loading