Skip to content

Commit

Permalink
add example for solid model (#695)
Browse files Browse the repository at this point in the history
* add solid model example

* add plotting examples for a solid model
  • Loading branch information
roosre authored Nov 25, 2024
1 parent 8cf45e0 commit 88f7bc1
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 2 deletions.
6 changes: 6 additions & 0 deletions doc/source/user_guide/howto/visualize_model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ Visualize model
>>> acp.stop(timeout=0)


Showing solid model
~~~~~~~~~~~~~~~~~~~

The visualization of a solid mesh and its elemental data is shown in the example :ref:`solid_model_example`.


{% else %}
.. note::

Expand Down
228 changes: 228 additions & 0 deletions examples/modeling_features/020-solid_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# 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.

"""
.. _solid_model_example:
Solid Model
===========
This example shows how to create and shape a solid model.
The solid model implements an extrusion algorithm which creates
a layered solid mesh based on the shell mesh and layup definition.
This solid mesh can be further processed by :class:`.ExtrusionGuide`,
:class:`.SnapToGeometry`, and :class:`.CutOffGeometry`.
"""
# %%
# Import the standard library and third-party dependencies.
import pathlib
import tempfile

import pyvista

# %%
# Import the PyACP dependencies.
from ansys.acp.core import (
ACPWorkflow,
CADGeometry,
CutOffGeometryOrientationType,
EdgeSetType,
ExtrusionGuideType,
SnapToGeometryOrientationType,
VirtualGeometry,
get_directions_plotter,
launch_acp,
)
from ansys.acp.core.extras import ExampleKeys, get_example_file

# sphinx_gallery_thumbnail_number = 4


# %%
# Load a minimal model
# ----------------------------
tempdir = tempfile.TemporaryDirectory()
WORKING_DIR = pathlib.Path(tempdir.name)
input_file = get_example_file(ExampleKeys.MINIMAL_FLAT_PLATE, WORKING_DIR)

# %%
# Launch the PyACP server and connect to it.
acp = launch_acp()

# %%
# Define the input file and instantiate an ``ACPWorkflow`` instance.
workflow = ACPWorkflow.from_acph5_file(
acp=acp,
acph5_file_path=input_file,
local_working_directory=WORKING_DIR,
)

model = workflow.model

# %%
# Create a simple layup
# ---------------------
# %%
# Add more layers to the modeling ply so that it is easier to see the
# effects of the selection rules.
modeling_ply = model.modeling_groups["modeling_group"].modeling_plies["ply"]
modeling_ply.number_of_layers = 3

# %%
# Create an initial solid model
# -----------------------------
#
# By default, the layup is extruded along the normal direction of the shell mesh.
solid_model = model.create_solid_model(
name="Solid Model",
element_sets=[model.element_sets["All_Elements"]],
)

model.update()
model.solid_mesh.to_pyvista().plot(show_edges=True)


def create_virtual_geometry_from_file(
example_key: ExampleKeys,
) -> tuple[CADGeometry, VirtualGeometry]:
"""Create a CAD geometry and virtual geometry."""
geometry_file = get_example_file(example_key, WORKING_DIR)
geometry_obj = workflow.add_cad_geometry_from_local_file(geometry_file)
workflow.model.update()
virtual_geometry = model.create_virtual_geometry(
name="thickness_virtual_geometry", cad_components=geometry_obj.root_shapes.values()
)
return geometry_obj, virtual_geometry


def plot_model_with_geometry(cad_geometry: CADGeometry, cad_geom_opacity: float = 0.1):
"""Plot solid model and geometry."""
plotter = pyvista.Plotter()
geom_mesh = cad_geometry.visualization_mesh.to_pyvista()
plotter.add_mesh(geom_mesh, color="green", opacity=cad_geom_opacity)
edges = geom_mesh.extract_feature_edges()
plotter.add_mesh(edges, color="white", line_width=4)
plotter.add_mesh(edges, color="black", line_width=2)
plotter.add_mesh(workflow.model.solid_mesh.to_pyvista(), show_edges=True)
plotter.show()


# %%
# Snap the top to a geometry
# --------------------------
#
# The :class:`.SnapToGeometry` allows to shape the bottom or top of the solid model.
# First, import the geometry and then add the snap-to feature to the solid model.
snap_to_geom, snap_to_virtual_geom = create_virtual_geometry_from_file(ExampleKeys.SNAP_TO_GEOMETRY)
solid_model.create_snap_to_geometry(
name="Snap-to Geometry",
cad_geometry=snap_to_virtual_geom,
orientation_type=SnapToGeometryOrientationType.TOP,
oriented_selection_set=model.oriented_selection_sets["oss"],
)

model.update()
plot_model_with_geometry(snap_to_geom, 0.5)

# %%
# Shape the walls
# ---------------
#
# The :class:`.ExtrusionGuide` is used to shape the side walls of the solid model.
# The feature can be defined by a direction as shown here or through a geometry.
edge_set = model.create_edge_set(
name="Edge Set",
edge_set_type=EdgeSetType.BY_REFERENCE,
element_set=model.element_sets["All_Elements"],
limit_angle=30,
origin=(0.05, 0, 0),
)
solid_model.create_extrusion_guide(
name="Extrusion Guide",
edge_set=edge_set,
extrusion_guide_type=ExtrusionGuideType.BY_DIRECTION,
direction=(-0.5, 1, 0),
radius=0.005,
depth=0.6,
)
model.update()
model.solid_mesh.to_pyvista().plot(show_edges=True)

# %%
# Cut-off an edge
# ---------------
#
# The :class:`.CutOffGeometry` is used to crop elements from the solid model.
cutoff_cad_geom, cutoff_virtual_geom = create_virtual_geometry_from_file(
ExampleKeys.CUT_OFF_GEOMETRY_SOLID_MODEL
)
solid_model.create_cut_off_geometry(
name="Cut-off Geometry",
cad_geometry=cutoff_virtual_geom,
orientation_type=CutOffGeometryOrientationType.UP,
)

model.update()
plot_model_with_geometry(cutoff_cad_geom)


# %%
# Plot results on the solid mesh
# ------------------------------
#
# The plotting capabilities also support the visualization of ply-wise results,
# such as directions or thicknesses as shown here.

# %%
# Get the analysis ply of interest
ap = (
model.modeling_groups["modeling_group"]
.modeling_plies["ply"]
.production_plies["ProductionPly.2"]
.analysis_plies["P2L1__ply"]
)

# %%
# Plot fiber directions
# ~~~~~~~~~~~~~~~~~~~~~
direction_plotter = get_directions_plotter(
model=model,
mesh=ap.solid_mesh,
components=[
ap.elemental_data.fiber_direction,
],
length_factor=10.0,
culling_factor=10,
)
direction_plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=True)
direction_plotter.show()

# %%
# Plot thicknesses
# ~~~~~~~~~~~~~~~~
thickness_data = ap.elemental_data.thickness
thickness_pyvista_mesh = thickness_data.get_pyvista_mesh(mesh=ap.solid_mesh) # type: ignore
plotter = pyvista.Plotter()
plotter.add_mesh(thickness_pyvista_mesh)
plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=True)
plotter.show()
4 changes: 2 additions & 2 deletions src/ansys/acp/core/extras/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
# SOFTWARE.
"""Extras of the Ansys Composites PrepPost module."""

from ansys.acp.core.extras.example_helpers import ExampleKeys, get_example_file # pragma: no cover
from ansys.acp.core.extras.example_helpers import ExampleKeys, get_example_file

__all__ = [ # pragma: no cover
__all__ = [
"ExampleKeys",
"get_example_file",
]
8 changes: 8 additions & 0 deletions src/ansys/acp/core/extras/example_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class ExampleKeys(Enum):
CLASS40_AGDB = auto()
CLASS40_CDB = auto()
MATERIALS_XML = auto()
SNAP_TO_GEOMETRY = auto()
CUT_OFF_GEOMETRY_SOLID_MODEL = auto()


EXAMPLE_FILES: dict[ExampleKeys, _ExampleLocation] = {
Expand Down Expand Up @@ -100,6 +102,12 @@ class ExampleKeys(Enum):
ExampleKeys.CLASS40_AGDB: _ExampleLocation(directory="class40", filename="class40.agdb"),
ExampleKeys.CLASS40_CDB: _ExampleLocation(directory="class40", filename="class40.cdb"),
ExampleKeys.MATERIALS_XML: _ExampleLocation(directory="materials", filename="materials.engd"),
ExampleKeys.SNAP_TO_GEOMETRY: _ExampleLocation(
directory="geometries", filename="snap_to_geometry.stp"
),
ExampleKeys.CUT_OFF_GEOMETRY_SOLID_MODEL: _ExampleLocation(
directory="geometries", filename="cut_off_geometry_solid_model.stp"
),
}


Expand Down
10 changes: 10 additions & 0 deletions tests/unittests/test_modeling_ply.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,13 @@ def test_linked_cutoff_selection_rule_operation_type(operation_type):
operation_type=operation_type,
)
assert "INTERSECT" in str(exc.value)


def test_taper_edge(parent_model):
edge_1 = parent_model.create_edge_set()
taper_edge = TaperEdge(edge_set=edge_1, angle=1, offset=2)
assert taper_edge != TaperEdge(edge_set=parent_model.create_edge_set(), angle=1, offset=2)
assert taper_edge != TaperEdge(edge_set=edge_1, angle=2, offset=2)
assert taper_edge != TaperEdge(edge_set=edge_1, angle=1, offset=3)
assert taper_edge == TaperEdge(edge_set=edge_1, angle=1, offset=2)
print(taper_edge)
8 changes: 8 additions & 0 deletions tests/unittests/test_stackup.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,11 @@ def test_add_fabric(parent_object):
stackup.add_fabric(fabric2, angle=45.0)
assert stackup.fabrics[-1].fabric == fabric2
assert stackup.fabrics[-1].angle == 45.0


def test_fabric_wit_angle(parent_object):
fabric1 = parent_object.create_fabric()
fabric_with_angle = FabricWithAngle(fabric=fabric1, angle=45.0)
assert fabric_with_angle != FabricWithAngle(fabric=parent_object.create_fabric(), angle=45.0)
assert fabric_with_angle != FabricWithAngle(fabric=fabric1, angle=55.0)
assert fabric_with_angle == FabricWithAngle(fabric=fabric1, angle=45.0)
14 changes: 14 additions & 0 deletions tests/unittests/test_sublaminate.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,17 @@ def test_add_lamina(parent_object):
assert sublaminate.materials[1].angle == 0.0
assert sublaminate.materials[2].material == fabric1
assert sublaminate.materials[2].angle == -45.0


def test_lamina(parent_object):
fabric1 = parent_object.create_fabric()
fabric1.material = parent_object.create_material()
stackup = parent_object.create_stackup()
stackup.add_fabric(fabric1, angle=30.0)
stackup.add_fabric(fabric1, angle=-30.0)

lamina = Lamina(material=fabric1, angle=45.0)
assert lamina != Lamina(material=stackup, angle=45.0)
assert lamina != Lamina(material=fabric1, angle=-45.0)
assert lamina == Lamina(material=fabric1, angle=45.0)
print(lamina)
10 changes: 10 additions & 0 deletions tests/unittests/test_virtual_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,13 @@ def test_virtual_geometry_no_or_invalid_links(parent_object, load_cad_geometry):
cad_components=cad_geometry.root_shapes.values(),
sub_shapes=[SubShape(cad_geometry=cad_geometry, path="some/path/to/shape")],
)


def test_sub_shape(parent_object, load_cad_geometry):
model = parent_object
with load_cad_geometry(model) as cad_geometry:
sub_shape = SubShape(cad_geometry=cad_geometry, path="some/path/to/shape")
with load_cad_geometry(model) as other_cad_geometry:
assert sub_shape != SubShape(cad_geometry=other_cad_geometry, path="some/path/to/shape")
assert sub_shape != SubShape(cad_geometry=cad_geometry, path="some/other/path/to/shape")
assert sub_shape == SubShape(cad_geometry=cad_geometry, path="some/path/to/shape")

0 comments on commit 88f7bc1

Please sign in to comment.