PyMechanical solid workflow#

This example shows how to set up a simple solid model with PyACP and PyMechanical:

  • The geometry is imported into Mechanical and meshed.

  • The mesh is exported to ACP.

  • A simple lay-up and solid model is defined in ACP.

  • The solid model is exported, to a CDB file and a composite definition file.

  • In a separate Mechanical instance, the solid model is imported.

  • Materials and plies are imported.

  • Boundary conditions are set.

  • The model is solved.

  • The results are post-processed in PyDPF - Composites.

Warning

The PyACP / PyMechanical integration is still experimental. Refer to the limitations section for more information.

Note

Outputs and plots for this example are not shown in the rendered online documentation. To see the outputs and plots, run the example script or Jupyter notebook locally.

Import modules and start the Ansys products#

Import the standard library and third-party dependencies.

from concurrent.futures import ThreadPoolExecutor
import pathlib
import tempfile
import textwrap

Import PyACP, PyMechanical, and PyDPF - Composites.

# isort: off

import ansys.acp.core as pyacp
from ansys.acp.core.extras import set_plot_theme
import ansys.dpf.composites as pydpf_composites
import ansys.mechanical.core as pymechanical

Set the plot theme for the example. This is optional, and ensures that you get the same plot style (theme, color map, etc.) as in the online documentation.

set_plot_theme()

Start the ACP, Mechanical, and DPF servers. We use a ThreadPoolExecutor to start them in parallel.

with ThreadPoolExecutor() as executor:
    futures = [
        executor.submit(pymechanical.launch_mechanical, batch=True),
        executor.submit(pymechanical.launch_mechanical, batch=True),
        executor.submit(pyacp.launch_acp),
        executor.submit(pydpf_composites.server_helpers.connect_to_or_start_server),
    ]
    mechanical_shell_geometry, mechanical_solid_model, acp, dpf = (fut.result() for fut in futures)

Get example input files#

Create a temporary working directory, and download the example input files to this directory.

working_dir = tempfile.TemporaryDirectory()
working_dir_path = pathlib.Path(working_dir.name)
input_geometry = pyacp.extras.example_helpers.get_example_file(
    pyacp.extras.example_helpers.ExampleKeys.CLASS40_AGDB, working_dir_path
)

Generate the mesh in PyMechanical#

Load the geometry into Mechanical, generate the mesh, and export it to the appropriate transfer format for ACP.

mesh_path = working_dir_path / "mesh.h5"
mechanical_shell_geometry.run_python_script(
    # This script runs in the Mechanical Python environment, which uses IronPython 2.7.
    textwrap.dedent(
        f"""\
        geometry_import = Model.GeometryImportGroup.AddGeometryImport()

        import_format = Ansys.Mechanical.DataModel.Enums.GeometryImportPreference.Format.Automatic
        import_preferences = Ansys.ACT.Mechanical.Utilities.GeometryImportPreferences()
        import_preferences.ProcessNamedSelections = True
        import_preferences.ProcessCoordinateSystems = True

        geometry_file = {str(input_geometry)!r}
        geometry_import.Import(
            geometry_file,
            import_format,
            import_preferences
        )

        for body in Model.Geometry.GetChildren(
            Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True
        ):
            body.Thickness = Quantity(1e-6, "m")

        hull = Model.AddNamedSelection()
        hull.Name = "hull"
        hull.ScopingMethod = GeometryDefineByType.Worksheet
        # Add all faces with Z location < 0.9 m
        hull.GenerationCriteria.Add(None)
        hull.GenerationCriteria[0].EntityType = SelectionType.GeoFace
        hull.GenerationCriteria[0].Operator = SelectionOperatorType.LessThan
        hull.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationZ
        hull.GenerationCriteria[0].Value = Quantity('0.9 [m]')
        # Remove keeltower
        hull.GenerationCriteria.Add(None)
        hull.GenerationCriteria[1].Action = SelectionActionType.Remove
        hull.GenerationCriteria[1].Criterion = SelectionCriterionType.LocationX
        hull.GenerationCriteria[1].Operator = SelectionOperatorType.RangeInclude
        hull.GenerationCriteria[1].LowerBound = Quantity('-6.7 [m]')
        hull.GenerationCriteria[1].UpperBound = Quantity('-5.9 [m]')
        # Add back keeltower bottom
        hull.GenerationCriteria.Add(None)
        hull.GenerationCriteria[2].Criterion = SelectionCriterionType.LocationZ
        hull.GenerationCriteria[2].Operator = SelectionOperatorType.LessThan
        hull.GenerationCriteria[2].Value = Quantity('-0.25 [m]')
        # Remove bulkhead
        hull.GenerationCriteria.Add(None)
        hull.GenerationCriteria[3].Action = SelectionActionType.Remove
        hull.GenerationCriteria[3].Criterion = SelectionCriterionType.LocationX
        hull.GenerationCriteria[3].Operator = SelectionOperatorType.RangeInclude
        hull.GenerationCriteria[3].LowerBound = Quantity('-5.7 [m]')
        hull.GenerationCriteria[3].UpperBound = Quantity('-5.6 [m]')
        hull.Generate()

        Model.Mesh.GenerateMesh()
        """
    )
)
pyacp.mechanical_integration_helpers.export_mesh_for_acp(
    mechanical=mechanical_shell_geometry, path=mesh_path
)

Set up the ACP model#

Setup basic ACP lay-up based on the mesh in mesh_path, and export the following files to output_path:

  • Materials XML file

  • Composite definitions HDF5 file

  • Solid model composite definitions HDF5 file

  • Solid model CDB file

matml_file = "materials.xml"
solid_model_cdb_file = "SolidModel.cdb"
solid_model_composite_definitions_h5 = "SolidModel.h5"

model = acp.import_model(path=mesh_path, format="ansys:h5")

mat = model.create_material(name="mat")

mat.ply_type = "regular"
mat.engineering_constants.E1 = 1e12
mat.engineering_constants.E2 = 1e11
mat.engineering_constants.E3 = 1e11
mat.engineering_constants.G12 = 1e10
mat.engineering_constants.G23 = 1e10
mat.engineering_constants.G31 = 1e10
mat.engineering_constants.nu12 = 0.3
mat.engineering_constants.nu13 = 0.3
mat.engineering_constants.nu23 = 0.3

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

corecell_81kg_5mm = model.create_fabric(name="Corecell 81kg", thickness=0.005, material=mat)

ros = model.create_rosette(name="ros", origin=(0, 0, 0))

oss = model.create_oriented_selection_set(
    name="oss",
    orientation_point=(-0, 0, 0),
    orientation_direction=(0.0, 1, 0.0),
    element_sets=[model.element_sets["All_Elements"]],
    rosettes=[ros],
)

mg = model.create_modeling_group(name="group")
mg.create_modeling_ply(
    name="ply",
    ply_material=corecell_81kg_5mm,
    oriented_selection_sets=[oss],
    ply_angle=45,
    number_of_layers=1,
    global_ply_nr=0,  # add at the end
)
mg.create_modeling_ply(
    name="ply2",
    ply_material=corecell_81kg_5mm,
    oriented_selection_sets=[oss],
    ply_angle=0,
    number_of_layers=2,
    global_ply_nr=0,  # add at the end
)

solid_model = model.create_solid_model(
    element_sets=[model.element_sets["hull"]],
)

Update and Save the ACP model#

model.update()

model.export_materials(working_dir_path / matml_file)
solid_model.export(working_dir_path / solid_model_cdb_file, format="ansys:cdb")
solid_model.export(working_dir_path / solid_model_composite_definitions_h5, format="ansys:h5")

Import mesh, materials and plies into Mechanical#

Import geometry, mesh, and named selections into Mechanical

pyacp.mechanical_integration_helpers.import_acp_mesh_from_cdb(
    mechanical=mechanical_solid_model, cdb_path=working_dir_path / solid_model_cdb_file
)

Import materials into Mechanical

mechanical_solid_model.run_python_script(
    f"Model.Materials.Import({str(working_dir_path / matml_file)!r})"
)

Import plies into Mechanical

pyacp.mechanical_integration_helpers.import_acp_composite_definitions(
    mechanical=mechanical_solid_model, path=working_dir_path / solid_model_composite_definitions_h5
)

Set boundary condition and solve#

Set boundary condition and solve

mechanical_solid_model.run_python_script(
    textwrap.dedent(
        """\
        analysis = Model.AddStaticStructuralAnalysis()

        front_face = Model.AddNamedSelection()
        front_face.Name = "front_face"
        front_face.ScopingMethod = GeometryDefineByType.Worksheet
        front_face.GenerationCriteria.Add(None)
        front_face.GenerationCriteria[0].EntityType = SelectionType.GeoFace
        front_face.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX
        front_face.GenerationCriteria[0].Operator = SelectionOperatorType.Largest
        front_face.Generate()

        back_face = Model.AddNamedSelection()
        back_face.Name = "back_face"
        back_face.ScopingMethod = GeometryDefineByType.Worksheet
        back_face.GenerationCriteria.Add(None)
        back_face.GenerationCriteria[0].EntityType = SelectionType.GeoFace
        back_face.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX
        back_face.GenerationCriteria[0].Operator = SelectionOperatorType.Smallest
        back_face.Generate()

        fixed_support = analysis.AddFixedSupport()
        fixed_support.Location = back_face

        force = analysis.AddForce()
        force.DefineBy = LoadDefineBy.Components
        force.XComponent.Output.SetDiscreteValue(0, Quantity(1e5, "N"))
        force.Location = front_face

        analysis.Solve(True)
        """
    )
)

rst_file = [
    filename for filename in mechanical_solid_model.list_files() if filename.endswith(".rst")
][0]
matml_out = [
    filename for filename in mechanical_solid_model.list_files() if filename.endswith("MatML.xml")
][0]

Postprocess results#

Evaluate the failure criteria using the PyDPF - Composites.

max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion()
cfc = pydpf_composites.failure_criteria.CombinedFailureCriterion(
    name="Combined Failure Criterion",
    failure_criteria=[max_strain],
)

composite_model = pydpf_composites.composite_model.CompositeModel(
    composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles(
        rst=rst_file,
        composite={
            "solid": pydpf_composites.data_sources.CompositeDefinitionFiles(
                definition=working_dir_path / solid_model_composite_definitions_h5
            ),
        },
        engineering_data=working_dir_path / matml_out,
    ),
    server=dpf,
)

# Evaluate the failure criteria
output_all_elements = composite_model.evaluate_failure_criteria(cfc)

# Query and plot the results
irf_field = output_all_elements.get_field(
    {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE}
)

irf_field.plot()

Gallery generated by Sphinx-Gallery