.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "examples/workflows/03-pymechanical-shell-workflow.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_examples_workflows_03-pymechanical-shell-workflow.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_examples_workflows_03-pymechanical-shell-workflow.py:


.. _pymechanical_shell_example:

PyMechanical shell workflow
===========================

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

- The geometry is imported into Mechanical and meshed.
- The mesh is exported to ACP.
- A simple lay-up is defined in ACP.
- Plies and materials are exported from ACP, and imported into Mechanical.
- Boundary conditions are set in Mechanical.
- The model is solved.
- The results are post-processed in PyDPF - Composites.

.. warning::

    The PyACP / PyMechanical integration is still experimental. Refer to the
    :ref:`limitations section <limitations>` for more information.

.. GENERATED FROM PYTHON SOURCE LINES 50-52

Import modules and start the Ansys products
-------------------------------------------

.. GENERATED FROM PYTHON SOURCE LINES 55-56

Import the standard library and third-party dependencies.

.. GENERATED FROM PYTHON SOURCE LINES 56-62

.. code-block:: Python


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


.. GENERATED FROM PYTHON SOURCE LINES 63-64

Import PyACP, PyMechanical, and PyDPF - Composites.

.. GENERATED FROM PYTHON SOURCE LINES 64-71

.. code-block:: Python


    # isort: off
    import ansys.acp.core as pyacp
    import ansys.dpf.composites as pydpf_composites
    import ansys.mechanical.core as pymechanical



.. GENERATED FROM PYTHON SOURCE LINES 73-75

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

.. GENERATED FROM PYTHON SOURCE LINES 75-83

.. code-block:: Python

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


.. GENERATED FROM PYTHON SOURCE LINES 84-89

Get example input files
-----------------------

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

.. GENERATED FROM PYTHON SOURCE LINES 89-96

.. code-block:: Python


    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
    )


.. GENERATED FROM PYTHON SOURCE LINES 97-102

Generate the mesh in PyMechanical
---------------------------------

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

.. GENERATED FROM PYTHON SOURCE LINES 102-136

.. code-block:: Python


    mesh_path = working_dir_path / "mesh.h5"
    mechanical.run_python_script(
        # This script runs in the Mechanical Python environment, which uses IronPython 2.7.
        textwrap.dedent(
            f"""\
            # Import the geometry
            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
            )

            # The thickness will be overridden by the ACP model, but is required
            # for the model to be valid.
            for body in Model.Geometry.GetChildren(
                Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True
            ):
                body.Thickness = Quantity(1e-6, "m")

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


.. GENERATED FROM PYTHON SOURCE LINES 137-142

Set up the ACP model
--------------------

Setup basic ACP lay-up based on the mesh in ``mesh_path``, and export material and composite
definition file to output_path.

.. GENERATED FROM PYTHON SOURCE LINES 142-204

.. code-block:: Python


    composite_definitions_h5 = "ACPCompositeDefinitions.h5"
    matml_file = "materials.xml"


    model = acp.import_model(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
    )


.. GENERATED FROM PYTHON SOURCE LINES 205-207

Update and Save the ACP model
-----------------------------

.. GENERATED FROM PYTHON SOURCE LINES 207-213

.. code-block:: Python


    model.update()

    model.export_shell_composite_definitions(working_dir_path / composite_definitions_h5)
    model.export_materials(working_dir_path / matml_file)


.. GENERATED FROM PYTHON SOURCE LINES 214-218

Import materials and plies into Mechanical
------------------------------------------

Import materials into Mechanical

.. GENERATED FROM PYTHON SOURCE LINES 218-221

.. code-block:: Python


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


.. GENERATED FROM PYTHON SOURCE LINES 222-223

Import plies into Mechanical

.. GENERATED FROM PYTHON SOURCE LINES 223-229

.. code-block:: Python


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


.. GENERATED FROM PYTHON SOURCE LINES 230-233

Set boundary condition and solve
---------------------------------


.. GENERATED FROM PYTHON SOURCE LINES 233-277

.. code-block:: Python


    mechanical.run_python_script(
        textwrap.dedent(
            """\
            front_edge = Model.AddNamedSelection()
            front_edge.Name = "Front Edge"
            front_edge.ScopingMethod = GeometryDefineByType.Worksheet

            front_edge.GenerationCriteria.Add(None)
            front_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge
            front_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX
            front_edge.GenerationCriteria[0].Operator = SelectionOperatorType.GreaterThan
            front_edge.GenerationCriteria[0].Value = Quantity('-4.6 [m]')
            front_edge.Generate()

            back_edge = Model.AddNamedSelection()
            back_edge.Name = "Back Edge"
            back_edge.ScopingMethod = GeometryDefineByType.Worksheet

            back_edge.GenerationCriteria.Add(None)
            back_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge
            back_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX
            back_edge.GenerationCriteria[0].Operator = SelectionOperatorType.LessThan
            back_edge.GenerationCriteria[0].Value = Quantity('-7.8 [m]')
            back_edge.Generate()

            analysis = Model.AddStaticStructuralAnalysis()

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

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

            analysis.Solution.Solve(True)
            """
        )
    )

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


.. GENERATED FROM PYTHON SOURCE LINES 278-282

Postprocess results
-------------------

Evaluate the failure criteria using the PyDPF - Composites.

.. GENERATED FROM PYTHON SOURCE LINES 282-312

.. code-block:: Python



    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={
                "shell": pydpf_composites.data_sources.CompositeDefinitionFiles(
                    definition=working_dir_path / 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()


.. _sphx_glr_download_examples_workflows_03-pymechanical-shell-workflow.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: 03-pymechanical-shell-workflow.ipynb <03-pymechanical-shell-workflow.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: 03-pymechanical-shell-workflow.py <03-pymechanical-shell-workflow.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: 03-pymechanical-shell-workflow.zip <03-pymechanical-shell-workflow.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_