From eb3417b97d8bee76bbdc97e01722dcc06f072e65 Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Mon, 18 Aug 2025 09:53:10 -0500 Subject: [PATCH 001/100] draft --- mapdl-dpf/README.rst | 4 + mapdl-dpf/requirements_24.1.txt | 2 + mapdl-dpf/requirements_24.2.txt | 3 + mapdl-dpf/requirements_25.1.txt | 3 + mapdl-dpf/wf_mapdl-dpf.py | 350 ++++++++++++++++++++++++++++++++ 5 files changed, 362 insertions(+) create mode 100644 mapdl-dpf/README.rst create mode 100644 mapdl-dpf/requirements_24.1.txt create mode 100644 mapdl-dpf/requirements_24.2.txt create mode 100644 mapdl-dpf/requirements_25.1.txt create mode 100644 mapdl-dpf/wf_mapdl-dpf.py diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst new file mode 100644 index 000000000..42f1490ca --- /dev/null +++ b/mapdl-dpf/README.rst @@ -0,0 +1,4 @@ +TBD +=== + +TBD diff --git a/mapdl-dpf/requirements_24.1.txt b/mapdl-dpf/requirements_24.1.txt new file mode 100644 index 000000000..b328588a2 --- /dev/null +++ b/mapdl-dpf/requirements_24.1.txt @@ -0,0 +1,2 @@ + +ansys-dpf-core[plotting]==0.12.2 diff --git a/mapdl-dpf/requirements_24.2.txt b/mapdl-dpf/requirements_24.2.txt new file mode 100644 index 000000000..f431217ae --- /dev/null +++ b/mapdl-dpf/requirements_24.2.txt @@ -0,0 +1,3 @@ +ansys-mechanical-core==0.11.10 +ansys-fluent-core==0.26.1 +matplotlib==3.10.0 diff --git a/mapdl-dpf/requirements_25.1.txt b/mapdl-dpf/requirements_25.1.txt new file mode 100644 index 000000000..15189f805 --- /dev/null +++ b/mapdl-dpf/requirements_25.1.txt @@ -0,0 +1,3 @@ + +ansys-dpf-core[plotting]==0.12.2 +ansys.mapdl.core==0.64.0 \ No newline at end of file diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py new file mode 100644 index 000000000..a2bd16157 --- /dev/null +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -0,0 +1,350 @@ +# Copyright (C) 2024 - 2025 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. + +""" +.. _global-local_1: + +Consecutive submodeling with MAPDL pool +---------------------------- +Problem description: + - In this example we demonstrate how to use MAPDL pool to + perform a consecutive submodeling simulation. + +Analysis type: + - Static Analysis + +Material properties: + - Youngs modulus, :math:`E = 200 \, GPa` + - Poissons ratio, :math:`\mu = 0.3` + +Boundary conditions (global model): + - Fixed support applied at the bottom side + - Frictionless support applied at the right side + +Loading: + - Total displacement of –1 mm in the Y‑direction at the top surface, ramped linearly over 10 timesteps + +.. image:: ../_static/bvp.png + :width: 500 + :alt: Problem Sketch + +Modeling notes: + - At each timestep, the global model is solved with the specified boundary conditions; + the resulting nodal displacements are interpolated to the boundary nodes of the local model, using the DPF + interpolation operator. Those displacements are enforced as constraints to the local model, which is then solved + completing that timestep. +""" + +import os +import shutil +import time as tt + +from ansys.dpf import core as dpf +from ansys.mapdl.core import MapdlPool +import numpy as np + +############################################################################### +# Create directories to save the results +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +for fdr in folders: + try: + shutil.rmtree(fdr, ignore_errors=True) + os.makedirs(fdr) + except: + pass + +############################################################################### +# Create Mapdl pool +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# the global simulation and the other to the local simulation + +exec_file = "C:/Program Files/ANSYS Inc/v251/ansys/bin/winx64/ANSYS251.exe" +nCores = 2 +pool = MapdlPool(2, run_location="./Output/Common", nproc=nCores, exec_file=exec_file) + +############################################################################### +# Set up Global and Local FE models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We assign the instances to the local and global model, then use ``mapdl.cdread`` to load their geometry and mesh. +# Note the the .cdb files include named selections for the faces we want to apply the boundary conditions and the loads. +# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# The function ``Get_boundary`` is used to record the local model’s cut-boundary node coordinates as a dpf.field +# which will be later used in the DPF interpolator input + +mapdl_global = pool[0] # Global model +mapdl_global.cdread("db", "global.cdb") # Load global model +mapdl_global.cwd(os.getcwd() + "./Output/Global") # Set directory of the global model + +mapdl_local = pool[1] # Local model +mapdl_local.cdread("db", "local.cdb") # Load local model +mapdl_local.cwd(os.getcwd() + "./Output/Local") # Set directory of the local model + + +def define_BCs(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() + + # In the .cdb file for the global model the bottom, the right and the top faces + # are saved as named selections + + # Fixed support + mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face + mapdl.d("ALL", "ALL") + mapdl.nsel("ALL") + + # Frictionless support + mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face + mapdl.d("ALL", "UZ", "0") + mapdl.nsel("ALL") + + # Applied load + # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps + mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") + mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") + mapdl.starset("LOAD(1,1,1)", "0.") + mapdl.starset("LOAD(2,1,1)", "-0.1") + mapdl.starset("LOAD(3,1,1)", "-1.") + + mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face + mapdl.d("ALL", "UY", "%LOAD%") + mapdl.nsel("ALL") + + # Exit PREP7 + mapdl.finish() + pass + + +def Get_boundary(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() + + # In the .cdb file for the local model the boundary faces are saved as + # named selections + + mapdl.nsel("all") + nodes = mapdl.mesh.nodes # All nodes + node_id_all = mapdl.mesh.nnum # All nodes ID + mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces + node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID + map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + + mapdl.nsel("NONE") + boundary_coordinates = dpf.fields_factory.create_3d_vector_field( + num_entities=len(node_id_subset), location="Nodal" + ) # Define DPF field for DPF interpolator input + + nsel = "" + for nid in node_id_subset: # Iterate boundary nodes of the local model + nsel += "nsel,A,NODE,,{}\n".format( + nid + ) # Add selection command for the node to the str (only for ploting) + boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + + # Select all boundary nodes (only for ploting) + mapdl.input_strings(nsel) + + # Plot boundary nodes of the local model + mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + + # Exit PREP7 + mapdl.finish() + return boundary_coordinates + + +# Define the boundary conditions and the loading for the global model +define_BCs(mapdl_global) + +# Get the DPF field with the boundary nodes of the local model +boundary_coords = Get_boundary(mapdl_local) + +############################################################################### +# Set up DPF operators +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We define two dpf operators: the first reads the displacement results from the global model, +# and the second interpolates those displacements onto the boundary coordinates of the local model. +# The ``DataSources`` class to link results with the DPF operator inputs. + + +def define_dpf_operators(nCores): + # Define the DataSources class and link it to the results of the global model + dataSources = dpf.DataSources() + rst = r".\Output\Global\file{}.rst" + for i in range(nCores): + dataSources.set_domain_result_file_path(path=rst.format(i), key="rst", domain_id=i) + + global_model = dpf.Model(dataSources) + global_disp_op = ( + dpf.operators.result.displacement() + ) # Define displacement result operator to read nodal displacements + global_disp_op.inputs.data_sources.connect( + dataSources + ) # Connect displacement result operator with the global model's results file + disp_interpolator = ( + dpf.operators.mapping.on_coordinates() + ) # Define interpolator to interpolate the results inside the mesh elements with shape functions + return global_model, global_disp_op, disp_interpolator + + +def initialize_dpf_interpolator( + global_model, + local_Bc_coords, + disp_interpolator, +): + my_mesh = global_model.metadata.meshed_region # Global model's mesh + disp_interpolator.inputs.coordinates.connect( + local_Bc_coords + ) # Link interpolator inputs with the local model's boundary coordinates + disp_interpolator.inputs.mesh.connect( + my_mesh + ) # Link interpolator mesh with the global model's mesh + + +def interpolate_data(timestep): + global_disp_op.inputs.time_scoping.connect( + [timestep] + ) # Specify timestep value to read results from + global_disp = ( + global_disp_op.outputs.fields_container.get_data() + ) # Read global nodal displacements + + disp_interpolator.inputs.fields_container.connect( + global_disp + ) # Link the interpolation data with the interpolator + local_disp = disp_interpolator.outputs.fields_container.get_data()[ + 0 + ] # Get displacements of the boundary nodes of the local model + return local_disp + + +# Define the two dpf operators +global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) + +############################################################################### +# Set up simulation loop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We solve the two models sequentially for each loading step. First the global model is run producing +# a .rst results file. Then we extract the global displacements and use them to define +# cut-boundary conditions for the local model (an input string command will be used for faster excecution time). + + +def define_cut_boundary_constraint_template(local_Bc_coords): + # Define template of input string command to apply the displacement constraints + local_nids = local_Bc_coords.scoping.ids # Get Node ID of boundary nodes of the local model + template = "" + for nid in local_nids: + template += ( + "d," + + str(nid) + + ",ux,{:1.6e}\nd," + + str(nid) + + ",uy,{:1.6e}\nd," + + str(nid) + + ",uz,{:1.6e}\n" + ) + return template + + +def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): + + # Enter solution processor + mapdl_global.solution() + mapdl_local.solution() + + # Static analysis + mapdl_global.antype("STATIC") + mapdl_local.antype("STATIC") + + constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + + for i in range(1, timesteps + 1): # Iterate timesteps + print(f"Timestep: {i}") + st = tt.time() + # Set loadstep time for the global model + mapdl_global.time(i) + # No extrapolation + mapdl_global.eresx("NO") + mapdl_global.allsel("ALL") + # Write ALL results to database + mapdl_global.outres("ALL", "ALL") + # Solve global model + mapdl_global.solve() + print("Global solve took ", tt.time() - st) + + # Initialize interpolator + if i == 1: + initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) + # Read & Interpolate displacement data + local_disp = interpolate_data(timestep=i) + # Run MAPDL input string command to apply the displacement constraints + data_array = np.array(local_disp.data).flatten() + mapdl_local.input_strings(constraint_template.format(*data_array)) + + st = tt.time() + mapdl_local.allsel("ALL") + # Set loadstep time for the local model + mapdl_local.time(i) + # No extrapolation + mapdl_local.eresx("NO") + # Write ALL results to database + mapdl_local.outres("ALL", "ALL") + # Solve local model + mapdl_local.solve() + print("Local solve took ", tt.time() - st) + + # Exit solution processor + mapdl_global.finish() + mapdl_local.finish() + + +############################################################################### +# Solve system +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +n_steps = 10 # Number of timesteps +solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) + +############################################################################### +# Visualize results +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def visualize(mapdl): + # Enter post-processing + mapdl.post1() + # Set the current results set to the last set to be read from result file + mapdl.set("LAST") + # Plot nodal displacement of the loading direction + mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") + # Exit post-processing + mapdl.finish() + + +# Plot Y displacement of global model +visualize(mapdl_global) + +# Plot Y displacement of local model +visualize(mapdl_local) + +# Exit MAPDL pool instances +pool.exit() From 498eb2d1dcfa7811daeba823e6dabf5a87ee26b0 Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Mon, 18 Aug 2025 10:15:45 -0500 Subject: [PATCH 002/100] draft --- .github/workflows/docs.yml | 12 +- .github/workflows/mapdl-dpf.yml | 232 ++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/mapdl-dpf.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 55551e0f8..f15732ae0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,23 +14,29 @@ jobs: uses: ./.github/workflows/geometry-mesh.yml secrets: inherit with: - doc-build: true + doc-build: false geometry-mesh-fluent: uses: ./.github/workflows/geometry-mesh-fluent.yml secrets: inherit with: - doc-build: true + doc-build: false geometry-mechanical-dpf: uses: ./.github/workflows/geometry-mechanical-dpf.yml secrets: inherit with: - doc-build: true + doc-build: false fluent-mechanical: uses: ./.github/workflows/fluent-mechanical.yml secrets: inherit + with: + doc-build: false + + mapdl-dpf: + uses: ./.github/workflows/mapdl-dpf.yml + secrets: inherit with: doc-build: true diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml new file mode 100644 index 000000000..8a7240b4b --- /dev/null +++ b/.github/workflows/mapdl-dpf.yml @@ -0,0 +1,232 @@ +name: MAPDL-DPF Workflow + +on: + workflow_dispatch: + inputs: + doc-build: + required: false + default: false + type: boolean + description: 'Whether to build the documentation' + workflow_call: + inputs: + doc-build: + required: false + default: false + type: boolean + description: 'Whether to build the documentation' + push: + branches: + - main + pull_request: + paths: + - 'fluent-mechanical/**' + +env: + MAIN_PYTHON_VERSION: '3.12' + FLUENT_DOCKER_IMAGE: 'ghcr.io/ansys/pyfluent' + MECHANICAL_DOCKER_IMAGE: 'ghcr.io/ansys/mechanical' + DOCKER_MECH_CONTAINER_NAME: mechanical + PYMECHANICAL_PORT: 10000 + ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} + PYANSYS_WORKFLOWS_CI: true + ANSYS_RELEASE_FOR_DOCS: 25.1 + RUN_DOC_BUILD: false + PYMECHANICAL_START_INSTANCE: false + +jobs: + fluent: + name: Fluent + runs-on: public-ubuntu-latest-8-cores + strategy: + fail-fast: false + matrix: + ansys-release: [24.1, 24.2, 25.1] + steps: + + - name: Checkout code + uses: actions/checkout@v4 + with: + sparse-checkout: | + fluent-mechanical + doc + + - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.MAIN_PYTHON_VERSION }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r fluent-mechanical/requirements_${{ matrix.ansys-release }}.txt + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Download Fluent service container + run: docker pull ${{ env.FLUENT_DOCKER_IMAGE }}:v${{ matrix.ansys-release }}.0 + + - name: Run the Fluent script + env: + FLUENT_IMAGE_TAG: v${{ matrix.ansys-release }}.0 + run: | + python fluent-mechanical/wf_fm_01_fluent.py + + - name: Store the outputs + uses: actions/upload-artifact@v4 + with: + name: fluent-mechanical-workflow-fluent-outputs-${{ matrix.ansys-release }} + path: | + fluent-mechanical/outputs/htc_temp_mapping_LOW_TEMP.csv + fluent-mechanical/outputs/htc_temp_mapping_MEDIUM_TEMP.csv + fluent-mechanical/outputs/htc_temp_mapping_HIGH_TEMP.csv + + - name: Stop all containers (if any) + run: | + if [ -n "$(docker ps -a -q)" ]; then + docker rm -f $(docker ps -a -q) + fi + + - name: (DOCS) Check if docs should be built + if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build + run: | + echo "Requested to build docs..." + if [ "${{ matrix.ansys-release }}" == "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then + echo "Building docs" + echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV + else + echo "Not building docs - since not primary release" + echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV + fi + + - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) + if: ${{ env.RUN_DOC_BUILD == 'true' }} + env: + FLUENT_IMAGE_TAG: v${{ matrix.ansys-release }}.0 + BUILD_DOCS_SCRIPT: 'fluent-mechanical/wf_fm_01_fluent.py' + run: | + cd doc + pip install -r requirements.txt + make html + + - name: (DOCS) Upload docs artifacts + if: ${{ env.RUN_DOC_BUILD == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: fluent-mechanical-docs-stage-fluent + path: | + doc/_build/ + doc/source/examples/fluent-mechanical/ + overwrite: true + + mechanical: + name: Mechanical + runs-on: [public-ubuntu-latest-8-cores] + needs: fluent + strategy: + fail-fast: false + matrix: + ansys-release: [24.1, 24.2, 25.1] + steps: + + - name: Checkout code + uses: actions/checkout@v4 + with: + sparse-checkout: | + fluent-mechanical + doc + + - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.MAIN_PYTHON_VERSION }} + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y nodejs npm graphviz xvfb + npm install -g @mermaid-js/mermaid-cli + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m venv .venv + . .venv/bin/activate + pip install -r fluent-mechanical/requirements_${{ matrix.ansys-release }}.txt + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Download (if needed) launch, and validate Mechanical service + env: + LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} + MECHANICAL_IMAGE: ${{ env.MECHANICAL_DOCKER_IMAGE }}:${{ matrix.ansys-release }}.0 + run: | + docker pull ${{ env.MECHANICAL_IMAGE }} + docker run --restart always --name ${{ env.DOCKER_MECH_CONTAINER_NAME }} -e ANSYSLMD_LICENSE_FILE=1055@${{ env.LICENSE_SERVER }} -p ${{ env.PYMECHANICAL_PORT }}:10000 ${{ env.MECHANICAL_IMAGE }} > log.txt & + grep -q 'WB Initialize Done' <(timeout 60 tail -f log.txt) + + - name: Check out the fluent outputs + uses: actions/download-artifact@v4 + with: + name: fluent-mechanical-workflow-fluent-outputs-${{ matrix.ansys-release }} + path: fluent-mechanical/outputs + + - name: Run the PyMechanical script + run: | + . .venv/bin/activate + xvfb-run python fluent-mechanical/wf_fm_02_mechanical.py + + - name: Store the outputs + uses: actions/upload-artifact@v4 + with: + name: fluent-mechanical-workflow-mechanical-outputs-${{ matrix.ansys-release }} + path: fluent-mechanical/outputs + + - name: (DOCS) Check if docs should be built + if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build + run: | + echo "Requested to build docs..." + if [ "${{ matrix.ansys-release }}" = "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then + echo "Building docs" + echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV + else + echo "Not building docs - since not primary release" + echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV + fi + + - name: (DOCS) Download the docs artifacts + uses: actions/download-artifact@v4 + if: ${{ env.RUN_DOC_BUILD == 'true' }} + with: + name: fluent-mechanical-docs-stage-fluent + path: doc + + - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS }}) + if: ${{ env.RUN_DOC_BUILD == 'true' }} + env: + BUILD_DOCS_SCRIPT: 'fluent-mechanical/wf_fm_02_mechanical.py' + run: | + . .venv/bin/activate + cd doc + pip install -r requirements.txt + xvfb-run make html + + - name: (DOCS) Upload docs artifacts + if: ${{ env.RUN_DOC_BUILD == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: fluent-mechanical-docs + path: | + doc/_build/ + doc/source/examples/fluent-mechanical/ + overwrite: true \ No newline at end of file From 3a2fa8fb318f6b4085b2f7117779fd0fa94581d7 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:09:17 +0200 Subject: [PATCH 003/100] feat: applying latest changes --- mapdl-dpf/requirements_24.1.txt | 4 +- mapdl-dpf/requirements_24.2.txt | 5 +- mapdl-dpf/requirements_25.1.txt | 5 +- mapdl-dpf/wf_mapdl-dpf.py | 307 +++++++++++++------------------- 4 files changed, 131 insertions(+), 190 deletions(-) diff --git a/mapdl-dpf/requirements_24.1.txt b/mapdl-dpf/requirements_24.1.txt index b328588a2..cb53bc1be 100644 --- a/mapdl-dpf/requirements_24.1.txt +++ b/mapdl-dpf/requirements_24.1.txt @@ -1,2 +1,2 @@ - -ansys-dpf-core[plotting]==0.12.2 +ansys-mapdl-core[graphics]==0.70.2 +ansys-dpf-core==0.14.1 diff --git a/mapdl-dpf/requirements_24.2.txt b/mapdl-dpf/requirements_24.2.txt index f431217ae..cb53bc1be 100644 --- a/mapdl-dpf/requirements_24.2.txt +++ b/mapdl-dpf/requirements_24.2.txt @@ -1,3 +1,2 @@ -ansys-mechanical-core==0.11.10 -ansys-fluent-core==0.26.1 -matplotlib==3.10.0 +ansys-mapdl-core[graphics]==0.70.2 +ansys-dpf-core==0.14.1 diff --git a/mapdl-dpf/requirements_25.1.txt b/mapdl-dpf/requirements_25.1.txt index 15189f805..cb53bc1be 100644 --- a/mapdl-dpf/requirements_25.1.txt +++ b/mapdl-dpf/requirements_25.1.txt @@ -1,3 +1,2 @@ - -ansys-dpf-core[plotting]==0.12.2 -ansys.mapdl.core==0.64.0 \ No newline at end of file +ansys-mapdl-core[graphics]==0.70.2 +ansys-dpf-core==0.14.1 diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index a2bd16157..dbb403813 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -1,24 +1,3 @@ -# Copyright (C) 2024 - 2025 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. """ .. _global-local_1: @@ -26,7 +5,7 @@ Consecutive submodeling with MAPDL pool ---------------------------- Problem description: - - In this example we demonstrate how to use MAPDL pool to + - In this example we demonstrate how to use MAPDL pool to perform a consecutive submodeling simulation. Analysis type: @@ -37,36 +16,36 @@ - Poissons ratio, :math:`\mu = 0.3` Boundary conditions (global model): - - Fixed support applied at the bottom side + - Fixed support applied at the bottom side - Frictionless support applied at the right side Loading: - - Total displacement of –1 mm in the Y‑direction at the top surface, ramped linearly over 10 timesteps + - Total displacement of -1 mm in the Y-direction at the top surface, ramped linearly over 10 timesteps .. image:: ../_static/bvp.png :width: 500 :alt: Problem Sketch Modeling notes: - - At each timestep, the global model is solved with the specified boundary conditions; + - At each timestep, the global model is solved with the specified boundary conditions; the resulting nodal displacements are interpolated to the boundary nodes of the local model, using the DPF - interpolation operator. Those displacements are enforced as constraints to the local model, which is then solved + interpolation operator. Those displacements are enforced as constraints to the local model, which is then solved completing that timestep. """ +import numpy as np import os import shutil -import time as tt - -from ansys.dpf import core as dpf from ansys.mapdl.core import MapdlPool -import numpy as np +from ansys.dpf import core as dpf +import time as tt +from pathlib import Path ############################################################################### # Create directories to save the results # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +folders = [ './Output/Common' , './Output/Global' , './Output/Local' ] for fdr in folders: try: shutil.rmtree(fdr, ignore_errors=True) @@ -76,103 +55,99 @@ ############################################################################### # Create Mapdl pool -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We use the ``MapdlPool`` class to create two separate instances — one dedicated to -# the global simulation and the other to the local simulation +# ~~~~~~~~~~~~~~~~~ +# We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# the global simulation and the other to the local simulation -exec_file = "C:/Program Files/ANSYS Inc/v251/ansys/bin/winx64/ANSYS251.exe" -nCores = 2 -pool = MapdlPool(2, run_location="./Output/Common", nproc=nCores, exec_file=exec_file) +nCores = 2 # Number of cores to use +pool = MapdlPool(2, nproc=nCores) ############################################################################### # Set up Global and Local FE models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # We assign the instances to the local and global model, then use ``mapdl.cdread`` to load their geometry and mesh. -# Note the the .cdb files include named selections for the faces we want to apply the boundary conditions and the loads. -# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# Note the the .cdb files include named selections for the faces we want to apply the boundary conditions and the loads. +# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. # The function ``Get_boundary`` is used to record the local model’s cut-boundary node coordinates as a dpf.field # which will be later used in the DPF interpolator input -mapdl_global = pool[0] # Global model -mapdl_global.cdread("db", "global.cdb") # Load global model -mapdl_global.cwd(os.getcwd() + "./Output/Global") # Set directory of the global model +cwd = Path.cwd() # Get current working directory + -mapdl_local = pool[1] # Local model -mapdl_local.cdread("db", "local.cdb") # Load local model -mapdl_local.cwd(os.getcwd() + "./Output/Local") # Set directory of the local model +mapdl_global = pool[0] # Global model +mapdl_global.cdread('db','global.cdb') # Load global model +mapdl_global.cwd(cwd/Path('Output/Global')) # Set directory of the global model +mapdl_local = pool[1] # Local model +mapdl_local.cdread('db','local.cdb') # Load local model +mapdl_local.cwd(cwd/Path("Output/Local")) # Set directory of the local model def define_BCs(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() # In the .cdb file for the global model the bottom, the right and the top faces - # are saved as named selections + # are saved as named selections # Fixed support - mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face - mapdl.d("ALL", "ALL") - mapdl.nsel("ALL") + mapdl.cmsel('S','BOTTOM_SIDE','NODE') # Select bottom face + mapdl.d('ALL','ALL') + mapdl.nsel('ALL') # Frictionless support - mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face - mapdl.d("ALL", "UZ", "0") - mapdl.nsel("ALL") + mapdl.cmsel('S','RIGHT_SIDE','NODE') # Select right face + mapdl.d('ALL','UZ','0') + mapdl.nsel('ALL') # Applied load # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps - mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") - mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") - mapdl.starset("LOAD(1,1,1)", "0.") - mapdl.starset("LOAD(2,1,1)", "-0.1") - mapdl.starset("LOAD(3,1,1)", "-1.") - - mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face - mapdl.d("ALL", "UY", "%LOAD%") - mapdl.nsel("ALL") - + mapdl.dim('LOAD','TABLE','3','1','1','TIME', '', '', '0') + mapdl.taxis('LOAD(1)','1','0.','1.','10.') + mapdl.starset('LOAD(1,1,1)','0.') + mapdl.starset('LOAD(2,1,1)','-0.1') + mapdl.starset('LOAD(3,1,1)','-1.') + + mapdl.cmsel('S','TOP_SIDE','NODE') # Select top face + mapdl.d('ALL','UY','%LOAD%') + mapdl.nsel('ALL') + # Exit PREP7 mapdl.finish() pass - def Get_boundary(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() - + # In the .cdb file for the local model the boundary faces are saved as # named selections - - mapdl.nsel("all") - nodes = mapdl.mesh.nodes # All nodes - node_id_all = mapdl.mesh.nnum # All nodes ID - mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces - node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID - map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) - - mapdl.nsel("NONE") + + mapdl.nsel('all') + nodes = mapdl.mesh.nodes # All nodes + node_id_all = mapdl.mesh.nnum # All nodes ID + mapdl.cmsel('S','boundary','NODE') # Select all boundary faces + node_id_subset = mapdl.get_array('NODE', item1="NLIST").astype(int) # Boundary nodes ID + map_ = dict(zip(node_id_all,list(range(len(node_id_all))))) + + mapdl.nsel('NONE') boundary_coordinates = dpf.fields_factory.create_3d_vector_field( - num_entities=len(node_id_subset), location="Nodal" - ) # Define DPF field for DPF interpolator input - - nsel = "" - for nid in node_id_subset: # Iterate boundary nodes of the local model - nsel += "nsel,A,NODE,,{}\n".format( - nid - ) # Add selection command for the node to the str (only for ploting) - boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + num_entities=len(node_id_subset),location='Nodal') # Define DPF field for DPF interpolator input + + nsel = '' + for nid in node_id_subset: # Iterate boundary nodes of the local model + nsel+='nsel,A,NODE,,{}\n'.format(nid) # Add selection command for the node to the str (only for ploting) + boundary_coordinates.append( nodes[ map_[nid] ] , nid ) # Add node to the DPF field # Select all boundary nodes (only for ploting) - mapdl.input_strings(nsel) - + mapdl.input_strings(nsel) + # Plot boundary nodes of the local model - mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") - + mapdl.nplot(background='w',color='b',show_bounds=True,title='Constrained nodes') + # Exit PREP7 mapdl.finish() return boundary_coordinates - # Define the boundary conditions and the loading for the global model define_BCs(mapdl_global) @@ -180,171 +155,139 @@ def Get_boundary(mapdl): boundary_coords = Get_boundary(mapdl_local) ############################################################################### -# Set up DPF operators +# Set up DPF operators # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We define two dpf operators: the first reads the displacement results from the global model, +# We define two dpf operators: the first reads the displacement results from the global model, # and the second interpolates those displacements onto the boundary coordinates of the local model. # The ``DataSources`` class to link results with the DPF operator inputs. - def define_dpf_operators(nCores): # Define the DataSources class and link it to the results of the global model dataSources = dpf.DataSources() - rst = r".\Output\Global\file{}.rst" for i in range(nCores): - dataSources.set_domain_result_file_path(path=rst.format(i), key="rst", domain_id=i) - + dataSources.set_domain_result_file_path(path=Path(f"./Output/Global/file{i}.rst"), key='rst', domain_id=i) + global_model = dpf.Model(dataSources) - global_disp_op = ( - dpf.operators.result.displacement() - ) # Define displacement result operator to read nodal displacements - global_disp_op.inputs.data_sources.connect( - dataSources - ) # Connect displacement result operator with the global model's results file - disp_interpolator = ( - dpf.operators.mapping.on_coordinates() - ) # Define interpolator to interpolate the results inside the mesh elements with shape functions - return global_model, global_disp_op, disp_interpolator - - -def initialize_dpf_interpolator( - global_model, - local_Bc_coords, - disp_interpolator, -): - my_mesh = global_model.metadata.meshed_region # Global model's mesh - disp_interpolator.inputs.coordinates.connect( - local_Bc_coords - ) # Link interpolator inputs with the local model's boundary coordinates - disp_interpolator.inputs.mesh.connect( - my_mesh - ) # Link interpolator mesh with the global model's mesh - - + global_disp_op = dpf.operators.result.displacement() # Define displacement result operator to read nodal displacements + global_disp_op.inputs.data_sources.connect(dataSources) # Connect displacement result operator with the global model's results file + disp_interpolator = dpf.operators.mapping.on_coordinates() # Define interpolator to interpolate the results inside the mesh elements with shape functions + return global_model,global_disp_op,disp_interpolator + +def initialize_dpf_interpolator(global_model,local_Bc_coords,disp_interpolator,): + my_mesh = global_model.metadata.meshed_region # Global model's mesh + disp_interpolator.inputs.coordinates.connect(local_Bc_coords) # Link interpolator inputs with the local model's boundary coordinates + disp_interpolator.inputs.mesh.connect(my_mesh) # Link interpolator mesh with the global model's mesh + def interpolate_data(timestep): - global_disp_op.inputs.time_scoping.connect( - [timestep] - ) # Specify timestep value to read results from - global_disp = ( - global_disp_op.outputs.fields_container.get_data() - ) # Read global nodal displacements - - disp_interpolator.inputs.fields_container.connect( - global_disp - ) # Link the interpolation data with the interpolator - local_disp = disp_interpolator.outputs.fields_container.get_data()[ - 0 - ] # Get displacements of the boundary nodes of the local model + global_disp_op.inputs.time_scoping.connect([timestep]) # Specify timestep value to read results from + global_disp = global_disp_op.outputs.fields_container.get_data() # Read global nodal displacements + + disp_interpolator.inputs.fields_container.connect(global_disp) # Link the interpolation data with the interpolator + local_disp = disp_interpolator.outputs.fields_container.get_data()[0] # Get displacements of the boundary nodes of the local model return local_disp - # Define the two dpf operators -global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) +global_model,global_disp_op,disp_interpolator = define_dpf_operators(nCores) ############################################################################### -# Set up simulation loop +# Set up simulation loop # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We solve the two models sequentially for each loading step. First the global model is run producing -# a .rst results file. Then we extract the global displacements and use them to define +# We solve the two models sequentially for each loading step. First the global model is run producing +# a .rst results file. Then we extract the global displacements and use them to define # cut-boundary conditions for the local model (an input string command will be used for faster excecution time). - def define_cut_boundary_constraint_template(local_Bc_coords): # Define template of input string command to apply the displacement constraints - local_nids = local_Bc_coords.scoping.ids # Get Node ID of boundary nodes of the local model - template = "" + local_nids = local_Bc_coords.scoping.ids # Get Node ID of boundary nodes of the local model + template = '' for nid in local_nids: - template += ( - "d," - + str(nid) - + ",ux,{:1.6e}\nd," - + str(nid) - + ",uy,{:1.6e}\nd," - + str(nid) - + ",uz,{:1.6e}\n" - ) + template += 'd,'+str(nid)+',ux,{:1.6e}\nd,'+str(nid)+',uy,{:1.6e}\nd,'+str(nid)+',uz,{:1.6e}\n' return template -def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): +def solve_global_local(mapdl_global,mapdl_local,timesteps,local_Bc_coords): + # Enter solution processor mapdl_global.solution() mapdl_local.solution() # Static analysis - mapdl_global.antype("STATIC") - mapdl_local.antype("STATIC") - + mapdl_global.antype("STATIC") + mapdl_local.antype("STATIC") + constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - for i in range(1, timesteps + 1): # Iterate timesteps - print(f"Timestep: {i}") + for i in range(1,timesteps+1): # Iterate timesteps + print(f'Timestep: {i}') st = tt.time() # Set loadstep time for the global model mapdl_global.time(i) - # No extrapolation - mapdl_global.eresx("NO") - mapdl_global.allsel("ALL") + # No extrapolation + mapdl_global.eresx('NO') + mapdl_global.allsel('ALL') # Write ALL results to database - mapdl_global.outres("ALL", "ALL") + mapdl_global.outres('ALL','ALL') # Solve global model mapdl_global.solve() - print("Global solve took ", tt.time() - st) + print('Global solve took ' , tt.time()-st ) - # Initialize interpolator - if i == 1: - initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) - # Read & Interpolate displacement data + #Initialize interpolator + if i==1: + initialize_dpf_interpolator(global_model,local_Bc_coords,disp_interpolator) + # Read & Interpolate displacement data local_disp = interpolate_data(timestep=i) - # Run MAPDL input string command to apply the displacement constraints + # Run MAPDL input string command to apply the displacement constraints data_array = np.array(local_disp.data).flatten() - mapdl_local.input_strings(constraint_template.format(*data_array)) + mapdl_local.input_strings(constraint_template.format( *data_array)) st = tt.time() - mapdl_local.allsel("ALL") + mapdl_local.allsel('ALL') # Set loadstep time for the local model mapdl_local.time(i) # No extrapolation - mapdl_local.eresx("NO") + mapdl_local.eresx('NO') # Write ALL results to database - mapdl_local.outres("ALL", "ALL") + mapdl_local.outres('ALL','ALL') # Solve local model mapdl_local.solve() - print("Local solve took ", tt.time() - st) - + print('Local solve took ' , tt.time()-st ) + # Exit solution processor mapdl_global.finish() mapdl_local.finish() - ############################################################################### -# Solve system -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -n_steps = 10 # Number of timesteps -solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) +# Solve system +# ~~~~~~~~~~~~ +n_steps = 10 # Number of timesteps +solve_global_local(mapdl_global,mapdl_local,n_steps,boundary_coords) ############################################################################### # Visualize results -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +# ~~~~~~~~~~~~~~~~~ def visualize(mapdl): # Enter post-processing mapdl.post1() # Set the current results set to the last set to be read from result file - mapdl.set("LAST") + mapdl.set('LAST') # Plot nodal displacement of the loading direction - mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") + mapdl.post_processing.plot_nodal_displacement( + "Y", + cmap="jet", + background='w', + cpos='zy' + ) # Exit post-processing mapdl.finish() - # Plot Y displacement of global model visualize(mapdl_global) # Plot Y displacement of local model visualize(mapdl_local) +############################################################################### # Exit MAPDL pool instances pool.exit() From facb2d3217b655963602545cfdfc2388ee83e645 Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Wed, 3 Sep 2025 15:51:58 -0500 Subject: [PATCH 004/100] update path --- mapdl-dpf/wf_mapdl-dpf.py | 320 +++++++++++++++++++++++--------------- 1 file changed, 196 insertions(+), 124 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index dbb403813..ddd4af759 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -1,3 +1,24 @@ +# Copyright (C) 2024 - 2025 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. """ .. _global-local_1: @@ -5,7 +26,7 @@ Consecutive submodeling with MAPDL pool ---------------------------- Problem description: - - In this example we demonstrate how to use MAPDL pool to + - In this example we demonstrate how to use MAPDL pool to perform a consecutive submodeling simulation. Analysis type: @@ -16,36 +37,40 @@ - Poissons ratio, :math:`\mu = 0.3` Boundary conditions (global model): - - Fixed support applied at the bottom side + - Fixed support applied at the bottom side - Frictionless support applied at the right side Loading: - - Total displacement of -1 mm in the Y-direction at the top surface, ramped linearly over 10 timesteps + - Total displacement of -1 mm in the Y-direction at the top surface, + ramped linearly over 10 timesteps .. image:: ../_static/bvp.png :width: 500 :alt: Problem Sketch Modeling notes: - - At each timestep, the global model is solved with the specified boundary conditions; - the resulting nodal displacements are interpolated to the boundary nodes of the local model, using the DPF - interpolation operator. Those displacements are enforced as constraints to the local model, which is then solved - completing that timestep. + - At each timestep, the global model is solved with the specified boundary + conditions;the resulting nodal displacements are interpolated to the + boundary nodes of the local model, using the DPF interpolation operator. + Those displacements are enforced as constraints to the local model, + which is then solved completing that timestep. """ -import numpy as np import os +from pathlib import Path import shutil -from ansys.mapdl.core import MapdlPool -from ansys.dpf import core as dpf import time as tt -from pathlib import Path + +from ansys.dpf import core as dpf +from ansys.mapdl.core import MapdlPool +from ansys.mapdl.core.examples.downloads import download_example_data +import numpy as np ############################################################################### # Create directories to save the results # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -folders = [ './Output/Common' , './Output/Global' , './Output/Local' ] +folders = ["./Output/Common", "./Output/Global", "./Output/Local"] for fdr in folders: try: shutil.rmtree(fdr, ignore_errors=True) @@ -56,98 +81,110 @@ ############################################################################### # Create Mapdl pool # ~~~~~~~~~~~~~~~~~ -# We use the ``MapdlPool`` class to create two separate instances — one dedicated to -# the global simulation and the other to the local simulation +# We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# the global simulation and the other to the local simulation -nCores = 2 # Number of cores to use +nCores = 2 # Number of cores to use pool = MapdlPool(2, nproc=nCores) ############################################################################### # Set up Global and Local FE models # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We assign the instances to the local and global model, then use ``mapdl.cdread`` to load their geometry and mesh. -# Note the the .cdb files include named selections for the faces we want to apply the boundary conditions and the loads. -# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. -# The function ``Get_boundary`` is used to record the local model’s cut-boundary node coordinates as a dpf.field -# which will be later used in the DPF interpolator input +# We assign the instances to the local and global model, then use +# ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files +# include named selections for the faces we want to apply the boundary conditions and the loads. +# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# The function ``Get_boundary`` is used to record the local model’s cut-boundary +# node coordinates as a dpf.field which will be later used in the DPF interpolator input -cwd = Path.cwd() # Get current working directory +cwd = Path.cwd() # Get current working directory +# download example data +local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") +global_cdb = download_example_data( + filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" +) -mapdl_global = pool[0] # Global model -mapdl_global.cdread('db','global.cdb') # Load global model -mapdl_global.cwd(cwd/Path('Output/Global')) # Set directory of the global model +mapdl_global = pool[0] # Global model +mapdl_global.cdread("db", global_cdb) # Load global model +mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model + +mapdl_local = pool[1] # Local model +mapdl_local.cdread("db", local_cdb) # Load local model +mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model -mapdl_local = pool[1] # Local model -mapdl_local.cdread('db','local.cdb') # Load local model -mapdl_local.cwd(cwd/Path("Output/Local")) # Set directory of the local model def define_BCs(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() # In the .cdb file for the global model the bottom, the right and the top faces - # are saved as named selections + # are saved as named selections # Fixed support - mapdl.cmsel('S','BOTTOM_SIDE','NODE') # Select bottom face - mapdl.d('ALL','ALL') - mapdl.nsel('ALL') + mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face + mapdl.d("ALL", "ALL") + mapdl.nsel("ALL") # Frictionless support - mapdl.cmsel('S','RIGHT_SIDE','NODE') # Select right face - mapdl.d('ALL','UZ','0') - mapdl.nsel('ALL') + mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face + mapdl.d("ALL", "UZ", "0") + mapdl.nsel("ALL") # Applied load # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps - mapdl.dim('LOAD','TABLE','3','1','1','TIME', '', '', '0') - mapdl.taxis('LOAD(1)','1','0.','1.','10.') - mapdl.starset('LOAD(1,1,1)','0.') - mapdl.starset('LOAD(2,1,1)','-0.1') - mapdl.starset('LOAD(3,1,1)','-1.') - - mapdl.cmsel('S','TOP_SIDE','NODE') # Select top face - mapdl.d('ALL','UY','%LOAD%') - mapdl.nsel('ALL') - + mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") + mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") + mapdl.starset("LOAD(1,1,1)", "0.") + mapdl.starset("LOAD(2,1,1)", "-0.1") + mapdl.starset("LOAD(3,1,1)", "-1.") + + mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face + mapdl.d("ALL", "UY", "%LOAD%") + mapdl.nsel("ALL") + # Exit PREP7 mapdl.finish() pass + def Get_boundary(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() - + # In the .cdb file for the local model the boundary faces are saved as # named selections - - mapdl.nsel('all') - nodes = mapdl.mesh.nodes # All nodes - node_id_all = mapdl.mesh.nnum # All nodes ID - mapdl.cmsel('S','boundary','NODE') # Select all boundary faces - node_id_subset = mapdl.get_array('NODE', item1="NLIST").astype(int) # Boundary nodes ID - map_ = dict(zip(node_id_all,list(range(len(node_id_all))))) - - mapdl.nsel('NONE') + + mapdl.nsel("all") + nodes = mapdl.mesh.nodes # All nodes + node_id_all = mapdl.mesh.nnum # All nodes ID + mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces + node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID + map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + + mapdl.nsel("NONE") boundary_coordinates = dpf.fields_factory.create_3d_vector_field( - num_entities=len(node_id_subset),location='Nodal') # Define DPF field for DPF interpolator input - - nsel = '' - for nid in node_id_subset: # Iterate boundary nodes of the local model - nsel+='nsel,A,NODE,,{}\n'.format(nid) # Add selection command for the node to the str (only for ploting) - boundary_coordinates.append( nodes[ map_[nid] ] , nid ) # Add node to the DPF field + num_entities=len(node_id_subset), location="Nodal" + ) # Define DPF field for DPF interpolator input + + nsel = "" + for nid in node_id_subset: # Iterate boundary nodes of the local model + nsel += "nsel,A,NODE,,{}\n".format( + nid + ) # Add selection command for the node to the str (only for ploting) + boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field # Select all boundary nodes (only for ploting) - mapdl.input_strings(nsel) - + mapdl.input_strings(nsel) + # Plot boundary nodes of the local model - mapdl.nplot(background='w',color='b',show_bounds=True,title='Constrained nodes') - + mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + # Exit PREP7 mapdl.finish() return boundary_coordinates + # Define the boundary conditions and the loading for the global model define_BCs(mapdl_global) @@ -155,133 +192,168 @@ def Get_boundary(mapdl): boundary_coords = Get_boundary(mapdl_local) ############################################################################### -# Set up DPF operators +# Set up DPF operators # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We define two dpf operators: the first reads the displacement results from the global model, +# We define two dpf operators: the first reads the displacement results from the global model, # and the second interpolates those displacements onto the boundary coordinates of the local model. # The ``DataSources`` class to link results with the DPF operator inputs. + def define_dpf_operators(nCores): # Define the DataSources class and link it to the results of the global model dataSources = dpf.DataSources() for i in range(nCores): - dataSources.set_domain_result_file_path(path=Path(f"./Output/Global/file{i}.rst"), key='rst', domain_id=i) - + dataSources.set_domain_result_file_path( + path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i + ) + global_model = dpf.Model(dataSources) - global_disp_op = dpf.operators.result.displacement() # Define displacement result operator to read nodal displacements - global_disp_op.inputs.data_sources.connect(dataSources) # Connect displacement result operator with the global model's results file - disp_interpolator = dpf.operators.mapping.on_coordinates() # Define interpolator to interpolate the results inside the mesh elements with shape functions - return global_model,global_disp_op,disp_interpolator - -def initialize_dpf_interpolator(global_model,local_Bc_coords,disp_interpolator,): - my_mesh = global_model.metadata.meshed_region # Global model's mesh - disp_interpolator.inputs.coordinates.connect(local_Bc_coords) # Link interpolator inputs with the local model's boundary coordinates - disp_interpolator.inputs.mesh.connect(my_mesh) # Link interpolator mesh with the global model's mesh - + # Define displacement result operator to read nodal displacements + global_disp_op = dpf.operators.result.displacement() + # Connect displacement result operator with the global model's results file + global_disp_op.inputs.data_sources.connect(dataSources) + # Define interpolator to interpolate the results inside the mesh elements + # with shape functions + disp_interpolator = dpf.operators.mapping.on_coordinates() + return global_model, global_disp_op, disp_interpolator + + +def initialize_dpf_interpolator( + global_model, + local_Bc_coords, + disp_interpolator, +): + my_mesh = global_model.metadata.meshed_region # Global model's mesh + disp_interpolator.inputs.coordinates.connect( + local_Bc_coords + ) # Link interpolator inputs with the local model's boundary coordinates + disp_interpolator.inputs.mesh.connect( + my_mesh + ) # Link interpolator mesh with the global model's mesh + + def interpolate_data(timestep): - global_disp_op.inputs.time_scoping.connect([timestep]) # Specify timestep value to read results from - global_disp = global_disp_op.outputs.fields_container.get_data() # Read global nodal displacements - - disp_interpolator.inputs.fields_container.connect(global_disp) # Link the interpolation data with the interpolator - local_disp = disp_interpolator.outputs.fields_container.get_data()[0] # Get displacements of the boundary nodes of the local model + global_disp_op.inputs.time_scoping.connect( + [timestep] + ) # Specify timestep value to read results from + global_disp = ( + global_disp_op.outputs.fields_container.get_data() + ) # Read global nodal displacements + + disp_interpolator.inputs.fields_container.connect( + global_disp + ) # Link the interpolation data with the interpolator + local_disp = disp_interpolator.outputs.fields_container.get_data()[ + 0 + ] # Get displacements of the boundary nodes of the local model return local_disp + # Define the two dpf operators -global_model,global_disp_op,disp_interpolator = define_dpf_operators(nCores) +global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) ############################################################################### -# Set up simulation loop +# Set up simulation loop # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We solve the two models sequentially for each loading step. First the global model is run producing -# a .rst results file. Then we extract the global displacements and use them to define -# cut-boundary conditions for the local model (an input string command will be used for faster excecution time). +# We solve the two models sequentially for each loading step. +# First the global model is run producing a .rst results file. +# Then we extract the global displacements and use them to define +# cut-boundary conditions for the local model +# (an input string command will be used for faster excecution time). + def define_cut_boundary_constraint_template(local_Bc_coords): # Define template of input string command to apply the displacement constraints - local_nids = local_Bc_coords.scoping.ids # Get Node ID of boundary nodes of the local model - template = '' + local_nids = local_Bc_coords.scoping.ids + # Get Node ID of boundary nodes of the local model + template = "" for nid in local_nids: - template += 'd,'+str(nid)+',ux,{:1.6e}\nd,'+str(nid)+',uy,{:1.6e}\nd,'+str(nid)+',uz,{:1.6e}\n' + template += ( + "d," + + str(nid) + + ",ux,{:1.6e}\nd," + + str(nid) + + ",uy,{:1.6e}\nd," + + str(nid) + + ",uz,{:1.6e}\n" + ) return template +def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): -def solve_global_local(mapdl_global,mapdl_local,timesteps,local_Bc_coords): - # Enter solution processor mapdl_global.solution() mapdl_local.solution() # Static analysis - mapdl_global.antype("STATIC") - mapdl_local.antype("STATIC") - + mapdl_global.antype("STATIC") + mapdl_local.antype("STATIC") + constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - for i in range(1,timesteps+1): # Iterate timesteps - print(f'Timestep: {i}') + for i in range(1, timesteps + 1): # Iterate timesteps + print(f"Timestep: {i}") st = tt.time() # Set loadstep time for the global model mapdl_global.time(i) - # No extrapolation - mapdl_global.eresx('NO') - mapdl_global.allsel('ALL') + # No extrapolation + mapdl_global.eresx("NO") + mapdl_global.allsel("ALL") # Write ALL results to database - mapdl_global.outres('ALL','ALL') + mapdl_global.outres("ALL", "ALL") # Solve global model mapdl_global.solve() - print('Global solve took ' , tt.time()-st ) + print("Global solve took ", tt.time() - st) - #Initialize interpolator - if i==1: - initialize_dpf_interpolator(global_model,local_Bc_coords,disp_interpolator) - # Read & Interpolate displacement data + # Initialize interpolator + if i == 1: + initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) + # Read & Interpolate displacement data local_disp = interpolate_data(timestep=i) - # Run MAPDL input string command to apply the displacement constraints + # Run MAPDL input string command to apply the displacement constraints data_array = np.array(local_disp.data).flatten() - mapdl_local.input_strings(constraint_template.format( *data_array)) + mapdl_local.input_strings(constraint_template.format(*data_array)) st = tt.time() - mapdl_local.allsel('ALL') + mapdl_local.allsel("ALL") # Set loadstep time for the local model mapdl_local.time(i) # No extrapolation - mapdl_local.eresx('NO') + mapdl_local.eresx("NO") # Write ALL results to database - mapdl_local.outres('ALL','ALL') + mapdl_local.outres("ALL", "ALL") # Solve local model mapdl_local.solve() - print('Local solve took ' , tt.time()-st ) - + print("Local solve took ", tt.time() - st) + # Exit solution processor mapdl_global.finish() mapdl_local.finish() + ############################################################################### -# Solve system +# Solve system # ~~~~~~~~~~~~ -n_steps = 10 # Number of timesteps -solve_global_local(mapdl_global,mapdl_local,n_steps,boundary_coords) +n_steps = 10 # Number of timesteps +solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) ############################################################################### # Visualize results # ~~~~~~~~~~~~~~~~~ + def visualize(mapdl): # Enter post-processing mapdl.post1() # Set the current results set to the last set to be read from result file - mapdl.set('LAST') + mapdl.set("LAST") # Plot nodal displacement of the loading direction - mapdl.post_processing.plot_nodal_displacement( - "Y", - cmap="jet", - background='w', - cpos='zy' - ) + mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") # Exit post-processing mapdl.finish() + # Plot Y displacement of global model visualize(mapdl_global) From 4bfe7bf3f7122f0db069efdf0657012d6a6428d8 Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Wed, 3 Sep 2025 16:09:18 -0500 Subject: [PATCH 005/100] update workflow --- .ci/start_mapdl.sh | 57 ++++++++++ .ci/waiting_services.sh | 19 ++++ .github/workflows/mapdl-dpf.yml | 182 +++++++++----------------------- 3 files changed, 125 insertions(+), 133 deletions(-) create mode 100644 .ci/start_mapdl.sh create mode 100644 .ci/waiting_services.sh diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh new file mode 100644 index 000000000..e8504eb7b --- /dev/null +++ b/.ci/start_mapdl.sh @@ -0,0 +1,57 @@ +#!/bin/bash +echo "MAPDL Instance name: $INSTANCE_NAME" +echo "MAPDL_VERSION: $MAPDL_VERSION" + +export MAPDL_IMAGE="$MAPDL_PACKAGE:$MAPDL_VERSION" +echo "MAPDL_IMAGE: $MAPDL_IMAGE" +docker pull "$MAPDL_IMAGE" + +export MAJOR=$(echo "$MAPDL_VERSION" | head -c 3 | tail -c 2) +export MINOR=$(echo "$MAPDL_VERSION" | head -c 5 | tail -c 1) + +export VERSION="$MAJOR$MINOR" +echo "MAPDL VERSION: $VERSION" + + +if [[ $MAPDL_VERSION == *"latest-ubuntu"* ]]; then + echo "It is latest-ubuntu. Using 'ansys' script to launch" + export EXEC_PATH=ansys + # export P_SCHEMA=/ansys_inc/ansys/ac4/schema + +elif [[ $MAPDL_VERSION == *"ubuntu"* ]] ; then + echo "It is an ubuntu based image" + export EXEC_PATH=/ansys_inc/v$VERSION/ansys/bin/mapdl + export P_SCHEMA=/ansys_inc/v$VERSION/ansys/ac4/schema + +else + echo "It is a CentOS based image" + export EXEC_PATH=/ansys_inc/ansys/bin/mapdl + export P_SCHEMA=/ansys_inc/ansys/ac4/schema +fi; + +echo "EXEC_PATH: $EXEC_PATH" +echo "P_SCHEMA: $P_SCHEMA" + +docker run \ + --entrypoint "/bin/bash" \ + --name "$INSTANCE_NAME" \ + --restart always \ + --health-cmd="ps aux | grep \"[/]ansys_inc/.*ansys\.e.*grpc\" -q && echo 0 || echo 1" \ + --health-interval=0.5s \ + --health-retries=4 \ + --health-timeout=0.5s \ + --health-start-period=10s \ + -e ANSYSLMD_LICENSE_FILE=1055@"$LICENSE_SERVER" \ + -e ANSYS_LOCK="OFF" \ + -p "$PYMAPDL_PORT":50052 \ + -p "$PYMAPDL_DB_PORT":50055 \ + --shm-size=2gb \ + -e I_MPI_SHM_LMT=shm \ + -e P_SCHEMA="$P_SCHEMA" \ + -w /jobs \ + -u=0:0 \ + --memory=6656MB \ + --memory-swap=16896MB \ + "$MAPDL_IMAGE" "$EXEC_PATH" -grpc -dir /jobs -"$DISTRIBUTED_MODE" -np 2 > log.txt & + +# grep -q 'Server listening on' <(timeout 60 tail -f log.txt) diff --git a/.ci/waiting_services.sh b/.ci/waiting_services.sh new file mode 100644 index 000000000..0222548ee --- /dev/null +++ b/.ci/waiting_services.sh @@ -0,0 +1,19 @@ +#!/bin/bash +echo "Waiting for the MAPDL service to be up..." +nc -v -z localhost "$PYMAPDL_PORT" +echo "::group:: ps aux Output" && ps aux && echo "::endgroup::" + +echo "Waiting for MAPDL port is open..." +echo "::group:: Waiting for the MAPDL port to be open..." +while ! nc -z localhost "$PYMAPDL_PORT"; do +sleep 0.1 +done +echo "::endgroup::" +echo "MAPDL service is up!" + +echo "::group:: Waiting for the DPF port to be open..." +while ! nc -z localhost "$DPF_PORT"; do + sleep 0.1 +done +echo "::endgroup::" +echo "DPF service is up!" \ No newline at end of file diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 8a7240b4b..486ad698f 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -20,35 +20,40 @@ on: - main pull_request: paths: - - 'fluent-mechanical/**' + - 'mapdl-dpf/**' env: MAIN_PYTHON_VERSION: '3.12' - FLUENT_DOCKER_IMAGE: 'ghcr.io/ansys/pyfluent' - MECHANICAL_DOCKER_IMAGE: 'ghcr.io/ansys/mechanical' - DOCKER_MECH_CONTAINER_NAME: mechanical - PYMECHANICAL_PORT: 10000 + MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.1 RUN_DOC_BUILD: false - PYMECHANICAL_START_INSTANCE: false + DPF_PORT: 21002 + DPF_START_SERVER: False + MAPDL_IMAGE_VERSION_DOCS_BUILD: v24.2-ubuntu jobs: - fluent: - name: Fluent + mapdl-dpf: + name: MAPDL-DPF runs-on: public-ubuntu-latest-8-cores strategy: fail-fast: false matrix: ansys-release: [24.1, 24.2, 25.1] + env: + PYMAPDL_PORT: 21000 # default won't work on GitHub runners + PYMAPDL_DB_PORT: 21001 # default won't work on GitHub runners + PYMAPDL_START_INSTANCE: FALSE + ON_DOCUMENTATION: TRUE + steps: - name: Checkout code uses: actions/checkout@v4 with: sparse-checkout: | - fluent-mechanical + mapdl-dpf doc - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} @@ -59,7 +64,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r fluent-mechanical/requirements_${{ matrix.ansys-release }}.txt + pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt - name: Login to GitHub Container Registry uses: docker/login-action@v3 @@ -68,135 +73,54 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Download Fluent service container - run: docker pull ${{ env.FLUENT_DOCKER_IMAGE }}:v${{ matrix.ansys-release }}.0 - - - name: Run the Fluent script + - name: "Pull, launch, and validate MAPDL service" + id: start_mapdl env: - FLUENT_IMAGE_TAG: v${{ matrix.ansys-release }}.0 + LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} + MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }} + DISTRIBUTED_MODE: "dmp" run: | - python fluent-mechanical/wf_fm_01_fluent.py - - - name: Store the outputs - uses: actions/upload-artifact@v4 - with: - name: fluent-mechanical-workflow-fluent-outputs-${{ matrix.ansys-release }} - path: | - fluent-mechanical/outputs/htc_temp_mapping_LOW_TEMP.csv - fluent-mechanical/outputs/htc_temp_mapping_MEDIUM_TEMP.csv - fluent-mechanical/outputs/htc_temp_mapping_HIGH_TEMP.csv - - - name: Stop all containers (if any) + export INSTANCE_NAME=MAPDL_0 + .ci/start_mapdl.sh & export DOCKER_PID=$! + echo "Launching MAPDL service at PID: $DOCKER_PID" + echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT + - name: "DPF Server Activation" run: | - if [ -n "$(docker ps -a -q)" ]; then - docker rm -f $(docker ps -a -q) - fi + $(docker pull ghcr.io/ansys/dpf-core:22.2dev && docker run -d --name dpfserver -p ${{ env.DPF_PORT }}:50052 ghcr.io/ansys/dpf-core:22.2dev && echo "DPF Server active on port ${{ env.DPF_PORT }}.") & - - name: (DOCS) Check if docs should be built - if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build + - name: "Test virtual framebuffer" run: | - echo "Requested to build docs..." - if [ "${{ matrix.ansys-release }}" == "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then - echo "Building docs" - echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV - else - echo "Not building docs - since not primary release" - echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV - fi + xvfb-run python .ci/display_test.py - - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) - if: ${{ env.RUN_DOC_BUILD == 'true' }} - env: - FLUENT_IMAGE_TAG: v${{ matrix.ansys-release }}.0 - BUILD_DOCS_SCRIPT: 'fluent-mechanical/wf_fm_01_fluent.py' + - name: "Retrieve PyMAPDL version" + id: version run: | - cd doc - pip install -r requirements.txt - make html - - - name: (DOCS) Upload docs artifacts - if: ${{ env.RUN_DOC_BUILD == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: fluent-mechanical-docs-stage-fluent - path: | - doc/_build/ - doc/source/examples/fluent-mechanical/ - overwrite: true + echo "PYMAPDL_VERSION=$(python -c 'from ansys.mapdl.core import __version__; print(__version__)')" >> $GITHUB_OUTPUT + echo "PyMAPDL version is: $(python -c "from ansys.mapdl.core import __version__; print(__version__)")" - mechanical: - name: Mechanical - runs-on: [public-ubuntu-latest-8-cores] - needs: fluent - strategy: - fail-fast: false - matrix: - ansys-release: [24.1, 24.2, 25.1] - steps: - - - name: Checkout code - uses: actions/checkout@v4 - with: - sparse-checkout: | - fluent-mechanical - doc - - - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} - uses: actions/setup-python@v5 - with: - python-version: ${{ env.MAIN_PYTHON_VERSION }} - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y nodejs npm graphviz xvfb - npm install -g @mermaid-js/mermaid-cli - - - name: Install dependencies + - name: "Waiting for the services to be up" + timeout-minutes: 15 run: | - python -m pip install --upgrade pip - python -m venv .venv - . .venv/bin/activate - pip install -r fluent-mechanical/requirements_${{ matrix.ansys-release }}.txt + .ci/waiting_services.sh - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Download (if needed) launch, and validate Mechanical service + - name: Run the MAPDL-DPF script env: - LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} - MECHANICAL_IMAGE: ${{ env.MECHANICAL_DOCKER_IMAGE }}:${{ matrix.ansys-release }}.0 + LIBGL_ALWAYS_SOFTWARE: 1 + PYANSYS_VISUALIZER_HTML_BACKEND: true run: | - docker pull ${{ env.MECHANICAL_IMAGE }} - docker run --restart always --name ${{ env.DOCKER_MECH_CONTAINER_NAME }} -e ANSYSLMD_LICENSE_FILE=1055@${{ env.LICENSE_SERVER }} -p ${{ env.PYMECHANICAL_PORT }}:10000 ${{ env.MECHANICAL_IMAGE }} > log.txt & - grep -q 'WB Initialize Done' <(timeout 60 tail -f log.txt) - - - name: Check out the fluent outputs - uses: actions/download-artifact@v4 - with: - name: fluent-mechanical-workflow-fluent-outputs-${{ matrix.ansys-release }} - path: fluent-mechanical/outputs + python mapdl-dpf/wf_mapdl-dpf.py - - name: Run the PyMechanical script + - name: Stop all containers (if any) run: | - . .venv/bin/activate - xvfb-run python fluent-mechanical/wf_fm_02_mechanical.py - - - name: Store the outputs - uses: actions/upload-artifact@v4 - with: - name: fluent-mechanical-workflow-mechanical-outputs-${{ matrix.ansys-release }} - path: fluent-mechanical/outputs + if [ -n "$(docker ps -a -q)" ]; then + docker rm -f $(docker ps -a -q) + fi - name: (DOCS) Check if docs should be built if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build run: | echo "Requested to build docs..." - if [ "${{ matrix.ansys-release }}" = "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then + if [ "${{ matrix.ansys-release }}" == "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then echo "Building docs" echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV else @@ -204,19 +128,11 @@ jobs: echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV fi - - name: (DOCS) Download the docs artifacts - uses: actions/download-artifact@v4 - if: ${{ env.RUN_DOC_BUILD == 'true' }} - with: - name: fluent-mechanical-docs-stage-fluent - path: doc - - - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS }}) + - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) if: ${{ env.RUN_DOC_BUILD == 'true' }} env: - BUILD_DOCS_SCRIPT: 'fluent-mechanical/wf_fm_02_mechanical.py' + BUILD_DOCS_SCRIPT: 'mapdl-dpf/wf_mapdl-dpf.py' run: | - . .venv/bin/activate cd doc pip install -r requirements.txt xvfb-run make html @@ -225,8 +141,8 @@ jobs: if: ${{ env.RUN_DOC_BUILD == 'true' }} uses: actions/upload-artifact@v4 with: - name: fluent-mechanical-docs + name: mapdl-dpf-docs-stage-mapdl path: | doc/_build/ - doc/source/examples/fluent-mechanical/ - overwrite: true \ No newline at end of file + doc/source/examples/mapdl-dpf/ + overwrite: true From 4d2042fc8625b58710342f79fb9aef861dc7f36b Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Wed, 3 Sep 2025 16:13:14 -0500 Subject: [PATCH 006/100] fix style --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9f501e494..591c4ca66 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -33,7 +33,7 @@ jobs: secrets: inherit with: doc-build: false - + mapdl-dpf: uses: ./.github/workflows/mapdl-dpf.yml secrets: inherit From 0ac3a44d54440b10bd1a1679842700a41028adaa Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Wed, 3 Sep 2025 16:19:32 -0500 Subject: [PATCH 007/100] add mapdl py file --- .ci/display_test.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .ci/display_test.py diff --git a/.ci/display_test.py b/.ci/display_test.py new file mode 100644 index 000000000..8e8e49f7e --- /dev/null +++ b/.ci/display_test.py @@ -0,0 +1,30 @@ +# Copyright (C) 2024 - 2025 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. + +"""Quickly check if VTK off screen plotting works.""" + +import pyvista +from pyvista.plotting import system_supports_plotting + +print("system_supports_plotting", system_supports_plotting()) +pyvista.OFF_SCREEN = True +pyvista.plot(pyvista.Sphere()) From 97a7814c01b718828f743d85de0099b3525ac1ca Mon Sep 17 00:00:00 2001 From: dkunhamb Date: Wed, 3 Sep 2025 16:25:08 -0500 Subject: [PATCH 008/100] update checkout --- .github/workflows/mapdl-dpf.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 486ad698f..dab1d5b60 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -55,6 +55,7 @@ jobs: sparse-checkout: | mapdl-dpf doc + .ci - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} uses: actions/setup-python@v5 From fb97a977e6a46204a94cd83edf1cf387e2eb0d3a Mon Sep 17 00:00:00 2001 From: Dipin Nair Date: Thu, 4 Sep 2025 09:12:40 -0400 Subject: [PATCH 009/100] change shel script permission --- .ci/start_mapdl.sh | 0 .ci/waiting_services.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .ci/start_mapdl.sh mode change 100644 => 100755 .ci/waiting_services.sh diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh old mode 100644 new mode 100755 diff --git a/.ci/waiting_services.sh b/.ci/waiting_services.sh old mode 100644 new mode 100755 From b0c784b8f8af4f835a23144d4ba8621232586f01 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:57:13 +0200 Subject: [PATCH 010/100] feat: using latest PyMAPDL docker files --- .ci/start_mapdl.sh | 139 ++++++++++++++++++++++++-------- .ci/waiting_services.sh | 8 +- .github/workflows/mapdl-dpf.yml | 11 ++- 3 files changed, 120 insertions(+), 38 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index e8504eb7b..121fccc9e 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -1,57 +1,130 @@ #!/bin/bash +# This script is used to start a MAPDL instance in a Docker container. +# +# Usage: +# ------ +# This script is intended to be run in a CI/CD environment where the necessary environment variables are set. +# +# Required environment variables: +# ------------------------------- +# +# - DISTRIBUTED_MODE: The mode of operation for MAPDL (e.g., "smp", "dmp"). +# - DPF_PORT: The port for the DPF service (e.g., 50055). +# - INSTANCE_NAME: The name of the MAPDL instance (e.g., "MAPDL_0"). +# - LICENSE_SERVER: The address of the license server (e.g., "123.123.123.123"). +# - MAPDL_PACKAGE: The Docker image package name (e.g., ansys/mapdl). +# - MAPDL_VERSION: The version of the MAPDL image to use (e.g., "v25.2-ubuntu-cicd"). +# - PYMAPDL_DB_PORT: The port for the PyMAPDL database service (e.g., 50056). +# - PYMAPDL_PORT: The port for the PyMAPDL service (e.g., 50052). +# +# Example: +# -------- +# +# export DISTRIBUTED_MODE="smp" +# export DPF_PORT=50055 +# export INSTANCE_NAME=MAPDL_0 +# export LICENSE_SERVER="123.123.123.123" +# export MAPDL_PACKAGE=ghcr.io/ansys/mapdl +# export MAPDL_VERSION=v25.2-ubuntu-cicd +# export PYMAPDL_DB_PORT=50056 +# export PYMAPDL_PORT=50052 +# ./start_mapdl.sh +# + +export MAJOR MINOR MAPDL_IMAGE VERSION + echo "MAPDL Instance name: $INSTANCE_NAME" echo "MAPDL_VERSION: $MAPDL_VERSION" -export MAPDL_IMAGE="$MAPDL_PACKAGE:$MAPDL_VERSION" +MAPDL_IMAGE="$MAPDL_PACKAGE:$MAPDL_VERSION" echo "MAPDL_IMAGE: $MAPDL_IMAGE" docker pull "$MAPDL_IMAGE" -export MAJOR=$(echo "$MAPDL_VERSION" | head -c 3 | tail -c 2) -export MINOR=$(echo "$MAPDL_VERSION" | head -c 5 | tail -c 1) +MAJOR=$(echo "$MAPDL_VERSION" | head -c 3 | tail -c 2) +MINOR=$(echo "$MAPDL_VERSION" | head -c 5 | tail -c 1) -export VERSION="$MAJOR$MINOR" +VERSION="$MAJOR$MINOR" echo "MAPDL VERSION: $VERSION" if [[ $MAPDL_VERSION == *"latest-ubuntu"* ]]; then echo "It is latest-ubuntu. Using 'ansys' script to launch" - export EXEC_PATH=ansys - # export P_SCHEMA=/ansys_inc/ansys/ac4/schema + EXEC_PATH=ansys + # P_SCHEMA=/ansys_inc/ansys/ac4/schema elif [[ $MAPDL_VERSION == *"ubuntu"* ]] ; then echo "It is an ubuntu based image" - export EXEC_PATH=/ansys_inc/v$VERSION/ansys/bin/mapdl - export P_SCHEMA=/ansys_inc/v$VERSION/ansys/ac4/schema + EXEC_PATH=/ansys_inc/v$VERSION/ansys/bin/mapdl + P_SCHEMA=/ansys_inc/v$VERSION/ansys/ac4/schema else echo "It is a CentOS based image" - export EXEC_PATH=/ansys_inc/ansys/bin/mapdl - export P_SCHEMA=/ansys_inc/ansys/ac4/schema + EXEC_PATH=/ansys_inc/ansys/bin/mapdl + P_SCHEMA=/ansys_inc/ansys/ac4/schema +fi; + +if [[ $MAPDL_VERSION == *"cicd"* ]] ; then + echo "It is a CICD version, binding DPF port too" + if [ "$RUN_DPF_SERVER" == "true" ]; then + echo "RUN_DPF_SERVER is set to true, starting DPF server" + export DPF_ON="-e ANSYS_DPF_ACCEPT_LA=Y" + fi + + export DPF_PORT_INTERNAL=50055 + export DPF_PORT_ARG="-p ${DPF_PORT}:${DPF_PORT_INTERNAL}" + export DB_INT_PORT=50056 + + echo "DPF_PORT_ARG: $DPF_PORT_ARG" + echo "DB_INT_PORT: $DB_INT_PORT" + + echo "Overriding DISTRIBUTED_MODE to 'dmp' for CICD version" + export DISTRIBUTED_MODE="dmp" +else + export DPF_PORT_ARG="" + export DB_INT_PORT=50055 + export DPF_ON="" fi; echo "EXEC_PATH: $EXEC_PATH" echo "P_SCHEMA: $P_SCHEMA" -docker run \ - --entrypoint "/bin/bash" \ - --name "$INSTANCE_NAME" \ - --restart always \ - --health-cmd="ps aux | grep \"[/]ansys_inc/.*ansys\.e.*grpc\" -q && echo 0 || echo 1" \ - --health-interval=0.5s \ - --health-retries=4 \ - --health-timeout=0.5s \ - --health-start-period=10s \ - -e ANSYSLMD_LICENSE_FILE=1055@"$LICENSE_SERVER" \ - -e ANSYS_LOCK="OFF" \ - -p "$PYMAPDL_PORT":50052 \ - -p "$PYMAPDL_DB_PORT":50055 \ - --shm-size=2gb \ - -e I_MPI_SHM_LMT=shm \ - -e P_SCHEMA="$P_SCHEMA" \ - -w /jobs \ - -u=0:0 \ - --memory=6656MB \ - --memory-swap=16896MB \ - "$MAPDL_IMAGE" "$EXEC_PATH" -grpc -dir /jobs -"$DISTRIBUTED_MODE" -np 2 > log.txt & - -# grep -q 'Server listening on' <(timeout 60 tail -f log.txt) +# Building docker command +CMD=$(cat <<-_EOT_ +run \ + --entrypoint /bin/bash \ + --name ${INSTANCE_NAME} \ + --restart always \ + --health-interval=0.5s \ + --health-retries=4 \ + --health-timeout=0.5s \ + --health-start-period=10s \ + -e ANSYSLMD_LICENSE_FILE=${ANSYSLMD_LICENSE_FILE} \ + -e ANSYS_LOCK="OFF" \ + ${DPF_ON} \ + -p ${PYMAPDL_PORT}:50052 \ + -p ${PYMAPDL_DB_PORT}:${DB_INT_PORT} \ + ${DPF_PORT_ARG} \ + -e VERSION=${VERSION} \ + -e DPF_PORT_INTERNAL=${DPF_PORT_INTERNAL} \ + -e EXEC_PATH=${EXEC_PATH} \ + -e DISTRIBUTED_MODE=${DISTRIBUTED_MODE} \ + --shm-size=2gb \ + -e I_MPI_SHM_LMT=shm \ + -e P_SCHEMA=${P_SCHEMA} \ + -w /jobs \ + -u=0:0 \ + --memory=6656MB \ + --memory-swap=16896MB \ + --mount type=bind,src=${PWD}/.ci/entrypoint.sh,dst=/entrypoint.sh \ + ${MAPDL_IMAGE} /entrypoint.sh +_EOT_ +) + +echo "Running docker command: " +echo "docker ${CMD}" +# shellcheck disable=SC2086 +docker $CMD > "${INSTANCE_NAME}.log" & +grep -q 'Server listening on' <(timeout 60 tail -f "${INSTANCE_NAME}.log") + +echo "Content of ${INSTANCE_NAME}.log:" +cat "${INSTANCE_NAME}.log" diff --git a/.ci/waiting_services.sh b/.ci/waiting_services.sh index 0222548ee..e174a7b95 100755 --- a/.ci/waiting_services.sh +++ b/.ci/waiting_services.sh @@ -1,16 +1,20 @@ #!/bin/bash +echo "::group:: Docker services" && docker ps && echo "::endgroup::" + echo "Waiting for the MAPDL service to be up..." nc -v -z localhost "$PYMAPDL_PORT" + echo "::group:: ps aux Output" && ps aux && echo "::endgroup::" -echo "Waiting for MAPDL port is open..." + echo "::group:: Waiting for the MAPDL port to be open..." while ! nc -z localhost "$PYMAPDL_PORT"; do -sleep 0.1 + sleep 0.1 done echo "::endgroup::" echo "MAPDL service is up!" + echo "::group:: Waiting for the DPF port to be open..." while ! nc -z localhost "$DPF_PORT"; do sleep 0.1 diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index dab1d5b60..6beeba0bd 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -23,9 +23,10 @@ on: - 'mapdl-dpf/**' env: + ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} MAIN_PYTHON_VERSION: '3.12' MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' - ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} + DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.1 RUN_DOC_BUILD: false @@ -79,15 +80,19 @@ jobs: env: LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }} + MAPDL_PACKAGE: ${{ env.MAPDL_DOCKER_IMAGE }} + ANSYSLMD_LICENSE_FILE: ${{ env.ANSYSLMD_LICENSE_FILE }} DISTRIBUTED_MODE: "dmp" run: | export INSTANCE_NAME=MAPDL_0 .ci/start_mapdl.sh & export DOCKER_PID=$! echo "Launching MAPDL service at PID: $DOCKER_PID" echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT - - name: "DPF Server Activation" + + - name: "DPF server activation" + shell: bash run: | - $(docker pull ghcr.io/ansys/dpf-core:22.2dev && docker run -d --name dpfserver -p ${{ env.DPF_PORT }}:50052 ghcr.io/ansys/dpf-core:22.2dev && echo "DPF Server active on port ${{ env.DPF_PORT }}.") & + $(docker pull $DPF_DOCKER_IMAGE && docker run -d --name dpfserver --env ANSYS_DPF_ACCEPT_LA=Y -p ${{ env.DPF_PORT }}:50052 $DPF_DOCKER_IMAGE && echo "DPF Server active on port ${{ env.DPF_PORT }}.") & - name: "Test virtual framebuffer" run: | From 2c00834218041c146699c569bfdab8762af508de Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:42:44 +0200 Subject: [PATCH 011/100] fix: MAPDL workflow --- .github/workflows/mapdl-dpf.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 6beeba0bd..af1224616 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -43,8 +43,6 @@ jobs: matrix: ansys-release: [24.1, 24.2, 25.1] env: - PYMAPDL_PORT: 21000 # default won't work on GitHub runners - PYMAPDL_DB_PORT: 21001 # default won't work on GitHub runners PYMAPDL_START_INSTANCE: FALSE ON_DOCUMENTATION: TRUE From d29bbc9fd4f4a94ec883ebf1cddea43e1a8b529a Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:13:16 +0200 Subject: [PATCH 012/100] fix: MAPDL workflow - 2 --- .ci/waiting_services.sh | 6 ++++++ .github/workflows/mapdl-dpf.yml | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.ci/waiting_services.sh b/.ci/waiting_services.sh index e174a7b95..bcc29d0c3 100755 --- a/.ci/waiting_services.sh +++ b/.ci/waiting_services.sh @@ -1,4 +1,10 @@ #!/bin/bash + +# Required environment variables: +# ------------------------------- +# - PYMAPDL_PORT: The port for the PyMAPDL service (e.g., 50052). +# - DPF_PORT: The port for the DPF service (e.g., 50055). + echo "::group:: Docker services" && docker ps && echo "::endgroup::" echo "Waiting for the MAPDL service to be up..." diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index af1224616..ab311e651 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -33,6 +33,8 @@ env: DPF_PORT: 21002 DPF_START_SERVER: False MAPDL_IMAGE_VERSION_DOCS_BUILD: v24.2-ubuntu + PYMAPDL_PORT: 21000 + PYMAPDL_DB_PORT: 21001 jobs: mapdl-dpf: @@ -76,11 +78,14 @@ jobs: - name: "Pull, launch, and validate MAPDL service" id: start_mapdl env: + DISTRIBUTED_MODE: "dmp" LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }} MAPDL_PACKAGE: ${{ env.MAPDL_DOCKER_IMAGE }} ANSYSLMD_LICENSE_FILE: ${{ env.ANSYSLMD_LICENSE_FILE }} - DISTRIBUTED_MODE: "dmp" + PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} + PYMAPDL_DB_PORT: ${{ env.PYMAPDL_DB_PORT }} + DPF_PORT: ${{ env.DPF_PORT }} run: | export INSTANCE_NAME=MAPDL_0 .ci/start_mapdl.sh & export DOCKER_PID=$! @@ -104,6 +109,9 @@ jobs: - name: "Waiting for the services to be up" timeout-minutes: 15 + env: + PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} + DPF_PORT: ${{ env.DPF_PORT }} run: | .ci/waiting_services.sh From 5263eee255bcbb96115b591c34e79f2348c7aaf5 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:25:42 +0200 Subject: [PATCH 013/100] fix: adding missing file --- .ci/entrypoint.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .ci/entrypoint.sh diff --git a/.ci/entrypoint.sh b/.ci/entrypoint.sh new file mode 100644 index 000000000..ca61a1ee2 --- /dev/null +++ b/.ci/entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/bash +export OMPI_ALLOW_RUN_AS_ROOT=1 +export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 + +if [ -z "${VERSION}" ]; then + echo "VERSION environment variable is not set. Please set it to the desired Ansys version." + exit 1 +fi + +RUN_DPF_SERVER=${RUN_DPF_SERVER:-false} + +if [ -n "${ANSYS_DPF_ACCEPT_LA}" ]; then + if [ "${ANSYS_DPF_ACCEPT_LA}" == "Y" ]; then + RUN_DPF_SERVER=true + fi +fi + +echo "RUN_DPF_SERVER: $RUN_DPF_SERVER" + +if [ "$RUN_DPF_SERVER" == "true" ]; then + echo "Starting DPF server..." + "/ansys_inc/v${VERSION}/aisol/bin/linx64/Ans.Dpf.Grpc.sh" --port "${DPF_PORT_INTERNAL}" > log_dpf.log & + echo "DPF server started." +fi + +echo "Starting MAPDL..." +echo "Using executable path: ${EXEC_PATH}" + +$EXEC_PATH -grpc -dir /jobs -"${DISTRIBUTED_MODE}" -np 2 -db -6000 -m -6000 - \ No newline at end of file From 54a8b9e08f91e13455bf08a5fc99e26d55960a93 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:00:28 +0200 Subject: [PATCH 014/100] fix: applying @germa89's suggestions --- .github/workflows/mapdl-dpf.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index ab311e651..1586afafa 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -40,6 +40,12 @@ jobs: mapdl-dpf: name: MAPDL-DPF runs-on: public-ubuntu-latest-8-cores + container: + image: "ghcr.io/ansys/mapdl:v25.2.4-ubuntu-cicd" + options: -u=0:0 --oom-kill-disable --memory=6656MB --memory-swap=16896MB --shm-size=1gb --entrypoint /bin/bash + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} strategy: fail-fast: false matrix: @@ -120,6 +126,9 @@ jobs: LIBGL_ALWAYS_SOFTWARE: 1 PYANSYS_VISUALIZER_HTML_BACKEND: true run: | + echo "Starting MAPDL-DPF workflow..." + echo "PYMAPDL_PORT: $PYMAPDL_PORT" + echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" python mapdl-dpf/wf_mapdl-dpf.py - name: Stop all containers (if any) From 21df61ab89312941f43f29aa10ba8516ba5d4da5 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:25:19 +0200 Subject: [PATCH 015/100] fix: python version --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 1586afafa..86d434e57 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -24,7 +24,7 @@ on: env: ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} - MAIN_PYTHON_VERSION: '3.12' + MAIN_PYTHON_VERSION: "3.11" MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true From 68d167d277b38c80d96d85e0d920e19bafb4a9d3 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:36:08 +0200 Subject: [PATCH 016/100] fix: applying @germa89 feedback --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 86d434e57..58b7a6c1b 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -39,7 +39,7 @@ env: jobs: mapdl-dpf: name: MAPDL-DPF - runs-on: public-ubuntu-latest-8-cores + runs-on: ubuntu-22.04 container: image: "ghcr.io/ansys/mapdl:v25.2.4-ubuntu-cicd" options: -u=0:0 --oom-kill-disable --memory=6656MB --memory-swap=16896MB --shm-size=1gb --entrypoint /bin/bash From 62b7628e5739147158a0e8395e503d770e8d344c Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:40:42 +0200 Subject: [PATCH 017/100] fix: adding a venv --- .github/workflows/mapdl-dpf.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 58b7a6c1b..8e8cdc1f5 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -69,6 +69,11 @@ jobs: with: python-version: ${{ env.MAIN_PYTHON_VERSION }} + - name: Create a virtual environment and activate it + run: | + python -m venv .venv + .venv\Scripts\activate.bat + - name: Install dependencies run: | python -m pip install --upgrade pip From 1fd3bf29a7570fa9a6071bac3f675ea4d516c3f8 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:43:04 +0200 Subject: [PATCH 018/100] fix: activate venv --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 8e8cdc1f5..203f44cc2 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -72,7 +72,7 @@ jobs: - name: Create a virtual environment and activate it run: | python -m venv .venv - .venv\Scripts\activate.bat + .venv/bin/activate - name: Install dependencies run: | From 23dace67aabf99c1281398b45f18d7d98cb39c0d Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:45:20 +0200 Subject: [PATCH 019/100] fix: activate venv -2 --- .github/workflows/mapdl-dpf.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 203f44cc2..48a61ece2 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -69,13 +69,13 @@ jobs: with: python-version: ${{ env.MAIN_PYTHON_VERSION }} - - name: Create a virtual environment and activate it + - name: Create a virtual environment run: | python -m venv .venv - .venv/bin/activate - name: Install dependencies run: | + .venv/bin/activate python -m pip install --upgrade pip pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt @@ -98,6 +98,7 @@ jobs: PYMAPDL_DB_PORT: ${{ env.PYMAPDL_DB_PORT }} DPF_PORT: ${{ env.DPF_PORT }} run: | + .venv/bin/activate export INSTANCE_NAME=MAPDL_0 .ci/start_mapdl.sh & export DOCKER_PID=$! echo "Launching MAPDL service at PID: $DOCKER_PID" @@ -110,6 +111,7 @@ jobs: - name: "Test virtual framebuffer" run: | + .venv/bin/activate xvfb-run python .ci/display_test.py - name: "Retrieve PyMAPDL version" @@ -131,6 +133,7 @@ jobs: LIBGL_ALWAYS_SOFTWARE: 1 PYANSYS_VISUALIZER_HTML_BACKEND: true run: | + .venv/bin/activate echo "Starting MAPDL-DPF workflow..." echo "PYMAPDL_PORT: $PYMAPDL_PORT" echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" From f858ee5a355ddf4e373e941271bcb019de2eac46 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:47:44 +0200 Subject: [PATCH 020/100] fix: applying @germa89 feedback - 2 --- .github/workflows/mapdl-dpf.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 48a61ece2..ff5c675db 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -72,6 +72,8 @@ jobs: - name: Create a virtual environment run: | python -m venv .venv + .venv/bin/activate + apt-get update && apt-get -y install gcc mono-mcs g++ - name: Install dependencies run: | From 6d80a0f7b8fbb22568fd6cbfc5d8a273991e5279 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:35:10 +0200 Subject: [PATCH 021/100] fix: virtual environment --- .github/workflows/mapdl-dpf.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index ff5c675db..aa87548e8 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -72,14 +72,14 @@ jobs: - name: Create a virtual environment run: | python -m venv .venv - .venv/bin/activate + source .venv/bin/activate apt-get update && apt-get -y install gcc mono-mcs g++ - name: Install dependencies run: | - .venv/bin/activate - python -m pip install --upgrade pip - pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt + source .venv/bin/activate + python -m pip install --upgrade pip --root-user-action=ignore + pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt --root-user-action=ignore - name: Login to GitHub Container Registry uses: docker/login-action@v3 @@ -100,7 +100,7 @@ jobs: PYMAPDL_DB_PORT: ${{ env.PYMAPDL_DB_PORT }} DPF_PORT: ${{ env.DPF_PORT }} run: | - .venv/bin/activate + source .venv/bin/activate export INSTANCE_NAME=MAPDL_0 .ci/start_mapdl.sh & export DOCKER_PID=$! echo "Launching MAPDL service at PID: $DOCKER_PID" @@ -113,12 +113,13 @@ jobs: - name: "Test virtual framebuffer" run: | - .venv/bin/activate + source .venv/bin/activate xvfb-run python .ci/display_test.py - name: "Retrieve PyMAPDL version" id: version run: | + source .venv/bin/activate echo "PYMAPDL_VERSION=$(python -c 'from ansys.mapdl.core import __version__; print(__version__)')" >> $GITHUB_OUTPUT echo "PyMAPDL version is: $(python -c "from ansys.mapdl.core import __version__; print(__version__)")" @@ -135,7 +136,7 @@ jobs: LIBGL_ALWAYS_SOFTWARE: 1 PYANSYS_VISUALIZER_HTML_BACKEND: true run: | - .venv/bin/activate + source .venv/bin/activate echo "Starting MAPDL-DPF workflow..." echo "PYMAPDL_PORT: $PYMAPDL_PORT" echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" @@ -164,8 +165,9 @@ jobs: env: BUILD_DOCS_SCRIPT: 'mapdl-dpf/wf_mapdl-dpf.py' run: | + source .venv/bin/activate cd doc - pip install -r requirements.txt + pip install -r requirements.txt --root-user-action=ignore xvfb-run make html - name: (DOCS) Upload docs artifacts From 23e4b3c231dd6d690e2fb6e0da047149aa309f55 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:38:15 +0200 Subject: [PATCH 022/100] fix: issue with source --- .github/workflows/mapdl-dpf.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index aa87548e8..bc5d4cc8b 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -70,14 +70,16 @@ jobs: python-version: ${{ env.MAIN_PYTHON_VERSION }} - name: Create a virtual environment + shell: bash run: | python -m venv .venv - source .venv/bin/activate + . .venv/bin/activate apt-get update && apt-get -y install gcc mono-mcs g++ - name: Install dependencies + shell: bash run: | - source .venv/bin/activate + . .venv/bin/activate python -m pip install --upgrade pip --root-user-action=ignore pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt --root-user-action=ignore @@ -90,6 +92,7 @@ jobs: - name: "Pull, launch, and validate MAPDL service" id: start_mapdl + shell: bash env: DISTRIBUTED_MODE: "dmp" LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} @@ -100,7 +103,7 @@ jobs: PYMAPDL_DB_PORT: ${{ env.PYMAPDL_DB_PORT }} DPF_PORT: ${{ env.DPF_PORT }} run: | - source .venv/bin/activate + . .venv/bin/activate export INSTANCE_NAME=MAPDL_0 .ci/start_mapdl.sh & export DOCKER_PID=$! echo "Launching MAPDL service at PID: $DOCKER_PID" @@ -112,14 +115,16 @@ jobs: $(docker pull $DPF_DOCKER_IMAGE && docker run -d --name dpfserver --env ANSYS_DPF_ACCEPT_LA=Y -p ${{ env.DPF_PORT }}:50052 $DPF_DOCKER_IMAGE && echo "DPF Server active on port ${{ env.DPF_PORT }}.") & - name: "Test virtual framebuffer" + shell: bash run: | - source .venv/bin/activate + . .venv/bin/activate xvfb-run python .ci/display_test.py - name: "Retrieve PyMAPDL version" id: version + shell: bash run: | - source .venv/bin/activate + . .venv/bin/activate echo "PYMAPDL_VERSION=$(python -c 'from ansys.mapdl.core import __version__; print(__version__)')" >> $GITHUB_OUTPUT echo "PyMAPDL version is: $(python -c "from ansys.mapdl.core import __version__; print(__version__)")" @@ -132,11 +137,12 @@ jobs: .ci/waiting_services.sh - name: Run the MAPDL-DPF script + shell: bash env: LIBGL_ALWAYS_SOFTWARE: 1 PYANSYS_VISUALIZER_HTML_BACKEND: true run: | - source .venv/bin/activate + . .venv/bin/activate echo "Starting MAPDL-DPF workflow..." echo "PYMAPDL_PORT: $PYMAPDL_PORT" echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" @@ -162,10 +168,11 @@ jobs: - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) if: ${{ env.RUN_DOC_BUILD == 'true' }} + shell: bash env: BUILD_DOCS_SCRIPT: 'mapdl-dpf/wf_mapdl-dpf.py' run: | - source .venv/bin/activate + . .venv/bin/activate cd doc pip install -r requirements.txt --root-user-action=ignore xvfb-run make html From 99e95b66abdc2e950f06980e9c2467f937ce80cc Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:04:44 +0200 Subject: [PATCH 023/100] fix: removing ` Login to GitHub Container Registry` step --- .github/workflows/mapdl-dpf.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index bc5d4cc8b..91485fac0 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -83,14 +83,7 @@ jobs: python -m pip install --upgrade pip --root-user-action=ignore pip install -r mapdl-dpf/requirements_${{ matrix.ansys-release }}.txt --root-user-action=ignore - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: "Pull, launch, and validate MAPDL service" + - name: "Start MAPDL service directly (inside container)" id: start_mapdl shell: bash env: From 7c61be054eec34d0504dbdea73bebcf4eb955e37 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:52:14 +0200 Subject: [PATCH 024/100] fix: trying to fix ``start_mapdl.sh`` --- .ci/start_mapdl.sh | 166 +++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 73 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 121fccc9e..fbf99d365 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -1,9 +1,9 @@ #!/bin/bash -# This script is used to start a MAPDL instance in a Docker container. +# This script is modified to start MAPDL directly inside a container (no Docker-in-Docker) # # Usage: # ------ -# This script is intended to be run in a CI/CD environment where the necessary environment variables are set. +# This script is intended to be run in a CI/CD environment where we're already inside a MAPDL container. # # Required environment variables: # ------------------------------- @@ -12,45 +12,32 @@ # - DPF_PORT: The port for the DPF service (e.g., 50055). # - INSTANCE_NAME: The name of the MAPDL instance (e.g., "MAPDL_0"). # - LICENSE_SERVER: The address of the license server (e.g., "123.123.123.123"). -# - MAPDL_PACKAGE: The Docker image package name (e.g., ansys/mapdl). # - MAPDL_VERSION: The version of the MAPDL image to use (e.g., "v25.2-ubuntu-cicd"). # - PYMAPDL_DB_PORT: The port for the PyMAPDL database service (e.g., 50056). # - PYMAPDL_PORT: The port for the PyMAPDL service (e.g., 50052). # -# Example: -# -------- -# -# export DISTRIBUTED_MODE="smp" -# export DPF_PORT=50055 -# export INSTANCE_NAME=MAPDL_0 -# export LICENSE_SERVER="123.123.123.123" -# export MAPDL_PACKAGE=ghcr.io/ansys/mapdl -# export MAPDL_VERSION=v25.2-ubuntu-cicd -# export PYMAPDL_DB_PORT=50056 -# export PYMAPDL_PORT=50052 -# ./start_mapdl.sh -# -export MAJOR MINOR MAPDL_IMAGE VERSION +export MAJOR MINOR VERSION echo "MAPDL Instance name: $INSTANCE_NAME" echo "MAPDL_VERSION: $MAPDL_VERSION" +echo "Running inside container - no Docker pull needed" -MAPDL_IMAGE="$MAPDL_PACKAGE:$MAPDL_VERSION" -echo "MAPDL_IMAGE: $MAPDL_IMAGE" -docker pull "$MAPDL_IMAGE" - +# Extract version from MAPDL_VERSION MAJOR=$(echo "$MAPDL_VERSION" | head -c 3 | tail -c 2) MINOR=$(echo "$MAPDL_VERSION" | head -c 5 | tail -c 1) VERSION="$MAJOR$MINOR" echo "MAPDL VERSION: $VERSION" +# Configure licensing +export ANSYSLMD_LICENSE_FILE="1055@$LICENSE_SERVER" +# Determine executable path based on image type if [[ $MAPDL_VERSION == *"latest-ubuntu"* ]]; then echo "It is latest-ubuntu. Using 'ansys' script to launch" EXEC_PATH=ansys - # P_SCHEMA=/ansys_inc/ansys/ac4/schema + P_SCHEMA=/ansys_inc/ansys/ac4/schema elif [[ $MAPDL_VERSION == *"ubuntu"* ]] ; then echo "It is an ubuntu based image" @@ -63,68 +50,101 @@ else P_SCHEMA=/ansys_inc/ansys/ac4/schema fi; +# Handle CICD version specifics if [[ $MAPDL_VERSION == *"cicd"* ]] ; then - echo "It is a CICD version, binding DPF port too" + echo "It is a CICD version" if [ "$RUN_DPF_SERVER" == "true" ]; then - echo "RUN_DPF_SERVER is set to true, starting DPF server" - export DPF_ON="-e ANSYS_DPF_ACCEPT_LA=Y" + echo "RUN_DPF_SERVER is set to true, DPF will be available" + export ANSYS_DPF_ACCEPT_LA=Y fi - export DPF_PORT_INTERNAL=50055 - export DPF_PORT_ARG="-p ${DPF_PORT}:${DPF_PORT_INTERNAL}" - export DB_INT_PORT=50056 + echo "DPF_PORT: $DPF_PORT" + echo "PYMAPDL_DB_PORT: $PYMAPDL_DB_PORT" - echo "DPF_PORT_ARG: $DPF_PORT_ARG" - echo "DB_INT_PORT: $DB_INT_PORT" - - echo "Overriding DISTRIBUTED_MODE to 'dmp' for CICD version" - export DISTRIBUTED_MODE="dmp" + echo "Setting DISTRIBUTED_MODE to $DISTRIBUTED_MODE for CICD version" else - export DPF_PORT_ARG="" - export DB_INT_PORT=50055 - export DPF_ON="" + export PYMAPDL_DB_PORT=DPF_PORT fi; echo "EXEC_PATH: $EXEC_PATH" echo "P_SCHEMA: $P_SCHEMA" -# Building docker command -CMD=$(cat <<-_EOT_ -run \ - --entrypoint /bin/bash \ - --name ${INSTANCE_NAME} \ - --restart always \ - --health-interval=0.5s \ - --health-retries=4 \ - --health-timeout=0.5s \ - --health-start-period=10s \ - -e ANSYSLMD_LICENSE_FILE=${ANSYSLMD_LICENSE_FILE} \ - -e ANSYS_LOCK="OFF" \ - ${DPF_ON} \ - -p ${PYMAPDL_PORT}:50052 \ - -p ${PYMAPDL_DB_PORT}:${DB_INT_PORT} \ - ${DPF_PORT_ARG} \ - -e VERSION=${VERSION} \ - -e DPF_PORT_INTERNAL=${DPF_PORT_INTERNAL} \ - -e EXEC_PATH=${EXEC_PATH} \ - -e DISTRIBUTED_MODE=${DISTRIBUTED_MODE} \ - --shm-size=2gb \ - -e I_MPI_SHM_LMT=shm \ - -e P_SCHEMA=${P_SCHEMA} \ - -w /jobs \ - -u=0:0 \ - --memory=6656MB \ - --memory-swap=16896MB \ - --mount type=bind,src=${PWD}/.ci/entrypoint.sh,dst=/entrypoint.sh \ - ${MAPDL_IMAGE} /entrypoint.sh -_EOT_ -) - -echo "Running docker command: " -echo "docker ${CMD}" -# shellcheck disable=SC2086 -docker $CMD > "${INSTANCE_NAME}.log" & -grep -q 'Server listening on' <(timeout 60 tail -f "${INSTANCE_NAME}.log") +# Verify executable exists +if [ ! -f "$EXEC_PATH" ] && [ "$EXEC_PATH" != "ansys" ]; then + echo "Executable not found at $EXEC_PATH, searching for alternatives..." + + # Try alternative paths + ALTERNATIVE_PATHS=( + "/ansys_inc/v$VERSION/ansys/bin/ansys$VERSION" + "/ansys_inc/ansys/bin/mapdl" + "/ansys_inc/ansys/bin/ansys$VERSION" + "/opt/ansys_inc/v$VERSION/ansys/bin/mapdl" + ) + + for alt_path in "${ALTERNATIVE_PATHS[@]}"; do + if [ -f "$alt_path" ]; then + EXEC_PATH="$alt_path" + echo "Found alternative executable: $EXEC_PATH" + break + fi + done + + # If still not found, try which command + if [ ! -f "$EXEC_PATH" ]; then + if command -v "ansys$VERSION" >/dev/null 2>&1; then + EXEC_PATH="ansys$VERSION" + echo "Using system command: $EXEC_PATH" + else + echo "ERROR: Could not find MAPDL executable" + echo "Searched paths:" + printf '%s\n' "${ALTERNATIVE_PATHS[@]}" + exit 1 + fi + fi +fi + +# Set environment variables for MAPDL +export ANSYS_LOCK="OFF" +export I_MPI_SHM_LMT=shm +export VERSION=$VERSION +export P_SCHEMA=$P_SCHEMA + +echo "Starting MAPDL directly in container..." + +# Start MAPDL using the entrypoint logic directly +echo "Starting MAPDL with: $EXEC_PATH -grpc -p $PYMAPDL_PORT -$DISTRIBUTED_MODE" + +# Create the log file +touch "${INSTANCE_NAME}.log" + +# Start MAPDL in background +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -distributed > "${INSTANCE_NAME}.log" 2>&1 & +MAPDL_PID=$! + +echo "MAPDL started with PID: $MAPDL_PID" + +# Wait for MAPDL to be ready (similar to original script) +echo "Waiting for MAPDL to start..." +timeout 60 bash -c " + while ! grep -q 'Server listening on' '${INSTANCE_NAME}.log' 2>/dev/null; do + if ! kill -0 $MAPDL_PID 2>/dev/null; then + echo 'ERROR: MAPDL process died' + exit 1 + fi + sleep 1 + done +" + +if [ $? -eq 0 ]; then + echo "MAPDL is ready!" +else + echo "ERROR: MAPDL failed to start or timed out" + echo "Content of ${INSTANCE_NAME}.log:" + cat "${INSTANCE_NAME}.log" + exit 1 +fi echo "Content of ${INSTANCE_NAME}.log:" cat "${INSTANCE_NAME}.log" + +echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From af82ebd54a2b683c3917839ae7a1d2888c1e7ed3 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:55:05 +0200 Subject: [PATCH 025/100] fix: DPF servie and test virtual framebuffer --- .github/workflows/mapdl-dpf.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 91485fac0..4361cafbf 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -102,16 +102,31 @@ jobs: echo "Launching MAPDL service at PID: $DOCKER_PID" echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT - - name: "DPF server activation" + - name: "DPF server setup" shell: bash run: | - $(docker pull $DPF_DOCKER_IMAGE && docker run -d --name dpfserver --env ANSYS_DPF_ACCEPT_LA=Y -p ${{ env.DPF_PORT }}:50052 $DPF_DOCKER_IMAGE && echo "DPF Server active on port ${{ env.DPF_PORT }}.") & + . .venv/bin/activate + echo "DPF service will be available through MAPDL container" + echo "DPF_PORT: $DPF_PORT" + # DPF is already available in the MAPDL container + # The Python script will connect to it as needed + export ANSYS_DPF_ACCEPT_LA=Y + echo "ANSYS_DPF_ACCEPT_LA set to Y" - name: "Test virtual framebuffer" shell: bash run: | . .venv/bin/activate - xvfb-run python .ci/display_test.py + echo "Testing virtual framebuffer..." + # Install xvfb if not available + which xvfb-run || (apt-get update && apt-get install -y xvfb) + + # Test display if the script exists + if [ -f ".ci/display_test.py" ]; then + xvfb-run python .ci/display_test.py + else + echo "Display test script not found, skipping..." + fi - name: "Retrieve PyMAPDL version" id: version From 433df22dfde2eaf37c9421b25b6f033903934f65 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:56:53 +0200 Subject: [PATCH 026/100] fix: MAPDL version --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 4361cafbf..f583955e0 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -32,7 +32,7 @@ env: RUN_DOC_BUILD: false DPF_PORT: 21002 DPF_START_SERVER: False - MAPDL_IMAGE_VERSION_DOCS_BUILD: v24.2-ubuntu + MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd PYMAPDL_PORT: 21000 PYMAPDL_DB_PORT: 21001 From dd550a27cdab8c0c5667d3db7fe0cc94e8ee79b8 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:57:30 +0200 Subject: [PATCH 027/100] fix: pre-commit --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index f583955e0..b539dac08 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -120,7 +120,7 @@ jobs: echo "Testing virtual framebuffer..." # Install xvfb if not available which xvfb-run || (apt-get update && apt-get install -y xvfb) - + # Test display if the script exists if [ -f ".ci/display_test.py" ]; then xvfb-run python .ci/display_test.py From 366dd8bb6ef325b62e3e4f48089e3161f1aba9b6 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:07:18 +0200 Subject: [PATCH 028/100] fix: commenting step as not used for the moment --- .github/workflows/mapdl-dpf.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index b539dac08..a0059431f 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -136,13 +136,13 @@ jobs: echo "PYMAPDL_VERSION=$(python -c 'from ansys.mapdl.core import __version__; print(__version__)')" >> $GITHUB_OUTPUT echo "PyMAPDL version is: $(python -c "from ansys.mapdl.core import __version__; print(__version__)")" - - name: "Waiting for the services to be up" - timeout-minutes: 15 - env: - PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} - DPF_PORT: ${{ env.DPF_PORT }} - run: | - .ci/waiting_services.sh + # - name: "Waiting for the services to be up" + # timeout-minutes: 15 + # env: + # PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} + # DPF_PORT: ${{ env.DPF_PORT }} + # run: | + # .ci/waiting_services.sh - name: Run the MAPDL-DPF script shell: bash From f77b6bf7ed84e2771341016453a6abb18b472676 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 19 Sep 2025 18:16:18 +0200 Subject: [PATCH 029/100] fix: ``start_mapdl.sh`` --- .ci/start_mapdl.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index fbf99d365..5c57a4610 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -109,16 +109,27 @@ export I_MPI_SHM_LMT=shm export VERSION=$VERSION export P_SCHEMA=$P_SCHEMA +# Allow MPI to run as root (required for container environments) +export OMPI_ALLOW_RUN_AS_ROOT=1 +export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 + echo "Starting MAPDL directly in container..." +echo "Environment variables:" +echo " ANSYSLMD_LICENSE_FILE: $ANSYSLMD_LICENSE_FILE" +echo " ANSYS_LOCK: $ANSYS_LOCK" +echo " I_MPI_SHM_LMT: $I_MPI_SHM_LMT" +echo " DISTRIBUTED_MODE: $DISTRIBUTED_MODE" +echo " OMPI_ALLOW_RUN_AS_ROOT: $OMPI_ALLOW_RUN_AS_ROOT" +echo " OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: $OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" # Start MAPDL using the entrypoint logic directly -echo "Starting MAPDL with: $EXEC_PATH -grpc -p $PYMAPDL_PORT -$DISTRIBUTED_MODE" +echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE" # Create the log file touch "${INSTANCE_NAME}.log" -# Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -distributed > "${INSTANCE_NAME}.log" 2>&1 & +# Start MAPDL in background - use the variable, not hardcoded +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! echo "MAPDL started with PID: $MAPDL_PID" From a1ff2ee5c6df733d86cc63763b2dc25dc8721658 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:30:21 +0200 Subject: [PATCH 030/100] fix: removing CICD --- .ci/start_mapdl.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 5c57a4610..12b50a4da 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -115,7 +115,6 @@ export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 echo "Starting MAPDL directly in container..." echo "Environment variables:" -echo " ANSYSLMD_LICENSE_FILE: $ANSYSLMD_LICENSE_FILE" echo " ANSYS_LOCK: $ANSYS_LOCK" echo " I_MPI_SHM_LMT: $I_MPI_SHM_LMT" echo " DISTRIBUTED_MODE: $DISTRIBUTED_MODE" From 641f93008903335ffbc3c625e7de96d876ada9c9 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:54:00 +0200 Subject: [PATCH 031/100] fix: removing matrix for testing purpose --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index a0059431f..c666a829a 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: false matrix: - ansys-release: [24.1, 24.2, 25.1] + ansys-release: [25.2] env: PYMAPDL_START_INSTANCE: FALSE ON_DOCUMENTATION: TRUE From 73a12b9e395c20ec76f4ad611de6ce838bc0c8c1 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:58:52 +0200 Subject: [PATCH 032/100] fix: requirements --- mapdl-dpf/requirements_25.2.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 mapdl-dpf/requirements_25.2.txt diff --git a/mapdl-dpf/requirements_25.2.txt b/mapdl-dpf/requirements_25.2.txt new file mode 100644 index 000000000..cb53bc1be --- /dev/null +++ b/mapdl-dpf/requirements_25.2.txt @@ -0,0 +1,2 @@ +ansys-mapdl-core[graphics]==0.70.2 +ansys-dpf-core==0.14.1 From e65dcdb69d6237a1372b9a1f63df0dca27a5b9ea Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:05:26 +0200 Subject: [PATCH 033/100] test: check if PyMAPDL port is free --- .ci/start_mapdl.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 12b50a4da..1f91aa9df 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -50,6 +50,9 @@ else P_SCHEMA=/ansys_inc/ansys/ac4/schema fi; +echo "EXEC_PATH: $EXEC_PATH" +echo "P_SCHEMA: $P_SCHEMA" + # Handle CICD version specifics if [[ $MAPDL_VERSION == *"cicd"* ]] ; then echo "It is a CICD version" @@ -124,10 +127,13 @@ echo " OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: $OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" # Start MAPDL using the entrypoint logic directly echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE" +# Checking PYMAPDL is free +netstat -tulpn | grep :$PYMAPDL_PORT || echo "Port $PYMAPDL_PORT is free" + # Create the log file touch "${INSTANCE_NAME}.log" -# Start MAPDL in background - use the variable, not hardcoded +# Start MAPDL in background nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! From 8cb7d373a3d2729a124f310f7cd690bf31bb65d5 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:13:20 +0200 Subject: [PATCH 034/100] fix: few fixes in ``start_mapdl`` --- .ci/start_mapdl.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 1f91aa9df..cdd0e1be8 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -30,9 +30,6 @@ MINOR=$(echo "$MAPDL_VERSION" | head -c 5 | tail -c 1) VERSION="$MAJOR$MINOR" echo "MAPDL VERSION: $VERSION" -# Configure licensing -export ANSYSLMD_LICENSE_FILE="1055@$LICENSE_SERVER" - # Determine executable path based on image type if [[ $MAPDL_VERSION == *"latest-ubuntu"* ]]; then echo "It is latest-ubuntu. Using 'ansys' script to launch" @@ -134,7 +131,7 @@ netstat -tulpn | grep :$PYMAPDL_PORT || echo "Port $PYMAPDL_PORT is free" touch "${INSTANCE_NAME}.log" # Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE > "${INSTANCE_NAME}.log" 2>&1 & +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! echo "MAPDL started with PID: $MAPDL_PID" From 07547d63a5333bd6dd944fff64330b2e87e79ebb Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:26:37 +0200 Subject: [PATCH 035/100] fix: ``start_mapdl`` --- .ci/start_mapdl.sh | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index cdd0e1be8..35adff313 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -136,28 +136,11 @@ MAPDL_PID=$! echo "MAPDL started with PID: $MAPDL_PID" -# Wait for MAPDL to be ready (similar to original script) +# Wait for MAPDL to be ready echo "Waiting for MAPDL to start..." -timeout 60 bash -c " - while ! grep -q 'Server listening on' '${INSTANCE_NAME}.log' 2>/dev/null; do - if ! kill -0 $MAPDL_PID 2>/dev/null; then - echo 'ERROR: MAPDL process died' - exit 1 - fi - sleep 1 - done -" - -if [ $? -eq 0 ]; then +if grep -q 'Server listening on' <(timeout 60 tail -f "${INSTANCE_NAME}.log"); then echo "MAPDL is ready!" else echo "ERROR: MAPDL failed to start or timed out" - echo "Content of ${INSTANCE_NAME}.log:" - cat "${INSTANCE_NAME}.log" exit 1 fi - -echo "Content of ${INSTANCE_NAME}.log:" -cat "${INSTANCE_NAME}.log" - -echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From 63264ed74bd507d26a43f4754579f81e328c7c50 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:32:18 +0200 Subject: [PATCH 036/100] fix: using ``nc`` approach --- .ci/start_mapdl.sh | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 35adff313..1546e8557 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -134,13 +134,15 @@ touch "${INSTANCE_NAME}.log" nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! -echo "MAPDL started with PID: $MAPDL_PID" - -# Wait for MAPDL to be ready -echo "Waiting for MAPDL to start..." -if grep -q 'Server listening on' <(timeout 60 tail -f "${INSTANCE_NAME}.log"); then - echo "MAPDL is ready!" -else - echo "ERROR: MAPDL failed to start or timed out" - exit 1 -fi +echo "Waiting for the MAPDL port to be open..." +while ! nc -z localhost "$PYMAPDL_PORT"; do + # Check if process is still running + if ! kill -0 $MAPDL_PID 2>/dev/null; then + echo "ERROR: MAPDL process died!" + echo "Log content:" + cat "${INSTANCE_NAME}.log" || echo "No log content" + exit 1 + fi + sleep 0.1 +done +echo "MAPDL service is up!" \ No newline at end of file From 59db90ea3311ca65958961f9a1d95f5944127a11 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:35:03 +0200 Subject: [PATCH 037/100] fix: installing netcat --- .ci/start_mapdl.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 1546e8557..35bb2dcc7 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -134,6 +134,13 @@ touch "${INSTANCE_NAME}.log" nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! +# Install netcat if not available +echo "Checking for netcat..." +if ! command -v nc >/dev/null 2>&1; then + echo "Installing netcat..." + apt-get update -qq && apt-get install -y netcat-openbsd +fi + echo "Waiting for the MAPDL port to be open..." while ! nc -z localhost "$PYMAPDL_PORT"; do # Check if process is still running From 9dbac4bde62327631cf43a61668cc830287c45a3 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:39:01 +0200 Subject: [PATCH 038/100] fix: using ``netstat`` --- .ci/start_mapdl.sh | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 35bb2dcc7..a67d80fe6 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -134,15 +134,9 @@ touch "${INSTANCE_NAME}.log" nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! -# Install netcat if not available -echo "Checking for netcat..." -if ! command -v nc >/dev/null 2>&1; then - echo "Installing netcat..." - apt-get update -qq && apt-get install -y netcat-openbsd -fi - +# Wait for MAPDL to be ready echo "Waiting for the MAPDL port to be open..." -while ! nc -z localhost "$PYMAPDL_PORT"; do +while ! netstat -tulpn | grep -q ":$PYMAPDL_PORT.*LISTEN"; do # Check if process is still running if ! kill -0 $MAPDL_PID 2>/dev/null; then echo "ERROR: MAPDL process died!" @@ -152,4 +146,6 @@ while ! nc -z localhost "$PYMAPDL_PORT"; do fi sleep 0.1 done -echo "MAPDL service is up!" \ No newline at end of file +echo "MAPDL service is up!" + +echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From 1425a1e6f2f8979b0cc5b05fdcfd6fadcf75420c Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:48:25 +0200 Subject: [PATCH 039/100] fix: CICD --- .ci/start_mapdl.sh | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index a67d80fe6..af1f28aa8 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -134,18 +134,17 @@ touch "${INSTANCE_NAME}.log" nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! -# Wait for MAPDL to be ready -echo "Waiting for the MAPDL port to be open..." -while ! netstat -tulpn | grep -q ":$PYMAPDL_PORT.*LISTEN"; do - # Check if process is still running - if ! kill -0 $MAPDL_PID 2>/dev/null; then - echo "ERROR: MAPDL process died!" - echo "Log content:" - cat "${INSTANCE_NAME}.log" || echo "No log content" - exit 1 - fi - sleep 0.1 -done -echo "MAPDL service is up!" +# Give MAPDL time to initialize +echo "Waiting for MAPDL to initialize..." +sleep 10 + +# Check if process is still running +if ! kill -0 $MAPDL_PID 2>/dev/null; then + echo "ERROR: MAPDL process died during startup!" + echo "Log content:" + cat "${INSTANCE_NAME}.log" || echo "No log content" + exit 1 +fi +echo "MAPDL process is running (PID: $MAPDL_PID)" echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From 998f213a70b549020b8340821b73b4647a09fdb0 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:51:01 +0200 Subject: [PATCH 040/100] fix: MAPDL_PID --- .github/workflows/mapdl-dpf.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index c666a829a..9626bf7cb 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -98,9 +98,9 @@ jobs: run: | . .venv/bin/activate export INSTANCE_NAME=MAPDL_0 - .ci/start_mapdl.sh & export DOCKER_PID=$! - echo "Launching MAPDL service at PID: $DOCKER_PID" - echo "DOCKER_PID=$(echo $DOCKER_PID)" >> $GITHUB_OUTPUT + .ci/start_mapdl.sh & export MAPDL_PID=$! + echo "Launching MAPDL service at PID: $MAPDL_PID" + echo "MAPDL_PID=$(echo $MAPDL_PID)" >> $GITHUB_OUTPUT - name: "DPF server setup" shell: bash From e8b64a07d8921606202963ae141296022ce055e6 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:53:34 +0200 Subject: [PATCH 041/100] fix: ``start_mapdl`` --- .ci/start_mapdl.sh | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index af1f28aa8..0e584703e 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -124,9 +124,6 @@ echo " OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: $OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" # Start MAPDL using the entrypoint logic directly echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE" -# Checking PYMAPDL is free -netstat -tulpn | grep :$PYMAPDL_PORT || echo "Port $PYMAPDL_PORT is free" - # Create the log file touch "${INSTANCE_NAME}.log" @@ -138,13 +135,20 @@ MAPDL_PID=$! echo "Waiting for MAPDL to initialize..." sleep 10 -# Check if process is still running -if ! kill -0 $MAPDL_PID 2>/dev/null; then - echo "ERROR: MAPDL process died during startup!" - echo "Log content:" - cat "${INSTANCE_NAME}.log" || echo "No log content" - exit 1 -fi +# Debug: Check immediately and periodically +for i in {1..10}; do + echo "Check $i: Waiting 1 second..." + sleep 1 + + if ! kill -0 $MAPDL_PID 2>/dev/null; then + echo "ERROR: MAPDL process died after $i seconds!" + echo "Log content:" + cat "${INSTANCE_NAME}.log" || echo "No log content" + exit 1 + fi + + echo "MAPDL process still alive after $i seconds" +done echo "MAPDL process is running (PID: $MAPDL_PID)" echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From 55e74cf9ae4b09fd841ac70389245d51cc8c0cbe Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:00:18 +0200 Subject: [PATCH 042/100] fix: ``start_mapdl`` - 2 --- .ci/start_mapdl.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 0e584703e..db5931c03 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -133,7 +133,6 @@ MAPDL_PID=$! # Give MAPDL time to initialize echo "Waiting for MAPDL to initialize..." -sleep 10 # Debug: Check immediately and periodically for i in {1..10}; do From 093f9db50c9721be1b71b0a50150bb05e2c8c880 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:09:36 +0200 Subject: [PATCH 043/100] fix: ``start_mapdl`` - 3 --- .ci/start_mapdl.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index db5931c03..de9c2c666 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -136,18 +136,24 @@ echo "Waiting for MAPDL to initialize..." # Debug: Check immediately and periodically for i in {1..10}; do - echo "Check $i: Waiting 1 second..." - sleep 1 + echo "Check $i: Testing process status..." + # Check process first if ! kill -0 $MAPDL_PID 2>/dev/null; then - echo "ERROR: MAPDL process died after $i seconds!" - echo "Log content:" + echo "ERROR: MAPDL process died after $i checks!" + echo "--- LOG CONTENT ---" cat "${INSTANCE_NAME}.log" || echo "No log content" + echo "--- END LOG ---" + echo "Process list:" + ps aux | grep mapdl || echo "No MAPDL processes found" exit 1 fi echo "MAPDL process still alive after $i seconds" + echo "About to sleep for 1 second..." + sleep 1 + echo "Sleep completed for check $i" done -echo "MAPDL process is running (PID: $MAPDL_PID)" +echo "All checks passed - MAPDL process is running (PID: $MAPDL_PID)" echo "MAPDL_PID=$MAPDL_PID" \ No newline at end of file From f6f389ba91d6547ce951394a66c3d418cf2a8413 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:14:40 +0200 Subject: [PATCH 044/100] fix: removing sleep --- .ci/start_mapdl.sh | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index de9c2c666..c8bcbfa21 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -128,7 +128,7 @@ echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MO touch "${INSTANCE_NAME}.log" # Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 > "${INSTANCE_NAME}.log" 2>&1 & +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 MAPDL_PID=$! # Give MAPDL time to initialize @@ -141,18 +141,12 @@ for i in {1..10}; do # Check process first if ! kill -0 $MAPDL_PID 2>/dev/null; then echo "ERROR: MAPDL process died after $i checks!" - echo "--- LOG CONTENT ---" - cat "${INSTANCE_NAME}.log" || echo "No log content" - echo "--- END LOG ---" echo "Process list:" ps aux | grep mapdl || echo "No MAPDL processes found" exit 1 fi - echo "MAPDL process still alive after $i seconds" - echo "About to sleep for 1 second..." - sleep 1 - echo "Sleep completed for check $i" + echo "MAPDL process still alive after $i." done echo "All checks passed - MAPDL process is running (PID: $MAPDL_PID)" From db088011588d31c035c0c8feec195cf96f71a5f2 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:36:55 +0200 Subject: [PATCH 045/100] feat: simplifying ``wf_mapdl-dpf.py`` for tests --- mapdl-dpf/wf_mapdl-dpf.py | 622 +++++++++++++++++++------------------- 1 file changed, 315 insertions(+), 307 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index ddd4af759..a59b8514c 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -56,310 +56,318 @@ which is then solved completing that timestep. """ -import os -from pathlib import Path -import shutil -import time as tt - -from ansys.dpf import core as dpf -from ansys.mapdl.core import MapdlPool -from ansys.mapdl.core.examples.downloads import download_example_data -import numpy as np - -############################################################################### -# Create directories to save the results -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -folders = ["./Output/Common", "./Output/Global", "./Output/Local"] -for fdr in folders: - try: - shutil.rmtree(fdr, ignore_errors=True) - os.makedirs(fdr) - except: - pass - -############################################################################### -# Create Mapdl pool -# ~~~~~~~~~~~~~~~~~ -# We use the ``MapdlPool`` class to create two separate instances — one dedicated to -# the global simulation and the other to the local simulation - -nCores = 2 # Number of cores to use -pool = MapdlPool(2, nproc=nCores) - -############################################################################### -# Set up Global and Local FE models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We assign the instances to the local and global model, then use -# ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files -# include named selections for the faces we want to apply the boundary conditions and the loads. -# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. -# The function ``Get_boundary`` is used to record the local model’s cut-boundary -# node coordinates as a dpf.field which will be later used in the DPF interpolator input - -cwd = Path.cwd() # Get current working directory - -# download example data -local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") -global_cdb = download_example_data( - filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" -) - -mapdl_global = pool[0] # Global model -mapdl_global.cdread("db", global_cdb) # Load global model -mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model - -mapdl_local = pool[1] # Local model -mapdl_local.cdread("db", local_cdb) # Load local model -mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model - - -def define_BCs(mapdl): - # Enter PREP7 in MAPDL - mapdl.prep7() - - # In the .cdb file for the global model the bottom, the right and the top faces - # are saved as named selections - - # Fixed support - mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face - mapdl.d("ALL", "ALL") - mapdl.nsel("ALL") - - # Frictionless support - mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face - mapdl.d("ALL", "UZ", "0") - mapdl.nsel("ALL") - - # Applied load - # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps - mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") - mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") - mapdl.starset("LOAD(1,1,1)", "0.") - mapdl.starset("LOAD(2,1,1)", "-0.1") - mapdl.starset("LOAD(3,1,1)", "-1.") - - mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face - mapdl.d("ALL", "UY", "%LOAD%") - mapdl.nsel("ALL") - - # Exit PREP7 - mapdl.finish() - pass - - -def Get_boundary(mapdl): - # Enter PREP7 in MAPDL - mapdl.prep7() - - # In the .cdb file for the local model the boundary faces are saved as - # named selections - - mapdl.nsel("all") - nodes = mapdl.mesh.nodes # All nodes - node_id_all = mapdl.mesh.nnum # All nodes ID - mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces - node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID - map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) - - mapdl.nsel("NONE") - boundary_coordinates = dpf.fields_factory.create_3d_vector_field( - num_entities=len(node_id_subset), location="Nodal" - ) # Define DPF field for DPF interpolator input - - nsel = "" - for nid in node_id_subset: # Iterate boundary nodes of the local model - nsel += "nsel,A,NODE,,{}\n".format( - nid - ) # Add selection command for the node to the str (only for ploting) - boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field - - # Select all boundary nodes (only for ploting) - mapdl.input_strings(nsel) - - # Plot boundary nodes of the local model - mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") - - # Exit PREP7 - mapdl.finish() - return boundary_coordinates - - -# Define the boundary conditions and the loading for the global model -define_BCs(mapdl_global) - -# Get the DPF field with the boundary nodes of the local model -boundary_coords = Get_boundary(mapdl_local) - -############################################################################### -# Set up DPF operators -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We define two dpf operators: the first reads the displacement results from the global model, -# and the second interpolates those displacements onto the boundary coordinates of the local model. -# The ``DataSources`` class to link results with the DPF operator inputs. - - -def define_dpf_operators(nCores): - # Define the DataSources class and link it to the results of the global model - dataSources = dpf.DataSources() - for i in range(nCores): - dataSources.set_domain_result_file_path( - path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i - ) - - global_model = dpf.Model(dataSources) - # Define displacement result operator to read nodal displacements - global_disp_op = dpf.operators.result.displacement() - # Connect displacement result operator with the global model's results file - global_disp_op.inputs.data_sources.connect(dataSources) - # Define interpolator to interpolate the results inside the mesh elements - # with shape functions - disp_interpolator = dpf.operators.mapping.on_coordinates() - return global_model, global_disp_op, disp_interpolator - - -def initialize_dpf_interpolator( - global_model, - local_Bc_coords, - disp_interpolator, -): - my_mesh = global_model.metadata.meshed_region # Global model's mesh - disp_interpolator.inputs.coordinates.connect( - local_Bc_coords - ) # Link interpolator inputs with the local model's boundary coordinates - disp_interpolator.inputs.mesh.connect( - my_mesh - ) # Link interpolator mesh with the global model's mesh - - -def interpolate_data(timestep): - global_disp_op.inputs.time_scoping.connect( - [timestep] - ) # Specify timestep value to read results from - global_disp = ( - global_disp_op.outputs.fields_container.get_data() - ) # Read global nodal displacements - - disp_interpolator.inputs.fields_container.connect( - global_disp - ) # Link the interpolation data with the interpolator - local_disp = disp_interpolator.outputs.fields_container.get_data()[ - 0 - ] # Get displacements of the boundary nodes of the local model - return local_disp - - -# Define the two dpf operators -global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) - -############################################################################### -# Set up simulation loop -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We solve the two models sequentially for each loading step. -# First the global model is run producing a .rst results file. -# Then we extract the global displacements and use them to define -# cut-boundary conditions for the local model -# (an input string command will be used for faster excecution time). - - -def define_cut_boundary_constraint_template(local_Bc_coords): - # Define template of input string command to apply the displacement constraints - local_nids = local_Bc_coords.scoping.ids - # Get Node ID of boundary nodes of the local model - template = "" - for nid in local_nids: - template += ( - "d," - + str(nid) - + ",ux,{:1.6e}\nd," - + str(nid) - + ",uy,{:1.6e}\nd," - + str(nid) - + ",uz,{:1.6e}\n" - ) - return template - - -def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): - - # Enter solution processor - mapdl_global.solution() - mapdl_local.solution() - - # Static analysis - mapdl_global.antype("STATIC") - mapdl_local.antype("STATIC") - - constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - - for i in range(1, timesteps + 1): # Iterate timesteps - print(f"Timestep: {i}") - st = tt.time() - # Set loadstep time for the global model - mapdl_global.time(i) - # No extrapolation - mapdl_global.eresx("NO") - mapdl_global.allsel("ALL") - # Write ALL results to database - mapdl_global.outres("ALL", "ALL") - # Solve global model - mapdl_global.solve() - print("Global solve took ", tt.time() - st) - - # Initialize interpolator - if i == 1: - initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) - # Read & Interpolate displacement data - local_disp = interpolate_data(timestep=i) - # Run MAPDL input string command to apply the displacement constraints - data_array = np.array(local_disp.data).flatten() - mapdl_local.input_strings(constraint_template.format(*data_array)) - - st = tt.time() - mapdl_local.allsel("ALL") - # Set loadstep time for the local model - mapdl_local.time(i) - # No extrapolation - mapdl_local.eresx("NO") - # Write ALL results to database - mapdl_local.outres("ALL", "ALL") - # Solve local model - mapdl_local.solve() - print("Local solve took ", tt.time() - st) - - # Exit solution processor - mapdl_global.finish() - mapdl_local.finish() - - -############################################################################### -# Solve system -# ~~~~~~~~~~~~ -n_steps = 10 # Number of timesteps -solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) - -############################################################################### -# Visualize results -# ~~~~~~~~~~~~~~~~~ - - -def visualize(mapdl): - # Enter post-processing - mapdl.post1() - # Set the current results set to the last set to be read from result file - mapdl.set("LAST") - # Plot nodal displacement of the loading direction - mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") - # Exit post-processing - mapdl.finish() - - -# Plot Y displacement of global model -visualize(mapdl_global) - -# Plot Y displacement of local model -visualize(mapdl_local) - -############################################################################### -# Exit MAPDL pool instances -pool.exit() +from ansys.mapdl.core import launch_mapdl + +mapdl = launch_mapdl() + +print(mapdl) + +mapdl.exit() + +# import os +# from pathlib import Path +# import shutil +# import time as tt + +# from ansys.dpf import core as dpf +# from ansys.mapdl.core import MapdlPool +# from ansys.mapdl.core.examples.downloads import download_example_data +# import numpy as np + +# ############################################################################### +# # Create directories to save the results +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +# for fdr in folders: +# try: +# shutil.rmtree(fdr, ignore_errors=True) +# os.makedirs(fdr) +# except: +# pass + +# ############################################################################### +# # Create Mapdl pool +# # ~~~~~~~~~~~~~~~~~ +# # We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# # the global simulation and the other to the local simulation + +# nCores = 2 # Number of cores to use +# pool = MapdlPool(2, nproc=nCores) + +# ############################################################################### +# # Set up Global and Local FE models +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We assign the instances to the local and global model, then use +# # ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files +# # include named selections for the faces we want to apply the boundary conditions and the loads. +# # The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# # The function ``Get_boundary`` is used to record the local model’s cut-boundary +# # node coordinates as a dpf.field which will be later used in the DPF interpolator input + +# cwd = Path.cwd() # Get current working directory + +# # download example data +# local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") +# global_cdb = download_example_data( +# filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" +# ) + +# mapdl_global = pool[0] # Global model +# mapdl_global.cdread("db", global_cdb) # Load global model +# mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model + +# mapdl_local = pool[1] # Local model +# mapdl_local.cdread("db", local_cdb) # Load local model +# mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model + + +# def define_BCs(mapdl): +# # Enter PREP7 in MAPDL +# mapdl.prep7() + +# # In the .cdb file for the global model the bottom, the right and the top faces +# # are saved as named selections + +# # Fixed support +# mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face +# mapdl.d("ALL", "ALL") +# mapdl.nsel("ALL") + +# # Frictionless support +# mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face +# mapdl.d("ALL", "UZ", "0") +# mapdl.nsel("ALL") + +# # Applied load +# # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps +# mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") +# mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") +# mapdl.starset("LOAD(1,1,1)", "0.") +# mapdl.starset("LOAD(2,1,1)", "-0.1") +# mapdl.starset("LOAD(3,1,1)", "-1.") + +# mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face +# mapdl.d("ALL", "UY", "%LOAD%") +# mapdl.nsel("ALL") + +# # Exit PREP7 +# mapdl.finish() +# pass + + +# def Get_boundary(mapdl): +# # Enter PREP7 in MAPDL +# mapdl.prep7() + +# # In the .cdb file for the local model the boundary faces are saved as +# # named selections + +# mapdl.nsel("all") +# nodes = mapdl.mesh.nodes # All nodes +# node_id_all = mapdl.mesh.nnum # All nodes ID +# mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces +# node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID +# map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + +# mapdl.nsel("NONE") +# boundary_coordinates = dpf.fields_factory.create_3d_vector_field( +# num_entities=len(node_id_subset), location="Nodal" +# ) # Define DPF field for DPF interpolator input + +# nsel = "" +# for nid in node_id_subset: # Iterate boundary nodes of the local model +# nsel += "nsel,A,NODE,,{}\n".format( +# nid +# ) # Add selection command for the node to the str (only for ploting) +# boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + +# # Select all boundary nodes (only for ploting) +# mapdl.input_strings(nsel) + +# # Plot boundary nodes of the local model +# mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + +# # Exit PREP7 +# mapdl.finish() +# return boundary_coordinates + + +# # Define the boundary conditions and the loading for the global model +# define_BCs(mapdl_global) + +# # Get the DPF field with the boundary nodes of the local model +# boundary_coords = Get_boundary(mapdl_local) + +# ############################################################################### +# # Set up DPF operators +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We define two dpf operators: the first reads the displacement results from the global model, +# # and the second interpolates those displacements onto the boundary coordinates of the local model. +# # The ``DataSources`` class to link results with the DPF operator inputs. + + +# def define_dpf_operators(nCores): +# # Define the DataSources class and link it to the results of the global model +# dataSources = dpf.DataSources() +# for i in range(nCores): +# dataSources.set_domain_result_file_path( +# path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i +# ) + +# global_model = dpf.Model(dataSources) +# # Define displacement result operator to read nodal displacements +# global_disp_op = dpf.operators.result.displacement() +# # Connect displacement result operator with the global model's results file +# global_disp_op.inputs.data_sources.connect(dataSources) +# # Define interpolator to interpolate the results inside the mesh elements +# # with shape functions +# disp_interpolator = dpf.operators.mapping.on_coordinates() +# return global_model, global_disp_op, disp_interpolator + + +# def initialize_dpf_interpolator( +# global_model, +# local_Bc_coords, +# disp_interpolator, +# ): +# my_mesh = global_model.metadata.meshed_region # Global model's mesh +# disp_interpolator.inputs.coordinates.connect( +# local_Bc_coords +# ) # Link interpolator inputs with the local model's boundary coordinates +# disp_interpolator.inputs.mesh.connect( +# my_mesh +# ) # Link interpolator mesh with the global model's mesh + + +# def interpolate_data(timestep): +# global_disp_op.inputs.time_scoping.connect( +# [timestep] +# ) # Specify timestep value to read results from +# global_disp = ( +# global_disp_op.outputs.fields_container.get_data() +# ) # Read global nodal displacements + +# disp_interpolator.inputs.fields_container.connect( +# global_disp +# ) # Link the interpolation data with the interpolator +# local_disp = disp_interpolator.outputs.fields_container.get_data()[ +# 0 +# ] # Get displacements of the boundary nodes of the local model +# return local_disp + + +# # Define the two dpf operators +# global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) + +# ############################################################################### +# # Set up simulation loop +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We solve the two models sequentially for each loading step. +# # First the global model is run producing a .rst results file. +# # Then we extract the global displacements and use them to define +# # cut-boundary conditions for the local model +# # (an input string command will be used for faster excecution time). + + +# def define_cut_boundary_constraint_template(local_Bc_coords): +# # Define template of input string command to apply the displacement constraints +# local_nids = local_Bc_coords.scoping.ids +# # Get Node ID of boundary nodes of the local model +# template = "" +# for nid in local_nids: +# template += ( +# "d," +# + str(nid) +# + ",ux,{:1.6e}\nd," +# + str(nid) +# + ",uy,{:1.6e}\nd," +# + str(nid) +# + ",uz,{:1.6e}\n" +# ) +# return template + + +# def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): + +# # Enter solution processor +# mapdl_global.solution() +# mapdl_local.solution() + +# # Static analysis +# mapdl_global.antype("STATIC") +# mapdl_local.antype("STATIC") + +# constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + +# for i in range(1, timesteps + 1): # Iterate timesteps +# print(f"Timestep: {i}") +# st = tt.time() +# # Set loadstep time for the global model +# mapdl_global.time(i) +# # No extrapolation +# mapdl_global.eresx("NO") +# mapdl_global.allsel("ALL") +# # Write ALL results to database +# mapdl_global.outres("ALL", "ALL") +# # Solve global model +# mapdl_global.solve() +# print("Global solve took ", tt.time() - st) + +# # Initialize interpolator +# if i == 1: +# initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) +# # Read & Interpolate displacement data +# local_disp = interpolate_data(timestep=i) +# # Run MAPDL input string command to apply the displacement constraints +# data_array = np.array(local_disp.data).flatten() +# mapdl_local.input_strings(constraint_template.format(*data_array)) + +# st = tt.time() +# mapdl_local.allsel("ALL") +# # Set loadstep time for the local model +# mapdl_local.time(i) +# # No extrapolation +# mapdl_local.eresx("NO") +# # Write ALL results to database +# mapdl_local.outres("ALL", "ALL") +# # Solve local model +# mapdl_local.solve() +# print("Local solve took ", tt.time() - st) + +# # Exit solution processor +# mapdl_global.finish() +# mapdl_local.finish() + + +# ############################################################################### +# # Solve system +# # ~~~~~~~~~~~~ +# n_steps = 10 # Number of timesteps +# solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) + +# ############################################################################### +# # Visualize results +# # ~~~~~~~~~~~~~~~~~ + + +# def visualize(mapdl): +# # Enter post-processing +# mapdl.post1() +# # Set the current results set to the last set to be read from result file +# mapdl.set("LAST") +# # Plot nodal displacement of the loading direction +# mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") +# # Exit post-processing +# mapdl.finish() + + +# # Plot Y displacement of global model +# visualize(mapdl_global) + +# # Plot Y displacement of local model +# visualize(mapdl_local) + +# ############################################################################### +# # Exit MAPDL pool instances +# pool.exit() From 7f722461a20257b1f2c0201842de512aa7da79fb Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:40:01 +0200 Subject: [PATCH 046/100] fix: pre-commit --- .ci/start_mapdl.sh | 4 ++-- mapdl-dpf/wf_mapdl-dpf.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index c8bcbfa21..bac68b63f 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -137,7 +137,7 @@ echo "Waiting for MAPDL to initialize..." # Debug: Check immediately and periodically for i in {1..10}; do echo "Check $i: Testing process status..." - + # Check process first if ! kill -0 $MAPDL_PID 2>/dev/null; then echo "ERROR: MAPDL process died after $i checks!" @@ -145,7 +145,7 @@ for i in {1..10}; do ps aux | grep mapdl || echo "No MAPDL processes found" exit 1 fi - + echo "MAPDL process still alive after $i." done diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index a59b8514c..cd03d66e9 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -108,7 +108,9 @@ # cwd = Path.cwd() # Get current working directory # # download example data -# local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") +# local_cdb = download_example_data( +# filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf" +# ) # global_cdb = download_example_data( # filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" # ) @@ -203,8 +205,8 @@ # # Set up DPF operators # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # We define two dpf operators: the first reads the displacement results from the global model, -# # and the second interpolates those displacements onto the boundary coordinates of the local model. -# # The ``DataSources`` class to link results with the DPF operator inputs. +# # and the second interpolates those displacements onto the boundary coordinates of the local +# # model. The ``DataSources`` class to link results with the DPF operator inputs. # def define_dpf_operators(nCores): From 5c99dfd4f80a8f7dfaa776c7da0e72556bd837c8 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:58:00 +0200 Subject: [PATCH 047/100] fix: make files --- doc/Makefile | 2 +- doc/make.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 22f547841..fb6e20534 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -12,7 +12,7 @@ IS_VTK_INSTALLED := $(pip show vtk) ifneq ($(IS_VTK_INSTALLED),) @echo "VTK is installed. Uninstalling vtk and installing vtk-osmesa" @pip uninstall -y vtk - @pip install --extra-index-url https://wheels.vtk.org vtk-osmesa==9.3.0 + @pip install --index-url https://wheels.vtk.org vtk-osmesa==9.3.0 endif # Put it first so that "make" without argument is like "make help". diff --git a/doc/make.bat b/doc/make.bat index caaa0c6b8..dadb7a3aa 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -23,7 +23,7 @@ if %ERRORLEVEL% EQU 0 ( ) if %IS_VTK_INSTALLED% EQU 1 ( echo Installing vtk-osmesa... - pip install --extra-index-url https://wheels.vtk.org vtk-osmesa==9.3.0 + pip install --index-url https://wheels.vtk.org vtk-osmesa==9.3.0 ) %SPHINXBUILD% >NUL 2>NUL From 07ba8c8393b28ba50632b630db80aeecc36ba4b1 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:27:06 +0200 Subject: [PATCH 048/100] fix: printing the logs --- .ci/start_mapdl.sh | 2 +- .github/workflows/mapdl-dpf.yml | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index bac68b63f..ae47b8371 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -128,7 +128,7 @@ echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MO touch "${INSTANCE_NAME}.log" # Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 >> "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! # Give MAPDL time to initialize diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 9626bf7cb..f055f8606 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -28,7 +28,7 @@ env: MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true - ANSYS_RELEASE_FOR_DOCS: 25.1 + ANSYS_RELEASE_FOR_DOCS: 25.2 RUN_DOC_BUILD: false DPF_PORT: 21002 DPF_START_SERVER: False @@ -194,3 +194,10 @@ jobs: doc/_build/ doc/source/examples/mapdl-dpf/ overwrite: true + + - name: print MAPDL log + if: failure() + shell: bash + run: | + echo "Printing MAPDL log for debugging..." + cat MAPDL_0.log \ No newline at end of file From 5aad9e8172622a23b4e89c3295cafe7a35501672 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:52:19 +0200 Subject: [PATCH 049/100] test: reverting doc version --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index f055f8606..47cdcc583 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -28,7 +28,7 @@ env: MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true - ANSYS_RELEASE_FOR_DOCS: 25.2 + ANSYS_RELEASE_FOR_DOCS: 25.1 RUN_DOC_BUILD: false DPF_PORT: 21002 DPF_START_SERVER: False From 603805180ad479a3b8f816c505d8e25a9c1cd581 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:57:50 +0200 Subject: [PATCH 050/100] test: reverting doc version + testing MapdlPool --- .github/workflows/mapdl-dpf.yml | 2 +- mapdl-dpf/wf_mapdl-dpf.py | 70 +++++++++++++++------------------ 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 47cdcc583..f055f8606 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -28,7 +28,7 @@ env: MAPDL_DOCKER_IMAGE: 'ghcr.io/ansys/mapdl' DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true - ANSYS_RELEASE_FOR_DOCS: 25.1 + ANSYS_RELEASE_FOR_DOCS: 25.2 RUN_DOC_BUILD: false DPF_PORT: 21002 DPF_START_SERVER: False diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index cd03d66e9..769e57b27 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -56,44 +56,38 @@ which is then solved completing that timestep. """ -from ansys.mapdl.core import launch_mapdl - -mapdl = launch_mapdl() - -print(mapdl) - -mapdl.exit() - -# import os -# from pathlib import Path -# import shutil -# import time as tt - -# from ansys.dpf import core as dpf -# from ansys.mapdl.core import MapdlPool -# from ansys.mapdl.core.examples.downloads import download_example_data -# import numpy as np - -# ############################################################################### -# # Create directories to save the results -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# folders = ["./Output/Common", "./Output/Global", "./Output/Local"] -# for fdr in folders: -# try: -# shutil.rmtree(fdr, ignore_errors=True) -# os.makedirs(fdr) -# except: -# pass - -# ############################################################################### -# # Create Mapdl pool -# # ~~~~~~~~~~~~~~~~~ -# # We use the ``MapdlPool`` class to create two separate instances — one dedicated to -# # the global simulation and the other to the local simulation - -# nCores = 2 # Number of cores to use -# pool = MapdlPool(2, nproc=nCores) +import os +from pathlib import Path +import shutil +import time as tt + +from ansys.dpf import core as dpf +from ansys.mapdl.core import MapdlPool +from ansys.mapdl.core.examples.downloads import download_example_data +import numpy as np + +############################################################################### +# Create directories to save the results +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +for fdr in folders: + try: + shutil.rmtree(fdr, ignore_errors=True) + os.makedirs(fdr) + except: + pass + +############################################################################### +# Create Mapdl pool +# ~~~~~~~~~~~~~~~~~ +# We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# the global simulation and the other to the local simulation + +nCores = 2 # Number of cores to use +pool = MapdlPool(2, nproc=nCores) + +pool.exit() # ############################################################################### # # Set up Global and Local FE models From a865a2cfaa5003b72fda468e7547fe23209b6961 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:11:33 +0200 Subject: [PATCH 051/100] fix: launching two MAPDL instances --- .github/workflows/mapdl-dpf.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index f055f8606..1575af209 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -33,8 +33,10 @@ env: DPF_PORT: 21002 DPF_START_SERVER: False MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd - PYMAPDL_PORT: 21000 - PYMAPDL_DB_PORT: 21001 + PYMAPDL_PORT_0: 21000 + PYMAPDL_DB_PORT_0: 21001 + PYMAPDL_PORT_1: 21002 + PYMAPDL_DB_PORT_1: 21003 jobs: mapdl-dpf: @@ -92,16 +94,26 @@ jobs: MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }} MAPDL_PACKAGE: ${{ env.MAPDL_DOCKER_IMAGE }} ANSYSLMD_LICENSE_FILE: ${{ env.ANSYSLMD_LICENSE_FILE }} - PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} - PYMAPDL_DB_PORT: ${{ env.PYMAPDL_DB_PORT }} DPF_PORT: ${{ env.DPF_PORT }} run: | . .venv/bin/activate + + echo "Launching first MAPDL instance..." export INSTANCE_NAME=MAPDL_0 + export PYMAPDL_PORT=$PYMAPDL_PORT_0 + export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_0 .ci/start_mapdl.sh & export MAPDL_PID=$! echo "Launching MAPDL service at PID: $MAPDL_PID" echo "MAPDL_PID=$(echo $MAPDL_PID)" >> $GITHUB_OUTPUT + echo "Launching second MAPDL instance..." + export INSTANCE_NAME=MAPDL_1 + export PYMAPDL_PORT=$PYMAPDL_PORT_1 + export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_1 + .ci/start_mapdl.sh & export MAPDL_PID_1=$! + echo "Launching second MAPDL service at PID: $MAPDL_PID_1" + echo "MAPDL_PID_1=$(echo $MAPDL_PID_1)" >> $GITHUB_OUTPUT + - name: "DPF server setup" shell: bash run: | From 6881bb7fb8484e4ec5b203fda908cde186b57bfc Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Oct 2025 16:19:14 +0200 Subject: [PATCH 052/100] fix: MapdlPool --- .github/workflows/mapdl-dpf.yml | 3 +-- mapdl-dpf/wf_mapdl-dpf.py | 34 +++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 1575af209..853c2a476 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -163,8 +163,7 @@ jobs: PYANSYS_VISUALIZER_HTML_BACKEND: true run: | . .venv/bin/activate - echo "Starting MAPDL-DPF workflow..." - echo "PYMAPDL_PORT: $PYMAPDL_PORT" + echo "Building MAPDL-DPF workflow..." echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" python mapdl-dpf/wf_mapdl-dpf.py diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 769e57b27..66586000c 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -66,17 +66,17 @@ from ansys.mapdl.core.examples.downloads import download_example_data import numpy as np -############################################################################### -# Create directories to save the results -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ############################################################################### +# # Create directories to save the results +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -folders = ["./Output/Common", "./Output/Global", "./Output/Local"] -for fdr in folders: - try: - shutil.rmtree(fdr, ignore_errors=True) - os.makedirs(fdr) - except: - pass +# folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +# for fdr in folders: +# try: +# shutil.rmtree(fdr, ignore_errors=True) +# os.makedirs(fdr) +# except: +# pass ############################################################################### # Create Mapdl pool @@ -85,9 +85,19 @@ # the global simulation and the other to the local simulation nCores = 2 # Number of cores to use -pool = MapdlPool(2, nproc=nCores) -pool.exit() +port_0 = os.environ.get("PYMAPDL_PORT_0", 50055) +port_1 = os.environ.get("PYMAPDL_PORT_1", 50057) + +mapdl_pool = MapdlPool( + 2, + license_server_check=False, + start_instance=False, + port=[port_0, port_1], + wait=True, +) + +mapdl_pool.exit() # ############################################################################### # # Set up Global and Local FE models From 631083ce17e72d2f5c2c95af5a2d47ecdc62eced Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:22:33 +0200 Subject: [PATCH 053/100] fix: MapdlPool --- mapdl-dpf/wf_mapdl-dpf.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 66586000c..550376564 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -85,16 +85,12 @@ # the global simulation and the other to the local simulation nCores = 2 # Number of cores to use - -port_0 = os.environ.get("PYMAPDL_PORT_0", 50055) -port_1 = os.environ.get("PYMAPDL_PORT_1", 50057) +port_0 = int(os.getenv("PYMAPDL_PORT_0", 21000)) +port_1 = int(os.getenv("PYMAPDL_PORT_1", 21001)) mapdl_pool = MapdlPool( - 2, - license_server_check=False, - start_instance=False, port=[port_0, port_1], - wait=True, + start_instance=False, ) mapdl_pool.exit() From 879e24185f7ccda0b09aa9b16dd320ff3c5f40d1 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:28:50 +0200 Subject: [PATCH 054/100] fix: MapdlPool - 2 --- .github/workflows/mapdl-dpf.yml | 1 + mapdl-dpf/wf_mapdl-dpf.py | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 853c2a476..482a2c9c7 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -55,6 +55,7 @@ jobs: env: PYMAPDL_START_INSTANCE: FALSE ON_DOCUMENTATION: TRUE + ON_CICD: TRUE steps: diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 550376564..96ddaf471 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -84,14 +84,19 @@ # We use the ``MapdlPool`` class to create two separate instances — one dedicated to # the global simulation and the other to the local simulation -nCores = 2 # Number of cores to use port_0 = int(os.getenv("PYMAPDL_PORT_0", 21000)) port_1 = int(os.getenv("PYMAPDL_PORT_1", 21001)) +is_cicd = os.getenv("ON_CICD", False) -mapdl_pool = MapdlPool( - port=[port_0, port_1], - start_instance=False, -) +print(is_cicd, port_0, port_1) + +if is_cicd: + mapdl_pool = MapdlPool( + port=[port_0, port_1], + ) + +else: + mapdl_pool = MapdlPool(2) mapdl_pool.exit() From 39b17f44d806e49e7adbfad0c7a6eef108c19f4a Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:31:30 +0200 Subject: [PATCH 055/100] feat: adding the all example --- mapdl-dpf/wf_mapdl-dpf.py | 622 +++++++++++++++++++------------------- 1 file changed, 310 insertions(+), 312 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 96ddaf471..76d6cb89e 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -1,24 +1,24 @@ -# Copyright (C) 2024 - 2025 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. +Copyright (C) 2024 - 2025 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. """ .. _global-local_1: @@ -66,19 +66,19 @@ from ansys.mapdl.core.examples.downloads import download_example_data import numpy as np -# ############################################################################### -# # Create directories to save the results -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -# folders = ["./Output/Common", "./Output/Global", "./Output/Local"] -# for fdr in folders: -# try: -# shutil.rmtree(fdr, ignore_errors=True) -# os.makedirs(fdr) -# except: -# pass - ############################################################################### +# Create directories to save the results +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +for fdr in folders: + try: + shutil.rmtree(fdr, ignore_errors=True) + os.makedirs(fdr) + except: + pass + +# ############################################################################## # Create Mapdl pool # ~~~~~~~~~~~~~~~~~ # We use the ``MapdlPool`` class to create two separate instances — one dedicated to @@ -98,283 +98,281 @@ else: mapdl_pool = MapdlPool(2) -mapdl_pool.exit() +############################################################################### +# Set up Global and Local FE models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We assign the instances to the local and global model, then use +# ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files +# include named selections for the faces we want to apply the boundary conditions and the loads. +# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# The function ``Get_boundary`` is used to record the local model’s cut-boundary +# node coordinates as a dpf.field which will be later used in the DPF interpolator input + +cwd = Path.cwd() # Get current working directory + +# download example data +local_cdb = download_example_data( + filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf" +) +global_cdb = download_example_data( + filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" +) + +mapdl_global = mapdl_pool[0] # Global model +mapdl_global.cdread("db", global_cdb) # Load global model +mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model + +mapdl_local = mapdl_pool[1] # Local model +mapdl_local.cdread("db", local_cdb) # Load local model +mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model + + +def define_BCs(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() + + # In the .cdb file for the global model the bottom, the right and the top faces + # are saved as named selections + + # Fixed support + mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face + mapdl.d("ALL", "ALL") + mapdl.nsel("ALL") + + # Frictionless support + mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face + mapdl.d("ALL", "UZ", "0") + mapdl.nsel("ALL") + + # Applied load + # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps + mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") + mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") + mapdl.starset("LOAD(1,1,1)", "0.") + mapdl.starset("LOAD(2,1,1)", "-0.1") + mapdl.starset("LOAD(3,1,1)", "-1.") + + mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face + mapdl.d("ALL", "UY", "%LOAD%") + mapdl.nsel("ALL") + + # Exit PREP7 + mapdl.finish() + pass + + +def Get_boundary(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() + + # In the .cdb file for the local model the boundary faces are saved as + # named selections + + mapdl.nsel("all") + nodes = mapdl.mesh.nodes # All nodes + node_id_all = mapdl.mesh.nnum # All nodes ID + mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces + node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID + map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + + mapdl.nsel("NONE") + boundary_coordinates = dpf.fields_factory.create_3d_vector_field( + num_entities=len(node_id_subset), location="Nodal" + ) # Define DPF field for DPF interpolator input + + nsel = "" + for nid in node_id_subset: # Iterate boundary nodes of the local model + nsel += "nsel,A,NODE,,{}\n".format( + nid + ) # Add selection command for the node to the str (only for ploting) + boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + + # Select all boundary nodes (only for ploting) + mapdl.input_strings(nsel) + + # Plot boundary nodes of the local model + mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + + # Exit PREP7 + mapdl.finish() + return boundary_coordinates + + +# Define the boundary conditions and the loading for the global model +define_BCs(mapdl_global) + +# Get the DPF field with the boundary nodes of the local model +boundary_coords = Get_boundary(mapdl_local) + +############################################################################### +# Set up DPF operators +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We define two dpf operators: the first reads the displacement results from the global model, +# and the second interpolates those displacements onto the boundary coordinates of the local +# model. The ``DataSources`` class to link results with the DPF operator inputs. + + +def define_dpf_operators(nCores): + # Define the DataSources class and link it to the results of the global model + dataSources = dpf.DataSources() + for i in range(nCores): + dataSources.set_domain_result_file_path( + path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i + ) + + global_model = dpf.Model(dataSources) + # Define displacement result operator to read nodal displacements + global_disp_op = dpf.operators.result.displacement() + # Connect displacement result operator with the global model's results file + global_disp_op.inputs.data_sources.connect(dataSources) + # Define interpolator to interpolate the results inside the mesh elements + # with shape functions + disp_interpolator = dpf.operators.mapping.on_coordinates() + return global_model, global_disp_op, disp_interpolator + + +def initialize_dpf_interpolator( + global_model, + local_Bc_coords, + disp_interpolator, +): + my_mesh = global_model.metadata.meshed_region # Global model's mesh + disp_interpolator.inputs.coordinates.connect( + local_Bc_coords + ) # Link interpolator inputs with the local model's boundary coordinates + disp_interpolator.inputs.mesh.connect( + my_mesh + ) # Link interpolator mesh with the global model's mesh + + +def interpolate_data(timestep): + global_disp_op.inputs.time_scoping.connect( + [timestep] + ) # Specify timestep value to read results from + global_disp = ( + global_disp_op.outputs.fields_container.get_data() + ) # Read global nodal displacements + + disp_interpolator.inputs.fields_container.connect( + global_disp + ) # Link the interpolation data with the interpolator + local_disp = disp_interpolator.outputs.fields_container.get_data()[ + 0 + ] # Get displacements of the boundary nodes of the local model + return local_disp + + +# Define the two dpf operators +global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) + +############################################################################### +# Set up simulation loop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We solve the two models sequentially for each loading step. +# First the global model is run producing a .rst results file. +# Then we extract the global displacements and use them to define +# cut-boundary conditions for the local model +# (an input string command will be used for faster excecution time). + + +def define_cut_boundary_constraint_template(local_Bc_coords): + # Define template of input string command to apply the displacement constraints + local_nids = local_Bc_coords.scoping.ids + # Get Node ID of boundary nodes of the local model + template = "" + for nid in local_nids: + template += ( + "d," + + str(nid) + + ",ux,{:1.6e}\nd," + + str(nid) + + ",uy,{:1.6e}\nd," + + str(nid) + + ",uz,{:1.6e}\n" + ) + return template + + +def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): + + # Enter solution processor + mapdl_global.solution() + mapdl_local.solution() + + # Static analysis + mapdl_global.antype("STATIC") + mapdl_local.antype("STATIC") + + constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + + for i in range(1, timesteps + 1): # Iterate timesteps + print(f"Timestep: {i}") + st = tt.time() + # Set loadstep time for the global model + mapdl_global.time(i) + # No extrapolation + mapdl_global.eresx("NO") + mapdl_global.allsel("ALL") + # Write ALL results to database + mapdl_global.outres("ALL", "ALL") + # Solve global model + mapdl_global.solve() + print("Global solve took ", tt.time() - st) + + # Initialize interpolator + if i == 1: + initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) + # Read & Interpolate displacement data + local_disp = interpolate_data(timestep=i) + # Run MAPDL input string command to apply the displacement constraints + data_array = np.array(local_disp.data).flatten() + mapdl_local.input_strings(constraint_template.format(*data_array)) + + st = tt.time() + mapdl_local.allsel("ALL") + # Set loadstep time for the local model + mapdl_local.time(i) + # No extrapolation + mapdl_local.eresx("NO") + # Write ALL results to database + mapdl_local.outres("ALL", "ALL") + # Solve local model + mapdl_local.solve() + print("Local solve took ", tt.time() - st) + + # Exit solution processor + mapdl_global.finish() + mapdl_local.finish() + -# ############################################################################### -# # Set up Global and Local FE models -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We assign the instances to the local and global model, then use -# # ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files -# # include named selections for the faces we want to apply the boundary conditions and the loads. -# # The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. -# # The function ``Get_boundary`` is used to record the local model’s cut-boundary -# # node coordinates as a dpf.field which will be later used in the DPF interpolator input - -# cwd = Path.cwd() # Get current working directory - -# # download example data -# local_cdb = download_example_data( -# filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf" -# ) -# global_cdb = download_example_data( -# filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" -# ) - -# mapdl_global = pool[0] # Global model -# mapdl_global.cdread("db", global_cdb) # Load global model -# mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model - -# mapdl_local = pool[1] # Local model -# mapdl_local.cdread("db", local_cdb) # Load local model -# mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model - - -# def define_BCs(mapdl): -# # Enter PREP7 in MAPDL -# mapdl.prep7() - -# # In the .cdb file for the global model the bottom, the right and the top faces -# # are saved as named selections - -# # Fixed support -# mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face -# mapdl.d("ALL", "ALL") -# mapdl.nsel("ALL") - -# # Frictionless support -# mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face -# mapdl.d("ALL", "UZ", "0") -# mapdl.nsel("ALL") - -# # Applied load -# # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps -# mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") -# mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") -# mapdl.starset("LOAD(1,1,1)", "0.") -# mapdl.starset("LOAD(2,1,1)", "-0.1") -# mapdl.starset("LOAD(3,1,1)", "-1.") - -# mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face -# mapdl.d("ALL", "UY", "%LOAD%") -# mapdl.nsel("ALL") - -# # Exit PREP7 -# mapdl.finish() -# pass - - -# def Get_boundary(mapdl): -# # Enter PREP7 in MAPDL -# mapdl.prep7() - -# # In the .cdb file for the local model the boundary faces are saved as -# # named selections - -# mapdl.nsel("all") -# nodes = mapdl.mesh.nodes # All nodes -# node_id_all = mapdl.mesh.nnum # All nodes ID -# mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces -# node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID -# map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) - -# mapdl.nsel("NONE") -# boundary_coordinates = dpf.fields_factory.create_3d_vector_field( -# num_entities=len(node_id_subset), location="Nodal" -# ) # Define DPF field for DPF interpolator input - -# nsel = "" -# for nid in node_id_subset: # Iterate boundary nodes of the local model -# nsel += "nsel,A,NODE,,{}\n".format( -# nid -# ) # Add selection command for the node to the str (only for ploting) -# boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field - -# # Select all boundary nodes (only for ploting) -# mapdl.input_strings(nsel) - -# # Plot boundary nodes of the local model -# mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") - -# # Exit PREP7 -# mapdl.finish() -# return boundary_coordinates - - -# # Define the boundary conditions and the loading for the global model -# define_BCs(mapdl_global) - -# # Get the DPF field with the boundary nodes of the local model -# boundary_coords = Get_boundary(mapdl_local) - -# ############################################################################### -# # Set up DPF operators -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We define two dpf operators: the first reads the displacement results from the global model, -# # and the second interpolates those displacements onto the boundary coordinates of the local -# # model. The ``DataSources`` class to link results with the DPF operator inputs. - - -# def define_dpf_operators(nCores): -# # Define the DataSources class and link it to the results of the global model -# dataSources = dpf.DataSources() -# for i in range(nCores): -# dataSources.set_domain_result_file_path( -# path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i -# ) - -# global_model = dpf.Model(dataSources) -# # Define displacement result operator to read nodal displacements -# global_disp_op = dpf.operators.result.displacement() -# # Connect displacement result operator with the global model's results file -# global_disp_op.inputs.data_sources.connect(dataSources) -# # Define interpolator to interpolate the results inside the mesh elements -# # with shape functions -# disp_interpolator = dpf.operators.mapping.on_coordinates() -# return global_model, global_disp_op, disp_interpolator - - -# def initialize_dpf_interpolator( -# global_model, -# local_Bc_coords, -# disp_interpolator, -# ): -# my_mesh = global_model.metadata.meshed_region # Global model's mesh -# disp_interpolator.inputs.coordinates.connect( -# local_Bc_coords -# ) # Link interpolator inputs with the local model's boundary coordinates -# disp_interpolator.inputs.mesh.connect( -# my_mesh -# ) # Link interpolator mesh with the global model's mesh - - -# def interpolate_data(timestep): -# global_disp_op.inputs.time_scoping.connect( -# [timestep] -# ) # Specify timestep value to read results from -# global_disp = ( -# global_disp_op.outputs.fields_container.get_data() -# ) # Read global nodal displacements - -# disp_interpolator.inputs.fields_container.connect( -# global_disp -# ) # Link the interpolation data with the interpolator -# local_disp = disp_interpolator.outputs.fields_container.get_data()[ -# 0 -# ] # Get displacements of the boundary nodes of the local model -# return local_disp - - -# # Define the two dpf operators -# global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) - -# ############################################################################### -# # Set up simulation loop -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We solve the two models sequentially for each loading step. -# # First the global model is run producing a .rst results file. -# # Then we extract the global displacements and use them to define -# # cut-boundary conditions for the local model -# # (an input string command will be used for faster excecution time). - - -# def define_cut_boundary_constraint_template(local_Bc_coords): -# # Define template of input string command to apply the displacement constraints -# local_nids = local_Bc_coords.scoping.ids -# # Get Node ID of boundary nodes of the local model -# template = "" -# for nid in local_nids: -# template += ( -# "d," -# + str(nid) -# + ",ux,{:1.6e}\nd," -# + str(nid) -# + ",uy,{:1.6e}\nd," -# + str(nid) -# + ",uz,{:1.6e}\n" -# ) -# return template - - -# def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): - -# # Enter solution processor -# mapdl_global.solution() -# mapdl_local.solution() - -# # Static analysis -# mapdl_global.antype("STATIC") -# mapdl_local.antype("STATIC") - -# constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - -# for i in range(1, timesteps + 1): # Iterate timesteps -# print(f"Timestep: {i}") -# st = tt.time() -# # Set loadstep time for the global model -# mapdl_global.time(i) -# # No extrapolation -# mapdl_global.eresx("NO") -# mapdl_global.allsel("ALL") -# # Write ALL results to database -# mapdl_global.outres("ALL", "ALL") -# # Solve global model -# mapdl_global.solve() -# print("Global solve took ", tt.time() - st) - -# # Initialize interpolator -# if i == 1: -# initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) -# # Read & Interpolate displacement data -# local_disp = interpolate_data(timestep=i) -# # Run MAPDL input string command to apply the displacement constraints -# data_array = np.array(local_disp.data).flatten() -# mapdl_local.input_strings(constraint_template.format(*data_array)) - -# st = tt.time() -# mapdl_local.allsel("ALL") -# # Set loadstep time for the local model -# mapdl_local.time(i) -# # No extrapolation -# mapdl_local.eresx("NO") -# # Write ALL results to database -# mapdl_local.outres("ALL", "ALL") -# # Solve local model -# mapdl_local.solve() -# print("Local solve took ", tt.time() - st) - -# # Exit solution processor -# mapdl_global.finish() -# mapdl_local.finish() - - -# ############################################################################### -# # Solve system -# # ~~~~~~~~~~~~ -# n_steps = 10 # Number of timesteps -# solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) - -# ############################################################################### -# # Visualize results -# # ~~~~~~~~~~~~~~~~~ - - -# def visualize(mapdl): -# # Enter post-processing -# mapdl.post1() -# # Set the current results set to the last set to be read from result file -# mapdl.set("LAST") -# # Plot nodal displacement of the loading direction -# mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") -# # Exit post-processing -# mapdl.finish() - - -# # Plot Y displacement of global model -# visualize(mapdl_global) - -# # Plot Y displacement of local model -# visualize(mapdl_local) - -# ############################################################################### -# # Exit MAPDL pool instances -# pool.exit() +############################################################################### +# Solve system +# ~~~~~~~~~~~~ +n_steps = 10 # Number of timesteps +solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) + +############################################################################### +# Visualize results +# ~~~~~~~~~~~~~~~~~ + + +def visualize(mapdl): + # Enter post-processing + mapdl.post1() + # Set the current results set to the last set to be read from result file + mapdl.set("LAST") + # Plot nodal displacement of the loading direction + mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") + # Exit post-processing + mapdl.finish() + + +# Plot Y displacement of global model +visualize(mapdl_global) + +# Plot Y displacement of local model +visualize(mapdl_local) + +############################################################################### +# Exit MAPDL pool instances +mapdl_pool.exit() From 5d91422462c5dcf1314b0b9d69b181de0b05029e Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:33:41 +0200 Subject: [PATCH 056/100] fix: example --- mapdl-dpf/wf_mapdl-dpf.py | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 76d6cb89e..93811494d 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -1,24 +1,24 @@ -Copyright (C) 2024 - 2025 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. +# Copyright (C) 2024 - 2025 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. """ .. _global-local_1: From 2ea8491f71905c8a10a4c6a551b7ddb5f667c050 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:34:48 +0200 Subject: [PATCH 057/100] fix: pre-commit --- mapdl-dpf/wf_mapdl-dpf.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 93811494d..b7ea8ae9a 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -1,5 +1,6 @@ # Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT +# # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -91,12 +92,12 @@ print(is_cicd, port_0, port_1) if is_cicd: - mapdl_pool = MapdlPool( - port=[port_0, port_1], - ) + mapdl_pool = MapdlPool( + port=[port_0, port_1], + ) else: - mapdl_pool = MapdlPool(2) + mapdl_pool = MapdlPool(2) ############################################################################### # Set up Global and Local FE models @@ -111,9 +112,7 @@ cwd = Path.cwd() # Get current working directory # download example data -local_cdb = download_example_data( - filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf" -) +local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") global_cdb = download_example_data( filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" ) From a39a986c15de47f19739aa2d07053a7250ee204d Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 11:50:34 +0200 Subject: [PATCH 058/100] test: calling pydpf --- mapdl-dpf/wf_mapdl-dpf.py | 551 +++++++++++++++++++------------------- 1 file changed, 280 insertions(+), 271 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index b7ea8ae9a..5313a2d0d 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -99,279 +99,288 @@ else: mapdl_pool = MapdlPool(2) -############################################################################### -# Set up Global and Local FE models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We assign the instances to the local and global model, then use -# ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files -# include named selections for the faces we want to apply the boundary conditions and the loads. -# The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. -# The function ``Get_boundary`` is used to record the local model’s cut-boundary -# node coordinates as a dpf.field which will be later used in the DPF interpolator input - -cwd = Path.cwd() # Get current working directory - -# download example data -local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") -global_cdb = download_example_data( - filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" -) - -mapdl_global = mapdl_pool[0] # Global model -mapdl_global.cdread("db", global_cdb) # Load global model -mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model - -mapdl_local = mapdl_pool[1] # Local model -mapdl_local.cdread("db", local_cdb) # Load local model -mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model - - -def define_BCs(mapdl): - # Enter PREP7 in MAPDL - mapdl.prep7() - - # In the .cdb file for the global model the bottom, the right and the top faces - # are saved as named selections - - # Fixed support - mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face - mapdl.d("ALL", "ALL") - mapdl.nsel("ALL") - - # Frictionless support - mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face - mapdl.d("ALL", "UZ", "0") - mapdl.nsel("ALL") - - # Applied load - # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps - mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") - mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") - mapdl.starset("LOAD(1,1,1)", "0.") - mapdl.starset("LOAD(2,1,1)", "-0.1") - mapdl.starset("LOAD(3,1,1)", "-1.") - - mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face - mapdl.d("ALL", "UY", "%LOAD%") - mapdl.nsel("ALL") - - # Exit PREP7 - mapdl.finish() - pass - - -def Get_boundary(mapdl): - # Enter PREP7 in MAPDL - mapdl.prep7() - - # In the .cdb file for the local model the boundary faces are saved as - # named selections - - mapdl.nsel("all") - nodes = mapdl.mesh.nodes # All nodes - node_id_all = mapdl.mesh.nnum # All nodes ID - mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces - node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID - map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) - - mapdl.nsel("NONE") - boundary_coordinates = dpf.fields_factory.create_3d_vector_field( - num_entities=len(node_id_subset), location="Nodal" - ) # Define DPF field for DPF interpolator input - - nsel = "" - for nid in node_id_subset: # Iterate boundary nodes of the local model - nsel += "nsel,A,NODE,,{}\n".format( - nid - ) # Add selection command for the node to the str (only for ploting) - boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field - - # Select all boundary nodes (only for ploting) - mapdl.input_strings(nsel) - - # Plot boundary nodes of the local model - mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") - # Exit PREP7 - mapdl.finish() - return boundary_coordinates - - -# Define the boundary conditions and the loading for the global model -define_BCs(mapdl_global) - -# Get the DPF field with the boundary nodes of the local model -boundary_coords = Get_boundary(mapdl_local) - -############################################################################### -# Set up DPF operators -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We define two dpf operators: the first reads the displacement results from the global model, -# and the second interpolates those displacements onto the boundary coordinates of the local -# model. The ``DataSources`` class to link results with the DPF operator inputs. - - -def define_dpf_operators(nCores): - # Define the DataSources class and link it to the results of the global model - dataSources = dpf.DataSources() - for i in range(nCores): - dataSources.set_domain_result_file_path( - path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i - ) - - global_model = dpf.Model(dataSources) - # Define displacement result operator to read nodal displacements - global_disp_op = dpf.operators.result.displacement() - # Connect displacement result operator with the global model's results file - global_disp_op.inputs.data_sources.connect(dataSources) - # Define interpolator to interpolate the results inside the mesh elements - # with shape functions - disp_interpolator = dpf.operators.mapping.on_coordinates() - return global_model, global_disp_op, disp_interpolator - - -def initialize_dpf_interpolator( - global_model, - local_Bc_coords, - disp_interpolator, -): - my_mesh = global_model.metadata.meshed_region # Global model's mesh - disp_interpolator.inputs.coordinates.connect( - local_Bc_coords - ) # Link interpolator inputs with the local model's boundary coordinates - disp_interpolator.inputs.mesh.connect( - my_mesh - ) # Link interpolator mesh with the global model's mesh - - -def interpolate_data(timestep): - global_disp_op.inputs.time_scoping.connect( - [timestep] - ) # Specify timestep value to read results from - global_disp = ( - global_disp_op.outputs.fields_container.get_data() - ) # Read global nodal displacements - - disp_interpolator.inputs.fields_container.connect( - global_disp - ) # Link the interpolation data with the interpolator - local_disp = disp_interpolator.outputs.fields_container.get_data()[ - 0 - ] # Get displacements of the boundary nodes of the local model - return local_disp - - -# Define the two dpf operators -global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) - -############################################################################### -# Set up simulation loop -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We solve the two models sequentially for each loading step. -# First the global model is run producing a .rst results file. -# Then we extract the global displacements and use them to define -# cut-boundary conditions for the local model -# (an input string command will be used for faster excecution time). - - -def define_cut_boundary_constraint_template(local_Bc_coords): - # Define template of input string command to apply the displacement constraints - local_nids = local_Bc_coords.scoping.ids - # Get Node ID of boundary nodes of the local model - template = "" - for nid in local_nids: - template += ( - "d," - + str(nid) - + ",ux,{:1.6e}\nd," - + str(nid) - + ",uy,{:1.6e}\nd," - + str(nid) - + ",uz,{:1.6e}\n" - ) - return template - - -def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): - - # Enter solution processor - mapdl_global.solution() - mapdl_local.solution() - - # Static analysis - mapdl_global.antype("STATIC") - mapdl_local.antype("STATIC") - - constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - - for i in range(1, timesteps + 1): # Iterate timesteps - print(f"Timestep: {i}") - st = tt.time() - # Set loadstep time for the global model - mapdl_global.time(i) - # No extrapolation - mapdl_global.eresx("NO") - mapdl_global.allsel("ALL") - # Write ALL results to database - mapdl_global.outres("ALL", "ALL") - # Solve global model - mapdl_global.solve() - print("Global solve took ", tt.time() - st) - - # Initialize interpolator - if i == 1: - initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) - # Read & Interpolate displacement data - local_disp = interpolate_data(timestep=i) - # Run MAPDL input string command to apply the displacement constraints - data_array = np.array(local_disp.data).flatten() - mapdl_local.input_strings(constraint_template.format(*data_array)) - - st = tt.time() - mapdl_local.allsel("ALL") - # Set loadstep time for the local model - mapdl_local.time(i) - # No extrapolation - mapdl_local.eresx("NO") - # Write ALL results to database - mapdl_local.outres("ALL", "ALL") - # Solve local model - mapdl_local.solve() - print("Local solve took ", tt.time() - st) - - # Exit solution processor - mapdl_global.finish() - mapdl_local.finish() - - -############################################################################### -# Solve system -# ~~~~~~~~~~~~ -n_steps = 10 # Number of timesteps -solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) - -############################################################################### -# Visualize results -# ~~~~~~~~~~~~~~~~~ - - -def visualize(mapdl): - # Enter post-processing - mapdl.post1() - # Set the current results set to the last set to be read from result file - mapdl.set("LAST") - # Plot nodal displacement of the loading direction - mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") - # Exit post-processing - mapdl.finish() +from ansys.dpf import core as dpf +from ansys.dpf.core import examples +model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) +print(model) -# Plot Y displacement of global model -visualize(mapdl_global) +mapdl_pool.exit() -# Plot Y displacement of local model -visualize(mapdl_local) +# ############################################################################### +# # Set up Global and Local FE models +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We assign the instances to the local and global model, then use +# # ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files +# # include named selections for the faces we want to apply the boundary conditions and the loads. +# # The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. +# # The function ``Get_boundary`` is used to record the local model’s cut-boundary +# # node coordinates as a dpf.field which will be later used in the DPF interpolator input + +# cwd = Path.cwd() # Get current working directory -############################################################################### -# Exit MAPDL pool instances -mapdl_pool.exit() +# # download example data +# local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") +# global_cdb = download_example_data( +# filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" +# ) + +# mapdl_global = mapdl_pool[0] # Global model +# mapdl_global.cdread("db", global_cdb) # Load global model +# mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model + +# mapdl_local = mapdl_pool[1] # Local model +# mapdl_local.cdread("db", local_cdb) # Load local model +# mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model + + +# def define_BCs(mapdl): +# # Enter PREP7 in MAPDL +# mapdl.prep7() + +# # In the .cdb file for the global model the bottom, the right and the top faces +# # are saved as named selections + +# # Fixed support +# mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face +# mapdl.d("ALL", "ALL") +# mapdl.nsel("ALL") + +# # Frictionless support +# mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face +# mapdl.d("ALL", "UZ", "0") +# mapdl.nsel("ALL") + +# # Applied load +# # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps +# mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") +# mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") +# mapdl.starset("LOAD(1,1,1)", "0.") +# mapdl.starset("LOAD(2,1,1)", "-0.1") +# mapdl.starset("LOAD(3,1,1)", "-1.") + +# mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face +# mapdl.d("ALL", "UY", "%LOAD%") +# mapdl.nsel("ALL") + +# # Exit PREP7 +# mapdl.finish() +# pass + + +# def Get_boundary(mapdl): +# # Enter PREP7 in MAPDL +# mapdl.prep7() + +# # In the .cdb file for the local model the boundary faces are saved as +# # named selections + +# mapdl.nsel("all") +# nodes = mapdl.mesh.nodes # All nodes +# node_id_all = mapdl.mesh.nnum # All nodes ID +# mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces +# node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID +# map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + +# mapdl.nsel("NONE") +# boundary_coordinates = dpf.fields_factory.create_3d_vector_field( +# num_entities=len(node_id_subset), location="Nodal", server= +# ) # Define DPF field for DPF interpolator input + +# nsel = "" +# for nid in node_id_subset: # Iterate boundary nodes of the local model +# nsel += "nsel,A,NODE,,{}\n".format( +# nid +# ) # Add selection command for the node to the str (only for ploting) +# boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + +# # Select all boundary nodes (only for ploting) +# mapdl.input_strings(nsel) + +# # Plot boundary nodes of the local model +# mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + +# # Exit PREP7 +# mapdl.finish() +# return boundary_coordinates + + +# # Define the boundary conditions and the loading for the global model +# define_BCs(mapdl_global) + +# # Get the DPF field with the boundary nodes of the local model +# boundary_coords = Get_boundary(mapdl_local) + +# ############################################################################### +# # Set up DPF operators +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We define two dpf operators: the first reads the displacement results from the global model, +# # and the second interpolates those displacements onto the boundary coordinates of the local +# # model. The ``DataSources`` class to link results with the DPF operator inputs. + + +# def define_dpf_operators(nCores): +# # Define the DataSources class and link it to the results of the global model +# dataSources = dpf.DataSources() +# for i in range(nCores): +# dataSources.set_domain_result_file_path( +# path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i +# ) + +# global_model = dpf.Model(dataSources) +# # Define displacement result operator to read nodal displacements +# global_disp_op = dpf.operators.result.displacement() +# # Connect displacement result operator with the global model's results file +# global_disp_op.inputs.data_sources.connect(dataSources) +# # Define interpolator to interpolate the results inside the mesh elements +# # with shape functions +# disp_interpolator = dpf.operators.mapping.on_coordinates() +# return global_model, global_disp_op, disp_interpolator + + +# def initialize_dpf_interpolator( +# global_model, +# local_Bc_coords, +# disp_interpolator, +# ): +# my_mesh = global_model.metadata.meshed_region # Global model's mesh +# disp_interpolator.inputs.coordinates.connect( +# local_Bc_coords +# ) # Link interpolator inputs with the local model's boundary coordinates +# disp_interpolator.inputs.mesh.connect( +# my_mesh +# ) # Link interpolator mesh with the global model's mesh + + +# def interpolate_data(timestep): +# global_disp_op.inputs.time_scoping.connect( +# [timestep] +# ) # Specify timestep value to read results from +# global_disp = ( +# global_disp_op.outputs.fields_container.get_data() +# ) # Read global nodal displacements + +# disp_interpolator.inputs.fields_container.connect( +# global_disp +# ) # Link the interpolation data with the interpolator +# local_disp = disp_interpolator.outputs.fields_container.get_data()[ +# 0 +# ] # Get displacements of the boundary nodes of the local model +# return local_disp + + +# # Define the two dpf operators +# global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) + +# ############################################################################### +# # Set up simulation loop +# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# # We solve the two models sequentially for each loading step. +# # First the global model is run producing a .rst results file. +# # Then we extract the global displacements and use them to define +# # cut-boundary conditions for the local model +# # (an input string command will be used for faster excecution time). + + +# def define_cut_boundary_constraint_template(local_Bc_coords): +# # Define template of input string command to apply the displacement constraints +# local_nids = local_Bc_coords.scoping.ids +# # Get Node ID of boundary nodes of the local model +# template = "" +# for nid in local_nids: +# template += ( +# "d," +# + str(nid) +# + ",ux,{:1.6e}\nd," +# + str(nid) +# + ",uy,{:1.6e}\nd," +# + str(nid) +# + ",uz,{:1.6e}\n" +# ) +# return template + + +# def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): + +# # Enter solution processor +# mapdl_global.solution() +# mapdl_local.solution() + +# # Static analysis +# mapdl_global.antype("STATIC") +# mapdl_local.antype("STATIC") + +# constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + +# for i in range(1, timesteps + 1): # Iterate timesteps +# print(f"Timestep: {i}") +# st = tt.time() +# # Set loadstep time for the global model +# mapdl_global.time(i) +# # No extrapolation +# mapdl_global.eresx("NO") +# mapdl_global.allsel("ALL") +# # Write ALL results to database +# mapdl_global.outres("ALL", "ALL") +# # Solve global model +# mapdl_global.solve() +# print("Global solve took ", tt.time() - st) + +# # Initialize interpolator +# if i == 1: +# initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) +# # Read & Interpolate displacement data +# local_disp = interpolate_data(timestep=i) +# # Run MAPDL input string command to apply the displacement constraints +# data_array = np.array(local_disp.data).flatten() +# mapdl_local.input_strings(constraint_template.format(*data_array)) + +# st = tt.time() +# mapdl_local.allsel("ALL") +# # Set loadstep time for the local model +# mapdl_local.time(i) +# # No extrapolation +# mapdl_local.eresx("NO") +# # Write ALL results to database +# mapdl_local.outres("ALL", "ALL") +# # Solve local model +# mapdl_local.solve() +# print("Local solve took ", tt.time() - st) + +# # Exit solution processor +# mapdl_global.finish() +# mapdl_local.finish() + + +# ############################################################################### +# # Solve system +# # ~~~~~~~~~~~~ +# n_steps = 10 # Number of timesteps +# solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) + +# ############################################################################### +# # Visualize results +# # ~~~~~~~~~~~~~~~~~ + + +# def visualize(mapdl): +# # Enter post-processing +# mapdl.post1() +# # Set the current results set to the last set to be read from result file +# mapdl.set("LAST") +# # Plot nodal displacement of the loading direction +# mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") +# # Exit post-processing +# mapdl.finish() + + +# # Plot Y displacement of global model +# visualize(mapdl_global) + +# # Plot Y displacement of local model +# visualize(mapdl_local) + +# ############################################################################### +# # Exit MAPDL pool instances +# mapdl_pool.exit() From 45ddb754a1b74070bcf55d8cc86166a7c3366c76 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:00:16 +0200 Subject: [PATCH 059/100] fix: pydpf server --- mapdl-dpf/wf_mapdl-dpf.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 5313a2d0d..813df552d 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -65,6 +65,8 @@ from ansys.dpf import core as dpf from ansys.mapdl.core import MapdlPool from ansys.mapdl.core.examples.downloads import download_example_data +from ansys.dpf.core import examples + import numpy as np ############################################################################### @@ -88,6 +90,7 @@ port_0 = int(os.getenv("PYMAPDL_PORT_0", 21000)) port_1 = int(os.getenv("PYMAPDL_PORT_1", 21001)) is_cicd = os.getenv("ON_CICD", False) +nCores = 2 print(is_cicd, port_0, port_1) @@ -99,13 +102,29 @@ else: mapdl_pool = MapdlPool(2) +############################################################################### +# If you are working with a remote server, you might need to upload the ``RST`` +# file before working with it. +# Then you can create the :class:`DPF Model `. -from ansys.dpf import core as dpf -from ansys.dpf.core import examples +dpf.core.make_tmp_dir_server(dpf.SERVER) + +if dpf.SERVER.local_server: + model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) +else: + server_file_path = dpf.upload_file_in_tmp_folder(examples.download_all_kinds_of_complexity_modal()) + model = dpf.Model(server_file_path) -model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) print(model) +############################################################################### + +# from ansys.dpf import core as dpf +# from ansys.dpf.core import examples + +# model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) +# print(model) + mapdl_pool.exit() # ############################################################################### From eb8b4196f62db1e25c91d67e341be23364a30c14 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:04:36 +0200 Subject: [PATCH 060/100] fix: DPF_PORT value --- .github/workflows/mapdl-dpf.yml | 2 +- mapdl-dpf/wf_mapdl-dpf.py | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 482a2c9c7..46e74f1c5 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -30,7 +30,7 @@ env: PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 RUN_DOC_BUILD: false - DPF_PORT: 21002 + DPF_PORT: 21004 DPF_START_SERVER: False MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd PYMAPDL_PORT_0: 21000 diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 813df552d..1277bb2e3 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -107,13 +107,18 @@ # file before working with it. # Then you can create the :class:`DPF Model `. -dpf.core.make_tmp_dir_server(dpf.SERVER) +server_is_local = "DPF_PORT" not in os.environ + +if server_is_local: + # Local server + dpf_server = dpf.server.start_local_server() -if dpf.SERVER.local_server: - model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) else: - server_file_path = dpf.upload_file_in_tmp_folder(examples.download_all_kinds_of_complexity_modal()) - model = dpf.Model(server_file_path) + # Remote server + dpf_server = dpf.server.connect_to_server(port=int(os.environ["DPF_PORT"])) + +# Building the model +model = dpf.Model(examples.download_all_kinds_of_complexity_modal(), dpf_server) print(model) From f9dda24df99224e612d0f03a07f09beb3c0a9a25 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:13:28 +0200 Subject: [PATCH 061/100] fix: DPF server --- .ci/start_mapdl.sh | 3 ++- .github/workflows/mapdl-dpf.yml | 13 ++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index ae47b8371..38d67cc0d 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -21,6 +21,7 @@ export MAJOR MINOR VERSION echo "MAPDL Instance name: $INSTANCE_NAME" echo "MAPDL_VERSION: $MAPDL_VERSION" +echo "ANSYS_DPF_ACCEPT_LA: $ANSYS_DPF_ACCEPT_LA" echo "Running inside container - no Docker pull needed" # Extract version from MAPDL_VERSION @@ -128,7 +129,7 @@ echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MO touch "${INSTANCE_NAME}.log" # Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 >> "${INSTANCE_NAME}.log" 2>&1 & +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 --env $ANSYS_DPF_ACCEPT_LA >> "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! # Give MAPDL time to initialize diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 46e74f1c5..715d4eca9 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -32,6 +32,7 @@ env: RUN_DOC_BUILD: false DPF_PORT: 21004 DPF_START_SERVER: False + ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd PYMAPDL_PORT_0: 21000 PYMAPDL_DB_PORT_0: 21001 @@ -96,6 +97,7 @@ jobs: MAPDL_PACKAGE: ${{ env.MAPDL_DOCKER_IMAGE }} ANSYSLMD_LICENSE_FILE: ${{ env.ANSYSLMD_LICENSE_FILE }} DPF_PORT: ${{ env.DPF_PORT }} + ANSYS_DPF_ACCEPT_LA: ${{ env.ANSYS_DPF_ACCEPT_LA }} run: | . .venv/bin/activate @@ -122,9 +124,6 @@ jobs: echo "DPF service will be available through MAPDL container" echo "DPF_PORT: $DPF_PORT" # DPF is already available in the MAPDL container - # The Python script will connect to it as needed - export ANSYS_DPF_ACCEPT_LA=Y - echo "ANSYS_DPF_ACCEPT_LA set to Y" - name: "Test virtual framebuffer" shell: bash @@ -149,14 +148,6 @@ jobs: echo "PYMAPDL_VERSION=$(python -c 'from ansys.mapdl.core import __version__; print(__version__)')" >> $GITHUB_OUTPUT echo "PyMAPDL version is: $(python -c "from ansys.mapdl.core import __version__; print(__version__)")" - # - name: "Waiting for the services to be up" - # timeout-minutes: 15 - # env: - # PYMAPDL_PORT: ${{ env.PYMAPDL_PORT }} - # DPF_PORT: ${{ env.DPF_PORT }} - # run: | - # .ci/waiting_services.sh - - name: Run the MAPDL-DPF script shell: bash env: From fc1f9db2c9cf5e6fa5136a960fa7cc4fa6c5d87f Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:18:01 +0200 Subject: [PATCH 062/100] fix: env variable --- .ci/start_mapdl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/start_mapdl.sh b/.ci/start_mapdl.sh index 38d67cc0d..645cc7874 100755 --- a/.ci/start_mapdl.sh +++ b/.ci/start_mapdl.sh @@ -21,7 +21,6 @@ export MAJOR MINOR VERSION echo "MAPDL Instance name: $INSTANCE_NAME" echo "MAPDL_VERSION: $MAPDL_VERSION" -echo "ANSYS_DPF_ACCEPT_LA: $ANSYS_DPF_ACCEPT_LA" echo "Running inside container - no Docker pull needed" # Extract version from MAPDL_VERSION @@ -121,6 +120,7 @@ echo " I_MPI_SHM_LMT: $I_MPI_SHM_LMT" echo " DISTRIBUTED_MODE: $DISTRIBUTED_MODE" echo " OMPI_ALLOW_RUN_AS_ROOT: $OMPI_ALLOW_RUN_AS_ROOT" echo " OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: $OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" +echo " ANSYS_DPF_ACCEPT_LA: $ANSYS_DPF_ACCEPT_LA" # Start MAPDL using the entrypoint logic directly echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE" @@ -129,7 +129,7 @@ echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MO touch "${INSTANCE_NAME}.log" # Start MAPDL in background -nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 --env $ANSYS_DPF_ACCEPT_LA >> "${INSTANCE_NAME}.log" 2>&1 & +nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 >> "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! # Give MAPDL time to initialize From e3561aa6e9db54da9f4d50a79468bea50dff9a5e Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:01:04 +0200 Subject: [PATCH 063/100] fix: DPF ports --- .github/workflows/mapdl-dpf.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 715d4eca9..7284caeaf 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -30,7 +30,6 @@ env: PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 RUN_DOC_BUILD: false - DPF_PORT: 21004 DPF_START_SERVER: False ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd @@ -38,6 +37,8 @@ env: PYMAPDL_DB_PORT_0: 21001 PYMAPDL_PORT_1: 21002 PYMAPDL_DB_PORT_1: 21003 + DPF_PORT_0: 21004 + DPF_PORT_1: 21005 jobs: mapdl-dpf: @@ -96,7 +97,6 @@ jobs: MAPDL_VERSION: ${{ env.MAPDL_IMAGE_VERSION_DOCS_BUILD }} MAPDL_PACKAGE: ${{ env.MAPDL_DOCKER_IMAGE }} ANSYSLMD_LICENSE_FILE: ${{ env.ANSYSLMD_LICENSE_FILE }} - DPF_PORT: ${{ env.DPF_PORT }} ANSYS_DPF_ACCEPT_LA: ${{ env.ANSYS_DPF_ACCEPT_LA }} run: | . .venv/bin/activate @@ -105,6 +105,7 @@ jobs: export INSTANCE_NAME=MAPDL_0 export PYMAPDL_PORT=$PYMAPDL_PORT_0 export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_0 + export DPF_PORT=$DPF_PORT_0 .ci/start_mapdl.sh & export MAPDL_PID=$! echo "Launching MAPDL service at PID: $MAPDL_PID" echo "MAPDL_PID=$(echo $MAPDL_PID)" >> $GITHUB_OUTPUT @@ -113,6 +114,7 @@ jobs: export INSTANCE_NAME=MAPDL_1 export PYMAPDL_PORT=$PYMAPDL_PORT_1 export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_1 + export DPF_PORT=$DPF_PORT_1 .ci/start_mapdl.sh & export MAPDL_PID_1=$! echo "Launching second MAPDL service at PID: $MAPDL_PID_1" echo "MAPDL_PID_1=$(echo $MAPDL_PID_1)" >> $GITHUB_OUTPUT From 0a29fdcb25fefaa680d12691c69d075bb3cbc296 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:07:39 +0200 Subject: [PATCH 064/100] feat: adding the full example --- mapdl-dpf/wf_mapdl-dpf.py | 478 ++++++++++++++++++-------------------- 1 file changed, 232 insertions(+), 246 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 1277bb2e3..13497cd48 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -117,20 +117,6 @@ # Remote server dpf_server = dpf.server.connect_to_server(port=int(os.environ["DPF_PORT"])) -# Building the model -model = dpf.Model(examples.download_all_kinds_of_complexity_modal(), dpf_server) - -print(model) - -############################################################################### - -# from ansys.dpf import core as dpf -# from ansys.dpf.core import examples - -# model = dpf.Model(examples.download_all_kinds_of_complexity_modal()) -# print(model) - -mapdl_pool.exit() # ############################################################################### # # Set up Global and Local FE models @@ -142,269 +128,269 @@ # # The function ``Get_boundary`` is used to record the local model’s cut-boundary # # node coordinates as a dpf.field which will be later used in the DPF interpolator input -# cwd = Path.cwd() # Get current working directory +cwd = Path.cwd() # Get current working directory -# # download example data -# local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") -# global_cdb = download_example_data( -# filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" -# ) +# download example data +local_cdb = download_example_data(filename="local.cdb", directory="pyansys-workflow/pymapdl-pydpf") +global_cdb = download_example_data( + filename="global.cdb", directory="pyansys-workflow/pymapdl-pydpf" +) -# mapdl_global = mapdl_pool[0] # Global model -# mapdl_global.cdread("db", global_cdb) # Load global model -# mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model +mapdl_global = mapdl_pool[0] # Global model +mapdl_global.cdread("db", global_cdb) # Load global model +mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model -# mapdl_local = mapdl_pool[1] # Local model -# mapdl_local.cdread("db", local_cdb) # Load local model -# mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model +mapdl_local = mapdl_pool[1] # Local model +mapdl_local.cdread("db", local_cdb) # Load local model +mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model -# def define_BCs(mapdl): -# # Enter PREP7 in MAPDL -# mapdl.prep7() +def define_BCs(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() -# # In the .cdb file for the global model the bottom, the right and the top faces -# # are saved as named selections + # In the .cdb file for the global model the bottom, the right and the top faces + # are saved as named selections -# # Fixed support -# mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face -# mapdl.d("ALL", "ALL") -# mapdl.nsel("ALL") + # Fixed support + mapdl.cmsel("S", "BOTTOM_SIDE", "NODE") # Select bottom face + mapdl.d("ALL", "ALL") + mapdl.nsel("ALL") -# # Frictionless support -# mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face -# mapdl.d("ALL", "UZ", "0") -# mapdl.nsel("ALL") + # Frictionless support + mapdl.cmsel("S", "RIGHT_SIDE", "NODE") # Select right face + mapdl.d("ALL", "UZ", "0") + mapdl.nsel("ALL") -# # Applied load -# # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps -# mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") -# mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") -# mapdl.starset("LOAD(1,1,1)", "0.") -# mapdl.starset("LOAD(2,1,1)", "-0.1") -# mapdl.starset("LOAD(3,1,1)", "-1.") + # Applied load + # Ramped Y‑direction displacement of –1 mm is applied on the top face over 10 time steps + mapdl.dim("LOAD", "TABLE", "3", "1", "1", "TIME", "", "", "0") + mapdl.taxis("LOAD(1)", "1", "0.", "1.", "10.") + mapdl.starset("LOAD(1,1,1)", "0.") + mapdl.starset("LOAD(2,1,1)", "-0.1") + mapdl.starset("LOAD(3,1,1)", "-1.") -# mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face -# mapdl.d("ALL", "UY", "%LOAD%") -# mapdl.nsel("ALL") + mapdl.cmsel("S", "TOP_SIDE", "NODE") # Select top face + mapdl.d("ALL", "UY", "%LOAD%") + mapdl.nsel("ALL") -# # Exit PREP7 -# mapdl.finish() -# pass + # Exit PREP7 + mapdl.finish() + pass -# def Get_boundary(mapdl): -# # Enter PREP7 in MAPDL -# mapdl.prep7() +def Get_boundary(mapdl): + # Enter PREP7 in MAPDL + mapdl.prep7() -# # In the .cdb file for the local model the boundary faces are saved as -# # named selections + # In the .cdb file for the local model the boundary faces are saved as + # named selections -# mapdl.nsel("all") -# nodes = mapdl.mesh.nodes # All nodes -# node_id_all = mapdl.mesh.nnum # All nodes ID -# mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces -# node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID -# map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) + mapdl.nsel("all") + nodes = mapdl.mesh.nodes # All nodes + node_id_all = mapdl.mesh.nnum # All nodes ID + mapdl.cmsel("S", "boundary", "NODE") # Select all boundary faces + node_id_subset = mapdl.get_array("NODE", item1="NLIST").astype(int) # Boundary nodes ID + map_ = dict(zip(node_id_all, list(range(len(node_id_all))))) -# mapdl.nsel("NONE") -# boundary_coordinates = dpf.fields_factory.create_3d_vector_field( -# num_entities=len(node_id_subset), location="Nodal", server= -# ) # Define DPF field for DPF interpolator input + mapdl.nsel("NONE") + boundary_coordinates = dpf.fields_factory.create_3d_vector_field( + num_entities=len(node_id_subset), location="Nodal", server= + ) # Define DPF field for DPF interpolator input -# nsel = "" -# for nid in node_id_subset: # Iterate boundary nodes of the local model -# nsel += "nsel,A,NODE,,{}\n".format( -# nid -# ) # Add selection command for the node to the str (only for ploting) -# boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field + nsel = "" + for nid in node_id_subset: # Iterate boundary nodes of the local model + nsel += "nsel,A,NODE,,{}\n".format( + nid + ) # Add selection command for the node to the str (only for ploting) + boundary_coordinates.append(nodes[map_[nid]], nid) # Add node to the DPF field -# # Select all boundary nodes (only for ploting) -# mapdl.input_strings(nsel) + # Select all boundary nodes (only for ploting) + mapdl.input_strings(nsel) -# # Plot boundary nodes of the local model -# mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") + # Plot boundary nodes of the local model + mapdl.nplot(background="w", color="b", show_bounds=True, title="Constrained nodes") -# # Exit PREP7 -# mapdl.finish() -# return boundary_coordinates + # Exit PREP7 + mapdl.finish() + return boundary_coordinates -# # Define the boundary conditions and the loading for the global model -# define_BCs(mapdl_global) +# Define the boundary conditions and the loading for the global model +define_BCs(mapdl_global) -# # Get the DPF field with the boundary nodes of the local model -# boundary_coords = Get_boundary(mapdl_local) +# Get the DPF field with the boundary nodes of the local model +boundary_coords = Get_boundary(mapdl_local) -# ############################################################################### -# # Set up DPF operators -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We define two dpf operators: the first reads the displacement results from the global model, -# # and the second interpolates those displacements onto the boundary coordinates of the local -# # model. The ``DataSources`` class to link results with the DPF operator inputs. - - -# def define_dpf_operators(nCores): -# # Define the DataSources class and link it to the results of the global model -# dataSources = dpf.DataSources() -# for i in range(nCores): -# dataSources.set_domain_result_file_path( -# path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i -# ) - -# global_model = dpf.Model(dataSources) -# # Define displacement result operator to read nodal displacements -# global_disp_op = dpf.operators.result.displacement() -# # Connect displacement result operator with the global model's results file -# global_disp_op.inputs.data_sources.connect(dataSources) -# # Define interpolator to interpolate the results inside the mesh elements -# # with shape functions -# disp_interpolator = dpf.operators.mapping.on_coordinates() -# return global_model, global_disp_op, disp_interpolator - - -# def initialize_dpf_interpolator( -# global_model, -# local_Bc_coords, -# disp_interpolator, -# ): -# my_mesh = global_model.metadata.meshed_region # Global model's mesh -# disp_interpolator.inputs.coordinates.connect( -# local_Bc_coords -# ) # Link interpolator inputs with the local model's boundary coordinates -# disp_interpolator.inputs.mesh.connect( -# my_mesh -# ) # Link interpolator mesh with the global model's mesh - - -# def interpolate_data(timestep): -# global_disp_op.inputs.time_scoping.connect( -# [timestep] -# ) # Specify timestep value to read results from -# global_disp = ( -# global_disp_op.outputs.fields_container.get_data() -# ) # Read global nodal displacements - -# disp_interpolator.inputs.fields_container.connect( -# global_disp -# ) # Link the interpolation data with the interpolator -# local_disp = disp_interpolator.outputs.fields_container.get_data()[ -# 0 -# ] # Get displacements of the boundary nodes of the local model -# return local_disp - - -# # Define the two dpf operators -# global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) +############################################################################### +# Set up DPF operators +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We define two dpf operators: the first reads the displacement results from the global model, +# and the second interpolates those displacements onto the boundary coordinates of the local +# model. The ``DataSources`` class to link results with the DPF operator inputs. + + +def define_dpf_operators(nCores): + # Define the DataSources class and link it to the results of the global model + dataSources = dpf.DataSources() + for i in range(nCores): + dataSources.set_domain_result_file_path( + path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i + ) + + global_model = dpf.Model(dataSources) + # Define displacement result operator to read nodal displacements + global_disp_op = dpf.operators.result.displacement() + # Connect displacement result operator with the global model's results file + global_disp_op.inputs.data_sources.connect(dataSources) + # Define interpolator to interpolate the results inside the mesh elements + # with shape functions + disp_interpolator = dpf.operators.mapping.on_coordinates() + return global_model, global_disp_op, disp_interpolator + + +def initialize_dpf_interpolator( + global_model, + local_Bc_coords, + disp_interpolator, +): + my_mesh = global_model.metadata.meshed_region # Global model's mesh + disp_interpolator.inputs.coordinates.connect( + local_Bc_coords + ) # Link interpolator inputs with the local model's boundary coordinates + disp_interpolator.inputs.mesh.connect( + my_mesh + ) # Link interpolator mesh with the global model's mesh + + +def interpolate_data(timestep): + global_disp_op.inputs.time_scoping.connect( + [timestep] + ) # Specify timestep value to read results from + global_disp = ( + global_disp_op.outputs.fields_container.get_data() + ) # Read global nodal displacements + + disp_interpolator.inputs.fields_container.connect( + global_disp + ) # Link the interpolation data with the interpolator + local_disp = disp_interpolator.outputs.fields_container.get_data()[ + 0 + ] # Get displacements of the boundary nodes of the local model + return local_disp + + +# Define the two dpf operators +global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) -# ############################################################################### -# # Set up simulation loop -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We solve the two models sequentially for each loading step. -# # First the global model is run producing a .rst results file. -# # Then we extract the global displacements and use them to define -# # cut-boundary conditions for the local model -# # (an input string command will be used for faster excecution time). - - -# def define_cut_boundary_constraint_template(local_Bc_coords): -# # Define template of input string command to apply the displacement constraints -# local_nids = local_Bc_coords.scoping.ids -# # Get Node ID of boundary nodes of the local model -# template = "" -# for nid in local_nids: -# template += ( -# "d," -# + str(nid) -# + ",ux,{:1.6e}\nd," -# + str(nid) -# + ",uy,{:1.6e}\nd," -# + str(nid) -# + ",uz,{:1.6e}\n" -# ) -# return template - - -# def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): - -# # Enter solution processor -# mapdl_global.solution() -# mapdl_local.solution() - -# # Static analysis -# mapdl_global.antype("STATIC") -# mapdl_local.antype("STATIC") - -# constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) - -# for i in range(1, timesteps + 1): # Iterate timesteps -# print(f"Timestep: {i}") -# st = tt.time() -# # Set loadstep time for the global model -# mapdl_global.time(i) -# # No extrapolation -# mapdl_global.eresx("NO") -# mapdl_global.allsel("ALL") -# # Write ALL results to database -# mapdl_global.outres("ALL", "ALL") -# # Solve global model -# mapdl_global.solve() -# print("Global solve took ", tt.time() - st) - -# # Initialize interpolator -# if i == 1: -# initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) -# # Read & Interpolate displacement data -# local_disp = interpolate_data(timestep=i) -# # Run MAPDL input string command to apply the displacement constraints -# data_array = np.array(local_disp.data).flatten() -# mapdl_local.input_strings(constraint_template.format(*data_array)) - -# st = tt.time() -# mapdl_local.allsel("ALL") -# # Set loadstep time for the local model -# mapdl_local.time(i) -# # No extrapolation -# mapdl_local.eresx("NO") -# # Write ALL results to database -# mapdl_local.outres("ALL", "ALL") -# # Solve local model -# mapdl_local.solve() -# print("Local solve took ", tt.time() - st) - -# # Exit solution processor -# mapdl_global.finish() -# mapdl_local.finish() +############################################################################### +# Set up simulation loop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We solve the two models sequentially for each loading step. +# First the global model is run producing a .rst results file. +# Then we extract the global displacements and use them to define +# cut-boundary conditions for the local model +# (an input string command will be used for faster excecution time). + + +def define_cut_boundary_constraint_template(local_Bc_coords): + # Define template of input string command to apply the displacement constraints + local_nids = local_Bc_coords.scoping.ids + # Get Node ID of boundary nodes of the local model + template = "" + for nid in local_nids: + template += ( + "d," + + str(nid) + + ",ux,{:1.6e}\nd," + + str(nid) + + ",uy,{:1.6e}\nd," + + str(nid) + + ",uz,{:1.6e}\n" + ) + return template + + +def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): + + # Enter solution processor + mapdl_global.solution() + mapdl_local.solution() + + # Static analysis + mapdl_global.antype("STATIC") + mapdl_local.antype("STATIC") + + constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + + for i in range(1, timesteps + 1): # Iterate timesteps + print(f"Timestep: {i}") + st = tt.time() + # Set loadstep time for the global model + mapdl_global.time(i) + # No extrapolation + mapdl_global.eresx("NO") + mapdl_global.allsel("ALL") + # Write ALL results to database + mapdl_global.outres("ALL", "ALL") + # Solve global model + mapdl_global.solve() + print("Global solve took ", tt.time() - st) + + # Initialize interpolator + if i == 1: + initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) + # Read & Interpolate displacement data + local_disp = interpolate_data(timestep=i) + # Run MAPDL input string command to apply the displacement constraints + data_array = np.array(local_disp.data).flatten() + mapdl_local.input_strings(constraint_template.format(*data_array)) + + st = tt.time() + mapdl_local.allsel("ALL") + # Set loadstep time for the local model + mapdl_local.time(i) + # No extrapolation + mapdl_local.eresx("NO") + # Write ALL results to database + mapdl_local.outres("ALL", "ALL") + # Solve local model + mapdl_local.solve() + print("Local solve took ", tt.time() - st) + + # Exit solution processor + mapdl_global.finish() + mapdl_local.finish() -# ############################################################################### -# # Solve system -# # ~~~~~~~~~~~~ -# n_steps = 10 # Number of timesteps -# solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) +############################################################################### +# Solve system +# ~~~~~~~~~~~~ +n_steps = 10 # Number of timesteps +solve_global_local(mapdl_global, mapdl_local, n_steps, boundary_coords) -# ############################################################################### -# # Visualize results -# # ~~~~~~~~~~~~~~~~~ +############################################################################### +# Visualize results +# ~~~~~~~~~~~~~~~~~ -# def visualize(mapdl): -# # Enter post-processing -# mapdl.post1() -# # Set the current results set to the last set to be read from result file -# mapdl.set("LAST") -# # Plot nodal displacement of the loading direction -# mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") -# # Exit post-processing -# mapdl.finish() +def visualize(mapdl): + # Enter post-processing + mapdl.post1() + # Set the current results set to the last set to be read from result file + mapdl.set("LAST") + # Plot nodal displacement of the loading direction + mapdl.post_processing.plot_nodal_displacement("Y", cmap="jet", background="w", cpos="zy") + # Exit post-processing + mapdl.finish() -# # Plot Y displacement of global model -# visualize(mapdl_global) +# Plot Y displacement of global model +visualize(mapdl_global) -# # Plot Y displacement of local model -# visualize(mapdl_local) +# Plot Y displacement of local model +visualize(mapdl_local) -# ############################################################################### -# # Exit MAPDL pool instances -# mapdl_pool.exit() +############################################################################### +# Exit MAPDL pool instances +mapdl_pool.exit() From 43ce38d9b36fda3c0618f63e77342e8965dc6e62 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:12:49 +0200 Subject: [PATCH 065/100] fix: python file --- mapdl-dpf/wf_mapdl-dpf.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 13497cd48..e32e6ba35 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -65,8 +65,6 @@ from ansys.dpf import core as dpf from ansys.mapdl.core import MapdlPool from ansys.mapdl.core.examples.downloads import download_example_data -from ansys.dpf.core import examples - import numpy as np ############################################################################### @@ -195,7 +193,7 @@ def Get_boundary(mapdl): mapdl.nsel("NONE") boundary_coordinates = dpf.fields_factory.create_3d_vector_field( - num_entities=len(node_id_subset), location="Nodal", server= + num_entities=len(node_id_subset), location="Nodal" ) # Define DPF field for DPF interpolator input nsel = "" From b18fcd01d561f43eb6ba61dd695e7ef36f115940 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:24:05 +0200 Subject: [PATCH 066/100] fix: running python script in ``xvfb`` mode --- .github/workflows/mapdl-dpf.yml | 2 +- .gitignore | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 7284caeaf..4f3b33d2b 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -159,7 +159,7 @@ jobs: . .venv/bin/activate echo "Building MAPDL-DPF workflow..." echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" - python mapdl-dpf/wf_mapdl-dpf.py + xvfb-run python mapdl-dpf/wf_mapdl-dpf.py - name: Stop all containers (if any) run: | diff --git a/.gitignore b/.gitignore index dc20fd0f8..7faa1b753 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,6 @@ cython_debug/ # VSCode .vscode + +# Examples +Output/* \ No newline at end of file From af97bb547ea12babf56e58788d63624ccc726088 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:07:58 +0200 Subject: [PATCH 067/100] fix: pyvista off_screen --- mapdl-dpf/wf_mapdl-dpf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index e32e6ba35..4e3c3bbf2 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -66,6 +66,9 @@ from ansys.mapdl.core import MapdlPool from ansys.mapdl.core.examples.downloads import download_example_data import numpy as np +import pyvista as pv + +pv.OFF_SCREEN = True ############################################################################### # Create directories to save the results From 7d3242c1c0593bb11d4f63749e86c6c1c63b8c3b Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:08:55 +0200 Subject: [PATCH 068/100] fix: pyvista off_screen - 2 --- .github/workflows/mapdl-dpf.yml | 3 ++- mapdl-dpf/wf_mapdl-dpf.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 4f3b33d2b..72f245c79 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -30,7 +30,7 @@ env: PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 RUN_DOC_BUILD: false - DPF_START_SERVER: False + DPF_START_SERVER: false ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd PYMAPDL_PORT_0: 21000 @@ -39,6 +39,7 @@ env: PYMAPDL_DB_PORT_1: 21003 DPF_PORT_0: 21004 DPF_PORT_1: 21005 + PYVISTA_OFF_SCREEN: true jobs: mapdl-dpf: diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 4e3c3bbf2..e32e6ba35 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -66,9 +66,6 @@ from ansys.mapdl.core import MapdlPool from ansys.mapdl.core.examples.downloads import download_example_data import numpy as np -import pyvista as pv - -pv.OFF_SCREEN = True ############################################################################### # Create directories to save the results From 2dc3d6ab709d55db7b7c42654bdd3a957f0d4051 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:12:47 +0200 Subject: [PATCH 069/100] docs: run the doc --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 72f245c79..4c8db7fa4 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -29,7 +29,7 @@ env: DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 - RUN_DOC_BUILD: false + RUN_DOC_BUILD: true DPF_START_SERVER: false ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd From e4f7c9fe95811abab3b674ec16d051f1703aab7e Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:23:25 +0200 Subject: [PATCH 070/100] fix: documentation build --- doc/Makefile | 9 +-------- doc/make.bat | 14 +------------- doc/requirements.txt | 3 ++- mapdl-dpf/requirements_24.2.txt | 1 + 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index fb6e20534..dfb89be9f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,14 +6,7 @@ SPHINXOPTS = -j auto SPHINXBUILD = sphinx-build SOURCEDIR = source BUILDDIR = _build - -# Check if vtk is installed - if so, uninstall and install vtk-osmesa -IS_VTK_INSTALLED := $(pip show vtk) -ifneq ($(IS_VTK_INSTALLED),) - @echo "VTK is installed. Uninstalling vtk and installing vtk-osmesa" - @pip uninstall -y vtk - @pip install --index-url https://wheels.vtk.org vtk-osmesa==9.3.0 -endif +PYVISTA_OFF_SCREEN = true # Put it first so that "make" without argument is like "make help". help: diff --git a/doc/make.bat b/doc/make.bat index dadb7a3aa..a6c3c6ba4 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -9,23 +9,11 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=source set BUILDDIR=_build +set PYVISTA_OFF_SCREEN=true if "%1" == "" goto help if "%1" == "clean" goto clean -REM Check if vtk is installed - if so, uninstall and install vtk-osmesa -set IS_VTK_INSTALLED=0 -pip show vtk >NUL 2>NUL -if %ERRORLEVEL% EQU 0 ( - set IS_VTK_INSTALLED=1 - echo Uninstalling vtk... - pip uninstall -y vtk -) -if %IS_VTK_INSTALLED% EQU 1 ( - echo Installing vtk-osmesa... - pip install --index-url https://wheels.vtk.org vtk-osmesa==9.3.0 -) - %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. diff --git a/doc/requirements.txt b/doc/requirements.txt index 477d4f1b9..7f0397f6f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -21,5 +21,6 @@ pythreejs==2.4.2 pillow==11.3.0 ipyvtklink==0.2.3 ipykernel==6.30.1 -pyvista[jupyter]==0.45.3 +pyvista[jupyter,trame]==0.45.3 trame-vtk!=2.8.16 +vtk==9.4.2 diff --git a/mapdl-dpf/requirements_24.2.txt b/mapdl-dpf/requirements_24.2.txt index cb53bc1be..4c0959647 100644 --- a/mapdl-dpf/requirements_24.2.txt +++ b/mapdl-dpf/requirements_24.2.txt @@ -1,2 +1,3 @@ ansys-mapdl-core[graphics]==0.70.2 ansys-dpf-core==0.14.1 +vtk==9.4.2 \ No newline at end of file From bd19bac78bec351291596253b57c79ebb42bd675 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:32:17 +0200 Subject: [PATCH 071/100] fix: install make --- .github/workflows/mapdl-dpf.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 4c8db7fa4..5de9e99d7 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -162,11 +162,10 @@ jobs: echo "PYMAPDL_START_INSTANCE: $PYMAPDL_START_INSTANCE" xvfb-run python mapdl-dpf/wf_mapdl-dpf.py - - name: Stop all containers (if any) + - name: "Install make" + shell: bash run: | - if [ -n "$(docker ps -a -q)" ]; then - docker rm -f $(docker ps -a -q) - fi + apt-get update -qq && apt-get install -y make - name: (DOCS) Check if docs should be built if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build From c7738dbbe8a7d174b1899c16d6b3693b920d3a4d Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:58:04 +0200 Subject: [PATCH 072/100] fix: requirement files --- mapdl-dpf/requirements_24.1.txt | 2 -- mapdl-dpf/requirements_24.2.txt | 3 --- mapdl-dpf/requirements_25.1.txt | 2 -- mapdl-dpf/wf_mapdl-dpf.py | 2 +- 4 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 mapdl-dpf/requirements_24.1.txt delete mode 100644 mapdl-dpf/requirements_24.2.txt delete mode 100644 mapdl-dpf/requirements_25.1.txt diff --git a/mapdl-dpf/requirements_24.1.txt b/mapdl-dpf/requirements_24.1.txt deleted file mode 100644 index cb53bc1be..000000000 --- a/mapdl-dpf/requirements_24.1.txt +++ /dev/null @@ -1,2 +0,0 @@ -ansys-mapdl-core[graphics]==0.70.2 -ansys-dpf-core==0.14.1 diff --git a/mapdl-dpf/requirements_24.2.txt b/mapdl-dpf/requirements_24.2.txt deleted file mode 100644 index 4c0959647..000000000 --- a/mapdl-dpf/requirements_24.2.txt +++ /dev/null @@ -1,3 +0,0 @@ -ansys-mapdl-core[graphics]==0.70.2 -ansys-dpf-core==0.14.1 -vtk==9.4.2 \ No newline at end of file diff --git a/mapdl-dpf/requirements_25.1.txt b/mapdl-dpf/requirements_25.1.txt deleted file mode 100644 index cb53bc1be..000000000 --- a/mapdl-dpf/requirements_25.1.txt +++ /dev/null @@ -1,2 +0,0 @@ -ansys-mapdl-core[graphics]==0.70.2 -ansys-dpf-core==0.14.1 diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index e32e6ba35..e74afc404 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -25,7 +25,7 @@ .. _global-local_1: Consecutive submodeling with MAPDL pool ----------------------------- +--------------------------------------- Problem description: - In this example we demonstrate how to use MAPDL pool to perform a consecutive submodeling simulation. From 0733db7146bf4623d5a20188979d02e1b86bfe58 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Oct 2025 17:24:35 +0200 Subject: [PATCH 073/100] feat: adding ``bvp.png`` sketch --- doc/source/_static/bvp.png | Bin 0 -> 402305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/source/_static/bvp.png diff --git a/doc/source/_static/bvp.png b/doc/source/_static/bvp.png new file mode 100644 index 0000000000000000000000000000000000000000..6d4fb7061834e1afbb30fd2224e77910aeeb636a GIT binary patch literal 402305 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%0DB)Kx={F@qLg1my;4kqqI^anAf5z8)#}~yx7{S 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

BL4w!(9%=x<6a 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 literal 0 HcmV?d00001 From 7c4c034ea1c58139eda4c1d0c512d42c7410bb69 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 13 Oct 2025 09:40:50 +0200 Subject: [PATCH 074/100] feat: clean up --- .ci/entrypoint.sh | 29 ----------------------------- .ci/waiting_services.sh | 29 ----------------------------- 2 files changed, 58 deletions(-) delete mode 100644 .ci/entrypoint.sh delete mode 100755 .ci/waiting_services.sh diff --git a/.ci/entrypoint.sh b/.ci/entrypoint.sh deleted file mode 100644 index ca61a1ee2..000000000 --- a/.ci/entrypoint.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -export OMPI_ALLOW_RUN_AS_ROOT=1 -export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 - -if [ -z "${VERSION}" ]; then - echo "VERSION environment variable is not set. Please set it to the desired Ansys version." - exit 1 -fi - -RUN_DPF_SERVER=${RUN_DPF_SERVER:-false} - -if [ -n "${ANSYS_DPF_ACCEPT_LA}" ]; then - if [ "${ANSYS_DPF_ACCEPT_LA}" == "Y" ]; then - RUN_DPF_SERVER=true - fi -fi - -echo "RUN_DPF_SERVER: $RUN_DPF_SERVER" - -if [ "$RUN_DPF_SERVER" == "true" ]; then - echo "Starting DPF server..." - "/ansys_inc/v${VERSION}/aisol/bin/linx64/Ans.Dpf.Grpc.sh" --port "${DPF_PORT_INTERNAL}" > log_dpf.log & - echo "DPF server started." -fi - -echo "Starting MAPDL..." -echo "Using executable path: ${EXEC_PATH}" - -$EXEC_PATH -grpc -dir /jobs -"${DISTRIBUTED_MODE}" -np 2 -db -6000 -m -6000 - \ No newline at end of file diff --git a/.ci/waiting_services.sh b/.ci/waiting_services.sh deleted file mode 100755 index bcc29d0c3..000000000 --- a/.ci/waiting_services.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Required environment variables: -# ------------------------------- -# - PYMAPDL_PORT: The port for the PyMAPDL service (e.g., 50052). -# - DPF_PORT: The port for the DPF service (e.g., 50055). - -echo "::group:: Docker services" && docker ps && echo "::endgroup::" - -echo "Waiting for the MAPDL service to be up..." -nc -v -z localhost "$PYMAPDL_PORT" - -echo "::group:: ps aux Output" && ps aux && echo "::endgroup::" - - -echo "::group:: Waiting for the MAPDL port to be open..." -while ! nc -z localhost "$PYMAPDL_PORT"; do - sleep 0.1 -done -echo "::endgroup::" -echo "MAPDL service is up!" - - -echo "::group:: Waiting for the DPF port to be open..." -while ! nc -z localhost "$DPF_PORT"; do - sleep 0.1 -done -echo "::endgroup::" -echo "DPF service is up!" \ No newline at end of file From 82792f2707818670c5f5047f8e00411101f7d23a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 07:31:33 +0000 Subject: [PATCH 075/100] chore: auto fixes from pre-commit hooks --- .github/workflows/mapdl-dpf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 5de9e99d7..75e1e3be1 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -205,4 +205,4 @@ jobs: shell: bash run: | echo "Printing MAPDL log for debugging..." - cat MAPDL_0.log \ No newline at end of file + cat MAPDL_0.log From 832da11c0aeed8fb5d82a62cb1dd417a7d37cfff Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:38:08 +0200 Subject: [PATCH 076/100] docs: adding ``README.rst`` --- mapdl-dpf/README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst index 42f1490ca..2df036731 100644 --- a/mapdl-dpf/README.rst +++ b/mapdl-dpf/README.rst @@ -1,4 +1,4 @@ -TBD -=== +MAPDL and DPF workflow +====================== -TBD +This workflow showcase an example using MAPDL and DPF. From 386eaf95004c919bc3e08bc191a778db9186766e Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:40:14 +0200 Subject: [PATCH 077/100] feat: outputs stored in ``outputs/mapdl-dpf`` folder --- mapdl-dpf/wf_mapdl-dpf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index e74afc404..e78183962 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -28,7 +28,7 @@ --------------------------------------- Problem description: - In this example we demonstrate how to use MAPDL pool to - perform a consecutive submodeling simulation. + perform a consecutive submodeling simulation with MAPDL and DPF. Analysis type: - Static Analysis @@ -71,7 +71,7 @@ # Create directories to save the results # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -folders = ["./Output/Common", "./Output/Global", "./Output/Local"] +folders = ["./outputs/mapdl-dpf/global", "./outputs/mapdl-dpf/local"] for fdr in folders: try: shutil.rmtree(fdr, ignore_errors=True) @@ -136,11 +136,11 @@ mapdl_global = mapdl_pool[0] # Global model mapdl_global.cdread("db", global_cdb) # Load global model -mapdl_global.cwd(cwd / Path("Output/Global")) # Set directory of the global model +mapdl_global.cwd(cwd / Path("outputs/mapdl-dpf/global")) # Set directory of the global model mapdl_local = mapdl_pool[1] # Local model mapdl_local.cdread("db", local_cdb) # Load local model -mapdl_local.cwd(cwd / Path("Output/Local")) # Set directory of the local model +mapdl_local.cwd(cwd / Path("outputs/mapdl-dpf/local")) # Set directory of the local model def define_BCs(mapdl): @@ -233,7 +233,7 @@ def define_dpf_operators(nCores): dataSources = dpf.DataSources() for i in range(nCores): dataSources.set_domain_result_file_path( - path=Path(f"./Output/Global/file{i}.rst"), key="rst", domain_id=i + path=Path(f"./outputs/mapdl-dpf/global/file{i}.rst"), key="rst", domain_id=i ) global_model = dpf.Model(dataSources) From 1d1cabf11855e2bb3329427c13388c3323d0e29e Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:03:40 +0200 Subject: [PATCH 078/100] fix: typo --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index d08f12ab8..e0cb7a3b3 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -21,6 +21,6 @@ pythreejs==2.4.2 pillow==11.3.0 ipyvtklink==0.2.3 ipykernel==7.0.1 -pyvista[[jupyter,trame]]==0.45.3 +pyvista[jupyter,trame]==0.45.3 trame-vtk!=2.8.16 vtk==9.4.2 From 5a89dc137982d43f47933606507c59cf2691aea9 Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:32:03 +0200 Subject: [PATCH 079/100] Update mapdl-dpf/wf_mapdl-dpf.py --- mapdl-dpf/wf_mapdl-dpf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index e78183962..5b9df4e65 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -90,7 +90,6 @@ is_cicd = os.getenv("ON_CICD", False) nCores = 2 -print(is_cicd, port_0, port_1) if is_cicd: mapdl_pool = MapdlPool( From 730bd89bf08c7cc8d0a6fbcf84074c538d720c2d Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:00:03 +0200 Subject: [PATCH 080/100] feat: move ``.ci`` into ``mapdl-dpf`` --- .github/workflows/mapdl-dpf.yml | 13 ++++--------- {.ci => mapdl-dpf/.ci}/display_test.py | 0 {.ci => mapdl-dpf/.ci}/start_mapdl.sh | 0 3 files changed, 4 insertions(+), 9 deletions(-) rename {.ci => mapdl-dpf/.ci}/display_test.py (100%) rename {.ci => mapdl-dpf/.ci}/start_mapdl.sh (100%) mode change 100755 => 100644 diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 75e1e3be1..309912869 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -64,11 +64,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - with: - sparse-checkout: | - mapdl-dpf - doc - .ci - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} uses: actions/setup-python@v5 @@ -107,7 +102,7 @@ jobs: export PYMAPDL_PORT=$PYMAPDL_PORT_0 export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_0 export DPF_PORT=$DPF_PORT_0 - .ci/start_mapdl.sh & export MAPDL_PID=$! + mapdl-dpf/.ci/start_mapdl.sh & export MAPDL_PID=$! echo "Launching MAPDL service at PID: $MAPDL_PID" echo "MAPDL_PID=$(echo $MAPDL_PID)" >> $GITHUB_OUTPUT @@ -116,7 +111,7 @@ jobs: export PYMAPDL_PORT=$PYMAPDL_PORT_1 export PYMAPDL_DB_PORT=$PYMAPDL_DB_PORT_1 export DPF_PORT=$DPF_PORT_1 - .ci/start_mapdl.sh & export MAPDL_PID_1=$! + mapdl-dpf/.ci/start_mapdl.sh & export MAPDL_PID_1=$! echo "Launching second MAPDL service at PID: $MAPDL_PID_1" echo "MAPDL_PID_1=$(echo $MAPDL_PID_1)" >> $GITHUB_OUTPUT @@ -137,8 +132,8 @@ jobs: which xvfb-run || (apt-get update && apt-get install -y xvfb) # Test display if the script exists - if [ -f ".ci/display_test.py" ]; then - xvfb-run python .ci/display_test.py + if [ -f "mapdl-dpf/.ci/display_test.py" ]; then + xvfb-run python mapdl-dpf/.ci/display_test.py else echo "Display test script not found, skipping..." fi diff --git a/.ci/display_test.py b/mapdl-dpf/.ci/display_test.py similarity index 100% rename from .ci/display_test.py rename to mapdl-dpf/.ci/display_test.py diff --git a/.ci/start_mapdl.sh b/mapdl-dpf/.ci/start_mapdl.sh old mode 100755 new mode 100644 similarity index 100% rename from .ci/start_mapdl.sh rename to mapdl-dpf/.ci/start_mapdl.sh From 39882020b0d0313ea06c8faab4d0e4756dd82780 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:05:19 +0200 Subject: [PATCH 081/100] fix: file access --- mapdl-dpf/.ci/start_mapdl.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 mapdl-dpf/.ci/start_mapdl.sh diff --git a/mapdl-dpf/.ci/start_mapdl.sh b/mapdl-dpf/.ci/start_mapdl.sh old mode 100644 new mode 100755 From e24351d39da1ab3f478d8be818475ab399fb9d64 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:14:01 +0200 Subject: [PATCH 082/100] feat: aligning to other workflows --- .github/workflows/docs.yml | 8 ++++---- .github/workflows/mapdl-dpf.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 80954f454..020f39b96 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,25 +14,25 @@ jobs: uses: ./.github/workflows/geometry-mesh.yml secrets: inherit with: - doc-build: false + doc-build: true geometry-mesh-fluent: uses: ./.github/workflows/geometry-mesh-fluent.yml secrets: inherit with: - doc-build: false + doc-build: true geometry-mechanical-dpf: uses: ./.github/workflows/geometry-mechanical-dpf.yml secrets: inherit with: - doc-build: false + doc-build: true fluent-mechanical: uses: ./.github/workflows/fluent-mechanical.yml secrets: inherit with: - doc-build: false + doc-build: true mapdl-dpf: uses: ./.github/workflows/mapdl-dpf.yml diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 309912869..449c2c3cb 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -29,7 +29,7 @@ env: DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 - RUN_DOC_BUILD: true + RUN_DOC_BUILD: false DPF_START_SERVER: false ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd From c51ee6359781cbc1f2a0cc911a3fe4d9c4ce8a75 Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:13:12 +0200 Subject: [PATCH 083/100] Update doc/make.bat --- doc/make.bat | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/make.bat b/doc/make.bat index a6c3c6ba4..7dec1b887 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -9,7 +9,6 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=source set BUILDDIR=_build -set PYVISTA_OFF_SCREEN=true if "%1" == "" goto help if "%1" == "clean" goto clean From 386f1c6a5367058196f5b774e8370a59811597df Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:15:41 +0200 Subject: [PATCH 084/100] fix: removing unused line --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2a18194ad..faa9edcd0 100644 --- a/.gitignore +++ b/.gitignore @@ -163,4 +163,4 @@ cython_debug/ .vscode # Output folders -outputs/ +outputs/ \ No newline at end of file From 25d0e61e92c17680a4960b94cdb4d33299d26bdc Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:16:34 +0200 Subject: [PATCH 085/100] fix: typo --- mapdl-dpf/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst index 2df036731..6d14f7dd9 100644 --- a/mapdl-dpf/README.rst +++ b/mapdl-dpf/README.rst @@ -1,4 +1,4 @@ MAPDL and DPF workflow ====================== -This workflow showcase an example using MAPDL and DPF. +This workflow showcases an example using MAPDL and DPF. From a402c771bed6765395f163f98f686673b333a2d4 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 15:48:00 +0200 Subject: [PATCH 086/100] fix: applying latest changes from #111 --- .github/workflows/mapdl-dpf.yml | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 449c2c3cb..5f66a62f6 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -29,7 +29,6 @@ env: DPF_DOCKER_IMAGE: ghcr.io/ansys/mapdl:v25.2-rocky-dpf-standalone PYANSYS_WORKFLOWS_CI: true ANSYS_RELEASE_FOR_DOCS: 25.2 - RUN_DOC_BUILD: false DPF_START_SERVER: false ANSYS_DPF_ACCEPT_LA: Y MAPDL_IMAGE_VERSION_DOCS_BUILD: v25.2.4-ubuntu-cicd @@ -42,9 +41,16 @@ env: PYVISTA_OFF_SCREEN: true jobs: + + is-only-docs-required: + uses: ./.github/workflows/check-docs-required.yml + with: + doc-build: ${{ inputs.doc-build || false }} + mapdl-dpf: name: MAPDL-DPF runs-on: ubuntu-22.04 + needs: is-only-docs-required container: image: "ghcr.io/ansys/mapdl:v25.2.4-ubuntu-cicd" options: -u=0:0 --oom-kill-disable --memory=6656MB --memory-swap=16896MB --shm-size=1gb --entrypoint /bin/bash @@ -54,7 +60,7 @@ jobs: strategy: fail-fast: false matrix: - ansys-release: [25.2] + ansys-release: ${{ needs.is-only-docs-required.outputs.only-docs == 'true' && fromJSON('[25.2]') || fromJSON('[25.2]') }} env: PYMAPDL_START_INSTANCE: FALSE ON_DOCUMENTATION: TRUE @@ -162,20 +168,8 @@ jobs: run: | apt-get update -qq && apt-get install -y make - - name: (DOCS) Check if docs should be built - if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build - run: | - echo "Requested to build docs..." - if [ "${{ matrix.ansys-release }}" == "${{ env.ANSYS_RELEASE_FOR_DOCS }}" ]; then - echo "Building docs" - echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV - else - echo "Not building docs - since not primary release" - echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV - fi - - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) - if: ${{ env.RUN_DOC_BUILD == 'true' }} + if: needs.is-only-docs-required.outputs.only-docs == 'true' && matrix.ansys-release == env.ANSYS_RELEASE_FOR_DOCS shell: bash env: BUILD_DOCS_SCRIPT: 'mapdl-dpf/wf_mapdl-dpf.py' @@ -186,7 +180,7 @@ jobs: xvfb-run make html - name: (DOCS) Upload docs artifacts - if: ${{ env.RUN_DOC_BUILD == 'true' }} + if: needs.is-only-docs-required.outputs.only-docs == 'true' && matrix.ansys-release == env.ANSYS_RELEASE_FOR_DOCS uses: actions/upload-artifact@v4 with: name: mapdl-dpf-docs-stage-mapdl From 19d753da3993493c59f78eda61946c2bc1f6c195 Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 15:57:41 +0200 Subject: [PATCH 087/100] Update mapdl-dpf/README.rst Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst index 6d14f7dd9..08b1869be 100644 --- a/mapdl-dpf/README.rst +++ b/mapdl-dpf/README.rst @@ -1,4 +1,4 @@ MAPDL and DPF workflow ====================== -This workflow showcases an example using MAPDL and DPF. +This example demonstrates consecutive sub modeling using PyMAPDL and PyDPF. The DPF framework interpolates global model displacements and transfers them as boundary conditions to the local model, enabling efficient global–local coupling with the MAPDL pool. From c5488f129005b531760dc48a828db1312d7540c8 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:00:13 +0200 Subject: [PATCH 088/100] fix: resizing README --- mapdl-dpf/README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst index 08b1869be..1a46bf7c0 100644 --- a/mapdl-dpf/README.rst +++ b/mapdl-dpf/README.rst @@ -1,4 +1,6 @@ MAPDL and DPF workflow ====================== -This example demonstrates consecutive sub modeling using PyMAPDL and PyDPF. The DPF framework interpolates global model displacements and transfers them as boundary conditions to the local model, enabling efficient global–local coupling with the MAPDL pool. +This example demonstrates consecutive sub modeling using PyMAPDL and PyDPF. The DPF framework + interpolates global model displacements and transfers them as boundary conditions to the local + model, enabling efficient global–local coupling with the MAPDL pool. From 65f62715ec6737b4f64d1ed161828fa645a0917b Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:05:44 +0200 Subject: [PATCH 089/100] Update mapdl-dpf/wf_mapdl-dpf.py Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 5b9df4e65..478a97657 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -27,7 +27,7 @@ Consecutive submodeling with MAPDL pool --------------------------------------- Problem description: - - In this example we demonstrate how to use MAPDL pool to + - In this example, we demonstrate how to use MAPDL pool to perform a consecutive submodeling simulation with MAPDL and DPF. Analysis type: From 56a564e617f78cbbf2b05925c9636e9a9ef75577 Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:06:13 +0200 Subject: [PATCH 090/100] Apply suggestions from code review Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 478a97657..ccf38323b 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -34,8 +34,8 @@ - Static Analysis Material properties: - - Youngs modulus, :math:`E = 200 \, GPa` - - Poissons ratio, :math:`\mu = 0.3` + - Young's modulus, :math:`E = 200 \, GPa` + - Poisson's ratio, :math:`\mu = 0.3` Boundary conditions (global model): - Fixed support applied at the bottom side @@ -51,10 +51,10 @@ Modeling notes: - At each timestep, the global model is solved with the specified boundary - conditions;the resulting nodal displacements are interpolated to the + conditions; the resulting nodal displacements are interpolated to the boundary nodes of the local model, using the DPF interpolation operator. Those displacements are enforced as constraints to the local model, - which is then solved completing that timestep. + which is then solved, completing that timestep. """ import os From a9302b76e6e6421223c13cf31c25bdef009df937 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:13:06 +0200 Subject: [PATCH 091/100] docs: README --- mapdl-dpf/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapdl-dpf/README.rst b/mapdl-dpf/README.rst index 1a46bf7c0..2258abcee 100644 --- a/mapdl-dpf/README.rst +++ b/mapdl-dpf/README.rst @@ -2,5 +2,5 @@ MAPDL and DPF workflow ====================== This example demonstrates consecutive sub modeling using PyMAPDL and PyDPF. The DPF framework - interpolates global model displacements and transfers them as boundary conditions to the local - model, enabling efficient global–local coupling with the MAPDL pool. +interpolates global model displacements and transfers them as boundary conditions to the local +model, enabling efficient global–local coupling with the MAPDL pool. From 54efe7514af720b119f9e71b6900f60c53236119 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:42:21 +0200 Subject: [PATCH 092/100] fix: download artifacts in ``docs.yml`` --- .github/workflows/docs.yml | 6 ++++++ .github/workflows/mapdl-dpf.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b3638790c..fa23d3801 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -100,6 +100,12 @@ jobs: name: speos-optislang-docs path: doc/ + - name: Download artifacts for pymapdl-dpf + uses: actions/download-artifact@v5 + with: + name: pymapdl-dpf-docs + path: doc/ + - name: Build the documentation run: | cd doc diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 5f66a62f6..082e0657f 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -183,7 +183,7 @@ jobs: if: needs.is-only-docs-required.outputs.only-docs == 'true' && matrix.ansys-release == env.ANSYS_RELEASE_FOR_DOCS uses: actions/upload-artifact@v4 with: - name: mapdl-dpf-docs-stage-mapdl + name: mapdl-dpf-docs path: | doc/_build/ doc/source/examples/mapdl-dpf/ From 37413d169d6e77ab5632d5a56cb56e7228b4667e Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:03:40 +0200 Subject: [PATCH 093/100] Apply suggestions from code review Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index ccf38323b..84e9edb0e 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -22,7 +22,7 @@ # SOFTWARE. """ -.. _global-local_1: +.. _ref_mapdl_dpf: Consecutive submodeling with MAPDL pool --------------------------------------- @@ -88,7 +88,7 @@ port_0 = int(os.getenv("PYMAPDL_PORT_0", 21000)) port_1 = int(os.getenv("PYMAPDL_PORT_1", 21001)) is_cicd = os.getenv("ON_CICD", False) -nCores = 2 +n_cores = 2 if is_cicd: @@ -121,8 +121,8 @@ # # We assign the instances to the local and global model, then use # # ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files # # include named selections for the faces we want to apply the boundary conditions and the loads. -# # The function ``define_BCs`` defines the global model’s boundary conditions and applied loads. -# # The function ``Get_boundary`` is used to record the local model’s cut-boundary +# # The function ``define_bcs`` defines the global model’s boundary conditions and applied loads. +# # The function ``get_boundary`` is used to record the local model’s cut-boundary # # node coordinates as a dpf.field which will be later used in the DPF interpolator input cwd = Path.cwd() # Get current working directory @@ -142,7 +142,7 @@ mapdl_local.cwd(cwd / Path("outputs/mapdl-dpf/local")) # Set directory of the local model -def define_BCs(mapdl): +def define_bcs(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() From a0e157741e59f812bf8fd3411997e92c30245c9f Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:04:19 +0200 Subject: [PATCH 094/100] Apply suggestions from code review Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 84e9edb0e..758f6cb02 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -176,7 +176,7 @@ def define_bcs(mapdl): pass -def Get_boundary(mapdl): +def get_boundary(mapdl): # Enter PREP7 in MAPDL mapdl.prep7() @@ -214,10 +214,10 @@ def Get_boundary(mapdl): # Define the boundary conditions and the loading for the global model -define_BCs(mapdl_global) +define_bcs(mapdl_global) # Get the DPF field with the boundary nodes of the local model -boundary_coords = Get_boundary(mapdl_local) +boundary_coords = get_boundary(mapdl_local) ############################################################################### # Set up DPF operators @@ -229,9 +229,9 @@ def Get_boundary(mapdl): def define_dpf_operators(nCores): # Define the DataSources class and link it to the results of the global model - dataSources = dpf.DataSources() + data_sources = dpf.DataSources() for i in range(nCores): - dataSources.set_domain_result_file_path( + data_sources.set_domain_result_file_path( path=Path(f"./outputs/mapdl-dpf/global/file{i}.rst"), key="rst", domain_id=i ) From b58e09b673412de2b16c907a015482a122bdb92a Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:04:49 +0200 Subject: [PATCH 095/100] Apply suggestions from code review Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 758f6cb02..13cb9e2d6 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -235,11 +235,11 @@ def define_dpf_operators(nCores): path=Path(f"./outputs/mapdl-dpf/global/file{i}.rst"), key="rst", domain_id=i ) - global_model = dpf.Model(dataSources) + global_model = dpf.Model(data_sources) # Define displacement result operator to read nodal displacements global_disp_op = dpf.operators.result.displacement() # Connect displacement result operator with the global model's results file - global_disp_op.inputs.data_sources.connect(dataSources) + global_disp_op.inputs.data_sources.connect(data_sources) # Define interpolator to interpolate the results inside the mesh elements # with shape functions disp_interpolator = dpf.operators.mapping.on_coordinates() @@ -278,7 +278,7 @@ def interpolate_data(timestep): # Define the two dpf operators -global_model, global_disp_op, disp_interpolator = define_dpf_operators(nCores) +global_model, global_disp_op, disp_interpolator = define_dpf_operators(n_cores) ############################################################################### # Set up simulation loop From 7f3bfe9417e7bc2c9c91301da30fef209f5f6c4d Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:06:00 +0200 Subject: [PATCH 096/100] Update mapdl-dpf/wf_mapdl-dpf.py Co-authored-by: Dipin <26918585+dipinknair@users.noreply.github.com> --- mapdl-dpf/wf_mapdl-dpf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 13cb9e2d6..b56abb99c 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -227,11 +227,11 @@ def get_boundary(mapdl): # model. The ``DataSources`` class to link results with the DPF operator inputs. -def define_dpf_operators(nCores): +def define_dpf_operators(n_cores): # Define the DataSources class and link it to the results of the global model - data_sources = dpf.DataSources() - for i in range(nCores): - data_sources.set_domain_result_file_path( + dataSources = dpf.DataSources() + for i in range(n_cores): + dataSources.set_domain_result_file_path( path=Path(f"./outputs/mapdl-dpf/global/file{i}.rst"), key="rst", domain_id=i ) From 12307defb4023ee3583a9516b8f69e14d1663584 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:03:23 +0200 Subject: [PATCH 097/100] fix: ``mapdl-dpf.py`` --- mapdl-dpf/wf_mapdl-dpf.py | 139 +++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index b56abb99c..3e2b61f34 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -73,16 +73,13 @@ folders = ["./outputs/mapdl-dpf/global", "./outputs/mapdl-dpf/local"] for fdr in folders: - try: - shutil.rmtree(fdr, ignore_errors=True) - os.makedirs(fdr) - except: - pass + shutil.rmtree(fdr, ignore_errors=True) + Path(fdr).mkdir(parents=True, exist_ok=True) # ############################################################################## -# Create Mapdl pool -# ~~~~~~~~~~~~~~~~~ -# We use the ``MapdlPool`` class to create two separate instances — one dedicated to +# Create a pool of MAPDL instances +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We use the ``MapdlPool`` class to create two separate instances: one dedicated to # the global simulation and the other to the local simulation port_0 = int(os.getenv("PYMAPDL_PORT_0", 21000)) @@ -100,6 +97,9 @@ mapdl_pool = MapdlPool(2) ############################################################################### +# Connect to DPF server +# ~~~~~~~~~~~~~~~~~~~~~ +# We connect to a local or remote DPF server. # If you are working with a remote server, you might need to upload the ``RST`` # file before working with it. # Then you can create the :class:`DPF Model `. @@ -116,14 +116,14 @@ # ############################################################################### -# # Set up Global and Local FE models -# # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# # We assign the instances to the local and global model, then use -# # ``mapdl.cdread`` to load their geometry and mesh. Note the the .cdb files -# # include named selections for the faces we want to apply the boundary conditions and the loads. -# # The function ``define_bcs`` defines the global model’s boundary conditions and applied loads. -# # The function ``get_boundary`` is used to record the local model’s cut-boundary -# # node coordinates as a dpf.field which will be later used in the DPF interpolator input +# Set up global and local FE models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We assign the instances to the local and global model, then use +# ``mapdl.cdread`` to load their geometry and mesh. Note the ``.cdb`` files +# include named selections for the faces we want to apply the boundary conditions and the loads. +# The function ``define_bcs`` defines the global model’s boundary conditions and applied loads. +# The function ``get_boundary`` is used to record the local model’s cut-boundary +# node coordinates as a ``dpf.Field`` which will be later used in the DPF interpolator input. cwd = Path.cwd() # Get current working directory @@ -143,6 +143,14 @@ def define_bcs(mapdl): + """ + Define boundary conditions and loading for the global model. + + Parameters + ---------- + mapdl : Mapdl + MAPDL instance for the global model. + """ # Enter PREP7 in MAPDL mapdl.prep7() @@ -177,6 +185,19 @@ def define_bcs(mapdl): def get_boundary(mapdl): + """ + Get the boundary node coordinates of the local model as a DPF field. + + Parameters + ---------- + mapdl : Mapdl + MAPDL instance for the local model. + + Returns + ------- + dpf.Field + DPF field containing the coordinates of the boundary nodes of the local model. + """ # Enter PREP7 in MAPDL mapdl.prep7() @@ -221,17 +242,34 @@ def get_boundary(mapdl): ############################################################################### # Set up DPF operators -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We define two dpf operators: the first reads the displacement results from the global model, +# ~~~~~~~~~~~~~~~~~~~~ +# We define two DPF operators: the first reads the displacement results from the global model, # and the second interpolates those displacements onto the boundary coordinates of the local # model. The ``DataSources`` class to link results with the DPF operator inputs. def define_dpf_operators(n_cores): + """ + Define DPF operators for the global model. + + Parameters + ---------- + n_cores : int + Number of cores used in the global model. + + Returns + ------- + dpf.Model + DPF model for the global model. + dpf.Operator + DPF operator to read nodal displacements from the global model. + dpf.Operator + DPF operator to interpolate displacements onto local model boundary coordinates. + """ # Define the DataSources class and link it to the results of the global model - dataSources = dpf.DataSources() + data_sources = dpf.DataSources() for i in range(n_cores): - dataSources.set_domain_result_file_path( + data_sources.set_domain_result_file_path( path=Path(f"./outputs/mapdl-dpf/global/file{i}.rst"), key="rst", domain_id=i ) @@ -248,12 +286,24 @@ def define_dpf_operators(n_cores): def initialize_dpf_interpolator( global_model, - local_Bc_coords, + local_bc_coords, disp_interpolator, ): + """ + Initialize the DPF interpolator for the local model. + + Parameters + ---------- + global_model : dpf.Model + DPF model for the global model. + local_bc_coords : dpf.Field + DPF field containing the coordinates of the boundary nodes of the local model. + disp_interpolator : dpf.Operator + DPF operator to interpolate displacements onto local model boundary coordinates. + """ my_mesh = global_model.metadata.meshed_region # Global model's mesh disp_interpolator.inputs.coordinates.connect( - local_Bc_coords + local_bc_coords ) # Link interpolator inputs with the local model's boundary coordinates disp_interpolator.inputs.mesh.connect( my_mesh @@ -277,12 +327,12 @@ def interpolate_data(timestep): return local_disp -# Define the two dpf operators +# Define the two DPF operators global_model, global_disp_op, disp_interpolator = define_dpf_operators(n_cores) ############################################################################### # Set up simulation loop -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ~~~~~~~~~~~~~~~~~~~~~~ # We solve the two models sequentially for each loading step. # First the global model is run producing a .rst results file. # Then we extract the global displacements and use them to define @@ -290,9 +340,22 @@ def interpolate_data(timestep): # (an input string command will be used for faster excecution time). -def define_cut_boundary_constraint_template(local_Bc_coords): +def define_cut_boundary_constraint_template(local_bc_coords): + """ + Define template of input string command to apply the displacement constraints. + + Parameters + ---------- + local_bc_coords : dpf.Field + DPF field containing the coordinates of the boundary nodes of the local model. + + Returns + ------- + str + Template of input string command to apply the displacement constraints. + """ # Define template of input string command to apply the displacement constraints - local_nids = local_Bc_coords.scoping.ids + local_nids = local_bc_coords.scoping.ids # Get Node ID of boundary nodes of the local model template = "" for nid in local_nids: @@ -308,8 +371,21 @@ def define_cut_boundary_constraint_template(local_Bc_coords): return template -def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): - +def solve_global_local(mapdl_global, mapdl_local, timesteps, local_bc_coords): + """ + Solve the global and local models sequentially for each timestep. + + Parameters + ---------- + mapdl_global : Mapdl + MAPDL instance for the global model. + mapdl_local : Mapdl + MAPDL instance for the local model. + timesteps : int + Number of timesteps to solve. + local_bc_coords : dpf.Field + DPF field containing the coordinates of the boundary nodes of the local model. + """ # Enter solution processor mapdl_global.solution() mapdl_local.solution() @@ -318,7 +394,7 @@ def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): mapdl_global.antype("STATIC") mapdl_local.antype("STATIC") - constraint_template = define_cut_boundary_constraint_template(local_Bc_coords) + constraint_template = define_cut_boundary_constraint_template(local_bc_coords) for i in range(1, timesteps + 1): # Iterate timesteps print(f"Timestep: {i}") @@ -336,7 +412,7 @@ def solve_global_local(mapdl_global, mapdl_local, timesteps, local_Bc_coords): # Initialize interpolator if i == 1: - initialize_dpf_interpolator(global_model, local_Bc_coords, disp_interpolator) + initialize_dpf_interpolator(global_model, local_bc_coords, disp_interpolator) # Read & Interpolate displacement data local_disp = interpolate_data(timestep=i) # Run MAPDL input string command to apply the displacement constraints @@ -389,5 +465,6 @@ def visualize(mapdl): visualize(mapdl_local) ############################################################################### -# Exit MAPDL pool instances +# Exit MAPDL instances +# ~~~~~~~~~~~~~~~~~~~~ mapdl_pool.exit() From 03e800ec42792371cc06043deaecd8b3708404ff Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:04:06 +0200 Subject: [PATCH 098/100] fix: pre-commit --- mapdl-dpf/wf_mapdl-dpf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mapdl-dpf/wf_mapdl-dpf.py b/mapdl-dpf/wf_mapdl-dpf.py index 3e2b61f34..56cbb0812 100644 --- a/mapdl-dpf/wf_mapdl-dpf.py +++ b/mapdl-dpf/wf_mapdl-dpf.py @@ -145,7 +145,7 @@ def define_bcs(mapdl): """ Define boundary conditions and loading for the global model. - + Parameters ---------- mapdl : Mapdl @@ -192,7 +192,7 @@ def get_boundary(mapdl): ---------- mapdl : Mapdl MAPDL instance for the local model. - + Returns ------- dpf.Field @@ -256,7 +256,7 @@ def define_dpf_operators(n_cores): ---------- n_cores : int Number of cores used in the global model. - + Returns ------- dpf.Model @@ -343,7 +343,7 @@ def interpolate_data(timestep): def define_cut_boundary_constraint_template(local_bc_coords): """ Define template of input string command to apply the displacement constraints. - + Parameters ---------- local_bc_coords : dpf.Field From 717baad1ba4ce18e18dc1ca1354745ac84fa08d9 Mon Sep 17 00:00:00 2001 From: Camille Latapie <78221213+clatapie@users.noreply.github.com> Date: Mon, 27 Oct 2025 11:26:26 +0100 Subject: [PATCH 099/100] Apply suggestions from code review Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com> --- mapdl-dpf/.ci/start_mapdl.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mapdl-dpf/.ci/start_mapdl.sh b/mapdl-dpf/.ci/start_mapdl.sh index 645cc7874..8caabb799 100755 --- a/mapdl-dpf/.ci/start_mapdl.sh +++ b/mapdl-dpf/.ci/start_mapdl.sh @@ -122,6 +122,11 @@ echo " OMPI_ALLOW_RUN_AS_ROOT: $OMPI_ALLOW_RUN_AS_ROOT" echo " OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: $OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" echo " ANSYS_DPF_ACCEPT_LA: $ANSYS_DPF_ACCEPT_LA" +# Check if the port is already in use +if lsof -i :"$PYMAPDL_PORT" >/dev/null; then + echo "ERROR: Port $PYMAPDL_PORT is already in use." + exit 1 +fi # Start MAPDL using the entrypoint logic directly echo "Starting MAPDL with: $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE" @@ -131,7 +136,8 @@ touch "${INSTANCE_NAME}.log" # Start MAPDL in background nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 >> "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! - +# Ensure MAPDL stops when the script exits +trap 'kill $MAPDL_PID 2>/dev/null || true' EXIT # Give MAPDL time to initialize echo "Waiting for MAPDL to initialize..." From d96aac9f6cfe30e3ae0bc1d1e0a9a2222c2a63a3 Mon Sep 17 00:00:00 2001 From: clatapie <78221213+clatapie@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:47:19 +0100 Subject: [PATCH 100/100] fix: stopping MAPDL instances with ``pymapdl stop --all`` --- .github/workflows/mapdl-dpf.yml | 2 ++ mapdl-dpf/.ci/start_mapdl.sh | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mapdl-dpf.yml b/.github/workflows/mapdl-dpf.yml index 082e0657f..ef3a07042 100644 --- a/.github/workflows/mapdl-dpf.yml +++ b/.github/workflows/mapdl-dpf.yml @@ -178,6 +178,7 @@ jobs: cd doc pip install -r requirements.txt --root-user-action=ignore xvfb-run make html + pymapdl stop --all - name: (DOCS) Upload docs artifacts if: needs.is-only-docs-required.outputs.only-docs == 'true' && matrix.ansys-release == env.ANSYS_RELEASE_FOR_DOCS @@ -193,5 +194,6 @@ jobs: if: failure() shell: bash run: | + pymapdl stop --all echo "Printing MAPDL log for debugging..." cat MAPDL_0.log diff --git a/mapdl-dpf/.ci/start_mapdl.sh b/mapdl-dpf/.ci/start_mapdl.sh index 8caabb799..b348f4cec 100755 --- a/mapdl-dpf/.ci/start_mapdl.sh +++ b/mapdl-dpf/.ci/start_mapdl.sh @@ -136,8 +136,6 @@ touch "${INSTANCE_NAME}.log" # Start MAPDL in background nohup $EXEC_PATH -grpc -port $PYMAPDL_PORT -$DISTRIBUTED_MODE -np 2 >> "${INSTANCE_NAME}.log" 2>&1 & MAPDL_PID=$! -# Ensure MAPDL stops when the script exits -trap 'kill $MAPDL_PID 2>/dev/null || true' EXIT # Give MAPDL time to initialize echo "Waiting for MAPDL to initialize..."