Skip to content

Commit

Permalink
Sandwich and selection rule examples (#423)
Browse files Browse the repository at this point in the history
* Add new examples for a sandwich panel, basic and advanced selection rules.
* Small improvement to the Virtual Geometries API
* Add tests for the create method with non-default arguments for all the objects.
  • Loading branch information
janvonrickenbach authored Feb 19, 2024
1 parent c0767e4 commit 4b4d4a1
Show file tree
Hide file tree
Showing 32 changed files with 1,167 additions and 235 deletions.
181 changes: 181 additions & 0 deletions examples/003_sandwich_panel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
"""
.. _basic_sandwich_panel:
Basic sandwich panel
====================
Define a Composite Lay-up for a sandwich panel with PyACP. This example shows just the
pyACP part of the setup. For a complete Composite analysis,
see the :ref:`sphx_glr_examples_gallery_examples_001_basic_flat_plate.py` example
"""


# %%
# Import standard library and third-party dependencies
import pathlib
import tempfile

# %%
# Import pyACP dependencies
from ansys.acp.core import (
ACPWorkflow,
FabricWithAngle,
Lamina,
PlyType,
get_directions_plotter,
launch_acp,
print_model,
)
from ansys.acp.core.example_helpers import ExampleKeys, get_example_file
from ansys.acp.core.material_property_sets import ConstantEngineeringConstants, ConstantStrainLimits

# %%
# Get example file from server
tempdir = tempfile.TemporaryDirectory()
WORKING_DIR = pathlib.Path(tempdir.name)
input_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_CDB, WORKING_DIR)

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

# %%
# Define the input file and instantiate an ACPWorkflow
# The ACPWorkflow class provides convenience methods which simplify the file handling.
# It automatically creates a model based on the input file.

workflow = ACPWorkflow.from_cdb_file(
acp=acp,
cdb_file_path=input_file,
local_working_directory=WORKING_DIR,
)

model = workflow.model
print(workflow.working_directory.path)
print(model.unit_system)

# %%
# Visualize the loaded mesh
mesh = model.mesh.to_pyvista()
mesh.plot(show_edges=True)


# %%
# Create the UD material and its corresponding fabric
engineering_constants_ud = ConstantEngineeringConstants.from_orthotropic_constants(
E1=5e10, E2=1e10, E3=1e10, nu12=0.28, nu13=0.28, nu23=0.3, G12=5e9, G23=4e9, G31=4e9
)

strain_limit = 0.01
strain_limits = ConstantStrainLimits.from_orthotropic_constants(
eXc=-strain_limit,
eYc=-strain_limit,
eZc=-strain_limit,
eXt=strain_limit,
eYt=strain_limit,
eZt=strain_limit,
eSxy=strain_limit,
eSyz=strain_limit,
eSxz=strain_limit,
)

ud_material = model.create_material(
name="UD",
ply_type=PlyType.REGULAR,
engineering_constants=engineering_constants_ud,
strain_limits=strain_limits,
)

ud_fabric = model.create_fabric(name="UD", material=ud_material, thickness=0.002)

# %%
# Create a multi-axial Stackup and a Sublaminate. Sublaminates and Stackups help to quickly
# build repeating laminates.

biax_carbon_ud = model.create_stackup(
name="Biax_Carbon_UD",
fabrics=(
FabricWithAngle(ud_fabric, -45),
FabricWithAngle(ud_fabric, 45),
),
)


sublaminate = model.create_sublaminate(
name="Sublaminate",
materials=(
Lamina(biax_carbon_ud, 0),
Lamina(ud_fabric, 90),
Lamina(biax_carbon_ud, 0),
),
)


# %%
# Create the Core Material and its corresponding Fabric
engineering_constants_core = ConstantEngineeringConstants.from_isotropic_constants(E=8.5e7, nu=0.3)

core = model.create_material(
name="Core",
ply_type=PlyType.ISOTROPIC_HOMOGENEOUS_CORE,
engineering_constants=engineering_constants_core,
strain_limits=strain_limits,
)

core_fabric = model.create_fabric(name="core", material=ud_material, thickness=0.015)

# %%
# Define a rosette and an oriented selection set and plot the orientations
rosette = model.create_rosette(origin=(0.0, 0.0, 0.0), dir1=(1.0, 0.0, 0.0), dir2=(0.0, 1.0, 0.0))

oss = model.create_oriented_selection_set(
name="oss",
orientation_point=(0.0, 0.0, 0.0),
orientation_direction=(0.0, 1.0, 0),
element_sets=[model.element_sets["All_Elements"]],
rosettes=[rosette],
)

model.update()
assert oss.elemental_data.orientation is not None
plotter = get_directions_plotter(model=model, components=[oss.elemental_data.orientation])
plotter.show()

# %%
# Create the modeling plies which define the layup of the sandwich panel.
modeling_group = model.create_modeling_group(name="modeling_group")

bottom_ply = modeling_group.create_modeling_ply(
name="bottom_ply",
ply_angle=0,
ply_material=sublaminate,
oriented_selection_sets=[oss],
)

core_ply = modeling_group.create_modeling_ply(
name="core_ply",
ply_angle=0,
ply_material=core_fabric,
oriented_selection_sets=[oss],
)


top_ply = modeling_group.create_modeling_ply(
name="top_ply",
ply_angle=90,
ply_material=ud_fabric,
oriented_selection_sets=[oss],
number_of_layers=3,
)

# %%
# Update and print the model.
model.update()
print_model(workflow.model)
# sphinx_gallery_start_ignore
from ansys.acp.core.example_helpers import _run_analysis

# Run the analysis so we are sure all the material properties have been correctly
# defined.
_run_analysis(workflow)
# sphinx_gallery_end_ignore
156 changes: 156 additions & 0 deletions examples/004_basic_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"""
.. _basic_rules_example:
Basic rule example
==================
Shows the basic usage of selection rules.
Selection Rules enable you to select elements through
geometrical operations and thus to shape plies.
This example shows just the
pyACP part of the setup. See the :ref:`sphx_glr_examples_gallery_examples_005_advanced_rules.py`
for more advanced rule examples. For a complete Composite analysis,
see the :ref:`sphx_glr_examples_gallery_examples_001_basic_flat_plate.py` example
"""


# %%
# Import standard library and third-party dependencies
import pathlib
import tempfile

# %%
# Import pyACP dependencies
from ansys.acp.core import ACPWorkflow, LinkedSelectionRule, PlyType, launch_acp
from ansys.acp.core.example_helpers import ExampleKeys, get_example_file
from ansys.acp.core.material_property_sets import ConstantEngineeringConstants

# %%
# Get example file from server
tempdir = tempfile.TemporaryDirectory()
WORKING_DIR = pathlib.Path(tempdir.name)
input_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_CDB, WORKING_DIR)

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

# %%
# Define the input file and instantiate an ACPWorkflow
# The ACPWorkflow class provides convenience methods which simplify the file handling.
# It automatically creates a model based on the input file.

workflow = ACPWorkflow.from_cdb_file(
acp=acp,
cdb_file_path=input_file,
local_working_directory=WORKING_DIR,
)

model = workflow.model
print(workflow.working_directory.path)
print(model.unit_system)

# %%
# Visualize the loaded mesh
mesh = model.mesh.to_pyvista()
mesh.plot(show_edges=True)


# %%
# Create the UD material and its corresponding fabric
engineering_constants_ud = ConstantEngineeringConstants.from_orthotropic_constants(
E1=5e10, E2=1e10, E3=1e10, nu12=0.28, nu13=0.28, nu23=0.3, G12=5e9, G23=4e9, G31=4e9
)

ud_material = model.create_material(
name="UD",
ply_type=PlyType.REGULAR,
engineering_constants=engineering_constants_ud,
)

ud_fabric = model.create_fabric(name="UD", material=ud_material, thickness=0.002)

# %%
# Define a rosette and an oriented selection set
rosette = model.create_rosette(origin=(0.0, 0.0, 0.0), dir1=(1.0, 0.0, 0.0), dir2=(0.0, 1.0, 0.0))

oss = model.create_oriented_selection_set(
name="oss",
orientation_point=(0.0, 0.0, 0.0),
orientation_direction=(0.0, 1.0, 0),
element_sets=[model.element_sets["All_Elements"]],
rosettes=[rosette],
)

# %%
# Create a ply with an attached parallel selection rule and plot the ply extent

parallel_rule = model.create_parallel_selection_rule(
name="parallel_rule",
origin=(0, 0, 0),
direction=(1, 0, 0),
lower_limit=0.005,
upper_limit=1,
)

modeling_group = model.create_modeling_group(name="modeling_group")

partial_ply = modeling_group.create_modeling_ply(
name="partial_ply",
ply_angle=90,
ply_material=ud_fabric,
oriented_selection_sets=[oss],
selection_rules=[LinkedSelectionRule(parallel_rule)],
number_of_layers=10,
)

model.update()
assert model.elemental_data.thickness is not None
model.elemental_data.thickness.get_pyvista_mesh(mesh=model.mesh).plot(show_edges=True)

# %%
# Create a cylindrical selection rule and add it to the ply. This will intersect the two rules.
cylindrical_rule = model.create_cylindrical_selection_rule(
name="cylindrical_rule",
origin=(0.005, 0, 0.005),
direction=(0, 1, 0),
radius=0.002,
)

partial_ply.selection_rules.append(LinkedSelectionRule(cylindrical_rule))

model.update()
assert model.elemental_data.thickness is not None
model.elemental_data.thickness.get_pyvista_mesh(mesh=model.mesh).plot(show_edges=True)

# %%
# Create a spherical selection rule and assign it to the ply. Now only the spherical rule is
# active.
spherical_rule = model.create_spherical_selection_rule(
name="spherical_rule",
origin=(0.003, 0, 0.005),
radius=0.002,
)

partial_ply.selection_rules = [LinkedSelectionRule(spherical_rule)]

model.update()
assert model.elemental_data.thickness is not None
model.elemental_data.thickness.get_pyvista_mesh(mesh=model.mesh).plot(show_edges=True)

# %%
# Create a tube selection rule and assign it to the ply. Now only the tube rule is
# active.
tube_rule = model.create_tube_selection_rule(
name="spherical_rule",
# Select the pre-exsting _FIXEDSU edge which is the edge at x=0
edge_set=model.edge_sets["_FIXEDSU"],
inner_radius=0.001,
outer_radius=0.003,
)

partial_ply.selection_rules = [LinkedSelectionRule(tube_rule)]

model.update()
assert model.elemental_data.thickness is not None
model.elemental_data.thickness.get_pyvista_mesh(mesh=model.mesh).plot(show_edges=True)
Loading

0 comments on commit 4b4d4a1

Please sign in to comment.