Skip to content
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