Skip to content

feat: remove materials #2180

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

Merged
merged 9 commits into from
Aug 20, 2025
1 change: 1 addition & 0 deletions doc/changelog.d/2180.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove materials
5 changes: 5 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/base/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ def get_assigned_material(self, **kwargs) -> dict:
"""Get the assigned material of a body."""
pass

@abstractmethod
def remove_assigned_material(self, **kwargs) -> dict:
"""Remove the assigned material of a body."""
pass

@abstractmethod
def set_name(self, **kwargs) -> dict:
"""Set the name of a body."""
Expand Down
5 changes: 5 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/base/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ def __init__(self, channel: grpc.Channel):
def add_material(self, **kwargs) -> dict:
"""Add material to the service design."""
pass

@abstractmethod
def remove_material(self, **kwargs) -> dict:
"""Remove material from the service design."""
pass
13 changes: 13 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v0/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,19 @@ def get_assigned_material(self, **kwargs) -> dict: # noqa: D102
# Return the response - formatted as a dictionary
return {"material": from_grpc_material_to_material(resp)}

@protect_grpc
def remove_assigned_material(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.bodies_pb2 import RemoveAssignedMaterialRequest

# Create the request - assumes all inputs are valid and of the proper type
request = RemoveAssignedMaterialRequest(ids=[build_grpc_id(id) for id in kwargs["ids"]])

# Call the gRPC service
resp = self.stub.RemoveAssignedMaterial(request=request)

# Return the response - formatted as a dictionary
return {"successfully_removed": [id for id in resp.successfully_removed]}

@protect_grpc
def set_name(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.bodies_pb2 import SetNameRequest
Expand Down
15 changes: 15 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v0/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,18 @@ def add_material(self, **kwargs) -> dict: # noqa: D102

# Convert the response to a dictionary
return {}

@protect_grpc
def remove_material(self, **kwargs) -> dict: # noqa: D102
from ansys.api.geometry.v0.materials_pb2 import RemoveFromDocumentRequest

# Create the request - assumes all inputs are valid and of the proper type
request = RemoveFromDocumentRequest(
request_data=[from_material_to_grpc_material(mat) for mat in kwargs["materials"]]
)

# Call the gRPC service
_ = self.stub.RemoveFromDocument(request=request)

# Convert the response to a dictionary
return {}
4 changes: 4 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v1/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ def set_assigned_material(self, **kwargs) -> dict: # noqa: D102
def get_assigned_material(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError

@protect_grpc
def remove_assigned_material(self, **kwargs): # noqa: D102
raise NotImplementedError

@protect_grpc
def set_name(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError
Expand Down
4 changes: 4 additions & 0 deletions src/ansys/geometry/core/_grpc/_services/v1/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ def __init__(self, channel: grpc.Channel): # noqa: D102
@protect_grpc
def add_material(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError

@protect_grpc
def remove_material(self, **kwargs) -> dict: # noqa: D102
raise NotImplementedError
14 changes: 14 additions & 0 deletions src/ansys/geometry/core/designer/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ def get_assigned_material(self) -> Material:
"""
return

@abstractmethod
def remove_assigned_material(self) -> None:
"""Remove the material assigned to the body."""
return

@abstractmethod
def add_midsurface_thickness(self, thickness: Quantity) -> None:
"""Add a mid-surface thickness to a surface body.
Expand Down Expand Up @@ -1061,6 +1066,11 @@ def get_assigned_material(self) -> Material: # noqa: D102
response = self._grpc_client.services.bodies.get_assigned_material(id=self.id)
return response.get("material")

@min_backend_version(26, 1, 0)
def remove_assigned_material(self) -> None: # noqa: D102
self._grpc_client.log.debug(f"Removing assigned material for body {self.id}.")
self._grpc_client.services.bodies.remove_assigned_material(ids=[self.id])

@protect_grpc
@check_input_types
def add_midsurface_thickness(self, thickness: Quantity) -> None: # noqa: D102
Expand Down Expand Up @@ -1593,6 +1603,10 @@ def assign_material(self, material: Material) -> None: # noqa: D102
def get_assigned_material(self) -> Material: # noqa: D102
return self._template.get_assigned_material()

@ensure_design_is_active
def remove_assigned_material(self): # noqa: D102
self._template.remove_assigned_material()

@ensure_design_is_active
def add_midsurface_thickness(self, thickness: Quantity) -> None: # noqa: D102
self._template.add_midsurface_thickness(thickness)
Expand Down
18 changes: 18 additions & 0 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,24 @@ def add_material(self, material: Material) -> None:
self._materials.append(material)
self._grpc_client.log.debug(f"Material {material.name} is successfully added to design.")

@min_backend_version(26, 1, 0)
@check_input_types
@ensure_design_is_active
def remove_material(self, material: Material | list[Material]) -> None:
"""Remove a material from the design.

Parameters
----------
material : Material | list[Material]
Material or list of materials to remove.
"""
material = material if isinstance(material, list) else [material]

self._grpc_client.services.materials.remove_material(materials=material)
for mat in material:
self._materials.remove(mat)
self._grpc_client.log.debug(f"Material {mat.name} is successfully removed from design.")

@check_input_types
@ensure_design_is_active
def save(self, file_location: Path | str, write_body_facets: bool = False) -> None:
Expand Down
32 changes: 32 additions & 0 deletions tests/integration/test_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,38 @@ def test_get_empty_material(modeler: Modeler):
assert len(mat_service.properties) == 1


def test_remove_material_from_body(modeler: Modeler):
"""Test removing a material from a body."""
# Create a design and a sketch
design = modeler.create_design("RemoveMaterialTest")
sketch = Sketch()
sketch.circle(Point2D([0, 0], UNITS.mm), Quantity(10, UNITS.mm))

# Extrude the sketch to create a body
body = design.extrude_sketch("CircleBody", sketch, Quantity(10, UNITS.mm))

# Create and assign a material
density = Quantity(7850, UNITS.kg / (UNITS.m**3))
material = Material(
"Steel",
density,
[MaterialProperty(MaterialPropertyType.POISSON_RATIO, "Poisson", Quantity(0.3))],
)
design.add_material(material)
body.assign_material(material)
assert body.material.name == "Steel"

# Remove the material from the body
body.remove_assigned_material()

# Check that the body no longer has a material assigned
assert body.material.name == ""
assert len(body.material.properties) == 1
assert body.material.properties[MaterialPropertyType.DENSITY].quantity == Quantity(
0, UNITS.kg / (UNITS.m**3)
)


def test_face_to_body_creation(modeler: Modeler):
"""Test in charge of validating the extrusion of an existing face."""
# Create a Sketch and draw a circle (all client side)
Expand Down
38 changes: 38 additions & 0 deletions tests/integration/test_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,41 @@ def test_material_creation(modeler: Modeler):
design.materials[0].properties[MaterialPropertyType.POISSON_RATIO].quantity
== mat_prop_quantity
)


def test_material_removal(modeler: Modeler):
"""Test the removal of a material from a design."""
design = modeler.create_design("my_design")

mat_name = "mat_1"
density = 1000 * UNITS.kg / (UNITS.m * UNITS.m * UNITS.m)
material = Material(mat_name, density)
design.add_material(material)

assert design.materials[0].name == mat_name

design.remove_material(material)

assert len(design.materials) == 0


def test_remove_multiple_materials(modeler: Modeler):
"""Test the removal of multiple materials from a design."""
design = modeler.create_design("my_design")

mat_name_1 = "mat_1"
density_1 = 1000 * UNITS.kg / (UNITS.m * UNITS.m * UNITS.m)
material_1 = Material(mat_name_1, density_1)
design.add_material(material_1)

mat_name_2 = "mat_2"
density_2 = 2000 * UNITS.kg / (UNITS.m * UNITS.m * UNITS.m)
material_2 = Material(mat_name_2, density_2)
design.add_material(material_2)

assert design.materials[0].name == mat_name_1
assert design.materials[1].name == mat_name_2

design.remove_material([material_1, material_2])

assert len(design.materials) == 0
Loading