.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "examples/workflows/05-pymechanical-to-cdb-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_05-pymechanical-to-cdb-workflow.py>`
        to download the full example code.

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

.. _sphx_glr_examples_workflows_05-pymechanical-to-cdb-workflow.py:


.. _pymechanical_to_cdb_example:

PyMechanical to CDB shell workflow
==================================

This example shows how to set up a workflow that uses PyMechanical to mesh the
geometry and define the load case, PyACP to define a layup, PyMAPDL to solve the
model, and PyDPF - Composites to post-process the results.

This workflow does *not* suffer from the limitations of the PyACP to
PyMechanical integration.

.. GENERATED FROM PYTHON SOURCE LINES 42-44

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

.. GENERATED FROM PYTHON SOURCE LINES 47-48

Import the standard library and third-party dependencies.

.. GENERATED FROM PYTHON SOURCE LINES 48-54

.. code-block:: Python


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


.. GENERATED FROM PYTHON SOURCE LINES 55-56

Import PyACP, PyMechanical, and PyDPF - Composites.

.. GENERATED FROM PYTHON SOURCE LINES 56-65

.. code-block:: Python


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



.. GENERATED FROM PYTHON SOURCE LINES 67-69

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

.. GENERATED FROM PYTHON SOURCE LINES 69-79

.. code-block:: Python

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


.. GENERATED FROM PYTHON SOURCE LINES 80-85

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

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

.. GENERATED FROM PYTHON SOURCE LINES 85-92

.. 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 93-98

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

Load the geometry into Mechanical, generate the mesh, and define the
load case.

.. GENERATED FROM PYTHON SOURCE LINES 98-169

.. code-block:: Python


    cdb_path_initial = working_dir_path / "model_from_mechanical.cdb"
    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()

            # Define named selections at the front and back edges
            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()

            # Create a static structural analysis, and define the boundary
            # conditions (fixed support at the back edge, force at the front edge).
            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

            # Export the model to a CDB file
            analysis.WriteInputFile({str(cdb_path_initial)!r})
            """
        )
    )


.. GENERATED FROM PYTHON SOURCE LINES 170-174

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

Setup basic ACP lay-up based on the CDB file.

.. GENERATED FROM PYTHON SOURCE LINES 174-233

.. code-block:: Python



    model = acp.import_model(path=cdb_path_initial, format="ansys:cdb")

    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 234-236

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

.. GENERATED FROM PYTHON SOURCE LINES 236-247

.. code-block:: Python


    model.update()

    cdb_filename_out = "model_from_acp.cdb"
    composite_definitions_h5_filename = "ACPCompositeDefinitions.h5"
    matml_filename = "materials.xml"

    model.export_analysis_model(working_dir_path / cdb_filename_out)
    model.export_shell_composite_definitions(working_dir_path / composite_definitions_h5_filename)
    model.export_materials(working_dir_path / matml_filename)


.. GENERATED FROM PYTHON SOURCE LINES 248-250

Solve with PyMAPDL
------------------

.. GENERATED FROM PYTHON SOURCE LINES 250-252

.. code-block:: Python


    mapdl.clear()

.. GENERATED FROM PYTHON SOURCE LINES 253-254

Load the CDB file into PyMAPDL.

.. GENERATED FROM PYTHON SOURCE LINES 254-256

.. code-block:: Python

    mapdl.input(str(working_dir_path / cdb_filename_out))


.. GENERATED FROM PYTHON SOURCE LINES 257-258

Solve the model.

.. GENERATED FROM PYTHON SOURCE LINES 258-262

.. code-block:: Python

    mapdl.allsel()
    mapdl.slashsolu()
    mapdl.solve()


.. GENERATED FROM PYTHON SOURCE LINES 263-264

Show the displacements in postprocessing.

.. GENERATED FROM PYTHON SOURCE LINES 264-268

.. code-block:: Python

    mapdl.post1()
    mapdl.set("last")
    mapdl.post_processing.plot_nodal_displacement(component="NORM")


.. GENERATED FROM PYTHON SOURCE LINES 269-270

Download the RST file for further postprocessing.

.. GENERATED FROM PYTHON SOURCE LINES 270-274

.. code-block:: Python

    rstfile_name = f"{mapdl.jobname}.rst"
    rst_file_local_path = working_dir_path / rstfile_name
    mapdl.download(rstfile_name, working_dir_path)


.. GENERATED FROM PYTHON SOURCE LINES 275-279

Postprocessing with PyDPF - Composites
--------------------------------------

Specify the combined failure criterion.

.. GENERATED FROM PYTHON SOURCE LINES 279-286

.. code-block:: Python

    max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion()

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


.. GENERATED FROM PYTHON SOURCE LINES 287-288

Create the composite model and configure its input.

.. GENERATED FROM PYTHON SOURCE LINES 288-302

.. code-block:: Python

    composite_model = pydpf_composites.composite_model.CompositeModel(
        composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles(
            rst=rst_file_local_path,
            composite={
                "shell": pydpf_composites.data_sources.CompositeDefinitionFiles(
                    definition=working_dir_path / composite_definitions_h5_filename
                ),
            },
            engineering_data=working_dir_path / matml_filename,
        ),
        default_unit_system=pydpf_core.unit_system.unit_systems.solver_nmm,
        server=dpf,
    )


.. GENERATED FROM PYTHON SOURCE LINES 303-304

Evaluate the failure criteria.

.. GENERATED FROM PYTHON SOURCE LINES 304-306

.. code-block:: Python

    output_all_elements = composite_model.evaluate_failure_criteria(cfc)


.. GENERATED FROM PYTHON SOURCE LINES 307-312

Query and plot the results.

Note that the maximum IRF is different when compared to :ref:`pymechanical_shell_example`
because ACP sets the ``ERESX,NO`` option in the CDB file. This option disables interpolation
of the results from the integration point to the nodes.

.. GENERATED FROM PYTHON SOURCE LINES 312-320

.. code-block:: Python


    irf_field = output_all_elements.get_field(
        {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE}
    )
    irf_field.plot()

    # Close MAPDL instance
    mapdl.exit()


.. _sphx_glr_download_examples_workflows_05-pymechanical-to-cdb-workflow.py:

.. only:: html

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

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

      :download:`Download Jupyter notebook: 05-pymechanical-to-cdb-workflow.ipynb <05-pymechanical-to-cdb-workflow.ipynb>`

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

      :download:`Download Python source code: 05-pymechanical-to-cdb-workflow.py <05-pymechanical-to-cdb-workflow.py>`

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

      :download:`Download zipped: 05-pymechanical-to-cdb-workflow.zip <05-pymechanical-to-cdb-workflow.zip>`


.. only:: html

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

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