diff --git a/.ci/build_matrix.sh b/.ci/build_matrix.sh index 8045341a18..7f90c11b2a 100755 --- a/.ci/build_matrix.sh +++ b/.ci/build_matrix.sh @@ -9,7 +9,10 @@ versions=( # if added more "latest", change "$LATEST" 'latest-ubuntu' 'latest-ubuntu-student' + 'v25.2.0' 'v25.1.0' + 'v25.1-ubuntu' + 'v25.1-ubuntu-student' 'v24.2.0' 'v24.2-ubuntu' 'v24.2-ubuntu-student' @@ -95,11 +98,11 @@ for version in "${versions[@]}"; do fi # Skipping student versions on auth_user - if [[ "$auth_user" == "true" && "$ON_STUDENT" == "true" ]]; then - echo "Skipping student versions when user is authenticated" - echo "" - continue - fi + # if [[ "$auth_user" == "true" && "$ON_STUDENT" == "true" ]]; then + # echo "Skipping student versions when user is authenticated" + # echo "" + # continue + # fi # main logic if [[ "$auth_user" == "true" ]]; then diff --git a/.ci/collect_mapdl_logs_locals.sh b/.ci/collect_mapdl_logs_locals.sh new file mode 100755 index 0000000000..5b4eff1c4a --- /dev/null +++ b/.ci/collect_mapdl_logs_locals.sh @@ -0,0 +1,12 @@ + +mkdir "$LOG_NAMES" && echo "Successfully generated directory $LOG_NAMES" + +cp *.log ./"$LOG_NAMES"/ || echo "No log files could be found" +cp *apdl.out ./"$LOG_NAMES"/ || echo "No APDL log files could be found" +cp *pymapdl.apdl ./"$LOG_NAMES"/ || echo "No PYMAPDL APDL log files could be found" + + +ls -la ./"$LOG_NAMES" + +echo "Tar files..." +tar cvzf ./"$LOG_NAMES".tgz ./"$LOG_NAMES" || echo "Failed to compress" \ No newline at end of file diff --git a/.ci/collect_mapdl_logs.sh b/.ci/collect_mapdl_logs_remote.sh similarity index 100% rename from .ci/collect_mapdl_logs.sh rename to .ci/collect_mapdl_logs_remote.sh diff --git a/.ci/display_logs_locals.sh b/.ci/display_logs_locals.sh new file mode 100755 index 0000000000..c4db9eb59c --- /dev/null +++ b/.ci/display_logs_locals.sh @@ -0,0 +1,19 @@ + +##### +# Displaying files +FILE_PAT=./"$LOG_NAMES"/pymapdl.log +FILE_DESCRIPTION="PyMAPDL log" + +if compgen -G "$FILE_PAT" > /dev/null ;then for f in "$FILE_PAT"; do echo "::group:: $FILE_DESCRIPTION: $f" && cat "$f" && echo "::endgroup::" ; done; fi || echo "Failed to show $FILE_DESCRIPTION file" + +##### +FILE_PAT=./"$LOG_NAMES"/pymapdl.apdl +FILE_DESCRIPTION="PyMAPDL APDL log" + +if compgen -G "$FILE_PAT" > /dev/null ;then for f in "$FILE_PAT"; do echo "::group:: $FILE_DESCRIPTION: $f" && cat "$f" && echo "::endgroup::" ; done; fi || echo "Failed to show $FILE_DESCRIPTION file" + +##### +FILE_PAT=./"$LOG_NAMES"/apdl.out +FILE_DESCRIPTION="MAPDL Output" + +if compgen -G "$FILE_PAT" > /dev/null ;then for f in "$FILE_PAT"; do echo "::group:: $FILE_DESCRIPTION: $f" && cat "$f" && echo "::endgroup::" ; done; fi || echo "Failed to show $FILE_DESCRIPTION file" \ No newline at end of file diff --git a/.ci/display_logs.sh b/.ci/display_logs_remote.sh similarity index 100% rename from .ci/display_logs.sh rename to .ci/display_logs_remote.sh diff --git a/.ci/requirements_minimal.txt b/.ci/requirements_minimal.txt new file mode 100644 index 0000000000..8d55e3ac3c --- /dev/null +++ b/.ci/requirements_minimal.txt @@ -0,0 +1,6 @@ +pyfakefs==5.7.2 +pytest-cov==6.0.0 +pytest-random-order==1.1.1 +pytest-rerunfailures==15.0 +pytest-timeout==2.3.1 +pytest==8.3.4 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc3c8632fd..59c904b32a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: PACKAGE_NAME: 'ansys-mapdl-core' PACKAGE_NAMESPACE: 'ansys.mapdl.core' DOCUMENTATION_CNAME: 'mapdl.docs.pyansys.com' - LATEST_VERSION: "242" + LATEST_VERSION: "252" MAPDL_IMAGE_VERSION_DOCS_BUILD: v24.2-ubuntu-student MEILISEARCH_API_KEY: ${{ secrets.MEILISEARCH_API_KEY }} MEILISEARCH_PUBLIC_API_KEY: ${{ secrets.MEILISEARCH_PUBLIC_API_KEY }} @@ -33,9 +33,11 @@ env: DPF_PORT: 21004 MAPDL_PACKAGE: ghcr.io/ansys/mapdl ON_CI: True - PYTEST_ARGUMENTS: '-vvv -rxXsa --color=yes --durations=10 --random-order --random-order-bucket=class --maxfail=100 --reruns 3 --reruns-delay 4 --cov=ansys.mapdl.core --cov-report=html' + PYTEST_ARGUMENTS: '-vvv -rxXsa --color=yes --durations=10 --random-order --random-order-bucket=class --maxfail=100 --reruns 3 --reruns-delay 4 --cov=ansys.mapdl.core --cov-report=html --timeout=180' + BUILD_CHEATSHEET: True + PYMAPDL_DEBUG_TESTING: True # Following env vars when changed will "reset" the mentioned cache, # by changing the cache file name. It is rendered as ...-v%RESET_XXX%-... @@ -62,7 +64,6 @@ permissions: jobs: - update-changelog: name: "Update CHANGELOG (on release)" if: github.event_name == 'push' && contains(github.ref, 'refs/tags') @@ -128,6 +129,7 @@ jobs: library-name: ${{ env.PACKAGE_NAME }} operating-system: ${{ matrix.os }} python-version: ${{ matrix.python-version }} + whitelist-license-check: "attrs" # This has MIT license but fails the check - name: "Importing library" run: | @@ -149,6 +151,8 @@ jobs: python-package-name: ${{ env.PACKAGE_NAME }} dev-mode: ${{ github.ref != 'refs/heads/main' }} upload-reports: True + hide-log: false + docs-build: name: "Build documentation" @@ -336,7 +340,7 @@ jobs: MAPDL_INSTANCE: MAPDL_0 LOG_NAMES: logs-build-docs run: | - .ci/collect_mapdl_logs.sh + .ci/collect_mapdl_logs_remote.sh - name: "Upload logs to GitHub" if: always() @@ -351,7 +355,7 @@ jobs: MAPDL_INSTANCE: MAPDL_0 LOG_NAMES: logs-build-docs run: | - .ci/display_logs.sh + .ci/display_logs_remote.sh build-test-remote-matrix: name: "Build remote test matrix" @@ -576,7 +580,7 @@ jobs: MAPDL_INSTANCE: MAPDL_0 LOG_NAMES: logs-remote-${{ matrix.mapdl-version }} run: | - .ci/collect_mapdl_logs.sh + .ci/collect_mapdl_logs_remote.sh - name: "Upload logs to GitHub" if: always() @@ -591,7 +595,7 @@ jobs: MAPDL_INSTANCE: MAPDL_0 LOG_NAMES: logs-remote-${{ matrix.mapdl-version }} run: | - .ci/display_logs.sh + .ci/display_logs_remote.sh build-test-local-minimal-matrix: name: "Build test matrix for minimal and local" @@ -615,7 +619,7 @@ jobs: - id: set-matrix env: ONLY_UBUNTU: true - LIMIT_VERSIONS: 3 + LIMIT_VERSIONS: 2 ON_SCHEDULE: ${{ github.event_name == 'schedule' }} ON_WORKFLOW_DISPATCH: ${{ github.event_name == 'workflow_dispatch' }} RUN_ALL_TEST: ${{ inputs.run_all_tests }} @@ -713,6 +717,27 @@ jobs: --reset_only_failed --add_missing_images \ --cov-report=xml:${{ matrix.mapdl-version }}-local.xml + - name: "Collect logs on failure" + if: always() + env: + LOG_NAMES: logs-local-${{ matrix.mapdl-version }} + run: | + .ci/collect_mapdl_logs_locals.sh + + - name: "Upload logs to GitHub" + if: always() + uses: actions/upload-artifact@master + with: + name: logs-local-${{ matrix.mapdl-version }}.tgz + path: ./logs-local-${{ matrix.mapdl-version }}.tgz + + - name: "Display files structure" + if: always() + env: + LOG_NAMES: logs-local-${{ matrix.mapdl-version }} + run: | + .ci/display_logs_locals.sh + - name: "Adding the directory as safe directory for later step" run: | git config --global --add safe.directory $GITHUB_WORKSPACE @@ -821,7 +846,7 @@ jobs: - name: "Unit testing requirements installation" run: | - python -m pip install pytest pytest-rerunfailures pytest-cov pytest-random-order + python -m pip install -r .ci/requirements_minimal.txt - name: "Unit testing" env: @@ -850,6 +875,27 @@ jobs: ${{ env.PYTEST_ARGUMENTS }} \ --cov-report=xml:${{ matrix.mapdl-version }}-minimal.xml + - name: "Collect logs on failure" + if: always() + env: + LOG_NAMES: logs-minimal-${{ matrix.mapdl-version }} + run: | + .ci/collect_mapdl_logs_locals.sh + + - name: "Upload logs to GitHub" + if: always() + uses: actions/upload-artifact@master + with: + name: logs-minimal-${{ matrix.mapdl-version }}.tgz + path: ./logs-minimal-${{ matrix.mapdl-version }}.tgz + + - name: "Display files structure" + if: always() + env: + LOG_NAMES: logs-minimal-${{ matrix.mapdl-version }} + run: | + .ci/display_logs_locals.sh + - uses: codecov/codecov-action@v5 name: "Upload coverage to Codecov" with: @@ -865,6 +911,135 @@ jobs: path: ./${{ matrix.mapdl-version }}-minimal.xml + build-test-ubuntu-console: + name: "Local-min-console: ${{ matrix.mapdl-version }}" + runs-on: ubuntu-latest + if: github.ref != 'refs/heads/main' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + needs: [smoke-tests, build-test-local-minimal-matrix] + timeout-minutes: 75 + strategy: + fail-fast: false + matrix: ${{fromJson(needs.build-test-local-minimal-matrix.outputs.matrix)}} + container: + image: ghcr.io/ansys/mapdl:${{ matrix.mapdl-version }} + 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 }} + env: + ON_LOCAL: true + ON_UBUNTU: true + TESTING_MINIMAL: true + ON_CONSOLE: true + + steps: + - name: "Install Git and checkout project" + uses: actions/checkout@v4.2.2 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: "Get if running student version" + id: student_check + run: | + if [[ "${{ matrix.mapdl-version }}" == *"student"* ]]; + then export ON_STUDENT=true; export TAG_STUDENT="student"; + else export ON_STUDENT=false; export TAG_STUDENT="non-student"; + fi + + echo "ON_STUDENT: $ON_STUDENT" + echo "TAG_STUDENT: $TAG_STUDENT" + echo "ON_STUDENT=$(echo $ON_STUDENT)" >> $GITHUB_OUTPUT + echo "TAG_STUDENT=$(echo $TAG_STUDENT)" >> $GITHUB_OUTPUT + + - name: "Installing missing package" + run: | + sudo apt-get update + sudo apt-get install -y libgomp1 + + - name: "Setup Python" + uses: actions/setup-python@v5 + with: + python-version: ${{ env.MAIN_PYTHON_VERSION }} + + - name: "Checking Python" + run: | + python --version + python -m pip install --upgrade pip + + - name: "Install ansys-mapdl-core" + run: | + python -m pip install . --no-deps + python -m pip install -r minimum_requirements.txt + python -c "from ansys.mapdl import core as pymapdl; print('Import successfull')" + + - name: "Unit testing requirements installation" + run: | + python -m pip install -r .ci/requirements_minimal.txt + + - name: "Unit testing" + env: + ANSYSLMD_LICENSE_FILE: "1055@${{ secrets.LICENSE_SERVER }}" + ON_STUDENT: ${{ steps.student_check.outputs.ON_STUDENT }} + run: | + echo "ON_UBUNTU: $ON_UBUNTU" + echo "ON_STUDENT: $ON_STUDENT" + + # Because there is no 'ansys-tools-path' we need to input the + # executable path with the env var: PYMAPDL_MAPDL_EXEC. + + if [[ "${{ matrix.mapdl-version }}" == *"latest-ubuntu"* ]] ; then + version=${{ env.LATEST_VERSION }} + else + version=$(echo "${{ matrix.mapdl-version }}" | head -c 5 | tail -c 4 | tr -d '.') + fi; + + echo "Version: $version" + + export PYMAPDL_MAPDL_EXEC=/ansys_inc/v"$version"/ansys/bin/ansys"$version" + echo "$PYMAPDL_MAPDL_EXEC" + + unset PYMAPDL_START_INSTANCE + pytest -k "console" \ + ${{ env.PYTEST_ARGUMENTS }} \ + --cov-report=xml:${{ matrix.mapdl-version }}-minimal-console.xml + + - name: "Collect logs on failure" + if: always() + env: + LOG_NAMES: logs-minimal-console-${{ matrix.mapdl-version }} + run: | + .ci/collect_mapdl_logs_locals.sh + + - name: "Upload logs to GitHub" + if: always() + uses: actions/upload-artifact@master + with: + name: logs-minimal-console-${{ matrix.mapdl-version }}.tgz + path: ./logs-minimal-console-${{ matrix.mapdl-version }}.tgz + + - name: "Display files structure" + if: always() + env: + LOG_NAMES: logs-minimal-console-${{ matrix.mapdl-version }} + run: | + .ci/display_logs_locals.sh + + - uses: codecov/codecov-action@v5 + name: "Upload coverage to Codecov" + with: + token: ${{ secrets.CODECOV_TOKEN }} # required + root_dir: ${{ github.workspace }} + name: ${{ matrix.mapdl-version }}-minimal-console.xml + flags: ubuntu,local,${{ matrix.mapdl-version }},minimal,console,${{ steps.student_check.outputs.TAG_STUDENT }},dmp + + - name: "Upload coverage artifacts" + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.mapdl-version }}-minimal-console.xml + path: ./${{ matrix.mapdl-version }}-minimal-console.xml + + test-windows: # Skipped if: github.repository == '' diff --git a/doc/changelog.d/3183.changed.md b/doc/changelog.d/3183.changed.md deleted file mode 100644 index 2e273fafd9..0000000000 --- a/doc/changelog.d/3183.changed.md +++ /dev/null @@ -1 +0,0 @@ -chore: update CHANGELOG for v0.68.2 \ No newline at end of file diff --git a/doc/changelog.d/3186.changed.md b/doc/changelog.d/3186.changed.md deleted file mode 100644 index 1049d536cf..0000000000 --- a/doc/changelog.d/3186.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: Use CICD only on ``v*`` tags. \ No newline at end of file diff --git a/doc/changelog.d/3188.fixed.md b/doc/changelog.d/3188.fixed.md deleted file mode 100644 index eb1e02a823..0000000000 --- a/doc/changelog.d/3188.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: using same labels everywhere \ No newline at end of file diff --git a/doc/changelog.d/3191.changed.md b/doc/changelog.d/3191.changed.md deleted file mode 100644 index 29ddbbb29d..0000000000 --- a/doc/changelog.d/3191.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: checking documentation style in ``Examples`` directory too \ No newline at end of file diff --git a/doc/changelog.d/3194.dependencies.md b/doc/changelog.d/3194.dependencies.md deleted file mode 100644 index 37b1cea7aa..0000000000 --- a/doc/changelog.d/3194.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pyvista[trame] from 0.43.9 to 0.43.10 \ No newline at end of file diff --git a/doc/changelog.d/3197.dependencies.md b/doc/changelog.d/3197.dependencies.md deleted file mode 100644 index 41dcf4e505..0000000000 --- a/doc/changelog.d/3197.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the minimal group across 1 directory with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3201.changed.md b/doc/changelog.d/3201.changed.md deleted file mode 100644 index 70afe387e5..0000000000 --- a/doc/changelog.d/3201.changed.md +++ /dev/null @@ -1 +0,0 @@ -chore: update CHANGELOG for v0.68.3 \ No newline at end of file diff --git a/doc/changelog.d/3204.fixed.md b/doc/changelog.d/3204.fixed.md deleted file mode 100644 index 6409eb5f2c..0000000000 --- a/doc/changelog.d/3204.fixed.md +++ /dev/null @@ -1 +0,0 @@ -ci: Fix missing labels format in dependabot file \ No newline at end of file diff --git a/doc/changelog.d/3206.miscellaneous.md b/doc/changelog.d/3206.miscellaneous.md deleted file mode 100644 index 511e2fa4d8..0000000000 --- a/doc/changelog.d/3206.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -ci: [pre-commit.ci] pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3210.miscellaneous.md b/doc/changelog.d/3210.miscellaneous.md deleted file mode 100644 index 91affe0358..0000000000 --- a/doc/changelog.d/3210.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -ci: Adding v251 CentOS based image to testing \ No newline at end of file diff --git a/doc/changelog.d/3211.changed.md b/doc/changelog.d/3211.changed.md deleted file mode 100644 index 04c58e9b24..0000000000 --- a/doc/changelog.d/3211.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: Update julia testing \ No newline at end of file diff --git a/doc/changelog.d/3212.dependencies.md b/doc/changelog.d/3212.dependencies.md deleted file mode 100644 index 13d8816d90..0000000000 --- a/doc/changelog.d/3212.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump importlib-metadata from 7.2.0 to 7.2.1 in the minimal group \ No newline at end of file diff --git a/doc/changelog.d/3213.dependencies.md b/doc/changelog.d/3213.dependencies.md deleted file mode 100644 index 7dc726a750..0000000000 --- a/doc/changelog.d/3213.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump scipy from 1.13.1 to 1.14.0 in the core group \ No newline at end of file diff --git a/doc/changelog.d/3214.dependencies.md b/doc/changelog.d/3214.dependencies.md deleted file mode 100644 index ed1f41ce94..0000000000 --- a/doc/changelog.d/3214.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the documentation group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3215.dependencies.md b/doc/changelog.d/3215.dependencies.md deleted file mode 100644 index ee0f4f3cf0..0000000000 --- a/doc/changelog.d/3215.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump autopep8 from 2.3.0 to 2.3.1 in the testing group \ No newline at end of file diff --git a/doc/changelog.d/3217.dependencies.md b/doc/changelog.d/3217.dependencies.md deleted file mode 100644 index 48cd409222..0000000000 --- a/doc/changelog.d/3217.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: update requirements in devcontainer directory \ No newline at end of file diff --git a/doc/changelog.d/3223.changed.md b/doc/changelog.d/3223.changed.md deleted file mode 100644 index 41de6421d3..0000000000 --- a/doc/changelog.d/3223.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: improving if to match also schedule and workflow_dispatch \ No newline at end of file diff --git a/doc/changelog.d/3224.dependencies.md b/doc/changelog.d/3224.dependencies.md deleted file mode 100644 index 058ea3d92a..0000000000 --- a/doc/changelog.d/3224.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: removing reredirect sphinx extension \ No newline at end of file diff --git a/doc/changelog.d/3225.fixed.md b/doc/changelog.d/3225.fixed.md deleted file mode 100644 index 8184740967..0000000000 --- a/doc/changelog.d/3225.fixed.md +++ /dev/null @@ -1 +0,0 @@ -ci: wrong tagging on the coverage artifacts \ No newline at end of file diff --git a/doc/changelog.d/3227.fixed.md b/doc/changelog.d/3227.fixed.md deleted file mode 100644 index c288ac1e7b..0000000000 --- a/doc/changelog.d/3227.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: avoid inspecting suspended processes \ No newline at end of file diff --git a/doc/changelog.d/3228.changed.md b/doc/changelog.d/3228.changed.md deleted file mode 100644 index 66edcb71d3..0000000000 --- a/doc/changelog.d/3228.changed.md +++ /dev/null @@ -1 +0,0 @@ -docs: documenting new naming conventions for commits, branches and PRs. \ No newline at end of file diff --git a/doc/changelog.d/3229.dependencies.md b/doc/changelog.d/3229.dependencies.md deleted file mode 100644 index a84499e3a4..0000000000 --- a/doc/changelog.d/3229.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump importlib-metadata from 7.2.1 to 8.0.0 in the minimal group \ No newline at end of file diff --git a/doc/changelog.d/3232.changed.md b/doc/changelog.d/3232.changed.md deleted file mode 100644 index 54d1f17327..0000000000 --- a/doc/changelog.d/3232.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: Using a dynamically generated matrix for testing job setup \ No newline at end of file diff --git a/doc/changelog.d/3237.changed.md b/doc/changelog.d/3237.changed.md deleted file mode 100644 index 98ce0e6c45..0000000000 --- a/doc/changelog.d/3237.changed.md +++ /dev/null @@ -1 +0,0 @@ -ci: increase the files checked for changes before load docs cache \ No newline at end of file diff --git a/doc/changelog.d/3238.miscellaneous.md b/doc/changelog.d/3238.miscellaneous.md deleted file mode 100644 index b42b241032..0000000000 --- a/doc/changelog.d/3238.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -[pre-commit.ci] pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3239.miscellaneous.md b/doc/changelog.d/3239.miscellaneous.md deleted file mode 100644 index 69a82cd64a..0000000000 --- a/doc/changelog.d/3239.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: refactoring `create_temp_dir` \ No newline at end of file diff --git a/doc/changelog.d/3241.dependencies.md b/doc/changelog.d/3241.dependencies.md deleted file mode 100644 index 7c5203d58d..0000000000 --- a/doc/changelog.d/3241.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the core group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3242.changed.md b/doc/changelog.d/3242.changed.md deleted file mode 100644 index f51c97d2bc..0000000000 --- a/doc/changelog.d/3242.changed.md +++ /dev/null @@ -1 +0,0 @@ -build: bump certifi from 2024.2.2 to 2024.7.4 in /doc/source/examples/extended_examples/hpc \ No newline at end of file diff --git a/doc/changelog.d/3247.fixed.md b/doc/changelog.d/3247.fixed.md deleted file mode 100644 index 99baa95369..0000000000 --- a/doc/changelog.d/3247.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: not deleting temporary file when ``remove_temp_dir_on_exit``=True \ No newline at end of file diff --git a/doc/changelog.d/3249.miscellaneous.md b/doc/changelog.d/3249.miscellaneous.md deleted file mode 100644 index e09f220aed..0000000000 --- a/doc/changelog.d/3249.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -docs: adapt static images to dark/light themes \ No newline at end of file diff --git a/doc/changelog.d/3251.fixed.md b/doc/changelog.d/3251.fixed.md deleted file mode 100644 index 609a22f090..0000000000 --- a/doc/changelog.d/3251.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: local tests always running as student \ No newline at end of file diff --git a/doc/changelog.d/3252.fixed.md b/doc/changelog.d/3252.fixed.md deleted file mode 100644 index a429719def..0000000000 --- a/doc/changelog.d/3252.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: incorrect env vars section \ No newline at end of file diff --git a/doc/changelog.d/3253.miscellaneous.md b/doc/changelog.d/3253.miscellaneous.md deleted file mode 100644 index b42b241032..0000000000 --- a/doc/changelog.d/3253.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -[pre-commit.ci] pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3255.dependencies.md b/doc/changelog.d/3255.dependencies.md deleted file mode 100644 index 42e7031e52..0000000000 --- a/doc/changelog.d/3255.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: update ansys-api-mapdl to 0.5.2 \ No newline at end of file diff --git a/doc/changelog.d/3259.added.md b/doc/changelog.d/3259.added.md deleted file mode 100644 index f8047af254..0000000000 --- a/doc/changelog.d/3259.added.md +++ /dev/null @@ -1 +0,0 @@ -test: skip test \ No newline at end of file diff --git a/doc/changelog.d/3466.documentation.md b/doc/changelog.d/3466.documentation.md deleted file mode 100644 index bd1c0ca801..0000000000 --- a/doc/changelog.d/3466.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: documenting using pymapdl on clusters diff --git a/doc/changelog.d/3474.added.md b/doc/changelog.d/3474.added.md deleted file mode 100644 index ea01459e8f..0000000000 --- a/doc/changelog.d/3474.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: modifying ``subprocess`` calls and removing ``try except continue`` statements \ No newline at end of file diff --git a/doc/changelog.d/3475.added.md b/doc/changelog.d/3475.added.md deleted file mode 100644 index 3750f99954..0000000000 --- a/doc/changelog.d/3475.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: launch_mapdl \ No newline at end of file diff --git a/doc/changelog.d/3479.added.md b/doc/changelog.d/3479.added.md deleted file mode 100644 index 1f7da7eaf7..0000000000 --- a/doc/changelog.d/3479.added.md +++ /dev/null @@ -1 +0,0 @@ -chore: update CHANGELOG for v0.68.6 \ No newline at end of file diff --git a/doc/changelog.d/3481.maintenance.md b/doc/changelog.d/3481.maintenance.md deleted file mode 100644 index 3f73b232e1..0000000000 --- a/doc/changelog.d/3481.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: bump thollander/actions-comment-pull-request from 2 to 3 in the actions group \ No newline at end of file diff --git a/doc/changelog.d/3482.maintenance.md b/doc/changelog.d/3482.maintenance.md deleted file mode 100644 index 23264207b9..0000000000 --- a/doc/changelog.d/3482.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3484.maintenance.md b/doc/changelog.d/3484.maintenance.md deleted file mode 100644 index 1caacc979a..0000000000 --- a/doc/changelog.d/3484.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: force coloring in pytest \ No newline at end of file diff --git a/doc/changelog.d/3487.miscellaneous.md b/doc/changelog.d/3487.miscellaneous.md deleted file mode 100644 index e9b60408a8..0000000000 --- a/doc/changelog.d/3487.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: adding 'pymapdl_nproc' to non-slurm runs \ No newline at end of file diff --git a/doc/changelog.d/3488.documentation.md b/doc/changelog.d/3488.documentation.md deleted file mode 100644 index ed1b85597f..0000000000 --- a/doc/changelog.d/3488.documentation.md +++ /dev/null @@ -1 +0,0 @@ -ci: avoiding linkcheck on changelog page \ No newline at end of file diff --git a/doc/changelog.d/3490.added.md b/doc/changelog.d/3490.added.md deleted file mode 100644 index 93351e68ee..0000000000 --- a/doc/changelog.d/3490.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: `__init__` file \ No newline at end of file diff --git a/doc/changelog.d/3491.added.md b/doc/changelog.d/3491.added.md deleted file mode 100644 index 9a165ce053..0000000000 --- a/doc/changelog.d/3491.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: moving information class to another module \ No newline at end of file diff --git a/doc/changelog.d/3492.maintenance.md b/doc/changelog.d/3492.maintenance.md deleted file mode 100644 index 31c89238ae..0000000000 --- a/doc/changelog.d/3492.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -build: bump psutil from 6.0.0 to 6.1.0 in the minimal group \ No newline at end of file diff --git a/doc/changelog.d/3493.dependencies.md b/doc/changelog.d/3493.dependencies.md deleted file mode 100644 index d1f604e862..0000000000 --- a/doc/changelog.d/3493.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump grpcio from 1.66.2 to 1.67.0 in the grpc-deps group \ No newline at end of file diff --git a/doc/changelog.d/3494.dependencies.md b/doc/changelog.d/3494.dependencies.md deleted file mode 100644 index 9d45698ce3..0000000000 --- a/doc/changelog.d/3494.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump ansys-sphinx-theme from 1.1.2 to 1.1.5 in the core group \ No newline at end of file diff --git a/doc/changelog.d/3495.dependencies.md b/doc/changelog.d/3495.dependencies.md deleted file mode 100644 index ed1f41ce94..0000000000 --- a/doc/changelog.d/3495.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the documentation group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3496.dependencies.md b/doc/changelog.d/3496.dependencies.md deleted file mode 100644 index f7c6c74b3d..0000000000 --- a/doc/changelog.d/3496.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump ansys-sphinx-theme from 1.1.2 to 1.1.6 in the core group across 1 directory \ No newline at end of file diff --git a/doc/changelog.d/3497.documentation.md b/doc/changelog.d/3497.documentation.md deleted file mode 100644 index 9034ad6606..0000000000 --- a/doc/changelog.d/3497.documentation.md +++ /dev/null @@ -1 +0,0 @@ -feat: support for launching an MAPDL instance in an SLURM HPC cluster \ No newline at end of file diff --git a/doc/changelog.d/3500.documentation.md b/doc/changelog.d/3500.documentation.md deleted file mode 100644 index 902767602d..0000000000 --- a/doc/changelog.d/3500.documentation.md +++ /dev/null @@ -1 +0,0 @@ -feat: passing tight integration env vars to mapdl \ No newline at end of file diff --git a/doc/changelog.d/3501.added.md b/doc/changelog.d/3501.added.md deleted file mode 100644 index 31292e041b..0000000000 --- a/doc/changelog.d/3501.added.md +++ /dev/null @@ -1 +0,0 @@ -test: check all commands are submitted \ No newline at end of file diff --git a/doc/changelog.d/3505.maintenance.md b/doc/changelog.d/3505.maintenance.md deleted file mode 100644 index b995717304..0000000000 --- a/doc/changelog.d/3505.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: ``ansys/actions/check-vulnerabilities`` to CI-CD \ No newline at end of file diff --git a/doc/changelog.d/3506.documentation.md b/doc/changelog.d/3506.documentation.md deleted file mode 100644 index 1977af6976..0000000000 --- a/doc/changelog.d/3506.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: review of documenting using pymapdl on clusters (#3466) \ No newline at end of file diff --git a/doc/changelog.d/3507.fixed.md b/doc/changelog.d/3507.fixed.md deleted file mode 100644 index 1f4db19a11..0000000000 --- a/doc/changelog.d/3507.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: raising port busy when connecting \ No newline at end of file diff --git a/doc/changelog.d/3511.added.md b/doc/changelog.d/3511.added.md deleted file mode 100644 index 49f004477a..0000000000 --- a/doc/changelog.d/3511.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: externalise the 'report' features to another file \ No newline at end of file diff --git a/doc/changelog.d/3513.documentation.md b/doc/changelog.d/3513.documentation.md deleted file mode 100644 index b2f60a9a07..0000000000 --- a/doc/changelog.d/3513.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: adding-sbatch-support \ No newline at end of file diff --git a/doc/changelog.d/3515.dependencies.md b/doc/changelog.d/3515.dependencies.md deleted file mode 100644 index 7c5203d58d..0000000000 --- a/doc/changelog.d/3515.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the core group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3516.dependencies.md b/doc/changelog.d/3516.dependencies.md deleted file mode 100644 index 147281e6e6..0000000000 --- a/doc/changelog.d/3516.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pyansys-tools-report from 0.8.0 to 0.8.1 in the testing group \ No newline at end of file diff --git a/doc/changelog.d/3517.added.md b/doc/changelog.d/3517.added.md deleted file mode 100644 index d245230f94..0000000000 --- a/doc/changelog.d/3517.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: simplifying directory setter property \ No newline at end of file diff --git a/doc/changelog.d/3519.added.md b/doc/changelog.d/3519.added.md deleted file mode 100644 index 4e26bc7b6d..0000000000 --- a/doc/changelog.d/3519.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: testing suite (random order) \ No newline at end of file diff --git a/doc/changelog.d/3521.maintenance.md b/doc/changelog.d/3521.maintenance.md deleted file mode 100644 index 0537b3a289..0000000000 --- a/doc/changelog.d/3521.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: bump actions/checkout from 4.2.1 to 4.2.2 in the actions group \ No newline at end of file diff --git a/doc/changelog.d/3522.maintenance.md b/doc/changelog.d/3522.maintenance.md deleted file mode 100644 index 23264207b9..0000000000 --- a/doc/changelog.d/3522.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3523.added.md b/doc/changelog.d/3523.added.md deleted file mode 100644 index 848608127d..0000000000 --- a/doc/changelog.d/3523.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: moving tests to classes to avoid repeated fixtures execution \ No newline at end of file diff --git a/doc/changelog.d/3524.added.md b/doc/changelog.d/3524.added.md deleted file mode 100644 index f25a84e3c7..0000000000 --- a/doc/changelog.d/3524.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: using test classes in test_inline tests \ No newline at end of file diff --git a/doc/changelog.d/3525.fixed.md b/doc/changelog.d/3525.fixed.md deleted file mode 100644 index a550b94b1f..0000000000 --- a/doc/changelog.d/3525.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: logo link \ No newline at end of file diff --git a/doc/changelog.d/3526.documentation.md b/doc/changelog.d/3526.documentation.md deleted file mode 100644 index 6eb67479e7..0000000000 --- a/doc/changelog.d/3526.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: removing extra links from landing page. \ No newline at end of file diff --git a/doc/changelog.d/3527.documentation.md b/doc/changelog.d/3527.documentation.md deleted file mode 100644 index f8883cc2ba..0000000000 --- a/doc/changelog.d/3527.documentation.md +++ /dev/null @@ -1 +0,0 @@ -DOC: Update pymapdl.rst \ No newline at end of file diff --git a/doc/changelog.d/3528.miscellaneous.md b/doc/changelog.d/3528.miscellaneous.md deleted file mode 100644 index 12e026ea29..0000000000 --- a/doc/changelog.d/3528.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: using version instead of exec_path for the MPI checks \ No newline at end of file diff --git a/doc/changelog.d/3533.dependencies.md b/doc/changelog.d/3533.dependencies.md deleted file mode 100644 index e25f8a7db5..0000000000 --- a/doc/changelog.d/3533.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump grpcio from 1.67.0 to 1.67.1 in the grpc-deps group \ No newline at end of file diff --git a/doc/changelog.d/3534.dependencies.md b/doc/changelog.d/3534.dependencies.md deleted file mode 100644 index 7c5203d58d..0000000000 --- a/doc/changelog.d/3534.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the core group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3535.dependencies.md b/doc/changelog.d/3535.dependencies.md deleted file mode 100644 index 369dd67ace..0000000000 --- a/doc/changelog.d/3535.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pytest-cov from 5.0.0 to 6.0.0 in the testing group \ No newline at end of file diff --git a/doc/changelog.d/3540.fixed.md b/doc/changelog.d/3540.fixed.md deleted file mode 100644 index b524a5f16d..0000000000 --- a/doc/changelog.d/3540.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: checking port on non-grpc mapdl instances \ No newline at end of file diff --git a/doc/changelog.d/3541.maintenance.md b/doc/changelog.d/3541.maintenance.md deleted file mode 100644 index f9f0f0bfa6..0000000000 --- a/doc/changelog.d/3541.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -build: bump numpy from 2.1.2 to 2.1.3 in the minimal group \ No newline at end of file diff --git a/doc/changelog.d/3542.added.md b/doc/changelog.d/3542.added.md deleted file mode 100644 index 1d40d19bf0..0000000000 --- a/doc/changelog.d/3542.added.md +++ /dev/null @@ -1 +0,0 @@ -chore: fix codecov.yml content \ No newline at end of file diff --git a/doc/changelog.d/3545.maintenance.md b/doc/changelog.d/3545.maintenance.md deleted file mode 100644 index 23264207b9..0000000000 --- a/doc/changelog.d/3545.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3546.documentation.md b/doc/changelog.d/3546.documentation.md deleted file mode 100644 index f6c4f3bef2..0000000000 --- a/doc/changelog.d/3546.documentation.md +++ /dev/null @@ -1 +0,0 @@ -[maint] remove importlib-metadata requirement \ No newline at end of file diff --git a/doc/changelog.d/3547.dependencies.md b/doc/changelog.d/3547.dependencies.md deleted file mode 100644 index 13e587a7a5..0000000000 --- a/doc/changelog.d/3547.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump ansys-sphinx-theme from 1.2.0 to 1.2.1 in the core group \ No newline at end of file diff --git a/doc/changelog.d/3549.documentation.md b/doc/changelog.d/3549.documentation.md deleted file mode 100644 index a119c57cd2..0000000000 --- a/doc/changelog.d/3549.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: extracting information to another rst file \ No newline at end of file diff --git a/doc/changelog.d/3550.added.md b/doc/changelog.d/3550.added.md deleted file mode 100644 index 9354879ed5..0000000000 --- a/doc/changelog.d/3550.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: adding logging calls to misc.py \ No newline at end of file diff --git a/doc/changelog.d/3551.added.md b/doc/changelog.d/3551.added.md deleted file mode 100644 index 987638c792..0000000000 --- a/doc/changelog.d/3551.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: removing-`run_as_prep7`-in-favour-of-`run_as` \ No newline at end of file diff --git a/doc/changelog.d/3552.fixed.md b/doc/changelog.d/3552.fixed.md deleted file mode 100644 index 382312cd67..0000000000 --- a/doc/changelog.d/3552.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: warning and add an exception if using class alone \ No newline at end of file diff --git a/doc/changelog.d/3553.added.md b/doc/changelog.d/3553.added.md deleted file mode 100644 index 252e997f3f..0000000000 --- a/doc/changelog.d/3553.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: adding-type-ints-to-misc \ No newline at end of file diff --git a/doc/changelog.d/3554.added.md b/doc/changelog.d/3554.added.md deleted file mode 100644 index 41caad6cb3..0000000000 --- a/doc/changelog.d/3554.added.md +++ /dev/null @@ -1 +0,0 @@ -test: adding test for start_timeout arg \ No newline at end of file diff --git a/doc/changelog.d/3555.fixed.md b/doc/changelog.d/3555.fixed.md deleted file mode 100644 index d76253d6b6..0000000000 --- a/doc/changelog.d/3555.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: gui-extended-example \ No newline at end of file diff --git a/doc/changelog.d/3556.added.md b/doc/changelog.d/3556.added.md deleted file mode 100644 index 64f1caa881..0000000000 --- a/doc/changelog.d/3556.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: increase post module coverage \ No newline at end of file diff --git a/doc/changelog.d/3557.maintenance.md b/doc/changelog.d/3557.maintenance.md deleted file mode 100644 index 0cc444e06d..0000000000 --- a/doc/changelog.d/3557.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: bump codecov/codecov-action from 4 to 5 in the actions group \ No newline at end of file diff --git a/doc/changelog.d/3559.miscellaneous.md b/doc/changelog.d/3559.miscellaneous.md deleted file mode 100644 index e2048aae00..0000000000 --- a/doc/changelog.d/3559.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: raising error if plot image cannot be obtained \ No newline at end of file diff --git a/doc/changelog.d/3560.added.md b/doc/changelog.d/3560.added.md deleted file mode 100644 index a6b1dd48f1..0000000000 --- a/doc/changelog.d/3560.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: using find_mapdl instead of find_ansys \ No newline at end of file diff --git a/doc/changelog.d/3564.maintenance.md b/doc/changelog.d/3564.maintenance.md deleted file mode 100644 index 84a3b529b3..0000000000 --- a/doc/changelog.d/3564.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: skipping student versions when user is authenticated \ No newline at end of file diff --git a/doc/changelog.d/3565.dependencies.md b/doc/changelog.d/3565.dependencies.md deleted file mode 100644 index 2d3173248c..0000000000 --- a/doc/changelog.d/3565.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump grpcio from 1.67.1 to 1.68.0 in the grpc-deps group \ No newline at end of file diff --git a/doc/changelog.d/3566.dependencies.md b/doc/changelog.d/3566.dependencies.md deleted file mode 100644 index 7c5203d58d..0000000000 --- a/doc/changelog.d/3566.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump the core group with 2 updates \ No newline at end of file diff --git a/doc/changelog.d/3567.dependencies.md b/doc/changelog.d/3567.dependencies.md deleted file mode 100644 index bdb63a5cb5..0000000000 --- a/doc/changelog.d/3567.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pytest-rerunfailures from 14.0 to 15.0 in the testing group \ No newline at end of file diff --git a/doc/changelog.d/3570.fixed.md b/doc/changelog.d/3570.fixed.md deleted file mode 100644 index eb89327d90..0000000000 --- a/doc/changelog.d/3570.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: python version warning \ No newline at end of file diff --git a/doc/changelog.d/3571.miscellaneous.md b/doc/changelog.d/3571.miscellaneous.md deleted file mode 100644 index 86d34b845d..0000000000 --- a/doc/changelog.d/3571.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: supporting v25.1 and v25.2 \ No newline at end of file diff --git a/doc/changelog.d/3572.documentation.md b/doc/changelog.d/3572.documentation.md deleted file mode 100644 index d2d23f08ae..0000000000 --- a/doc/changelog.d/3572.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: updating compatible Python versions \ No newline at end of file diff --git a/doc/changelog.d/3573.added.md b/doc/changelog.d/3573.added.md deleted file mode 100644 index 95bbfdd6b4..0000000000 --- a/doc/changelog.d/3573.added.md +++ /dev/null @@ -1 +0,0 @@ -refactor: replace `get_ansys_path` with `get_mapdl_path` \ No newline at end of file diff --git a/doc/changelog.d/3574.miscellaneous.md b/doc/changelog.d/3574.miscellaneous.md deleted file mode 100644 index 6f733033c0..0000000000 --- a/doc/changelog.d/3574.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: adding-mode-warning \ No newline at end of file diff --git a/doc/changelog.d/3575.miscellaneous.md b/doc/changelog.d/3575.miscellaneous.md deleted file mode 100644 index 085b441ea2..0000000000 --- a/doc/changelog.d/3575.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: running MPI fix only if on windows \ No newline at end of file diff --git a/doc/changelog.d/3576.miscellaneous.md b/doc/changelog.d/3576.miscellaneous.md deleted file mode 100644 index baf1ad34eb..0000000000 --- a/doc/changelog.d/3576.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: adding ``check_has_mapdl`` \ No newline at end of file diff --git a/doc/changelog.d/3580.documentation.md b/doc/changelog.d/3580.documentation.md deleted file mode 100644 index 50f8ff2ab4..0000000000 --- a/doc/changelog.d/3580.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: update docker instructions \ No newline at end of file diff --git a/doc/changelog.d/3582.fixed.md b/doc/changelog.d/3582.fixed.md deleted file mode 100644 index d6c82a7222..0000000000 --- a/doc/changelog.d/3582.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: components typo \ No newline at end of file diff --git a/doc/changelog.d/3584.documentation.md b/doc/changelog.d/3584.documentation.md deleted file mode 100644 index 0a948cb5e2..0000000000 --- a/doc/changelog.d/3584.documentation.md +++ /dev/null @@ -1 +0,0 @@ -docs: adding some info for getting multiple compose running \ No newline at end of file diff --git a/doc/changelog.d/3585.maintenance.md b/doc/changelog.d/3585.maintenance.md deleted file mode 100644 index d1dc9b079a..0000000000 --- a/doc/changelog.d/3585.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: adding codeql.yml \ No newline at end of file diff --git a/doc/changelog.d/3589.fixed.md b/doc/changelog.d/3589.fixed.md deleted file mode 100644 index bc7f39a54b..0000000000 --- a/doc/changelog.d/3589.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: linkchecker and cheatsheet links \ No newline at end of file diff --git a/doc/changelog.d/3590.miscellaneous.md b/doc/changelog.d/3590.miscellaneous.md deleted file mode 100644 index 913c469727..0000000000 --- a/doc/changelog.d/3590.miscellaneous.md +++ /dev/null @@ -1 +0,0 @@ -feat: improving load_array to reduce format line length \ No newline at end of file diff --git a/doc/changelog.d/3593.dependencies.md b/doc/changelog.d/3593.dependencies.md deleted file mode 100644 index 89ae38f55a..0000000000 --- a/doc/changelog.d/3593.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump imageio from 2.36.0 to 2.36.1 in the documentation group \ No newline at end of file diff --git a/doc/changelog.d/3599.maintenance.md b/doc/changelog.d/3599.maintenance.md deleted file mode 100644 index 23264207b9..0000000000 --- a/doc/changelog.d/3599.maintenance.md +++ /dev/null @@ -1 +0,0 @@ -ci: pre-commit autoupdate \ No newline at end of file diff --git a/doc/changelog.d/3601.dependencies.md b/doc/changelog.d/3601.dependencies.md deleted file mode 100644 index 805682d25f..0000000000 --- a/doc/changelog.d/3601.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump grpcio from 1.68.0 to 1.68.1 in the grpc-deps group \ No newline at end of file diff --git a/doc/changelog.d/3603.dependencies.md b/doc/changelog.d/3603.dependencies.md deleted file mode 100644 index 0bd643879e..0000000000 --- a/doc/changelog.d/3603.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pytest from 8.3.3 to 8.3.4 in the testing group \ No newline at end of file diff --git a/doc/changelog.d/3604.dependencies.md b/doc/changelog.d/3604.dependencies.md deleted file mode 100644 index 9eccbc1776..0000000000 --- a/doc/changelog.d/3604.dependencies.md +++ /dev/null @@ -1 +0,0 @@ -build: bump pyfakefs from 5.7.1 to 5.7.2 \ No newline at end of file diff --git a/doc/changelog.d/3608.fixed.md b/doc/changelog.d/3608.fixed.md deleted file mode 100644 index 46f20d76a2..0000000000 --- a/doc/changelog.d/3608.fixed.md +++ /dev/null @@ -1 +0,0 @@ -fix: avoid verbose grpc interface when solving \ No newline at end of file diff --git a/doc/changelog.d/3636.miscellaneous.md b/doc/changelog.d/3636.miscellaneous.md new file mode 100644 index 0000000000..5d0e245a47 --- /dev/null +++ b/doc/changelog.d/3636.miscellaneous.md @@ -0,0 +1 @@ +feat: node/element selection commands returning selected ids \ No newline at end of file diff --git a/doc/changelog.d/3641.added.md b/doc/changelog.d/3641.added.md new file mode 100644 index 0000000000..401ec81a64 --- /dev/null +++ b/doc/changelog.d/3641.added.md @@ -0,0 +1 @@ +chore: update CHANGELOG for v0.69.0 \ No newline at end of file diff --git a/doc/changelog.d/3642.fixed.md b/doc/changelog.d/3642.fixed.md new file mode 100644 index 0000000000..526ce0df6e --- /dev/null +++ b/doc/changelog.d/3642.fixed.md @@ -0,0 +1 @@ +fix: timeout for file checking \ No newline at end of file diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index c814e6df83..fe56e77b5d 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -9,6 +9,170 @@ This document contains the release notes for the project. .. towncrier release notes start +`0.69.0 `_ - 2025-01-08 +============================================================================== + +Added +^^^^^ + +- test: skip test `#3259 `_ +- refactor: modifying ``subprocess`` calls and removing ``try except continue`` statements `#3474 `_ +- refactor: launch_mapdl `#3475 `_ +- chore: update CHANGELOG for v0.68.6 `#3479 `_ +- refactor: `__init__` file `#3490 `_ +- refactor: moving information class to another module `#3491 `_ +- test: check all commands are submitted `#3501 `_ +- test: faking-v150 `#3509 `_ +- refactor: externalise the 'report' features to another file `#3511 `_ +- refactor: simplifying directory setter property `#3517 `_ +- refactor: testing suite (random order) `#3519 `_ +- refactor: moving tests to classes to avoid repeated fixtures execution `#3523 `_ +- refactor: using test classes in test_inline tests `#3524 `_ +- chore: fix codecov.yml content `#3542 `_ +- refactor: adding logging calls to misc.py `#3550 `_ +- refactor: removing-`run_as_prep7`-in-favour-of-`run_as` `#3551 `_ +- refactor: adding-type-ints-to-misc `#3553 `_ +- test: adding test for start_timeout arg `#3554 `_ +- refactor: increase post module coverage `#3556 `_ +- refactor: using find_mapdl instead of find_ansys `#3560 `_ +- refactor: annotate pymapdl part 1 `#3569 `_ +- refactor: replace `get_ansys_path` with `get_mapdl_path` `#3573 `_ +- refactor: small improvements to test settings `#3577 `_ +- tests: adding timeout to each test `#3621 `_ +- refactor: Iterate over the dictionary directly instead of using .keys(). `#3631 `_ + + +Changed +^^^^^^^ + +- chore: update CHANGELOG for v0.68.2 `#3183 `_ +- ci: Use CICD only on ``v*`` tags. `#3186 `_ +- ci: checking documentation style in ``Examples`` directory too `#3191 `_ +- chore: update CHANGELOG for v0.68.3 `#3201 `_ +- ci: Update julia testing `#3211 `_ +- ci: improving if to match also schedule and workflow_dispatch `#3223 `_ +- docs: documenting new naming conventions for commits, branches and PRs. `#3228 `_ +- ci: Using a dynamically generated matrix for testing job setup `#3232 `_ +- ci: increase the files checked for changes before load docs cache `#3237 `_ +- build: bump certifi from 2024.2.2 to 2024.7.4 in /doc/source/examples/extended_examples/hpc `#3242 `_ + + +Fixed +^^^^^ + +- fix: using same labels everywhere `#3188 `_ +- ci: Fix missing labels format in dependabot file `#3204 `_ +- ci: wrong tagging on the coverage artifacts `#3225 `_ +- fix: avoid inspecting suspended processes `#3227 `_ +- fix: not deleting temporary file when ``remove_temp_dir_on_exit`` =True `#3247 `_ +- fix: local tests always running as student `#3251 `_ +- fix: incorrect env vars section `#3252 `_ +- fix: raising port busy when connecting `#3507 `_ +- fix: logo link `#3525 `_ +- fix: checking port on non-grpc mapdl instances `#3540 `_ +- fix: warning and add an exception if using class alone `#3552 `_ +- fix: gui-extended-example `#3555 `_ +- fix: python version warning `#3570 `_ +- fix: components typo `#3582 `_ +- fix: avoiding long names in test arguments `#3583 `_ +- fix: console launching `#3586 `_ +- fix: linkchecker and cheatsheet links `#3589 `_ +- fix: avoid verbose grpc interface when solving `#3608 `_ +- fix: exit getting frozen if routine is not finished `#3617 `_ +- fix: changelog `#3640 `_ + + +Dependencies +^^^^^^^^^^^^ + +- build: bump pyvista[trame] from 0.43.9 to 0.43.10 `#3194 `_ +- build: bump the minimal group across 1 directory with 2 updates `#3197 `_ +- build: bump importlib-metadata from 7.2.0 to 7.2.1 in the minimal group `#3212 `_ +- build: bump scipy from 1.13.1 to 1.14.0 in the core group `#3213 `_ +- build: bump the documentation group with 2 updates `#3214 `_, `#3495 `_ +- build: bump autopep8 from 2.3.0 to 2.3.1 in the testing group `#3215 `_ +- build: update requirements in devcontainer directory `#3217 `_ +- build: removing reredirect sphinx extension `#3224 `_ +- build: bump importlib-metadata from 7.2.1 to 8.0.0 in the minimal group `#3229 `_ +- build: bump the core group with 2 updates `#3241 `_, `#3515 `_, `#3534 `_, `#3566 `_ +- build: update ansys-api-mapdl to 0.5.2 `#3255 `_ +- build: bump grpcio from 1.66.2 to 1.67.0 in the grpc-deps group `#3493 `_ +- build: bump ansys-sphinx-theme from 1.1.2 to 1.1.5 in the core group `#3494 `_ +- build: bump ansys-sphinx-theme from 1.1.2 to 1.1.6 in the core group across 1 directory `#3496 `_ +- build: bump pyansys-tools-report from 0.8.0 to 0.8.1 in the testing group `#3516 `_ +- build: bump grpcio from 1.67.0 to 1.67.1 in the grpc-deps group `#3533 `_ +- build: bump pytest-cov from 5.0.0 to 6.0.0 in the testing group `#3535 `_ +- build: bump ansys-sphinx-theme from 1.2.0 to 1.2.1 in the core group `#3547 `_ +- build: bump grpcio from 1.67.1 to 1.68.0 in the grpc-deps group `#3565 `_ +- build: bump pytest-rerunfailures from 14.0 to 15.0 in the testing group `#3567 `_ +- build: bump imageio from 2.36.0 to 2.36.1 in the documentation group `#3593 `_ +- build: bump grpcio from 1.68.0 to 1.68.1 in the grpc-deps group `#3601 `_ +- build: bump pytest from 8.3.3 to 8.3.4 in the testing group `#3603 `_ +- build: bump pyfakefs from 5.7.1 to 5.7.2 `#3604 `_ +- build: bump the core group across 1 directory with 3 updates `#3612 `_, `#3633 `_ +- ci: adding ubuntu 251 and 252 `#3626 `_ +- build: bump pyfakefs from 5.7.2 to 5.7.3 `#3630 `_ + + +Miscellaneous +^^^^^^^^^^^^^ + +- ci: [pre-commit.ci] pre-commit autoupdate `#3206 `_ +- ci: Adding v251 CentOS based image to testing `#3210 `_ +- [pre-commit.ci] pre-commit autoupdate `#3238 `_, `#3253 `_ +- feat: refactoring `create_temp_dir` `#3239 `_ +- docs: adapt static images to dark/light themes `#3249 `_ +- feat: adding 'pymapdl_nproc' to non-slurm runs `#3487 `_ +- feat: using version instead of exec_path for the MPI checks `#3528 `_ +- feat: raising error if plot image cannot be obtained `#3559 `_ +- feat: supporting v25.1 and v25.2 `#3571 `_ +- feat: adding-mode-warning `#3574 `_ +- feat: running MPI fix only if on windows `#3575 `_ +- feat: adding ``check_has_mapdl`` `#3576 `_ +- feat: improving load_array to reduce format line length `#3590 `_ +- feat: redirect MAPDL console output to a file `#3596 `_ +- feat: avoid errors when retrieving invalid routine `#3606 `_ + + +Documentation +^^^^^^^^^^^^^ + +- docs: documenting using pymapdl on clusters `#3466 `_ +- ci: avoiding linkcheck on changelog page `#3488 `_ +- feat: support for launching an MAPDL instance in an SLURM HPC cluster `#3497 `_ +- feat: passing tight integration env vars to mapdl `#3500 `_ +- docs: review of documenting using pymapdl on clusters (#3466) `#3506 `_ +- docs: adding-sbatch-support `#3513 `_ +- docs: removing extra links from landing page. `#3526 `_ +- DOC: Update pymapdl.rst `#3527 `_ +- [maint] remove importlib-metadata requirement `#3546 `_ +- docs: extracting information to another rst file `#3549 `_ +- docs: updating compatible Python versions `#3572 `_ +- docs: update docker instructions `#3580 `_ +- docs: adding some info for getting multiple compose running `#3584 `_ +- feat: update copyright year `#3637 `_ + + +Maintenance +^^^^^^^^^^^ + +- ci: bump thollander/actions-comment-pull-request from 2 to 3 in the actions group `#3481 `_ +- ci: pre-commit autoupdate `#3482 `_, `#3522 `_, `#3545 `_, `#3599 `_ +- ci: force coloring in pytest `#3484 `_ +- build: bump psutil from 6.0.0 to 6.1.0 in the minimal group `#3492 `_ +- ci: ``ansys/actions/check-vulnerabilities`` to CI-CD `#3505 `_ +- ci: bump actions/checkout from 4.2.1 to 4.2.2 in the actions group `#3521 `_ +- build: bump numpy from 2.1.2 to 2.1.3 in the minimal group `#3541 `_ +- ci: bump codecov/codecov-action from 4 to 5 in the actions group `#3557 `_ +- ci: skipping student versions when user is authenticated `#3564 `_ +- ci: adding codeql.yml `#3585 `_ +- feat: activate debug mode on testing using `PYMAPDL_DEBUG_TESTING` envvar `#3594 `_ +- build: bump numpy from 2.1.3 to 2.2.0 in the minimal group `#3619 `_ +- ci: adding student back `#3623 `_ +- ci: temporary skipping attrs license check `#3624 `_ +- build: bump the minimal group across 1 directory with 2 updates `#3632 `_ +- ci: fix safety issue `#3638 `_ + `0.68.6 `_ - 2024-10-11 ============================================================================== diff --git a/minimum_requirements.txt b/minimum_requirements.txt index 6548dd33be..fda055b768 100644 --- a/minimum_requirements.txt +++ b/minimum_requirements.txt @@ -1,5 +1,5 @@ ansys-api-mapdl==0.5.2 -numpy==2.1.3 +numpy==2.2.1 platformdirs==4.3.6 -psutil==6.1.0 +psutil==6.1.1 pyansys-tools-versioning==0.6.0 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3d3a06ed5f..1cea253558 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,18 +55,18 @@ jupyter = [ tests = [ "ansys-dpf-core==0.10.1", - "ansys-tools-visualization-interface==0.5.0", + "ansys-tools-visualization-interface==0.6.2", "autopep8==2.3.1", - "matplotlib==3.9.2", + "matplotlib==3.10.0", "pandas==2.2.3", "pyansys-tools-report==0.8.1", - "pyfakefs==5.7.2", + "pyfakefs==5.7.3", "pyiges[full]==0.3.1", "pytest-cov==6.0.0", - "pytest-memprof<0.3.0", "pytest-pyvista==0.1.9", "pytest-random-order==1.1.1", "pytest-rerunfailures==15.0", + "pytest-timeout==2.3.1", "pytest==8.3.4", "scipy==1.14.1", "vtk==9.3.1", @@ -74,8 +74,8 @@ tests = [ doc = [ "ansys-dpf-core==0.10.1", "ansys-mapdl-reader==0.54.2", - "ansys-sphinx-theme==1.2.2", - "ansys-tools-visualization-interface==0.5.0", + "ansys-sphinx-theme==1.2.4", + "ansys-tools-visualization-interface==0.6.2", "grpcio==1.68.1", "imageio-ffmpeg==0.5.1", "imageio==2.36.1", @@ -83,7 +83,7 @@ doc = [ "jupyter==1.1.1", "jupyterlab>=3.2.8", "linuxdoc==20240924", - "matplotlib==3.9.2", + "matplotlib==3.10.0", "nbformat==5.10.4", "numpydoc==1.8.0", "pandas==2.2.3", @@ -119,7 +119,7 @@ pymapdl_convert_script = "ansys.mapdl.core.cli:old_pymapdl_convert_script_entry_ pymapdl = "ansys.mapdl.core.cli:main" [tool.pytest.ini_options] -addopts = "-rxXsa -vvv --maxfail=10 --random-order-bucket=class --random-order --durations=10" +addopts = "-rxXsa -vvv --maxfail=10 --random-order-bucket=class --random-order --durations=10 --timeout=180" junit_family = "legacy" filterwarnings = [ "ignore::FutureWarning", diff --git a/src/ansys/mapdl/core/__init__.py b/src/ansys/mapdl/core/__init__.py index 3daee2418e..9d56358ad0 100644 --- a/src/ansys/mapdl/core/__init__.py +++ b/src/ansys/mapdl/core/__init__.py @@ -20,8 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import importlib.metadata as importlib_metadata - ############################################################################### # Imports # ======= @@ -40,17 +38,16 @@ # from ansys.mapdl.core.logging import Logger -LOG = Logger(level=logging.ERROR, to_file=False, to_stdout=True) +LOG: Logger = Logger(level=logging.ERROR, to_file=False, to_stdout=True) LOG.debug("Loaded logging module as LOG") ############################################################################### # Globals # ======= # +from ansys.mapdl.core._version import __version__ from ansys.mapdl.core.helpers import is_installed, run_every_import, run_first_time -__version__: str = importlib_metadata.version(__name__.replace(".", "-")) - # A dictionary relating PyMAPDL server versions with the unified install ones VERSION_MAP: Dict[Tuple[int, int, int], str] = { (0, 0, 0): "2020R2", @@ -69,17 +66,21 @@ # Import related globals _HAS_ATP: bool = is_installed("ansys.tools.path") +_HAS_CLICK: bool = is_installed("click") _HAS_PIM: bool = is_installed("ansys.platform.instancemanagement") +_HAS_PANDAS: bool = is_installed("pandas") _HAS_PYANSYS_REPORT: bool = is_installed("ansys.tools.report") _HAS_PYVISTA: bool = is_installed("pyvista") +_HAS_REQUESTS: bool = is_installed("requests") _HAS_TQDM: bool = is_installed("tqdm") _HAS_VISUALIZER: bool = is_installed("ansys.tools.visualization_interface") + # Setup directories USER_DATA_PATH: str = user_data_dir(appname="ansys_mapdl_core", appauthor="Ansys") -EXAMPLES_PATH = os.path.join(USER_DATA_PATH, "examples") +EXAMPLES_PATH: str = os.path.join(USER_DATA_PATH, "examples") -# Store local ports +# Store ports occupied by local instances _LOCAL_PORTS: List[int] = [] ############################################################################### diff --git a/src/ansys/mapdl/core/_version.py b/src/ansys/mapdl/core/_version.py index 5ea719ba27..0cb7a04bad 100644 --- a/src/ansys/mapdl/core/_version.py +++ b/src/ansys/mapdl/core/_version.py @@ -29,18 +29,15 @@ version_info = 0, 58, 'dev0' """ - -try: - import importlib.metadata as importlib_metadata -except ModuleNotFoundError: # pragma: no cover - import importlib_metadata +import importlib.metadata as importlib_metadata +from typing import Dict # Read from the pyproject.toml # major, minor, patch -__version__ = importlib_metadata.version("ansys-mapdl-core") +__version__: str = importlib_metadata.version("ansys-mapdl-core") # In descending order -SUPPORTED_ANSYS_VERSIONS = { +SUPPORTED_ANSYS_VERSIONS: Dict[int, str] = { 252: "2025R2", 251: "2025R1", 242: "2024R2", diff --git a/src/ansys/mapdl/core/cli/__init__.py b/src/ansys/mapdl/core/cli/__init__.py index 35aa06665f..040b9493c8 100644 --- a/src/ansys/mapdl/core/cli/__init__.py +++ b/src/ansys/mapdl/core/cli/__init__.py @@ -20,22 +20,16 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -try: - import click - - _HAS_CLICK = True - -except ModuleNotFoundError: - _HAS_CLICK = False - +from ansys.mapdl.core import _HAS_CLICK if _HAS_CLICK: ################################### # PyMAPDL CLI + import click @click.group(invoke_without_command=True) @click.pass_context - def main(ctx): + def main(ctx: click.Context): pass from ansys.mapdl.core.cli.convert import convert diff --git a/src/ansys/mapdl/core/cli/convert.py b/src/ansys/mapdl/core/cli/convert.py index 8fc9854d3a..0b5b9a56eb 100644 --- a/src/ansys/mapdl/core/cli/convert.py +++ b/src/ansys/mapdl/core/cli/convert.py @@ -21,13 +21,14 @@ # SOFTWARE. import os +from typing import Any, List import click -_USING_PIPE = [False] +_USING_PIPE: List[bool] = [False] -def get_input_source(ctx, param, value): +def get_input_source(ctx: click.Context, param: Any, value: Any): if not value and not click.get_text_stream("stdin").isatty(): _USING_PIPE[0] = True return click.get_text_stream("stdin").read().strip() @@ -180,7 +181,7 @@ def convert( use_vtk: bool, clear_at_start: bool, check_parameter_names: bool, -): +) -> None: """Convert MAPDL code to PyMAPDL""" from ansys.mapdl.core.convert import convert_apdl_block, convert_script diff --git a/src/ansys/mapdl/core/cli/list_instances.py b/src/ansys/mapdl/core/cli/list_instances.py index 55ca8e72f8..6256996848 100644 --- a/src/ansys/mapdl/core/cli/list_instances.py +++ b/src/ansys/mapdl/core/cli/list_instances.py @@ -65,7 +65,7 @@ default=False, help="Print running location info.", ) -def list_instances(instances, long, cmd, location): +def list_instances(instances, long, cmd, location) -> None: import psutil from tabulate import tabulate diff --git a/src/ansys/mapdl/core/cli/start.py b/src/ansys/mapdl/core/cli/start.py index 5d3c1abfb1..c34b3e7bbc 100644 --- a/src/ansys/mapdl/core/cli/start.py +++ b/src/ansys/mapdl/core/cli/start.py @@ -191,7 +191,7 @@ def start( add_env_vars: Dict[str, str], # ignored replace_env_vars: Dict[str, str], # ignored version: Union[int, str], -): +) -> None: from ansys.mapdl.core.launcher import launch_mapdl if mode: diff --git a/src/ansys/mapdl/core/cli/stop.py b/src/ansys/mapdl/core/cli/stop.py index 051a0b8e96..e49ebbff0c 100644 --- a/src/ansys/mapdl/core/cli/stop.py +++ b/src/ansys/mapdl/core/cli/stop.py @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from typing import Optional + import click @@ -49,7 +51,21 @@ default=False, help="Kill all MAPDL instances", ) -def stop(port, pid, all): +def stop(port: int, pid: Optional[int], all: bool) -> None: + """Stop MAPDL instances running on a given port or with a given process id (PID). + + This command stops MAPDL instances running on a given port or with a given process id (PID). + By default, it stops instances running on the port 50052. + + Parameters + ---------- + port : int + Port where the MAPDL instance is running. + pid : Optional[int] + PID of the MAPDL instance + all : bool + If :class:`True`, kill all the instances regardless their port or PID. + """ import psutil from ansys.mapdl.core.launcher import is_ansys_process diff --git a/src/ansys/mapdl/core/commands.py b/src/ansys/mapdl/core/commands.py index 38d740ae55..93c55b323a 100644 --- a/src/ansys/mapdl/core/commands.py +++ b/src/ansys/mapdl/core/commands.py @@ -22,9 +22,15 @@ from functools import wraps import re +from typing import Any, Callable, Dict, List, Optional, Tuple import numpy as np +from ansys.mapdl.core import _HAS_PANDAS + +if _HAS_PANDAS: + import pandas + from ._commands import ( apdl, aux2_, @@ -48,35 +54,37 @@ ) # compiled regular expressions used for parsing tablular outputs -REG_LETTERS = re.compile(r"[a-df-zA-DF-Z]+") # all except E or e -REG_FLOAT_INT = re.compile( +REG_LETTERS: re.Pattern = re.compile(r"[a-df-zA-DF-Z]+") # all except E or e +REG_FLOAT_INT: re.Pattern = re.compile( r"[+-]?[0-9]*[.]?[0-9]*[Ee]?[+-]?[0-9]+|\s[0-9]+\s" ) # match number groups -BC_REGREP = re.compile(r"^\s*([0-9]+)\s*([A-Za-z]+)((?:\s+[0-9]*[.]?[0-9]+)+)$") +BC_REGREP: re.Pattern = re.compile( + r"^\s*([0-9]+)\s*([A-Za-z]+)((?:\s+[0-9]*[.]?[0-9]+)+)$" +) -MSG_NOT_PANDAS = """'Pandas' is not installed or could not be found. +MSG_NOT_PANDAS: str = """'Pandas' is not installed or could not be found. Hence this command is not applicable. You can install it using: pip install pandas """ -MSG_BCLISTINGOUTPUT_TO_ARRAY = """This command has strings values in some of its columns (such 'UX', 'FX', 'UY', 'TEMP', etc), +MSG_BCLISTINGOUTPUT_TO_ARRAY: str = """This command has strings values in some of its columns (such 'UX', 'FX', 'UY', 'TEMP', etc), so it cannot be converted to Numpy Array. Please use 'to_list' or 'to_dataframe' instead.""" # Identify where the data start in the output -GROUP_DATA_START = ["NODE", "ELEM"] +GROUP_DATA_START: List[str] = ["NODE", "ELEM"] # Allowed commands to get output as array or dataframe. # In theory, these commands should follow the same format. # Some of them are not documented (already deprecated?) # So they are not in the Mapdl class, # so they won't be wrapped. -CMD_RESULT_LISTING = [ +CMD_RESULT_LISTING: List[str] = [ "NLIN", # not documented "PRCI", "PRDI", # Not documented. @@ -104,7 +112,7 @@ "SWLI", ] -CMD_BC_LISTING = [ +CMD_BC_LISTING: List[str] = [ "DKLI", "DLLI", "DALI", @@ -120,7 +128,7 @@ "BFAL", ] -COLNAMES_BC_LISTING = { +COLNAMES_BC_LISTING: Dict[str, List[str]] = { "DKLI": ["KEYPOINT", "LABEL", "REAL", "IMAG", "EXP KEY"], "DLLI": ["LINE", "LABEL", "REAL", "IMAG", "NAREA"], "DALI": ["AREA", "LABEL", "REAL", "IMAG"], @@ -133,7 +141,7 @@ "BFAL": ["AREA", "LABEL", "VALUE"], } -CMD_ENTITY_LISTING = [ +CMD_ENTITY_LISTING: List[str] = [ "NLIS", # "ELIS", # To be implemented later # "KLIS", @@ -142,12 +150,12 @@ # "VLIS", ] -CMD_LISTING = [] +CMD_LISTING: List[str] = [] CMD_LISTING.extend(CMD_ENTITY_LISTING) CMD_LISTING.extend(CMD_RESULT_LISTING) # Adding empty lines to match current format. -CMD_DOCSTRING_INJECTION = r""" +CMD_DOCSTRING_INJECTION: str = r""" Returns ------- @@ -166,7 +174,7 @@ """ -XSEL_DOCSTRING_INJECTION = r""" +XSEL_DOCSTRING_INJECTION: str = r""" Returns ------- @@ -178,23 +186,25 @@ """ -CMD_XSEL = [ +CMD_XSEL: List[str] = [ "NSEL", "ESEL", "KSEL", "LSEL", "ASEL", "VSEL", + "ESLN", + "NSLE", ] -def get_indentation(indentation_regx, docstring): +def get_indentation(indentation_regx: str, docstring: str) -> List[Any]: return re.findall(indentation_regx, docstring, flags=re.DOTALL | re.IGNORECASE)[0][ 0 ] -def indent_text(indentation, docstring_injection): +def indent_text(indentation: str, docstring_injection: str) -> str: return "\n".join( [ indentation + each @@ -204,18 +214,18 @@ def indent_text(indentation, docstring_injection): ) -def get_docstring_indentation(docstring): +def get_docstring_indentation(docstring: str) -> List[Any]: indentation_regx = r"\n(\s*)\n" return get_indentation(indentation_regx, docstring) -def get_sections(docstring): +def get_sections(docstring: str) -> List[str]: return [ each.strip().lower() for each in re.findall(r"\n\s*(\S*)\n\s*-+\n", docstring) ] -def get_section_indentation(section_name, docstring): +def get_section_indentation(section_name: str, docstring: str) -> List[Any]: sections = get_sections(docstring) if section_name.lower().strip() not in sections: raise ValueError( @@ -227,7 +237,9 @@ def get_section_indentation(section_name, docstring): return get_indentation(indentation_regx, docstring) -def inject_before(section, indentation, indented_doc_inject, docstring): +def inject_before( + section: str, indentation: str, indented_doc_inject: str, docstring: str +) -> str: return re.sub( section + r"\n\s*-*", f"{indented_doc_inject.strip()}\n\n{indentation}" + r"\g<0>", @@ -236,7 +248,7 @@ def inject_before(section, indentation, indented_doc_inject, docstring): ) -def inject_after_return_section(indented_doc_inject, docstring): +def inject_after_return_section(indented_doc_inject: str, docstring: str) -> str: return re.sub( "Returns" + r"\n\s*-*", f"{indented_doc_inject.strip()}\n", @@ -245,7 +257,7 @@ def inject_after_return_section(indented_doc_inject, docstring): ) -def inject_docs(docstring, docstring_injection=None): +def inject_docs(docstring: str, docstring_injection: Optional[str] = None) -> str: """Inject a string in a docstring""" if not docstring_injection: docstring_injection = CMD_DOCSTRING_INJECTION @@ -295,7 +307,7 @@ def inject_docs(docstring, docstring_injection=None): return docstring + "\n" + indented_doc_inject -def check_valid_output(func): +def check_valid_output(func: Callable) -> Callable: """Wrapper that check if output can be wrapped by pandas, if not, it will raise an exception.""" @wraps(func) @@ -512,18 +524,6 @@ class Commands( """Wrapped MAPDL commands""" -def _requires_pandas(func): - """Wrapper that check ``HAS_PANDAS``, if not, it will raise an exception.""" - - def func_wrapper(self, *args, **kwargs): - if HAS_PANDAS: - return func(self, *args, **kwargs) - else: - raise ModuleNotFoundError(MSG_NOT_PANDAS) - - return func_wrapper - - class CommandOutput(str): """Custom string subclass for handling the commands output. @@ -542,7 +542,7 @@ class CommandOutput(str): # - https://docs.python.org/3/library/collections.html#userstring-objects # - Source code of UserString - def __new__(cls, content, cmd=None): + def __new__(cls, content: str, cmd=None): obj = super().__new__(cls, content) obj._cmd = cmd return obj @@ -553,7 +553,7 @@ def cmd(self): return self._cmd.split(",")[0] @cmd.setter - def cmd(self, cmd): + def cmd(self, cmd: str): """Not allowed to change the value of ``cmd``.""" raise AttributeError("The `cmd` attribute cannot be set") @@ -584,23 +584,26 @@ class CommandListingOutput(CommandOutput): """ - def __new__(cls, content, cmd=None, magicwords=None, columns_names=None): + def __new__( + cls, + content: str, + cmd: Optional[str] = None, + magicwords: Optional[str] = None, + columns_names: Optional[List[str]] = None, + ): obj = super().__new__(cls, content) obj._cmd = cmd obj._magicwords = magicwords obj._columns_names = columns_names return obj - def __init__(self, *args, **kwargs): + def __init__(self, *args: Tuple[Any], **kwargs: Dict[Any, Any]) -> None: self._cache = None - def _is_data_start(self, line, magicwords=None): + def _is_data_start(self, line: str, magicwords: List[str] = None) -> bool: """Check if line is the start of a data group.""" if not magicwords: - if self._magicwords: - magicwords = self._magicwords - else: - magicwords = GROUP_DATA_START + magicwords = self._magicwords or GROUP_DATA_START # Checking if we are supplying a custom start function. if self.custom_data_start(line) is not None: @@ -611,7 +614,7 @@ def _is_data_start(self, line, magicwords=None): return True return False - def _is_data_end(self, line): + def _is_data_end(self, line: str) -> bool: """Check if line is the end of a data group.""" # Checking if we are supplying a custom start function. @@ -620,7 +623,7 @@ def _is_data_end(self, line): else: return self._is_empty(line) - def custom_data_start(self, line): + def custom_data_start(self, line: str) -> None: """Custom data start line check function. This function is left empty so it can be overwritten by the user. @@ -630,7 +633,7 @@ def custom_data_start(self, line): """ return None - def custom_data_end(self, line): + def custom_data_end(self, line: str) -> None: """Custom data end line check function. This function is left empty so it can be overwritten by the user. @@ -641,14 +644,14 @@ def custom_data_end(self, line): return None @staticmethod - def _is_empty_line(line): + def _is_empty_line(line: str) -> bool: return bool(line.split()) - def _format(self): + def _format(self) -> str: """Perform some formatting (replacing mainly) in the raw text.""" return re.sub(r"[^E](-)", " -", self.__str__()) - def _get_body(self, trail_header=None): + def _get_body(self, trail_header: List[str] = None) -> str: """Get command body text. It removes the maximum absolute values tail part and makes sure there is @@ -673,7 +676,9 @@ def _get_body(self, trail_header=None): body = body[:i] return body - def _get_data_group_indexes(self, body, magicwords=None): + def _get_data_group_indexes( + self, body: str, magicwords: Optional[List[str]] = None + ) -> List[Tuple[int, int]]: """Return the indexes of the start and end of the data groups.""" if "*****ANSYS VERIFICATION RUN ONLY*****" in str(self[:1000]): shift = 2 @@ -698,7 +703,7 @@ def _get_data_group_indexes(self, body, magicwords=None): return zip(start_idxs, ends) - def get_columns(self): + def get_columns(self) -> Optional[List[str]]: """Get the column names for the dataframe. Returns @@ -716,7 +721,7 @@ def get_columns(self): except: return None - def _parse_table(self): + def _parse_table(self) -> np.ndarray: """Parse tabular command output. Returns @@ -735,14 +740,14 @@ def _parse_table(self): return np.array(parsed_lines, dtype=np.float64) @property - def _parsed(self): + def _parsed(self) -> str: """Return parsed output.""" if self._cache is None: self._cache = self._parse_table() return self._cache @check_valid_output - def to_list(self): + def to_list(self) -> List[str]: """Export the command output a list or list of lists. Returns @@ -751,7 +756,7 @@ def to_list(self): """ return self._parsed.tolist() - def to_array(self): + def to_array(self) -> np.ndarray: """Export the command output as a numpy array. Returns @@ -761,7 +766,9 @@ def to_array(self): """ return self._parsed - def to_dataframe(self, data=None, columns=None): + def to_dataframe( + self, data: Optional[np.ndarray] = None, columns: Optional[List[str]] = None + ) -> "pandas.DataFrame": """Export the command output as a Pandas DataFrame. Parameters @@ -790,9 +797,9 @@ def to_dataframe(self, data=None, columns=None): (inheritate from :func:`to_array() ` method). """ - try: - import pandas as pd - except ModuleNotFoundError: + if _HAS_PANDAS: + import pandas + else: raise ModuleNotFoundError(MSG_NOT_PANDAS) if data is None: @@ -800,7 +807,7 @@ def to_dataframe(self, data=None, columns=None): if not columns: columns = self.get_columns() - return pd.DataFrame(data=data, columns=columns) + return pandas.DataFrame(data=data, columns=columns) class BoundaryConditionsListingOutput(CommandListingOutput): @@ -820,10 +827,10 @@ class BoundaryConditionsListingOutput(CommandListingOutput): """ - def bc_colnames(self): + def bc_colnames(self) -> Optional[List[str]]: """Get the column names based on bc list command""" - bc_type = { + bc_type: Dict[str, str] = { "BODY FORCES": "BF", "SURFACE LOAD": "SF", "POINT LOAD": "F", @@ -841,8 +848,8 @@ def bc_colnames(self): title = self._get_body()[0] - _bcType = [i for i in bc_type.keys() if i in title] - _entity = [i for i in entity.keys() if i in title] + _bcType = [i for i in bc_type if i in title] + _entity = [i for i in entity if i in title] if _bcType and _entity: @@ -872,7 +879,7 @@ def bc_colnames(self): return None - def get_columns(self): + def get_columns(self) -> List[str]: """Get the column names for the dataframe. Returns @@ -896,7 +903,7 @@ def get_columns(self): except: return None - def _parse_table(self): + def _parse_table(self) -> List[str]: """Parse tabular command output.""" parsed_lines = [] for line in self.splitlines(): @@ -910,7 +917,7 @@ def _parse_table(self): return parsed_lines @check_valid_output - def to_list(self): + def to_list(self) -> List[str]: """Export the command output a list or list of lists. Returns @@ -922,7 +929,7 @@ def to_list(self): def to_array(self): raise ValueError(MSG_BCLISTINGOUTPUT_TO_ARRAY) - def to_dataframe(self): + def to_dataframe(self) -> "pandas.DataFrame": """Convert the command output to a Pandas Dataframe. Returns @@ -961,7 +968,7 @@ def to_dataframe(self): class ComponentListing(CommandListingOutput): @property - def _parsed(self): + def _parsed(self) -> np.ndarray: from ansys.mapdl.core.component import _parse_cmlist # To keep same API as commands @@ -969,5 +976,5 @@ def _parsed(self): class StringWithLiteralRepr(str): - def __repr__(self): + def __repr__(self) -> str: return self.__str__() diff --git a/src/ansys/mapdl/core/common_grpc.py b/src/ansys/mapdl/core/common_grpc.py index edcb0698f1..95c9e87a90 100644 --- a/src/ansys/mapdl/core/common_grpc.py +++ b/src/ansys/mapdl/core/common_grpc.py @@ -22,17 +22,18 @@ """Common gRPC functions""" from time import sleep -from typing import List, Literal, get_args +from typing import Dict, Iterable, List, Literal, Optional, get_args +from ansys.api.mapdl.v0 import ansys_kernel_pb2 as anskernel import numpy as np from ansys.mapdl.core.errors import MapdlConnectionError, MapdlRuntimeError # chunk sizes for streaming and file streaming -DEFAULT_CHUNKSIZE = 256 * 1024 # 256 kB -DEFAULT_FILE_CHUNK_SIZE = 1024 * 1024 # 1MB +DEFAULT_CHUNKSIZE: int = 256 * 1024 # 256 kB +DEFAULT_FILE_CHUNK_SIZE: int = 1024 * 1024 # 1MB -ANSYS_VALUE_TYPE = { +ANSYS_VALUE_TYPE: Dict[int, Optional[np.typing.DTypeLike]] = { 0: None, # UNKNOWN 1: np.int32, # INTEGER 2: np.int64, # HYPER @@ -45,7 +46,7 @@ } -VGET_ENTITY_TYPES_TYPING = Literal[ +VGET_ENTITY_TYPES_TYPING: List[str] = Literal[ "NODE", "ELEM", "KP", @@ -59,9 +60,9 @@ VGET_ENTITY_TYPES: List[str] = list(get_args(VGET_ENTITY_TYPES_TYPING)) -STRESS_TYPES = ["X", "Y", "Z", "XY", "YZ", "XZ", "1", "2", "3", "INT", "EQV"] -COMP_TYPE = ["X", "Y", "Z", "SUM"] -VGET_NODE_ENTITY_TYPES = { +STRESS_TYPES: List[str] = ["X", "Y", "Z", "XY", "YZ", "XZ", "1", "2", "3", "INT", "EQV"] +COMP_TYPE: List[str] = ["X", "Y", "Z", "SUM"] +VGET_NODE_ENTITY_TYPES: Dict[str, List[str]] = { "U": ["X", "Y", "Z"], "S": STRESS_TYPES, "EPTO": STRESS_TYPES, @@ -89,7 +90,7 @@ class GrpcError(MapdlRuntimeError): """Raised when gRPC fails""" - def __init__(self, msg=""): + def __init__(self, msg: str = "") -> None: super().__init__(self, msg) @@ -168,7 +169,9 @@ def check_vget_input(entity: str, item: str, itnum: str) -> str: return "%s, , %s, %s" % (entity, item, itnum) -def parse_chunks(chunks, dtype=None): +def parse_chunks( + chunks: Iterable[anskernel.Chunk], dtype: Optional[np.typing.DTypeLike] = None +) -> np.ndarray: """Deserialize gRPC chunks into a numpy array Parameters diff --git a/src/ansys/mapdl/core/component.py b/src/ansys/mapdl/core/component.py index 2350eebe76..402f229338 100644 --- a/src/ansys/mapdl/core/component.py +++ b/src/ansys/mapdl/core/component.py @@ -25,7 +25,9 @@ from typing import ( TYPE_CHECKING, Any, + Callable, Dict, + Iterator, List, Literal, Optional, @@ -45,13 +47,13 @@ if TYPE_CHECKING: # pragma: no cover import logging -ENTITIES_TYP = Literal[ +ENTITIES_TYP: List[str] = Literal[ "NODE", "NODES", "ELEM", "ELEMS", "ELEMENTS", "VOLU", "AREA", "LINE", "KP" ] -VALID_ENTITIES = list(get_args(ENTITIES_TYP)) +VALID_ENTITIES: List[str] = list(get_args(ENTITIES_TYP)) -SELECTOR_FUNCTION = [ +SELECTOR_FUNCTION: List[str] = [ "NSEL", "NSEL", "ESEL", @@ -63,7 +65,7 @@ "KSEL", ] -ENTITIES_MAPPING = { +ENTITIES_MAPPING: Dict[str, Callable] = { entity.upper(): func for entity, func in zip(VALID_ENTITIES, SELECTOR_FUNCTION) } @@ -71,7 +73,7 @@ ITEMS_VALUES = Optional[Union[str, int, List[int], NDArray[Any]]] UNDERLYING_DICT = Dict[str, ITEMS_VALUES] -warning_entity = ( +WARNING_ENTITY: str = ( "Assuming a {default_entity} selection.\n" "It is recommended you use the following notation to avoid this warning:\n" ">>> mapdl.components['{key}'] = '{default_entity}', {value}\n" @@ -82,7 +84,7 @@ def _check_valid_pyobj_to_entities( items: Union[Tuple[int, ...], List[int], NDArray[Any]] -): +) -> None: """Check whether the python objects can be converted to entities. At the moment, only list and numpy arrays of ints are allowed. """ @@ -130,7 +132,7 @@ class Component(tuple): [1, 2, 3] """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Tuple[Any], **kwargs: Dict[Any, Any]) -> None: """Component object""" # Using tuple init because using object.__init__ # For more information visit: @@ -151,7 +153,7 @@ def __new__( return obj - def __str__(self): + def __str__(self) -> str: tup_str = super().__str__() return f"Component(type='{self._type}', items={tup_str})" @@ -165,7 +167,7 @@ def type(self) -> ENTITIES_TYP: return self._type @property - def items(self) -> tuple: + def items(self) -> Tuple[int]: """Return the ids of the entities in the component.""" return tuple(self) @@ -258,7 +260,7 @@ def __init__(self, mapdl: Mapdl) -> None: self._default_entity_warning: bool = True @property - def default_entity(self): + def default_entity(self) -> ENTITIES_TYP: """Default entity for component creation.""" return self._default_entity @@ -271,12 +273,12 @@ def default_entity(self, value: ENTITIES_TYP): self._default_entity = value.upper() @property - def default_entity_warning(self): + def default_entity_warning(self) -> bool: """Enables the warning when creating components other than node components without specifying its type.""" return self._default_entity_warning @default_entity_warning.setter - def default_entity_warning(self, value: bool): + def default_entity_warning(self, value: bool) -> None: self._default_entity_warning = value @property @@ -301,7 +303,7 @@ def _comp(self) -> UNDERLYING_DICT: return self.__comp @_comp.setter - def _comp(self, value): + def _comp(self, value) -> None: self.__comp = value def __getitem__(self, key: str) -> ITEMS_VALUES: @@ -371,7 +373,7 @@ def __setitem__(self, key: str, value: ITEMS_VALUES) -> None: # Asumng default entity if self.default_entity_warning: warnings.warn( - warning_entity.format( + WARNING_ENTITY.format( default_entity=self.default_entity, key=key, value=value, @@ -401,7 +403,7 @@ def __setitem__(self, key: str, value: ITEMS_VALUES) -> None: # Assuming we are defining a CM with nodes if self.default_entity_warning: warnings.warn( - warning_entity.format( + WARNING_ENTITY.format( default_entity=self.default_entity, key=key, value=value, @@ -457,7 +459,7 @@ def __contains__(self, key: str) -> bool: """ return key.upper() in self._comp.keys() - def __iter__(self): + def __iter__(self) -> Iterator: """ Return an iterator over the component names. @@ -470,7 +472,7 @@ def __iter__(self): yield from self._comp.keys() @property - def names(self): + def names(self) -> Tuple[str]: """ Return a tuple that contains the components. @@ -483,7 +485,7 @@ def names(self): return tuple(self._comp.keys()) @property - def types(self): + def types(self) -> Tuple[str]: """ Return the types of the components. @@ -495,7 +497,7 @@ def types(self): """ return tuple(self._comp.values()) - def items(self): + def items(self) -> UNDERLYING_DICT: """ Return a view object that contains the name-type pairs for each component. diff --git a/src/ansys/mapdl/core/convert.py b/src/ansys/mapdl/core/convert.py index 4df406395e..8784c0f745 100644 --- a/src/ansys/mapdl/core/convert.py +++ b/src/ansys/mapdl/core/convert.py @@ -23,6 +23,7 @@ from logging import Logger, StreamHandler import os import re +from typing import Any, Callable, Dict, List, Optional, Tuple from warnings import warn from ansys.mapdl.core import __version__ @@ -32,31 +33,31 @@ # Because the APDL version has empty arguments, whereas the PyMAPDL # doesn't have them. Hence the order of arguments is messed up. -FORMAT_OPTIONS = { +FORMAT_OPTIONS: Dict[str, Any] = { "select": "W191,W291,W293,W391,E115,E117,E122,E124,E125,E225,E231,E301,E303,F401,F403", "max-line-length": 100, } -LOGLEVEL_DEFAULT = "WARNING" -AUTO_EXIT_DEFAULT = True -LINE_ENDING_DEFAULT = None -EXEC_FILE_DEFAULT = None -MACROS_AS_FUNCTIONS_DEFAULT = True -USE_FUNCTION_NAMES_DEFAULT = True -SHOW_LOG_DEFAULT = False -ADD_IMPORTS_DEFAULT = True -COMMENT_SOLVE_DEFAULT = False -CLEANUP_OUTPUT_DEFAULT = True -HEADER_DEFAULT = True -PRINT_COM_DEFAULT = True -ONLY_COMMANDS_DEFAULT = False -USE_VTK_DEFAULT = None -CLEAR_AT_START_DEFAULT = False -CHECK_PARAMETER_NAMES_DEFAULT = True +LOGLEVEL_DEFAULT: StreamHandler = "WARNING" +AUTO_EXIT_DEFAULT: bool = True +LINE_ENDING_DEFAULT: Optional[str] = None +EXEC_FILE_DEFAULT: Optional[str] = None +MACROS_AS_FUNCTIONS_DEFAULT: bool = True +USE_FUNCTION_NAMES_DEFAULT: bool = True +SHOW_LOG_DEFAULT: bool = False +ADD_IMPORTS_DEFAULT: bool = True +COMMENT_SOLVE_DEFAULT: bool = False +CLEANUP_OUTPUT_DEFAULT: bool = True +HEADER_DEFAULT: bool = True +PRINT_COM_DEFAULT: bool = True +ONLY_COMMANDS_DEFAULT: bool = False +USE_VTK_DEFAULT: Optional[bool] = None +CLEAR_AT_START_DEFAULT: bool = False +CHECK_PARAMETER_NAMES_DEFAULT: bool = True # This commands have "--" as one or some arguments -COMMANDS_WITH_EMPTY_ARGS = { +COMMANDS_WITH_EMPTY_ARGS: Dict[str, Tuple[Any]] = { "/CMA": (), # "/CMAP, "/NER": (), # "/NERR, "/PBF": (), # "/PBF, @@ -114,37 +115,37 @@ } -COMMANDS_TO_NOT_BE_CONVERTED = [ +COMMANDS_TO_NOT_BE_CONVERTED: List[str] = [ "CMPL", # CMPLOT default behaviour does not match the `mapdl.cmplot`'s at the moemnt # CDREAD # commented above ] -FORCED_MAPPING = { +FORCED_MAPPING: Dict[str, str] = { # Forced mapping between MAPDL and PyMAPDL "SECT": "sectype", # Because it is shadowed by `sectinqr` } def convert_script( - filename_in, - filename_out=None, - loglevel="WARNING", - auto_exit=True, - line_ending=None, - exec_file=None, - macros_as_functions=True, - use_function_names=True, - show_log=False, - add_imports=True, - comment_solve=False, - cleanup_output=True, - header=True, - print_com=True, - only_commands=False, - use_vtk=None, - clear_at_start=False, - check_parameter_names=True, -): + filename_in: str, + filename_out: Optional[str] = None, + loglevel: str = "WARNING", + auto_exit: bool = True, + line_ending: Optional[str] = None, + exec_file: Optional[str] = None, + macros_as_functions: bool = True, + use_function_names: bool = True, + show_log: bool = False, + add_imports: bool = True, + comment_solve: bool = False, + cleanup_output: bool = True, + header: bool = True, + print_com: bool = True, + only_commands: bool = False, + use_vtk: Optional[bool] = None, + clear_at_start: bool = False, + check_parameter_names: bool = True, +) -> List[str]: """Converts an ANSYS input file to a python PyMAPDL script. Parameters @@ -287,24 +288,24 @@ def convert_script( def convert_apdl_block( - apdl_strings, - loglevel="WARNING", - auto_exit=True, - line_ending=None, - exec_file=None, - macros_as_functions=True, - use_function_names=True, - show_log=False, - add_imports=True, - comment_solve=False, - cleanup_output=True, - header=True, - print_com=True, - only_commands=False, - use_vtk=None, - clear_at_start=False, - check_parameter_names=False, -): + apdl_strings: str, + loglevel: str = "WARNING", + auto_exit: bool = True, + line_ending: Optional[str] = None, + exec_file: Optional[str] = None, + macros_as_functions: bool = True, + use_function_names: bool = True, + show_log: bool = False, + add_imports: bool = True, + comment_solve: bool = False, + cleanup_output: bool = True, + header: bool = True, + print_com: bool = True, + only_commands: bool = False, + use_vtk: Optional[bool] = None, + clear_at_start: bool = False, + check_parameter_names: bool = False, +) -> List[str]: """Converts an ANSYS input string to a python PyMAPDL string. Parameters @@ -428,67 +429,6 @@ def convert_apdl_block( return translator.lines -def _convert( - apdl_strings, - loglevel="WARNING", - auto_exit=True, - line_ending=None, - exec_file=None, - macros_as_functions=True, - use_function_names=True, - show_log=False, - add_imports=True, - comment_solve=False, - cleanup_output=True, - header=True, - print_com=True, - only_commands=False, - use_vtk=None, - clear_at_start=False, - check_parameter_names=True, -): - if only_commands: - auto_exit = False - add_imports = False - header = False - - translator = FileTranslator( - loglevel, - line_ending, - exec_file=exec_file, - macros_as_functions=macros_as_functions, - use_function_names=use_function_names, - show_log=show_log, - add_imports=add_imports, - comment_solve=comment_solve, - cleanup_output=cleanup_output, - header=header, - print_com=print_com, - use_vtk=use_vtk, - clear_at_start=clear_at_start, - check_parameter_names=check_parameter_names, - ) - - if isinstance(apdl_strings, str): - # os.linesep does not work well, so we are making sure - # the line separation is appropriate. - regx = f"[^\\r]({translator.line_ending})" - if not re.search(regx, apdl_strings): - if "\r\n" in apdl_strings: - translator.line_ending = "\r\n" - elif "\n" in apdl_strings: - translator.line_ending = "\n" - - apdl_strings = apdl_strings.split(translator.line_ending) - - for line in apdl_strings: - translator.translate_line(line) - - if auto_exit and add_imports: - translator.write_exit() - return translator - - class Lines(list): def __init__(self, mute): self._log = Logger("convert_logger") @@ -496,14 +436,14 @@ def __init__(self, mute): self._mute = mute super().__init__() - def append(self, item, mute=True): + def append(self, item: Any, mute: bool = True) -> None: # append the item to itself (the list) if not self._mute and item: stripped_msg = item.replace("\n", "\\n") self._log.info(msg=f"Converted: '{stripped_msg}'") super(Lines, self).append(item) - def _setup_logger(self): + def _setup_logger(self) -> None: stdhdl = StreamHandler() stdhdl.setLevel(10) stdhdl.set_name("stdout") @@ -518,21 +458,21 @@ class FileTranslator: def __init__( self, - loglevel="INFO", - line_ending=None, - exec_file=None, - macros_as_functions=True, - use_function_names=True, - show_log=False, - add_imports=True, - comment_solve=False, - cleanup_output=True, - header=True, - print_com=True, - use_vtk=None, - clear_at_start=False, - check_parameter_names=False, - ): + loglevel: str = "INFO", + line_ending: Optional[str] = None, + exec_file: Optional[str] = None, + macros_as_functions: bool = True, + use_function_names: bool = True, + show_log: bool = False, + add_imports: bool = True, + comment_solve: bool = False, + cleanup_output: bool = True, + header: bool = True, + print_com: bool = True, + use_vtk: Optional[bool] = None, + clear_at_start: bool = False, + check_parameter_names: bool = False, + ) -> None: self._non_interactive_level = 0 self.lines = Lines(mute=not show_log) self._functions = [] @@ -591,7 +531,7 @@ def __init__( self._in_block = False self._block_current_cmd = None - def write_header(self): + def write_header(self) -> None: if isinstance(self._header, bool): if self._header: header = f'"""Script generated by ansys-mapdl-core version {__version__}"""\n' @@ -604,10 +544,10 @@ def write_header(self): "The keyword argument 'header' should be a string or a boolean." ) - def write_exit(self): + def write_exit(self) -> None: self.lines.append(f"\n{self.obj_name}.exit()") - def format_using_autopep8(self, text=None): + def format_using_autopep8(self, text: str = None) -> str: """Format internal `self.lines` with autopep8. Parameters @@ -634,7 +574,7 @@ def format_using_autopep8(self, text=None): # for development purposes return autopep8.fix_code(text) - def save(self, filename, format_autopep8=True): + def save(self, filename, format_autopep8: bool = True) -> None: """Saves lines to file""" if os.path.isfile(filename): os.remove(filename) @@ -649,7 +589,7 @@ def save(self, filename, format_autopep8=True): with open(filename, "w") as f: f.write(self.line_ending.join(self.lines)) - def initialize_mapdl_object(self, loglevel, exec_file): + def initialize_mapdl_object(self, loglevel: str, exec_file: str) -> None: """Initializes ansys object as lines""" core_module = "ansys.mapdl.core" # shouldn't change self.lines.append(f"from {core_module} import launch_mapdl") @@ -675,16 +615,16 @@ def initialize_mapdl_object(self, loglevel, exec_file): self.lines.append(f"{self.obj_name}.clear() # Clearing session") @property - def line_ending(self): + def line_ending(self) -> str: return self._line_ending @line_ending.setter - def line_ending(self, line_ending): + def line_ending(self, line_ending: str) -> None: if line_ending not in ["\n", "\r\n"]: raise ValueError('Line ending must be either "\\n", "\\r\\n"') self._line_ending = line_ending - def translate_line(self, line): + def translate_line(self, line: str) -> Optional[str]: """Converts a single line from an ANSYS APDL script""" if "$" in line: @@ -1001,12 +941,12 @@ def translate_line(self, line): else: self.store_run_command(line.strip()) - def _pymapdl_command(self, command): + def _pymapdl_command(self, command: str) -> str: if command[0] in ["/", "*"]: command = command[1:] return command - def start_function(self, func_name): + def start_function(self, func_name: str) -> None: self._functions.append(func_name) self.store_empty_line() self.store_empty_line() @@ -1029,10 +969,10 @@ def start_function(self, func_name): self.lines.append(line) self.indent = self.indent + " " - def store_under_scored_run_command(self, command): + def store_under_scored_run_command(self, command: str) -> None: self.store_run_command(command, run_underscored=True) - def store_run_command(self, command, run_underscored=False): + def store_run_command(self, command: str, run_underscored: bool = False) -> None: """Stores pyansys.ANSYS command that cannot be broken down into a function and parameters. """ @@ -1078,20 +1018,20 @@ def store_run_command(self, command, run_underscored=False): ) self.lines.append(line) - def store_comment(self): + def store_comment(self) -> None: """Stores a line containing only a comment""" line = f"{self.indent}# {self.comment}" self.lines.append(line) - def store_empty_line(self): + def store_empty_line(self) -> None: """Stores an empty line""" self.lines.append("") - def store_python_command(self, command): + def store_python_command(self, command: str) -> None: line = f"{self.indent}{command}" self.lines.append(line) - def _parse_arguments(self, parameters): + def _parse_arguments(self, parameters: List[str]) -> str: parsed_parameters = [] for parameter in parameters: parameter = parameter.strip() @@ -1109,7 +1049,7 @@ def _parse_arguments(self, parameters): return ", ".join(parsed_parameters) - def store_command(self, function, parameters): + def store_command(self, function: Callable, parameters: List[str]) -> None: """Stores a valid pyansys function with parameters""" parameter_str = self._parse_arguments(parameters) @@ -1131,7 +1071,7 @@ def store_command(self, function, parameters): self.lines.append(line) - def start_non_interactive(self): + def start_non_interactive(self) -> None: self._non_interactive_level += 1 if self.non_interactive: return @@ -1140,13 +1080,13 @@ def start_non_interactive(self): self.non_interactive = True self.indent = self.indent + " " - def end_non_interactive(self): + def end_non_interactive(self) -> None: self._non_interactive_level -= 1 if self._non_interactive_level <= 0: self.indent = self.indent[4:] self.non_interactive = False - def start_chained_commands(self): + def start_chained_commands(self) -> None: self._chained_commands += 1 if self.chained_commands: return @@ -1155,13 +1095,13 @@ def start_chained_commands(self): self.chained_commands = True self.indent = self.indent + " " - def end_chained_commands(self): + def end_chained_commands(self) -> None: self._chained_commands -= 1 if self._chained_commands <= 0: self.indent = self.indent[4:] self.chained_commands = False - def output_to_file(self, line): + def output_to_file(self, line: str) -> bool: """Return if an APDL line is redirecting to a file.""" if line[:4].upper() == "/OUT": # We are redirecting the output to somewhere, probably a file. @@ -1182,7 +1122,7 @@ def output_to_file(self, line): return False - def output_to_default(self, line): + def output_to_default(self, line: str) -> bool: if line[:4].upper() == "/OUT": # We are redirecting the output to somewhere, probably a file. # Because of the problem with the ansys output, we need to execute @@ -1201,7 +1141,7 @@ def output_to_default(self, line): return False - def _get_items(self, line_): + def _get_items(self, line_: str) -> List[str]: """Parse the line items (comma separated elements) but ignoring the ones inside parenthesis, or brackets""" parenthesis_count = 0 @@ -1225,7 +1165,7 @@ def _get_items(self, line_): return items - def _get_valid_pymapdl_methods_short(self): + def _get_valid_pymapdl_methods_short(self) -> List[str]: pymethods = dir(Commands) reduced_list = [] @@ -1240,9 +1180,70 @@ def _get_valid_pymapdl_methods_short(self): reduced_list.append(each_method[:4]) return reduced_list - def find_match(self, cmd): + def find_match(self, cmd: str) -> str: pymethods = sorted(dir(Commands)) for each in pymethods: if each.startswith(cmd): return each + + +def _convert( + apdl_strings: str, + loglevel: str = "WARNING", + auto_exit: bool = True, + line_ending: Optional[str] = None, + exec_file: Optional[str] = None, + macros_as_functions: bool = True, + use_function_names: bool = True, + show_log: bool = False, + add_imports: bool = True, + comment_solve: bool = False, + cleanup_output: bool = True, + header: bool = True, + print_com: bool = True, + only_commands: bool = False, + use_vtk: Optional[bool] = None, + clear_at_start: bool = False, + check_parameter_names: bool = False, +) -> FileTranslator: + if only_commands: + auto_exit = False + add_imports = False + header = False + + translator = FileTranslator( + loglevel, + line_ending, + exec_file=exec_file, + macros_as_functions=macros_as_functions, + use_function_names=use_function_names, + show_log=show_log, + add_imports=add_imports, + comment_solve=comment_solve, + cleanup_output=cleanup_output, + header=header, + print_com=print_com, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, + ) + + if isinstance(apdl_strings, str): + # os.linesep does not work well, so we are making sure + # the line separation is appropriate. + regx = f"[^\\r]({translator.line_ending})" + if not re.search(regx, apdl_strings): + if "\r\n" in apdl_strings: + translator.line_ending = "\r\n" + elif "\n" in apdl_strings: + translator.line_ending = "\n" + + apdl_strings = apdl_strings.split(translator.line_ending) + + for line in apdl_strings: + translator.translate_line(line) + + if auto_exit and add_imports: + translator.write_exit() + return translator diff --git a/src/ansys/mapdl/core/database/database.py b/src/ansys/mapdl/core/database/database.py index 3e395a6297..d35bf5c94d 100644 --- a/src/ansys/mapdl/core/database/database.py +++ b/src/ansys/mapdl/core/database/database.py @@ -313,8 +313,9 @@ def stop(self): self._mapdl._log.debug("Closing the connection with the MAPDL DB Server") self._stop() - self._channel.close() - self._channel = None + if self._channel: + self._channel.close() + self._channel = None self._stub = None self._state = None diff --git a/src/ansys/mapdl/core/errors.py b/src/ansys/mapdl/core/errors.py index b4f1fcb8b2..a9723e5115 100644 --- a/src/ansys/mapdl/core/errors.py +++ b/src/ansys/mapdl/core/errors.py @@ -25,16 +25,16 @@ import signal import threading from time import sleep -from typing import Callable, Optional +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple import grpc from ansys.mapdl.core import LOG as logger -SIGINT_TRACKER = [] +SIGINT_TRACKER: List = [] -LOCKFILE_MSG = """ +LOCKFILE_MSG: str = """ Another ANSYS job with the same job name is already running in this directory, or the lock file has not been deleted from an abnormally terminated ANSYS run. @@ -44,7 +44,7 @@ """ -TYPE_MSG = ( +TYPE_MSG: str = ( "Invalid datatype. Must be one of the following:\n" + "np.int32, np.int64, or np.double" ) @@ -286,7 +286,7 @@ def handler(sig, frame): # pragma: no cover SIGINT_TRACKER.append(True) -def protect_grpc(func): +def protect_grpc(func: Callable) -> Callable: """Capture gRPC exceptions and return a more succinct error message Capture KeyboardInterrupt to avoid segfaulting MAPDL. @@ -390,7 +390,7 @@ def wrapper(*args, **kwargs): return wrapper -def retrieve_mapdl_from_args(args): +def retrieve_mapdl_from_args(args: Iterable[Any]) -> "Mapdl": # can't use isinstance here due to circular imports try: class_name = args[0].__class__.__name__ @@ -405,7 +405,14 @@ def retrieve_mapdl_from_args(args): return mapdl -def handle_generic_grpc_error(error, func, args, kwargs, reason="", suggestion=""): +def handle_generic_grpc_error( + error: Exception, + func: Callable, + args: Tuple[Any], + kwargs: Dict[Any, Any], + reason: str = "", + suggestion: str = "", +): """Handle non-custom gRPC errors""" mapdl = retrieve_mapdl_from_args(args) diff --git a/src/ansys/mapdl/core/examples/downloads.py b/src/ansys/mapdl/core/examples/downloads.py index 6112d97f1c..dd4c8a02d8 100644 --- a/src/ansys/mapdl/core/examples/downloads.py +++ b/src/ansys/mapdl/core/examples/downloads.py @@ -25,20 +25,17 @@ from functools import wraps import os import shutil +from typing import Callable, Dict, Optional import zipfile -try: - import requests - - _HAS_REQUESTS = True - -except ModuleNotFoundError: - _HAS_REQUESTS = False - from ansys.mapdl import core as pymapdl +from ansys.mapdl.core import _HAS_REQUESTS + +if _HAS_REQUESTS: + import requests -def check_directory_exist(directory): +def check_directory_exist(directory: str) -> Callable: # Wrapping LISTING FUNCTIONS. def wrap_function(func): @wraps(func) @@ -54,13 +51,13 @@ def inner_wrapper(*args, **kwargs): return wrap_function -def get_ext(filename): +def get_ext(filename: str) -> str: """Extract the extension of the filename""" ext = os.path.splitext(filename)[1].lower() return ext -def delete_downloads(): +def delete_downloads() -> bool: """Delete all downloaded examples to free space or update the files""" if os.path.exists(pymapdl.EXAMPLES_PATH): shutil.rmtree(pymapdl.EXAMPLES_PATH) @@ -68,13 +65,13 @@ def delete_downloads(): @check_directory_exist(pymapdl.EXAMPLES_PATH) -def _decompress(filename): +def _decompress(filename: str) -> None: zip_ref = zipfile.ZipFile(filename, "r") zip_ref.extractall(pymapdl.EXAMPLES_PATH) return zip_ref.close() -def _get_file_url(filename, directory=None): +def _get_file_url(filename: str, directory: Optional[str] = None) -> str: if directory: return ( f"https://github.com/ansys/example-data/raw/master/{directory}/{filename}" @@ -82,14 +79,14 @@ def _get_file_url(filename, directory=None): return f"https://github.com/ansys/example-data/raw/master/{filename}" -def _check_url_exist(url): +def _check_url_exist(url: str) -> bool: response = requests.get(url, timeout=10) # 10 seconds timeout return response.status_code == 200 @check_directory_exist(pymapdl.EXAMPLES_PATH) -def _retrieve_file(url, filename, _test=False): - # scape test +def _retrieve_file(url: str, filename: str, _test: bool = False) -> str: + # escape test if pymapdl.RUNNING_TESTS: return _check_url_exist(url) @@ -112,7 +109,9 @@ def _retrieve_file(url, filename, _test=False): return local_path -def _download_file(filename, directory=None, _test=False): +def _download_file( + filename: str, directory: Optional[str] = None, _test: Optional[bool] = False +) -> str: url = _get_file_url(filename, directory) try: return _retrieve_file(url, filename, _test) @@ -127,7 +126,7 @@ def _download_file(filename, directory=None, _test=False): ) -def download_bracket(): +def download_bracket() -> str: """Download an IGS bracket. Examples @@ -141,27 +140,27 @@ def download_bracket(): return _download_file("bracket.iges", "geometry") -def download_tech_demo_data(example, filename): +def download_tech_demo_data(example: str, filename: str) -> str: """Download Tech Demos external data.""" example = "tech_demos/" + example return _download_file(filename=filename, directory=example) -def download_vtk_rotor(): +def download_vtk_rotor() -> str: """Download rotor vtk file.""" return _download_file("rotor.vtk", "geometry") -def _download_rotor_tech_demo_vtk(): +def _download_rotor_tech_demo_vtk() -> str: """Download the rotor surface VTK file.""" return _download_file("rotor2.vtk", "geometry") -def download_example_data(filename, directory=None): +def download_example_data(filename: str, directory: Optional[str] = None) -> str: return _download_file(filename, directory=directory) -def download_manifold_example_data() -> dict: +def download_manifold_example_data() -> Dict[str, str]: """Download the manifold example data and return the download paths into a dictionary domain id->path. Examples files are downloaded to a persistent cache to avoid @@ -193,7 +192,7 @@ def download_manifold_example_data() -> dict: } -def download_cfx_mapping_example_data() -> dict: +def download_cfx_mapping_example_data() -> Dict[str, str]: """Download the CFX mapping data and return the download paths into a dictionary domain id->path. Examples files are downloaded to a persistent cache to avoid diff --git a/src/ansys/mapdl/core/examples/examples.py b/src/ansys/mapdl/core/examples/examples.py index 339f4d1bde..5f0489dd9a 100644 --- a/src/ansys/mapdl/core/examples/examples.py +++ b/src/ansys/mapdl/core/examples/examples.py @@ -23,20 +23,13 @@ """pymapdl examples""" import os -from ansys.mapdl.core.plotting.theme import PyMAPDL_cmap - # get location of this folder and the example files -dir_path = os.path.dirname(os.path.realpath(__file__)) +dir_path: str = os.path.dirname(os.path.realpath(__file__)) # add any files you'd like to import here. For example: -wing_model = os.path.join(dir_path, "wing.dat") +wing_model: str = os.path.join(dir_path, "wing.dat") # be sure to add the input file directly in this directory # This way, files can be loaded with: # from ansys.mapdl.core import examples # examples.wing_model - - -def ansys_colormap(): - """Return the default ansys color map made of 9 colours (blue-green-red).""" - return PyMAPDL_cmap diff --git a/src/ansys/mapdl/core/examples/verif_files.py b/src/ansys/mapdl/core/examples/verif_files.py index b684c59c74..a34ca23d74 100755 --- a/src/ansys/mapdl/core/examples/verif_files.py +++ b/src/ansys/mapdl/core/examples/verif_files.py @@ -25,14 +25,18 @@ import glob import inspect import os +from typing import Dict -module_path = os.path.dirname(inspect.getfile(inspect.currentframe())) +module_path: str = os.path.dirname(inspect.getfile(inspect.currentframe())) -def load_vmfiles(): +def load_vmfiles() -> Dict[str, str]: """load vmfiles and store their filenames""" vmfiles = {} verif_path = os.path.join(module_path, "verif") + if not os.path.exists(verif_path): + return {} + for filename in glob.glob(os.path.join(verif_path, "*dat")): basename = os.path.basename(filename) vmname = os.path.splitext(basename)[0] @@ -42,7 +46,4 @@ def load_vmfiles(): # save the module from failing if the verification files are unavailable. -try: - vmfiles = load_vmfiles() -except: - vmfiles = [] +vmfiles: Dict[str, str] = load_vmfiles() diff --git a/src/ansys/mapdl/core/information.py b/src/ansys/mapdl/core/information.py index c6120bfacd..35a6ba792b 100644 --- a/src/ansys/mapdl/core/information.py +++ b/src/ansys/mapdl/core/information.py @@ -22,12 +22,14 @@ from functools import wraps import re +from typing import Callable, List, Optional import weakref from ansys.mapdl import core as pymapdl +from ansys.mapdl.core.errors import MapdlExitedError -def update_information_first(update=False): +def update_information_first(update: bool = False) -> Callable: """ Decorator to wrap :class:`Information ` methods to force update the fields when accessed. @@ -85,7 +87,7 @@ class Information: """ - def __init__(self, mapdl): + def __init__(self, mapdl: "Mapdl") -> None: """Class Initializer""" from ansys.mapdl.core.mapdl import MapdlBase # lazy import to avoid circular @@ -101,11 +103,11 @@ def __init__(self, mapdl): } @property - def _mapdl(self): + def _mapdl(self) -> "Mapdl": """Return the weakly referenced MAPDL instance.""" return self._mapdl_weakref() - def _update(self): + def _update(self) -> None: """We might need to do more calls if we implement properties that change over the MAPDL session.""" try: @@ -121,7 +123,10 @@ def _update(self): self._stats = stats self._mapdl._log.debug("Information class: Updated") - def __repr__(self): + def __repr__(self) -> str: + if self._mapdl.is_console and self._mapdl.exited: + return "MAPDL exited" + if not self._stats: self._update() @@ -134,92 +139,92 @@ def __repr__(self): @property @update_information_first(False) - def product(self): + def product(self) -> str: """Retrieve the product from the MAPDL instance.""" return self._get_product() @property @update_information_first(False) - def mapdl_version(self): + def mapdl_version(self) -> str: """Retrieve the MAPDL version from the MAPDL instance.""" return self._get_mapdl_version() @property @update_information_first(False) - def mapdl_version_release(self): + def mapdl_version_release(self) -> str: """Retrieve the MAPDL version release from the MAPDL instance.""" st = self._get_mapdl_version() return self._get_between("RELEASE", "BUILD", st).strip() @property @update_information_first(False) - def mapdl_version_build(self): + def mapdl_version_build(self) -> str: """Retrieve the MAPDL version build from the MAPDL instance.""" st = self._get_mapdl_version() return self._get_between("BUILD", "UPDATE", st).strip() @property @update_information_first(False) - def mapdl_version_update(self): + def mapdl_version_update(self) -> str: """Retrieve the MAPDL version update from the MAPDL instance.""" st = self._get_mapdl_version() return self._get_between("UPDATE", "", st).strip() @property @update_information_first(False) - def pymapdl_version(self): + def pymapdl_version(self) -> str: """Retrieve the PyMAPDL version from the MAPDL instance.""" return self._get_pymapdl_version() @property @update_information_first(False) - def products(self): + def products(self) -> str: """Retrieve the products from the MAPDL instance.""" return self._get_products() @property @update_information_first(False) - def preprocessing_capabilities(self): + def preprocessing_capabilities(self) -> str: """Retrieve the preprocessing capabilities from the MAPDL instance.""" return self._get_preprocessing_capabilities() @property @update_information_first(False) - def aux_capabilities(self): + def aux_capabilities(self) -> str: """Retrieve the aux capabilities from the MAPDL instance.""" return self._get_aux_capabilities() @property @update_information_first(True) - def solution_options(self): + def solution_options(self) -> str: """Retrieve the solution options from the MAPDL instance.""" return self._get_solution_options() @property @update_information_first(False) - def post_capabilities(self): + def post_capabilities(self) -> str: """Retrieve the post capabilities from the MAPDL instance.""" return self._get_post_capabilities() @property @update_information_first(True) - def titles(self): + def titles(self) -> str: """Retrieve the titles from the MAPDL instance.""" return self._get_titles() @property @update_information_first(True) - def title(self): + def title(self) -> str: """Retrieve and set the title from the MAPDL instance.""" return self._mapdl.inquire("", "title") @title.setter - def title(self, title): + def title(self, title) -> str: return self._mapdl.run(f"/TITLE, {title}") @property @update_information_first(True) - def stitles(self, i=None): + def stitles(self, i: int = None) -> str: """Retrieve or set the value for the MAPDL stitle (subtitles). If 'stitle' includes newline characters (`\\n`), then each line @@ -237,7 +242,7 @@ def stitles(self, i=None): return self._get_stitles()[i] @stitles.setter - def stitles(self, stitle, i=None): + def stitles(self, stitle: str, i: int = None) -> None: if stitle is None: # Case to empty stitle = ["", "", "", ""] @@ -264,71 +269,76 @@ def stitles(self, stitle, i=None): @property @update_information_first(True) - def units(self): + def units(self) -> str: """Retrieve the units from the MAPDL instance.""" return self._get_units() @property @update_information_first(True) - def scratch_memory_status(self): + def scratch_memory_status(self) -> str: """Retrieve the scratch memory status from the MAPDL instance.""" return self._get_scratch_memory_status() @property @update_information_first(True) - def database_status(self): + def database_status(self) -> str: """Retrieve the database status from the MAPDL instance.""" return self._get_database_status() @property @update_information_first(True) - def config_values(self): + def config_values(self) -> str: """Retrieve the config values from the MAPDL instance.""" return self._get_config_values() @property @update_information_first(True) - def global_status(self): + def global_status(self) -> str: """Retrieve the global status from the MAPDL instance.""" return self._get_global_status() @property @update_information_first(True) - def job_information(self): + def job_information(self) -> str: """Retrieve the job information from the MAPDL instance.""" return self._get_job_information() @property @update_information_first(True) - def model_information(self): + def model_information(self) -> str: """Retrieve the model information from the MAPDL instance.""" return self._get_model_information() @property @update_information_first(True) - def boundary_condition_information(self): + def boundary_condition_information(self) -> str: """Retrieve the boundary condition information from the MAPDL instance.""" return self._get_boundary_condition_information() @property @update_information_first(True) - def routine_information(self): + def routine_information(self) -> str: """Retrieve the routine information from the MAPDL instance.""" return self._get_routine_information() @property @update_information_first(True) - def solution_options_configuration(self): + def solution_options_configuration(self) -> str: """Retrieve the solution options configuration from the MAPDL instance.""" return self._get_solution_options_configuration() @property @update_information_first(True) - def load_step_options(self): + def load_step_options(self) -> str: """Retrieve the load step options from the MAPDL instance.""" return self._get_load_step_options() - def _get_between(self, init_string, end_string=None, string=None): + def _get_between( + self, + init_string: str, + end_string: Optional[str] = None, + string: Optional[str] = None, + ) -> str: if not string: self._update() string = self._stats @@ -341,24 +351,24 @@ def _get_between(self, init_string, end_string=None, string=None): en = string.find(end_string) return "\n".join(string[st:en].splitlines()).strip() - def _get_product(self): + def _get_product(self) -> str: return self._get_products().splitlines()[0] - def _get_mapdl_version(self): + def _get_mapdl_version(self) -> str: titles_ = self._get_titles() st = titles_.find("RELEASE") en = titles_.find("INITIAL", st) return titles_[st:en].split("CUSTOMER")[0].strip() - def _get_pymapdl_version(self): + def _get_pymapdl_version(self) -> str: return pymapdl.__version__ - def _get_title(self): + def _get_title(self) -> str: match = re.match(r"TITLE=(.*)$", self._get_titles()) if match: return match.groups(1)[0].strip() - def _get_stitles(self): + def _get_stitles(self) -> List[str]: return [ ( re.search(f"SUBTITLE {i}=(.*)", self._get_titles()) @@ -370,87 +380,87 @@ def _get_stitles(self): for i in range(1, 5) ] - def _get_products(self): + def _get_products(self) -> str: init_ = "*** Products ***" end_string = "*** PreProcessing Capabilities ***" return self._get_between(init_, end_string) - def _get_preprocessing_capabilities(self): + def _get_preprocessing_capabilities(self) -> str: init_ = "*** PreProcessing Capabilities ***" end_string = "*** Aux Capabilities ***" return self._get_between(init_, end_string) - def _get_aux_capabilities(self): + def _get_aux_capabilities(self) -> str: init_ = "*** Aux Capabilities ***" end_string = "*** Solution Options ***" return self._get_between(init_, end_string) - def _get_solution_options(self): + def _get_solution_options(self) -> str: init_ = "*** Solution Options ***" end_string = "*** Post Capabilities ***" return self._get_between(init_, end_string) - def _get_post_capabilities(self): + def _get_post_capabilities(self) -> str: init_ = "*** Post Capabilities ***" end_string = "***** TITLES *****" return self._get_between(init_, end_string) - def _get_titles(self): + def _get_titles(self) -> str: init_ = "***** TITLES *****" end_string = "***** UNITS *****" return self._get_between(init_, end_string) - def _get_units(self): + def _get_units(self) -> str: init_ = "***** UNITS *****" end_string = "***** SCRATCH MEMORY STATUS *****" return self._get_between(init_, end_string) - def _get_scratch_memory_status(self): + def _get_scratch_memory_status(self) -> str: init_ = "***** SCRATCH MEMORY STATUS *****" end_string = "***** DATABASE STATUS *****" return self._get_between(init_, end_string) - def _get_database_status(self): + def _get_database_status(self) -> str: init_ = "***** DATABASE STATUS *****" end_string = "***** CONFIG VALUES *****" return self._get_between(init_, end_string) - def _get_config_values(self): + def _get_config_values(self) -> str: init_ = "***** CONFIG VALUES *****" end_string = "G L O B A L S T A T U S" return self._get_between(init_, end_string) - def _get_global_status(self): + def _get_global_status(self) -> str: init_ = "G L O B A L S T A T U S" end_string = "J O B I N F O R M A T I O N" return self._get_between(init_, end_string) - def _get_job_information(self): + def _get_job_information(self) -> str: init_ = "J O B I N F O R M A T I O N" end_string = "M O D E L I N F O R M A T I O N" return self._get_between(init_, end_string) - def _get_model_information(self): + def _get_model_information(self) -> str: init_ = "M O D E L I N F O R M A T I O N" end_string = "B O U N D A R Y C O N D I T I O N I N F O R M A T I O N" return self._get_between(init_, end_string) - def _get_boundary_condition_information(self): + def _get_boundary_condition_information(self) -> str: init_ = "B O U N D A R Y C O N D I T I O N I N F O R M A T I O N" end_string = "R O U T I N E I N F O R M A T I O N" return self._get_between(init_, end_string) - def _get_routine_information(self): + def _get_routine_information(self) -> str: init_ = "R O U T I N E I N F O R M A T I O N" end_string = None return self._get_between(init_, end_string) - def _get_solution_options_configuration(self): + def _get_solution_options_configuration(self) -> str: init_ = "S O L U T I O N O P T I O N S" end_string = "L O A D S T E P O P T I O N S" return self._get_between(init_, end_string) - def _get_load_step_options(self): + def _get_load_step_options(self) -> str: init_ = "L O A D S T E P O P T I O N S" end_string = None return self._get_between(init_, end_string) diff --git a/src/ansys/mapdl/core/inline_functions/connectivity_queries.py b/src/ansys/mapdl/core/inline_functions/connectivity_queries.py index dc23f9d10b..3fc143889c 100644 --- a/src/ansys/mapdl/core/inline_functions/connectivity_queries.py +++ b/src/ansys/mapdl/core/inline_functions/connectivity_queries.py @@ -26,7 +26,7 @@ class _ConnectivityQueries(_QueryExecution): _mapdl = None - def nelem(self, e, npos) -> int: + def nelem(self, e: int, npos: int) -> int: """Return the number of the node at position ``npos`` in element ``e``. Returns the node number in position `npos` for element number ``e``. @@ -64,7 +64,7 @@ def nelem(self, e, npos) -> int: """ return self._run_query(f"NELEM({e},{npos})", integer=True) - def enextn(self, n, loc) -> int: + def enextn(self, n: int, loc: int) -> int: """Returns the ``loc`` element connected to node ``n``. Returns the element connected to node ``n``. ``loc`` is the position diff --git a/src/ansys/mapdl/core/inline_functions/core.py b/src/ansys/mapdl/core/inline_functions/core.py index 69c3b55c55..110cefa683 100644 --- a/src/ansys/mapdl/core/inline_functions/core.py +++ b/src/ansys/mapdl/core/inline_functions/core.py @@ -102,12 +102,12 @@ def _run_query(self, command: str, integer: bool) -> Union[int, float]: return self._parse_parameter_integer_response(resp) return self._parse_parameter_float_response(resp) - def _parse_parameter_integer_response(self, response) -> int: + def _parse_parameter_integer_response(self, response: str) -> int: """Parse integer response.""" return int(self._parse_parameter_float_response(response)) @staticmethod - def _parse_parameter_float_response(response) -> float: + def _parse_parameter_float_response(response: str) -> float: if "PARAMETER" not in response or "=" not in response: raise TypeError(f"Parameter response not recognised: " f'"{response}"') parts = response.rsplit("=", 1) diff --git a/src/ansys/mapdl/core/inline_functions/geometry_queries.py b/src/ansys/mapdl/core/inline_functions/geometry_queries.py index 043006f0f3..16fa9bbbca 100644 --- a/src/ansys/mapdl/core/inline_functions/geometry_queries.py +++ b/src/ansys/mapdl/core/inline_functions/geometry_queries.py @@ -26,7 +26,7 @@ class _AngleQueries(_QueryExecution): _mapdl = None - def anglen(self, n1, n2, n3) -> float: + def anglen(self, n1: int, n2: int, n3: int) -> float: """Return the angle between 3 nodes where ``n1`` is the vertex. Subtended angle between two lines (defined by three @@ -66,7 +66,7 @@ def anglen(self, n1, n2, n3) -> float: """ return self._run_query(f"ANGLEN({n1},{n2},{n3})", integer=False) - def anglek(self, k1, k2, k3) -> float: + def anglek(self, k1: int, k2: int, k3: int) -> float: """Return the angle between 3 keypoints where ``k1`` is the vertex. Subtended angle between two lines (defined by three @@ -111,7 +111,7 @@ def anglek(self, k1, k2, k3) -> float: class _AreaQueries(_QueryExecution): _mapdl = None - def areand(self, n1, n2, n3) -> float: + def areand(self, n1: int, n2: int, n3: int) -> float: """Area of the triangle with vertices at nodes ``n1``, ``n2``, and ``n3``. Parameters @@ -143,7 +143,7 @@ def areand(self, n1, n2, n3) -> float: """ return self._run_query(f"AREAND({n1},{n2},{n3})", integer=False) - def areakp(self, k1, k2, k3) -> float: + def areakp(self, k1: int, k2: int, k3: int) -> float: """Area of the triangle with vertices at keypoints ``k1``, ``k2``, and ``k3``. Parameters @@ -179,7 +179,7 @@ def areakp(self, k1, k2, k3) -> float: class _DistanceQueries(_QueryExecution): _mapdl = None - def distnd(self, n1, n2) -> float: + def distnd(self, n1: int, n2: int) -> float: """Compute the distance between nodes ``n1`` and ``n2``. Parameters @@ -208,7 +208,7 @@ def distnd(self, n1, n2) -> float: """ return self._run_query(f"DISTND({n1},{n2})", integer=False) - def distkp(self, k1, k2) -> float: + def distkp(self, k1: int, k2: int) -> float: """Compute the distance between keypoints ``k1`` and ``k2``. Parameters diff --git a/src/ansys/mapdl/core/jupyter.py b/src/ansys/mapdl/core/jupyter.py index 7cfe39b41f..72a3818f50 100644 --- a/src/ansys/mapdl/core/jupyter.py +++ b/src/ansys/mapdl/core/jupyter.py @@ -31,11 +31,11 @@ ) -MAX_CPU = 128 -MAX_MEM = 256 +MAX_CPU: int = 128 +MAX_MEM: int = 256 -def check_manager(): +def check_manager() -> None: try: # response = manager.ping() manager.ping() @@ -44,14 +44,14 @@ def check_manager(): def launch_mapdl_on_cluster( - nproc=2, - memory=4, - loglevel="ERROR", - additional_switches="", - verbose=False, - start_timeout=600, - tag="latest", -): + nproc: int = 2, + memory: int = 4, + loglevel: str = "ERROR", + additional_switches: str = "", + verbose: bool = False, + start_timeout: int = 600, + tag: str = "latest", +) -> "Mapdl": """Start MAPDL on the ANSYS jupyter cluster in gRPC mode. Parameters diff --git a/src/ansys/mapdl/core/krylov.py b/src/ansys/mapdl/core/krylov.py index 7c8a6cd807..82ea6b430a 100644 --- a/src/ansys/mapdl/core/krylov.py +++ b/src/ansys/mapdl/core/krylov.py @@ -21,13 +21,26 @@ # SOFTWARE. import os +from typing import List, Literal, Optional, Tuple, Union import weakref +from ansys.math.core.math import AnsMath, AnsVec import numpy as np from ansys.mapdl.core import Mapdl from ansys.mapdl.core.errors import MapdlRuntimeError +RESIDUAL_ALGORITHM: List[str] = [ + "l-inf", + "linf", + "l-1", + "l1", + "l-2", + "l2", +] + +RESIDUAL_ALGORITHM_LITERAL = Literal[tuple(RESIDUAL_ALGORITHM)] + class KrylovSolver: """Abstract mapdl krylov class. Created from a ``Mapdl`` instance. @@ -94,11 +107,11 @@ def __init__(self, mapdl: Mapdl): self.orthogonality = None @property - def _mapdl(self): + def _mapdl(self) -> Mapdl: """Return the weakly referenced instance of mapdl.""" return self._mapdl_weakref() - def _check_full_file_exists(self, full_file=None): + def _check_full_file_exists(self, full_file: str = None) -> None: """Check full file exists.""" current_dir = self._mapdl.directory # Check if full file exists @@ -120,7 +133,7 @@ def _check_full_file_exists(self, full_file=None): ) @property - def is_orthogonal(self): + def is_orthogonal(self) -> bool: """ Check whether the solution is orthogonal. @@ -133,7 +146,9 @@ def is_orthogonal(self): eye_ = np.eye(N=self.orthogonality.shape[0]) return np.allclose(self.orthogonality, eye_) - def _check_input_gensubspace(self, max_dim_q, freq_val, check_orthogonality): + def _check_input_gensubspace( + self, max_dim_q: int, freq_val: int, check_orthogonality: bool + ): """Validate the inputs to the ``gensubspace`` method.""" # Check for illegal input values by the user @@ -154,7 +169,9 @@ def _check_input_gensubspace(self, max_dim_q, freq_val, check_orthogonality): "True or False" ) - def _check_input_solve(self, freq_start, freq_end, freq_steps, ramped_load): + def _check_input_solve( + self, freq_start: int, freq_end: int, freq_steps: int, ramped_load: bool + ): """Validate the inputs to the ``solve`` method.""" if not isinstance(freq_start, int) or freq_start < 0: @@ -182,7 +199,10 @@ def _check_input_solve(self, freq_start, freq_end, freq_steps, ramped_load): ) def _check_input_expand( - self, return_solution, residual_computation, residual_algorithm + self, + return_solution: bool, + residual_computation: bool, + residual_algorithm: RESIDUAL_ALGORITHM_LITERAL, ): """Validate the inputs to the ``expand`` method.""" @@ -192,21 +212,15 @@ def _check_input_expand( ) if not isinstance(residual_computation, bool): raise ValueError("The 'residual_computation' must be True or False.") - if not isinstance( - residual_algorithm, str - ) or residual_algorithm.lower() not in [ - "l-inf", - "linf", - "l-1", - "l1", - "l-2", - "l2", - ]: + if ( + not isinstance(residual_algorithm, str) + or residual_algorithm.lower() not in RESIDUAL_ALGORITHM + ): raise ValueError( "The provided 'residual_algorithm' is not allowed. Only allowed are 'L-inf', 'Linf', 'L-1', 'L1', 'L-2', 'L2'." ) - def _get_data_from_full_file(self): + def _get_data_from_full_file(self) -> None: """Extract stiffness, mass, damping, and force matrices from the FULL file.""" self._mat_k = self.mm.stiff(fname=self.full_file) @@ -221,7 +235,7 @@ def _get_data_from_full_file(self): self._mapdl.vec("fz0", "Z", "COPY", "fz") self.fz0 = self.mm.vec(name="fz0") - def _calculate_orthogonality(self, uz, num_q): + def _calculate_orthogonality(self, uz: AnsVec, num_q: int): """Check Orthonormality of vectors""" if self.orthogonality is not None: @@ -246,8 +260,12 @@ def _calculate_orthogonality(self, uz, num_q): return self.orthogonality def gensubspace( - self, max_dim_q, frequency, check_orthogonality=False, full_file=None - ): + self, + max_dim_q: int, + frequency: int, + check_orthogonality: bool = False, + full_file: Optional[str] = None, + ) -> AnsMath: """Generate a Krylov subspace for model reduction in a harmonic analysis. This method generates a Krylov subspace used for a model reduction @@ -406,7 +424,9 @@ def gensubspace( self._run_gensubspace = True return self.Qz - def solve(self, freq_start, freq_end, freq_steps, ramped_load=True): + def solve( + self, freq_start: str, freq_end: str, freq_steps: str, ramped_load: bool = True + ) -> AnsMath: """Reduce the system of equations and solve at each frequency. This method uses a Krylov subspace to solve a reduced harmonic @@ -516,11 +536,11 @@ def solve(self, freq_start, freq_end, freq_steps, ramped_load=True): def expand( self, - residual_computation=False, - residual_algorithm=None, - compute_solution_vectors=True, - return_solution=False, - ): + residual_computation: bool = False, + residual_algorithm: Optional[RESIDUAL_ALGORITHM_LITERAL] = None, + compute_solution_vectors: bool = True, + return_solution: bool = False, + ) -> np.ndarray: """Expand the reduced solution back to FE space. This method expands the reduced solution for a harmonic analysis @@ -637,7 +657,9 @@ def expand( if return_solution: return self.solution_vectors - def compute_residuals(self, iFreq, RzV, Xi, omega): + def compute_residuals( + self, iFreq: int, RzV: AnsMath, Xi: AnsMath, omega: float + ) -> Tuple[float]: """Compute residuals of the matrices""" # form {iRHS} self.iRHS.zeros() @@ -699,7 +721,9 @@ def compute_residuals(self, iFreq, RzV, Xi, omega): return norm_rz, norm_fz - def _compute_solution_vector(self, Xi): + def _compute_solution_vector( + self, Xi: AnsMath + ) -> List[Tuple[Union[int, int, complex]]]: self._mapdl.mult( m1=self.Nod2Solv.id, t1="TRANS", m2=Xi.id, t2="", m3="Xii" ) # Map {Xi} to internal (ANSYS) order diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py index bd365fd9d9..23dc24633c 100644 --- a/src/ansys/mapdl/core/launcher.py +++ b/src/ansys/mapdl/core/launcher.py @@ -35,7 +35,7 @@ import subprocess # nosec B404 import threading import time -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union import warnings import psutil @@ -105,7 +105,6 @@ def version_from_path(*args, **kwargs): "additional_switches", "cleanup_on_exit", "clear_on_connect", - "running_on_hpc", "exec_file", "force_intel" "ip", "ip", @@ -115,6 +114,7 @@ def version_from_path(*args, **kwargs): "license_type", "log_apdl", "loglevel", + "mapdl_output", "mode", "nproc", "override", @@ -124,6 +124,7 @@ def version_from_path(*args, **kwargs): "remove_temp_dir_on_exit", "replace_env_vars", "run_location", + "running_on_hpc", "scheduler_options", "set_no_abort", "start_instance", @@ -437,6 +438,7 @@ def launch_grpc( run_location: str = None, env_vars: Optional[Dict[str, str]] = None, launch_on_hpc: bool = False, + mapdl_output: Optional[str] = None, ) -> subprocess.Popen: """Start MAPDL locally in gRPC mode. @@ -456,6 +458,9 @@ def launch_grpc( If running on an HPC, this needs to be :class:`True` to avoid the temporary file creation on Windows. + mapdl_output : str, optional + Whether redirect MAPDL console output (stdout and stderr) to a file. + Returns ------- subprocess.Popen @@ -487,6 +492,13 @@ def launch_grpc( "\n============" ) + if mapdl_output: + stdout = open(str(mapdl_output), "wb", 0) + stderr = subprocess.STDOUT + else: + stdout = subprocess.PIPE + stderr = subprocess.PIPE + if os.name == "nt": # getting tmp file name if not launch_on_hpc: @@ -505,8 +517,8 @@ def launch_grpc( shell=shell, # sbatch does not work without shell. cwd=run_location, stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + stdout=stdout, + stderr=stderr, env_vars=env_vars, ) @@ -542,7 +554,7 @@ def check_mapdl_launch( MAPDL did not start. """ LOG.debug("Generating queue object for stdout") - stdout_queue, _ = _create_queue_for_std(process.stdout) + stdout_queue, thread = _create_queue_for_std(process.stdout) # Checking connection try: @@ -554,7 +566,7 @@ def check_mapdl_launch( if os.name == "posix" and not ON_WSL: LOG.debug("Checking if gRPC server is alive.") - _check_server_is_alive(stdout_queue, run_location, timeout) + _check_server_is_alive(stdout_queue, timeout) except MapdlDidNotStart as e: # pragma: no cover msg = ( @@ -596,7 +608,11 @@ def _check_file_error_created(run_location, timeout): raise MapdlDidNotStart(msg) -def _check_server_is_alive(stdout_queue, run_location, timeout): +def _check_server_is_alive(stdout_queue, timeout): + if not stdout_queue: + LOG.debug("No STDOUT queue. Not checking MAPDL this way.") + return + t0 = time.time() empty_attemps = 3 empty_i = 0 @@ -629,6 +645,9 @@ def _check_server_is_alive(stdout_queue, run_location, timeout): def _get_std_output(std_queue, timeout=1): + if not std_queue: + return [None] + lines = [] reach_empty = False t0 = time.time() @@ -642,10 +661,15 @@ def _get_std_output(std_queue, timeout=1): return lines -def _create_queue_for_std(std): +def _create_queue_for_std( + std: subprocess.PIPE, +) -> Tuple[Optional[Queue[str]], Optional[threading.Thread]]: """Create a queue and thread objects for a given PIPE std""" + if not std: + LOG.debug("No STDOUT. Not checking MAPDL this way.") + return None, None - def enqueue_output(out, queue): + def enqueue_output(out: subprocess.PIPE, queue: Queue[str]) -> None: try: for line in iter(out.readline, b""): queue.put(line) @@ -655,8 +679,8 @@ def enqueue_output(out, queue): # ValueError: PyMemoryView_FromBuffer(): info -> buf must not be NULL pass - q = Queue() - t = threading.Thread(target=enqueue_output, args=(std, q)) + q: Queue[str] = Queue() + t: threading.Thread = threading.Thread(target=enqueue_output, args=(std, q)) t.daemon = True # thread dies with the program t.start() @@ -664,7 +688,7 @@ def enqueue_output(out, queue): def launch_remote_mapdl( - version: str = None, + version: Optional[str] = None, cleanup_on_exit: bool = True, ) -> MapdlGrpc: """Start MAPDL remotely using the product instance management API. @@ -1020,6 +1044,7 @@ def launch_mapdl( version: Optional[Union[int, str]] = None, running_on_hpc: bool = True, launch_on_hpc: bool = False, + mapdl_output: Optional[str] = None, **kwargs: Dict[str, Any], ) -> Union[MapdlGrpc, "MapdlConsole"]: """Start MAPDL locally. @@ -1205,6 +1230,9 @@ def launch_mapdl( to specify the scheduler arguments as a string or as a dictionary. For more information, see :ref:`ref_hpc_slurm`. + mapdl_output : str, optional + Redirect the MAPDL console output to a given file. + kwargs : dict, Optional These keyword arguments are interface-specific or for development purposes. For more information, see Notes. @@ -1395,7 +1423,25 @@ def launch_mapdl( pre_check_args(args) + ######################################## + # PyPIM connection + # ---------------- + # Delegating to PyPIM if applicable + # + if _HAS_PIM and exec_file is None and pypim.is_configured(): + # Start MAPDL with PyPIM if the environment is configured for it + # and the user did not pass a directive on how to launch it. + LOG.info("Starting MAPDL remotely. The startup configuration will be ignored.") + + return launch_remote_mapdl( + cleanup_on_exit=args["cleanup_on_exit"], version=args["version"] + ) + + ######################################## # SLURM settings + # -------------- + # Checking if running on SLURM HPC + # if is_running_on_slurm(args): LOG.info("On Slurm mode.") @@ -1471,20 +1517,6 @@ def launch_mapdl( env_vars.setdefault("ANS_MULTIPLE_NODES", "1") env_vars.setdefault("HYDRA_BOOTSTRAP", "slurm") - ######################################## - # PyPIM connection - # ---------------- - # Delegating to PyPIM if applicable - # - if _HAS_PIM and exec_file is None and pypim.is_configured(): - # Start MAPDL with PyPIM if the environment is configured for it - # and the user did not pass a directive on how to launch it. - LOG.info("Starting MAPDL remotely. The startup configuration will be ignored.") - - return launch_remote_mapdl( - cleanup_on_exit=args["cleanup_on_exit"], version=args["version"] - ) - start_parm = generate_start_parameters(args) # Early exit for debugging. @@ -1541,6 +1573,7 @@ def launch_mapdl( # from ansys.mapdl.core.mapdl_console import MapdlConsole + start_parm = check_console_start_parameters(start_parm) mapdl = MapdlConsole( loglevel=args["loglevel"], log_apdl=args["log_apdl"], @@ -1575,6 +1608,7 @@ def launch_mapdl( run_location=args["run_location"], env_vars=env_vars, launch_on_hpc=args.get("launch_on_hpc"), + mapdl_output=args.get("mapdl_output"), ) if args["launch_on_hpc"]: @@ -2072,6 +2106,7 @@ def generate_start_parameters(args: Dict[str, Any]) -> Dict[str, Any]: start_parm["timeout"] = args["start_timeout"] start_parm["launched"] = True + start_parm.pop("mode") LOG.debug(f"Using start parameters {start_parm}") return start_parm @@ -2230,9 +2265,9 @@ def get_version( raise VersionError( "The MAPDL gRPC interface requires MAPDL 20.2 or later" ) - - # Early exit - return + else: + # Early exit + return if isinstance(version, float): version = int(version * 10) @@ -2841,4 +2876,20 @@ def submitter( stdout=stdout, stderr=stderr, env=env_vars, - ) # nosec B603 B607 + ) + + +def check_console_start_parameters(start_parm): + valid_args = [ + "exec_file", + "run_location", + "jobname", + "nproc", + "additional_switches", + "start_timeout", + ] + for each in list(start_parm.keys()): + if each not in valid_args: + start_parm.pop(each) + + return start_parm diff --git a/src/ansys/mapdl/core/mapdl_console.py b/src/ansys/mapdl/core/mapdl_console.py index cc4ebf8c3c..6931d8cfca 100644 --- a/src/ansys/mapdl/core/mapdl_console.py +++ b/src/ansys/mapdl/core/mapdl_console.py @@ -119,15 +119,18 @@ def __init__( self._continue_on_error = False self._process = None self._name = None + self._session_id = None + self._cleanup = None + self._launch(start_parm) super().__init__( loglevel=loglevel, use_vtk=use_vtk, log_apdl=log_apdl, print_com=print_com, + mode="console", **start_parm, ) - self._mode = "console" def _launch(self, start_parm): """Connect to MAPDL process using pexpect""" diff --git a/src/ansys/mapdl/core/mapdl_core.py b/src/ansys/mapdl/core/mapdl_core.py index 3a927ef249..44937c82b3 100644 --- a/src/ansys/mapdl/core/mapdl_core.py +++ b/src/ansys/mapdl/core/mapdl_core.py @@ -178,6 +178,7 @@ "jobid", "jobname", "launch_on_hpc", + "mode", "nproc", "override", "port", @@ -252,7 +253,7 @@ def __init__( self._store_commands: bool = False self._stored_commands = [] self._response = None - self._mode = None + self._mode = start_parm.get("mode", None) self._mapdl_process = None self._launched: bool = start_parm.get("launched", False) self._stderr = None @@ -1290,6 +1291,9 @@ def inner_wrapper(*args, **kwargs): def _wrap_xsel_commands(self): # Wrapping XSEL commands. + if self.is_console: + return + def wrap_xsel_function(func): if hasattr(func, "__func__"): func.__func__.__doc__ = inject_docs( @@ -1317,6 +1321,10 @@ def wrap_xsel_function_output(method): return self.geometry.anum elif name == "VSEL": return self.geometry.vnum + elif name == "ESLN": + return self.mesh.enum + elif name == "NSLE": + return self.mesh.nnum else: return None @@ -1392,7 +1400,8 @@ def __init__(self, parent): self._parent = weakref.ref(parent) def __enter__(self): - self._parent()._log.debug("Entering non-interactive mode") + self._parent()._log.debug("Entering in non-interactive mode") + self._parent().com("Entering in non_interactive mode") self._parent()._store_commands = True def __exit__(self, *args): @@ -2178,6 +2187,11 @@ def run( if "\n" in command or "\r" in command: raise ValueError("Use ``input_strings`` for multi-line commands") + if len(command) > 639: # CMD_MAX_LENGTH + # If using mapdl_grpc, this check is redundant on purpose. + # Console probably do not have this limitation, but I'm not certain. + raise ValueError("Maximum command length must be less than 640 characters") + # Check kwargs verbose = kwargs.pop("verbose", False) save_fig = kwargs.pop("savefig", False) @@ -2273,6 +2287,7 @@ def run( self._before_run(command) short_cmd = parse_to_short_cmd(command) + self._log.debug(f"Running (verbose: {verbose}, mute={mute}): '{command}'") text = self._run(command, verbose=verbose, mute=mute) if ( @@ -2903,6 +2918,7 @@ def _check_mapdl_os(self): self._platform = "windows" else: # pragma: no cover raise MapdlRuntimeError("Unknown platform: {}".format(platform)) + self.logger.debug(f"MAPDL is running on {self._platform} OS.") def _check_on_docker(self): """Check if MAPDL is running on docker.""" diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index 9ca743280f..8f44fe4a66 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -119,6 +119,10 @@ SESSION_ID_NAME = "__PYMAPDL_SESSION_ID__" +DEFAULT_TIME_STEP_STREAM = None +DEFAULT_TIME_STEP_STREAM_NT = 500 +DEFAULT_TIME_STEP_STREAM_POSIX = 100 + # Retry policy for gRPC calls. SERVICE_DEFAULT_CONFIG = { # see https://github.com/grpc/proposal/blob/master/A6-client-retries.md#retry-policy-capabilities @@ -400,6 +404,7 @@ def __init__( log_apdl=log_apdl, log_file=log_file, print_com=print_com, + mode="grpc", **start_parm, ) self._mode: Literal["grpc"] = "grpc" @@ -650,7 +655,7 @@ def _read_stds(self): _get_std_output, # Avoid circular import error ) - if self._mapdl_process is None: + if self._mapdl_process is None or not self._mapdl_process.stdout: return self._log.debug("Reading stdout") @@ -1068,7 +1073,8 @@ def _send_command(self, cmd: str, mute: bool = False) -> Optional[str]: def _send_command_stream(self, cmd, verbose=False) -> str: """Send a command and expect a streaming response""" request = pb_types.CmdRequest(command=cmd) - metadata = [("time_step_stream", "100")] + time_step = self._get_time_step_stream() + metadata = [("time_step_stream", str(time_step))] stream = self._stub.SendCommandS(request, metadata=metadata) response = [] for item in stream: @@ -1187,12 +1193,13 @@ def _exit_mapdl(self, path: str = None) -> None: if self._local: self._cache_pids() # Recache processes - if os.name == "nt": - self._kill_server() + self._exit_mapdl_server() + self._close_process() + self._remove_lock_file(path) else: - self._kill_server() + self._exit_mapdl_server() def _remove_temp_dir_on_exit(self, path=None): """Removes the temporary directory created by the launcher. @@ -1218,7 +1225,7 @@ def _remove_temp_dir_on_exit(self, path=None): tmp_dir, ) - def _kill_server(self): + def _exit_mapdl_server(self): """Call exit(0) on the server. Notes @@ -1231,14 +1238,18 @@ def _kill_server(self): if self._exited: return - if ( - self._version and self._version >= 24.2 - ): # We can't use the non-cached version because of recursion error. - # self.run("/EXIT,NOSAVE,,,,,SERVER") + # Default + if self._version is None or self._version < 24.1: self._ctrl("EXIT") - else: + + elif self._version >= 24.1: + # We can't use the non-cached version because of recursion error. + # self.run("/EXIT,NOSAVE,,,,,SERVER") + self.finish() self._ctrl("EXIT") + return + def _kill_process(self): """Kill process stored in self._mapdl_process""" if self._mapdl_process is not None: @@ -1287,15 +1298,12 @@ def _close_process(self, timeout=2): # pragma: no cover Notes ----- This is effectively the only way to completely close down MAPDL locally on - linux. Just killing the server with ``_kill_server`` leaves orphaned + linux. Just killing the server with ``_exit_mapdl_server`` leaves orphaned processes making this method ineffective for a local instance of MAPDL. """ self._log.debug("Closing processes") if self._local: - # killing server process - self._kill_server() - # killing main process (subprocess) self._kill_process() @@ -1331,7 +1339,12 @@ def _cache_pids(self): pids = set(re.findall(r"-9 (\d+)", raw)) self._pids = [int(pid) for pid in pids] - if not self._pids: + if not self._pids and not self._mapdl_process: + self._log.debug( + f"MAPDL process is not provided. PIDs could not be retrieved." + ) + return + elif not self._pids: # For the cases where the cleanup file is not generated, # we relay on the process. parent_pid = self._mapdl_process.pid @@ -1767,13 +1780,14 @@ def input( execution time. Due to stability issues, the default time_step_stream is - dependent on verbosity. The defaults are: + dependent on the OS MAPDL is running on. The defaults are: - - ``verbose=True``: ``time_step_stream=500`` - - ``verbose=False``: ``time_step_stream=50`` + - Windows: ``time_step_stream=500`` + - Linux: ``time_step_stream=100`` These defaults will be ignored if ``time_step_stream`` is - manually set. + manually set. See the *Examples* section to learn how to change + the default value globally. orig_cmd : str, optional Original command. There are some cases, were input is @@ -1823,6 +1837,11 @@ def input( >>> with mapdl.non_interactive: mapdl.run("/input,inputtrigger,inp") # This inputs 'myinput.inp' + You can also change them globably using: + + >>> from ansys.mapdl.core import mapdl_grpc + >>> mapdl_grpc.DEFAULT_TIME_STEP_STREAM=100 # in milliseconds + """ # Checking compatibility # Checking the user is not reusing old api: @@ -1903,18 +1922,14 @@ def input( # are unclear filename = self._get_file_path(fname, progress_bar) - if time_step_stream is not None: - if time_step_stream <= 0: - raise ValueError("``time_step_stream`` must be greater than 0``") + time_step_stream = self._get_time_step_stream(time_step_stream) - if verbose: - if time_step_stream is None: - time_step_stream = 500 - metadata = [ - ("time_step_stream", str(time_step_stream)), - ("chunk_size", str(chunk_size)), - ] + metadata = [ + ("time_step_stream", str(time_step_stream)), + ("chunk_size", str(chunk_size)), + ] + if verbose: request = pb_types.InputFileRequest(filename=filename) strouts = self._stub.InputFileS(request, metadata=metadata) responses = [] @@ -1926,13 +1941,8 @@ def input( response = "\n".join(responses) return response.strip() - # otherwise, not verbose - if time_step_stream is None: - time_step_stream = 50 - metadata = [ - ("time_step_stream", str(time_step_stream)), - ("chunk_size", str(chunk_size)), - ] + ## + # Otherwise, not verbose # since we can't directly run /INPUT, we have to write a # temporary input file that tells MAPDL to read the input @@ -2006,6 +2016,31 @@ def input( return output + def _get_time_step_stream( + self, time_step: Optional[Union[int, float]] = None + ) -> str: + """Return the time step for checking if MAPDL is done writing the + output to the file which later will be returned as response + """ + if time_step is None: + if DEFAULT_TIME_STEP_STREAM is not None: + time_step = DEFAULT_TIME_STEP_STREAM + elif self.platform == "windows": + time_step = DEFAULT_TIME_STEP_STREAM_NT + elif self.platform == "linux": + time_step = DEFAULT_TIME_STEP_STREAM_POSIX + else: + raise ValueError( + f"The MAPDL platform ('{self.platform}') is not recognaised." + ) + + else: + if time_step <= 0: + raise ValueError("``time_step`` argument must be greater than 0``") + + self.logger.debug(f"The time_step argument is set to: {time_step}") + return time_step + def _get_file_path(self, fname: str, progress_bar: bool = False) -> str: """Find files in the Python and MAPDL working directories. @@ -2699,7 +2734,7 @@ def _download_as_raw(self, target_name: str) -> str: @property def is_alive(self) -> bool: """True when there is an active connect to the gRPC server""" - if self.channel_state not in ["IDLE", "READY"]: + if self.channel_state not in ["IDLE", "READY", None]: self._log.debug( "MAPDL instance is not alive because the channel is not 'IDLE' o 'READY'." ) @@ -2938,7 +2973,7 @@ def _mat_data(self, pname, raw=False): @property def locked(self): - """Instance is in use within a pool""" + """Instance is in use within a pool.""" return self._locked @locked.setter @@ -2984,7 +3019,7 @@ def _generate_iges(self): @property def _distributed_result_file(self): - """Path of the distributed result file""" + """Path of the distributed result file.""" if not self._distributed: return @@ -3019,7 +3054,7 @@ def _distributed_result_file(self): @property def thermal_result(self): - """The thermal result object""" + """The thermal result object.""" self._prioritize_thermal = True result = self.result self._prioritize_thermal = False @@ -3042,7 +3077,7 @@ def list_error_file(self): return open(os.path.join(self.directory, error_file)).read() elif self._exited: raise MapdlExitedError( - "Cannot list error file when MAPDL Service has " "exited" + "Cannot list error file when MAPDL Service has exited" ) return self._download_as_raw(error_file).decode("latin-1") @@ -3057,9 +3092,7 @@ def cmatrix( capname="", **kwargs, ): - """Run CMATRIX in non-interactive mode and return the response - from file. - """ + """Run CMATRIX in non-interactive mode and return the response from file.""" # The CMATRIX command needs to run in non-interactive mode if not self._store_commands: diff --git a/src/ansys/mapdl/core/misc.py b/src/ansys/mapdl/core/misc.py index 610f6821ff..63b2e35b61 100644 --- a/src/ansys/mapdl/core/misc.py +++ b/src/ansys/mapdl/core/misc.py @@ -571,9 +571,10 @@ def wrapper(self, *args, **kwargs): # assuming you want to select nothing because you supplied an empty list/tuple/array return original_sel_func(self, "none") - self._perform_entity_list_selection( - entity, original_sel_func, type_, item, comp, vmin, kabs - ) + with self.non_interactive: # to speed up + self._perform_entity_list_selection( + entity, original_sel_func, type_, item, comp, vmin, kabs + ) if kwargs.pop("Used_P", False): # we want to return the diff --git a/src/ansys/mapdl/core/parameters.py b/src/ansys/mapdl/core/parameters.py index cfaef83c76..594b40188e 100644 --- a/src/ansys/mapdl/core/parameters.py +++ b/src/ansys/mapdl/core/parameters.py @@ -171,7 +171,14 @@ def routine(self) -> str: >>> mapdl.parameters.routine 'PREP7' """ - value = self._mapdl.get_value("ACTIVE", item1="ROUT") + value = int(self._mapdl.get_value("ACTIVE", item1="ROUT")) + if value not in ROUTINE_MAP: + self._mapdl.logger.info( + f"Getting a valid routine number failed. Routine obtained is {value}. Executing 'FINISH'." + ) + self._mapdl.finish() + value = 0 + return ROUTINE_MAP[int(value)] @property @@ -551,7 +558,7 @@ def _get_parameter_array(self, parm_name, shape): f"that could not be read using '{format_str}'." ) - arr_flat = np.fromstring(output, sep="\n").reshape(shape) + arr_flat = np.fromstring(output.strip(), sep="\n").reshape(shape) if len(shape) == 3: if shape[2] == 1: diff --git a/src/ansys/mapdl/core/pool.py b/src/ansys/mapdl/core/pool.py index 5a53d5f9f8..963e5bcf06 100755 --- a/src/ansys/mapdl/core/pool.py +++ b/src/ansys/mapdl/core/pool.py @@ -614,8 +614,10 @@ def run(): try: self._exiting_i += 1 instance.exit() - except Exception as e: - LOG.error("Failed to close instance", exc_info=True) + except Exception: + LOG.error( + f"Failed to close instance due to:\n{e}", exc_info=True + ) self._exiting_i -= 1 else: diff --git a/src/ansys/mapdl/core/post.py b/src/ansys/mapdl/core/post.py index 18ebc102c4..8110ec6f8a 100644 --- a/src/ansys/mapdl/core/post.py +++ b/src/ansys/mapdl/core/post.py @@ -139,13 +139,37 @@ def _mapdl(self): @supress_logging def __repr__(self): info = "PyMAPDL PostProcessing Instance\n" - info += "\tActive Result File: %s\n" % self.filename - info += "\tNumber of result sets: %d\n" % self.nsets - info += "\tCurrent load step: %d\n" % self.load_step - info += "\tCurrent sub step: %d\n" % self.sub_step + info += f"\tActive Result File: {self.filename}\n" + + # If there is no result file, this fails. + try: + nsets = int(self.nsets) + except MapdlRuntimeError as error: + self._mapdl.logger.debug( + f"Error when obtaining the number of sets:\n{error}" + ) + nsets = "NA" + + info += f"\tNumber of result sets: {nsets}\n" + info += f"\tCurrent load step: {self.load_step}\n" + info += f"\tCurrent sub step: {self.sub_step}\n" if self._mapdl.parameters.routine == "POST1": - info += "\n\n" + self._mapdl.set("LIST") + try: + nlist = self._mapdl.set("LIST") + except MapdlRuntimeError as err: + if ( + "An error occurred while attempting to open the results file" + in str(err) + ): + self._mapdl.logger.debug( + f"List of steps could not be obtained due to error:\n{err}" + ) + nlist = "Results file is not available" + else: + raise err + + info += "\n\n" + nlist else: info += "\n\nEnable routine POST1 to see a table of available results" diff --git a/tests/common.py b/tests/common.py index 5ad798693d..51ed2e2185 100644 --- a/tests/common.py +++ b/tests/common.py @@ -157,14 +157,14 @@ def testing_minimal(): return os.environ.get("TESTING_MINIMAL", "NO").upper().strip() in ["YES", "TRUE"] -def log_apdl() -> bool: - if os.environ.get("PYMAPDL_LOG_APDL"): - log_apdl = os.environ.get("PYMAPDL_LOG_APDL") +def debug_testing() -> bool: + if os.environ.get("PYMAPDL_DEBUG_TESTING"): + debug_testing = os.environ.get("PYMAPDL_DEBUG_TESTING") - if log_apdl.lower() in ["true", "false", "yes", "no"]: - return log_apdl.lower() in ["true", "yes"] + if debug_testing.lower() in ["true", "false", "yes", "no"]: + return debug_testing.lower() in ["true", "yes"] else: - return log_apdl + return debug_testing else: return False @@ -228,7 +228,7 @@ def log_test_start(mapdl: Mapdl) -> None: ) mapdl.run("!") - mapdl.run(f"! PyMAPDL running test: {test_name}") + mapdl.run(f"! PyMAPDL running test: {test_name}"[:639]) mapdl.run("!") # To see it also in MAPDL terminal output @@ -241,6 +241,7 @@ def log_test_start(mapdl: Mapdl) -> None: types_ = ["File path", "Test function"] mapdl._run("/com,Running test in:", mute=True) + for type_, name_ in zip(types_, test_name): mapdl._run(f"/com, {type_}: {name_}", mute=True) @@ -273,7 +274,10 @@ def is_exited(mapdl: Mapdl): # we cannot connect. # Kill the instance - mapdl.exit() + try: + mapdl.exit() + except Exception as e: + LOG.error(f"An error occurred when killing the instance:\n{str(e)}") # Relaunching MAPDL mapdl = launch_mapdl( @@ -284,6 +288,8 @@ def is_exited(mapdl: Mapdl): log_apdl=log_apdl(), ) + LOG.info("Successfully re-connected to MAPDL") + # Restoring the local configuration mapdl._local = local_ mapdl._exited = False diff --git a/tests/conftest.py b/tests/conftest.py index 1a36e31304..3f87e5fedd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,6 +35,7 @@ from common import ( Element, Node, + debug_testing, get_details_of_elements, get_details_of_nodes, has_dpf, @@ -44,7 +45,6 @@ is_on_ubuntu, is_running_on_student, is_smp, - log_apdl, log_test_start, make_sure_not_instances_are_left_open, restart_mapdl, @@ -58,6 +58,7 @@ # --------------------------- # +DEBUG_TESTING = debug_testing() TESTING_MINIMAL = testing_minimal() ON_LOCAL = is_on_local() @@ -74,7 +75,6 @@ HAS_DPF = has_dpf() SUPPORT_PLOTTING = support_plotting() IS_SMP = is_smp() -LOG_APDL = log_apdl() QUICK_LAUNCH_SWITCHES = "-smp -m 100 -db 100" VALID_PORTS = [] @@ -183,14 +183,27 @@ def requires_dependency(dependency: str): return pytest.mark.skip(reason=f"Requires '{dependency}' package") +if DEBUG_TESTING: + from ansys.mapdl.core import LOG + + LOG.setLevel("DEBUG") + + # If the following file name is changed, update `ci.yml`. + LOG.log_to_file("pymapdl.log") + + # the following files are also generated by MAPDL gRPC: + # - "pymapdl.apdl": The APDL commands sent to MAPDL by PyMAPDL + # - "apdl.out" : MAPDL console output. Very likely only contains the output + # until connected. + ################################################################ # # Importing packages # ------------------ # -if has_dependency("ansys-tools-package"): - from ansys.tools.path import find_mapdl, get_available_ansys_installations +if has_dependency("ansys-tools-path"): + from ansys.tools.path import find_mapdl if has_dependency("pyvista"): @@ -219,14 +232,12 @@ def requires_dependency(dependency: str): viz_interface.TESTING_MODE = True - ################################################################ # # Pytest configuration # -------------------- # - # check if the user wants to permit pytest to start MAPDL START_INSTANCE = get_start_instance() @@ -265,7 +276,7 @@ def pytest_report_header(config, start_path, startdir): text = [] text += ["Testing variables".center(get_terminal_size()[0], "-")] text += [ - f"Session dependent: ON_CI ({ON_CI}), TESTING_MINIMAL ({TESTING_MINIMAL}), SUPPORT_PLOTTING ({SUPPORT_PLOTTING})" + f"Session dependent: DEBUG_TESTING ({DEBUG_TESTING}), ON_CI ({ON_CI}), TESTING_MINIMAL ({TESTING_MINIMAL}), SUPPORT_PLOTTING ({SUPPORT_PLOTTING})" ] text += [ f"OS dependent: ON_LINUX ({ON_LINUX}), ON_UBUNTU ({ON_UBUNTU}), ON_WINDOWS ({ON_WINDOWS}), ON_MACOS ({ON_MACOS})" @@ -429,7 +440,7 @@ def run_before_and_after_tests( mapdl = restart_mapdl(mapdl) # Write test info to log_apdl - if LOG_APDL: + if DEBUG_TESTING: log_test_start(mapdl) # check if the local/remote state has changed or not @@ -537,14 +548,7 @@ def mapdl_console(request): raise MapdlRuntimeError( '"--console" testing option unavailable. ' "Only Linux is supported." ) - ansys_base_paths = get_available_ansys_installations() - - # find a valid version of console - console_path = None - for version in ansys_base_paths: - version = abs(version) - if version < 211: - console_path = find_mapdl(str(version))[0] + console_path = find_mapdl()[0] if console_path is None: raise MapdlRuntimeError( @@ -553,7 +557,12 @@ def mapdl_console(request): "Valid versions are up to 2020R2." ) - mapdl = launch_mapdl(console_path, log_apdl=LOG_APDL) + mapdl = launch_mapdl( + console_path, + mode="console", + log_apdl="pymapdl.apdl" if DEBUG_TESTING else None, + loglevel="DEBUG" if DEBUG_TESTING else "ERROR", + ) from ansys.mapdl.core.mapdl_console import MapdlConsole assert isinstance(mapdl, MapdlConsole) @@ -584,8 +593,12 @@ def mapdl(request, tmpdir_factory): cleanup_on_exit=cleanup, license_server_check=False, start_timeout=50, - log_apdl=LOG_APDL, + loglevel="DEBUG" if DEBUG_TESTING else "ERROR", + # If the following file names are changed, update `ci.yml`. + log_apdl="pymapdl.apdl" if DEBUG_TESTING else None, + mapdl_output="apdl.out" if (DEBUG_TESTING and ON_LOCAL) else None, ) + mapdl._show_matplotlib_figures = False # CI: don't show matplotlib figures MAPDL_VERSION = mapdl.version # Caching version @@ -594,7 +607,7 @@ def mapdl(request, tmpdir_factory): if ON_CI: mapdl._local = ON_LOCAL # CI: override for testing - if mapdl.is_local: + if ON_LOCAL and mapdl.is_local: assert Path(mapdl.directory) == Path(run_path) # using yield rather than return here to be able to test exit @@ -1144,6 +1157,24 @@ def create_geometry(mapdl): return areas, keypoints +@pytest.fixture(scope="function") +def two_dimensional_mesh(mapdl, cleared): + length = 4 + height = 1 + thickness = 0.2 + mesh_size = 0.1 + + mapdl.prep7() + + mapdl.r(r1=thickness) + mapdl.et(1, "PLANE182", kop3=3, kop6=0) + mapdl.rectng(0, length, 0, height) + mapdl.mshkey(1) + mapdl.mshape(0, "2D") + mapdl.esize(mesh_size) + mapdl.amesh("ALL") + + @pytest.fixture def query(mapdl, cleared): return mapdl.queries diff --git a/tests/test_console.py b/tests/test_console.py index b65115ccea..02aae3c419 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -30,7 +30,7 @@ import pytest -from conftest import has_dependency, requires +from conftest import clear, has_dependency, requires # skip entire module unless --console is enabled pytestmark = requires("console") @@ -48,6 +48,11 @@ from ansys.mapdl.core.errors import MapdlRuntimeError +@pytest.fixture(scope="function") +def cleared(mapdl_console): + clear(mapdl_console) + + @pytest.fixture(scope="function") def make_block(mapdl_console, cleared): mapdl_console.block(0, 1, 0, 1, 0, 1) @@ -74,7 +79,7 @@ def test_empty(mapdl_console, cleared): def test_str(mapdl_console, cleared): - assert "ANSYS Mechanical" in str(mapdl_console) + assert "Ansys Mechanical" in str(mapdl_console) def test_version(mapdl_console, cleared): @@ -127,6 +132,7 @@ def test_chaining(mapdl_console, cleared): def test_e(mapdl_console, cleared): + mapdl.prep7() mapdl_console.et("", 183) n0 = mapdl_console.n("", 0, 0, 0) n1 = mapdl_console.n("", 1, 0, 0) @@ -362,7 +368,7 @@ def test_nodes(tmpdir, cleared, mapdl_console): basename = "tmp.nodes" filename = str(tmpdir.mkdir("tmpdir").join(basename)) mapdl_console.nwrite(filename) - mapdl_console.download(basename, filename) + # mapdl_console.download(basename, filename) assert np.allclose(mapdl_console.mesh.nodes, np.loadtxt(filename)[:, 1:]) assert mapdl_console.mesh.n_node == 11 diff --git a/tests/test_database.py b/tests/test_database.py index d4bfe448b2..7cb8440eae 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -32,6 +32,8 @@ from ansys.mapdl.core.misc import random_string from conftest import ON_CI, TestClass +SKIP_ON_VERSIONS = ["22.2", "23.1", "23.2", "24.1", "24.2", "25.1", "25.2"] + @pytest.fixture(scope="session") def db(mapdl): @@ -49,9 +51,9 @@ def db(mapdl): ) ## Exceptions - if mapdl_version in ["22.2", "23.1", "23.2", "24.1", "24.2", "25.1"] and ON_CI: + if mapdl_version in SKIP_ON_VERSIONS and ON_CI: pytest.skip( - f"This MAPDL version ({mapdl_version}) docker image seems to not support DB, but local does." + f"This MAPDL version ({mapdl_version}) docker image seems to not support DB on CICD." ) if mapdl._server_version < (0, 4, 1): # 2021R2 @@ -61,6 +63,9 @@ def db(mapdl): ) mapdl.clear() + if mapdl.db.active or mapdl.db._stub is None: + mapdl.db.stop() + mapdl.db.start() return mapdl.db @@ -84,11 +89,14 @@ def test_database_start_stop(mapdl, cleared): ) # Exceptions - if mapdl_version in ["22.2", "23.1", "23.2", "24.1", "24.2", "25.1"] and ON_CI: + if mapdl_version in SKIP_ON_VERSIONS and ON_CI: pytest.skip( f"This MAPDL version ({mapdl_version}) docker image seems to not support DB, but local does." ) + if MapdlDb(mapdl).active: + MapdlDb(mapdl)._stop() + # verify it can be created twice mapdl.prep7() for _ in range(2): @@ -109,6 +117,9 @@ def test_database_start_stop(mapdl, cleared): with pytest.warns(UserWarning): database.stop() + # Starting the database for the rest of the test session + mapdl.db.start() + def test_database_repr(db): assert db._channel_str in str(db) @@ -136,8 +147,8 @@ def test_clear(db): def test__channel_str(db): assert db._channel_str is not None assert ":" in db._channel_str - assert re.search("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", db._channel_str) - assert re.search("\d{4,6}", db._channel_str) + assert re.search(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", db._channel_str) + assert re.search(r"\d{4,6}", db._channel_str) def test_off_db(mapdl, cleared, db): @@ -184,6 +195,10 @@ def test_repr(mapdl, cleared, db): def gen_block(mapdl): """Generate nodes and elements in a simple block.""" + from conftest import clear + + clear(mapdl) + mapdl.block(0, 1, 0, 1, 0, 1) mapdl.et(1, 186) mapdl.esize(0.25) @@ -224,8 +239,8 @@ def test_nodes_next(nodes): def test_nodes_info(nodes): assert nodes.info(1, DBDef.DB_SELECTED) == 1 - @pytest.mark.parametrize("selected", [True, False]) @staticmethod + @pytest.mark.parametrize("selected", [True, False]) def test_nodes_num(nodes, selected): assert nodes.num(selected=selected) == 425 @@ -248,7 +263,7 @@ def test_nodes_asarray(nodes): assert np.allclose(angles, 0) @staticmethod - def test_nodes_push(nodes): + def test_nodes_push(mapdl, nodes): nnum = 100000 x, y, z, xang, yang, zang = 1, 5, 10, 30, 40, 50 nodes.push(nnum, x, y, z, xang, yang, zang) @@ -263,6 +278,10 @@ def test_nodes_push(nodes): with pytest.raises(ValueError, match="X and Y angles must be input"): nodes.push(nnum, x, y, z, zang=1) + # this test changes the database, so let's restore it back + # as in `nodes` fixture. + gen_block(mapdl) + class Test_Elems(TestClass): diff --git a/tests/test_dpf.py b/tests/test_dpf.py index 8fd93f5c36..882e4e1f7e 100644 --- a/tests/test_dpf.py +++ b/tests/test_dpf.py @@ -25,7 +25,7 @@ import pytest -from conftest import HAS_DPF, has_dependency, requires +from conftest import HAS_DPF, ON_CI, has_dependency, requires if not has_dependency("ansys-dpf-core") or not HAS_DPF: pytest.skip(allow_module_level=True) @@ -36,9 +36,20 @@ DPF_PORT = os.environ.get("DPF_PORT", DPF_DEFAULT_PORT) # Set in ci.yaml +@pytest.fixture() +def skip_dpf(mapdl): + mapdl_version = str(mapdl.version) + if mapdl_version in ["25.2"] and ON_CI: + pytest.skip( + f"This MAPDL version ({mapdl_version}) docker image seems to not support DPF on CICD.", + allow_module_level=True, + ) + return + + @requires("dpf") @requires("ansys-dpf-core") -def test_dpf_connection(): +def test_dpf_connection(skip_dpf): # uses 127.0.0.1 and port 50054 by default try: grpc_con = dpf.connect_to_server(port=DPF_PORT) @@ -50,7 +61,7 @@ def test_dpf_connection(): @requires("dpf") @requires("ansys-dpf-core") -def test_upload(mapdl, solved_box, tmpdir): +def test_upload(skip_dpf, mapdl, solved_box, tmpdir): # Download RST file rst_path = mapdl.download_result(str(tmpdir.mkdir("tmpdir"))) diff --git a/tests/test_examples.py b/tests/test_examples.py index 3689deebc1..a7c9bdcf9a 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -171,20 +171,20 @@ def test_detach_examples_submodule(): """ import sys -assert 'ansys.mapdl.core' not in sys.modules -assert 'requests' not in sys.modules -assert 'ansys.mapdl.core.examples' not in sys.modules +assert 'ansys.mapdl.core' not in sys.modules, 'PyMAPDL is loaded!' +assert 'requests' not in sys.modules, 'Requests is loaded!' +assert 'ansys.mapdl.core.examples' not in sys.modules, 'Examples is loaded!' from ansys.mapdl import core as pymapdl -assert 'ansys.mapdl.core' in sys.modules -assert 'ansys.mapdl.core.examples' not in sys.modules -assert 'requests' not in sys.modules +assert 'ansys.mapdl.core' in sys.modules, 'PyMAPDL is not loaded!' +assert 'ansys.mapdl.core.examples' not in sys.modules, 'Examples is loaded!' +assert 'requests' in sys.modules, 'Requests is loaded!' from ansys.mapdl.core.examples import vmfiles -assert 'ansys.mapdl.core.examples' in sys.modules -assert 'requests' in sys.modules +assert 'ansys.mapdl.core.examples' in sys.modules, 'examples is not loaded!' +assert 'requests' in sys.modules, 'requests is not loaded!' print('Everything went well') """.strip() @@ -198,3 +198,11 @@ def test_detach_examples_submodule(): out = p.communicate()[0].decode() assert out.strip() == "Everything went well" + + +def test_external_models(): + from ansys.mapdl.core.examples import examples + + for each in dir(examples): + if each not in ["os", "dir_path"] and not each.startswith("__"): + obj = getattr(examples, each) diff --git a/tests/test_grpc.py b/tests/test_grpc.py index 0850df9f67..6d0aaf6fc2 100644 --- a/tests/test_grpc.py +++ b/tests/test_grpc.py @@ -25,6 +25,7 @@ import re import shutil import sys +from unittest.mock import patch import grpc import pytest @@ -577,13 +578,19 @@ def test_input_compatibility_api_change(mapdl, cleared): @requires("grpc") @requires("local") -def test__check_stds(mapdl, cleared): +@requires("nostudent") +def test__check_stds(): """Test that the standard input is checked.""" + from ansys.mapdl.core import launch_mapdl + + mapdl = launch_mapdl(port=50058) mapdl._read_stds() assert mapdl._stdout is not None assert mapdl._stderr is not None + mapdl.exit(force=True) + def test_subscribe_to_channel(mapdl, cleared): assert mapdl.channel_state in [ @@ -676,3 +683,32 @@ def _null_close_process(): mapdl.prep7(mapdl) mapdl._exited = False # Restoring + + +@pytest.mark.parametrize("platform", ["linux", "windows", "error"]) +def test__get_time_step_stream(mapdl, platform): + with patch("ansys.mapdl.core.mapdl_grpc.MapdlGrpc.platform", platform): + from ansys.mapdl.core import mapdl_grpc + + if platform == "linux": + DEFAULT_TIME_STEP_STREAM = mapdl_grpc.DEFAULT_TIME_STEP_STREAM_POSIX + elif platform == "windows": + DEFAULT_TIME_STEP_STREAM = mapdl_grpc.DEFAULT_TIME_STEP_STREAM_NT + else: + with pytest.raises(ValueError, match="The MAPDL platform"): + mapdl._get_time_step_stream() + + return # Early exit + + assert DEFAULT_TIME_STEP_STREAM == mapdl._get_time_step_stream() + + mapdl_grpc.DEFAULT_TIME_STEP_STREAM = 200 + assert mapdl_grpc.DEFAULT_TIME_STEP_STREAM == mapdl._get_time_step_stream() + mapdl_grpc.DEFAULT_TIME_STEP_STREAM = None + + assert 700 == mapdl._get_time_step_stream(700) + + with pytest.raises( + ValueError, match="``time_step`` argument must be greater than 0``" + ): + mapdl._get_time_step_stream(-700) diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 0f5b736505..c1de62b542 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -30,6 +30,7 @@ import warnings import psutil +from pyfakefs.fake_filesystem import OSType import pytest from ansys.mapdl import core as pymapdl @@ -94,15 +95,11 @@ from ansys.mapdl.core.launcher import get_default_ansys installed_mapdl_versions = list(get_available_ansys_installations().keys()) - try: - V150_EXEC = find_mapdl("150")[0] - except ValueError: - V150_EXEC = "" except: from conftest import MAPDL_VERSION installed_mapdl_versions = [MAPDL_VERSION] - V150_EXEC = "" + from ansys.mapdl.core._version import SUPPORTED_ANSYS_VERSIONS as versions @@ -136,6 +133,12 @@ class myprocess: return process +@pytest.fixture +def my_fs(fs): + # fs.add_real_directory("/proc", lazy_read=False) + yield fs + + @pytest.fixture def fake_local_mapdl(mapdl): """Fixture to execute asserts before and after a test is run""" @@ -148,8 +151,7 @@ def fake_local_mapdl(mapdl): mapdl._local = False -@requires("local") -@requires("windows") +@patch("os.name", "nt") def test_validate_sw(): # ensure that windows adds msmpi # fake windows path @@ -157,15 +159,20 @@ def test_validate_sw(): add_sw = set_MPI_additional_switches("", version=version) assert "msmpi" in add_sw - add_sw = set_MPI_additional_switches("-mpi intelmpi", version=version) - assert "msmpi" in add_sw and "intelmpi" not in add_sw + with pytest.warns( + UserWarning, match="Due to incompatibilities between this MAPDL version" + ): + add_sw = set_MPI_additional_switches("-mpi intelmpi", version=version) + assert "msmpi" in add_sw and "intelmpi" not in add_sw - add_sw = set_MPI_additional_switches("-mpi INTELMPI", version=version) - assert "msmpi" in add_sw and "INTELMPI" not in add_sw + with pytest.warns( + UserWarning, match="Due to incompatibilities between this MAPDL version" + ): + add_sw = set_MPI_additional_switches("-mpi INTELMPI", version=version) + assert "msmpi" in add_sw and "INTELMPI" not in add_sw @requires("ansys-tools-path") -@requires("local") @pytest.mark.parametrize("path_data", paths) def test_version_from_path(path_data): exec_file, version = path_data @@ -173,41 +180,102 @@ def test_version_from_path(path_data): @requires("ansys-tools-path") -@requires("local") def test_catch_version_from_path(): with pytest.raises(RuntimeError): version_from_path("mapdl", "abc") +@pytest.mark.parametrize( + "path,version,raises", + [ + ["/ansys_inc/v221/ansys/bin/ansys221", 22.1, None], + ["/ansys_inc/v222/ansys/bin/mapdl", 22.2, None], + ["/usr/ansys_inc/v231/ansys/bin/mapdl", 23.1, None], + ["/usr/ansys_inc/v232/ansys/bin/mapdl", 23.2, None], + ["/usr/ansys_inc/v241/ansys/bin/mapdl", 24.1, None], + ["/ansysinc/v242/ansys/bin/ansys2", 24.2, ValueError], + ["/ansysinc/v242/ansys/bin/mapdl", 24.2, ValueError], + ], +) @requires("ansys-tools-path") -@requires("local") -@requires("linux") -def test_find_mapdl_linux(): - # assuming ansys is installed, should be able to find it on linux - # without env var +def test_find_mapdl_linux(my_fs, path, version, raises): + my_fs.os = OSType.LINUX + my_fs.create_file(path) + bin_file, ver = pymapdl.launcher.find_mapdl() - assert os.path.isfile(bin_file) - assert isinstance(ver, float) + + if raises: + assert not bin_file + assert not ver + + else: + assert bin_file.startswith(path.replace("mapdl", "")) + assert isinstance(ver, float) + assert ver == version @requires("ansys-tools-path") -@requires("local") -def test_invalid_mode(mapdl, cleared): +@patch("psutil.cpu_count", lambda *args, **kwargs: 2) +@patch("ansys.mapdl.core.launcher._is_ubuntu", lambda *args, **kwargs: True) +@patch("ansys.mapdl.core.launcher.get_process_at_port", lambda *args, **kwargs: None) +def test_invalid_mode(mapdl, my_fs, cleared, monkeypatch): + monkeypatch.delenv("PYMAPDL_START_INSTANCE", False) + monkeypatch.delenv("PYMAPDL_IP", False) + monkeypatch.delenv("PYMAPDL_PORT", False) + + my_fs.create_file("/ansys_inc/v241/ansys/bin/ansys241") with pytest.raises(ValueError): - exec_file = find_mapdl(installed_mapdl_versions[0])[0] + exec_file = find_mapdl()[0] pymapdl.launch_mapdl( exec_file, port=mapdl.port + 1, mode="notamode", start_timeout=start_timeout ) @requires("ansys-tools-path") -@requires("local") -@pytest.mark.skipif(not os.path.isfile(V150_EXEC), reason="Requires v150") -def test_old_version(mapdl, cleared): - exec_file = find_mapdl("150")[0] - with pytest.raises(ValueError): +@pytest.mark.parametrize("version", [120, 170, 190]) +@patch("psutil.cpu_count", lambda *args, **kwargs: 2) +@patch("ansys.mapdl.core.launcher._is_ubuntu", lambda *args, **kwargs: True) +@patch("ansys.mapdl.core.launcher.get_process_at_port", lambda *args, **kwargs: None) +def test_old_version_not_version(mapdl, my_fs, cleared, monkeypatch, version): + monkeypatch.delenv("PYMAPDL_START_INSTANCE", False) + monkeypatch.delenv("PYMAPDL_IP", False) + monkeypatch.delenv("PYMAPDL_PORT", False) + + exec_file = f"/ansys_inc/v{version}/ansys/bin/ansys{version}" + my_fs.create_file(exec_file) + assert exec_file == find_mapdl()[0] + + with pytest.raises( + ValueError, match="The MAPDL gRPC interface requires MAPDL 20.2 or later" + ): pymapdl.launch_mapdl( - exec_file, port=mapdl.port + 1, mode="console", start_timeout=start_timeout + exec_file=exec_file, + port=mapdl.port + 1, + mode="grpc", + start_timeout=start_timeout, + ) + + +@requires("ansys-tools-path") +@pytest.mark.parametrize("version", [203, 213, 351]) +@patch("psutil.cpu_count", lambda *args, **kwargs: 2) +@patch("ansys.mapdl.core.launcher._is_ubuntu", lambda *args, **kwargs: True) +@patch("ansys.mapdl.core.launcher.get_process_at_port", lambda *args, **kwargs: None) +def test_not_valid_versions(mapdl, my_fs, cleared, monkeypatch, version): + monkeypatch.delenv("PYMAPDL_START_INSTANCE", False) + monkeypatch.delenv("PYMAPDL_IP", False) + monkeypatch.delenv("PYMAPDL_PORT", False) + + exec_file = f"/ansys_inc/v{version}/ansys/bin/ansys{version}" + my_fs.create_file(exec_file) + + assert exec_file == find_mapdl()[0] + with pytest.raises(ValueError, match="MAPDL version must be one of the following"): + pymapdl.launch_mapdl( + exec_file=exec_file, + port=mapdl.port + 1, + mode="grpc", + start_timeout=start_timeout, ) @@ -244,7 +312,6 @@ def test_license_type_keyword_names(monkeypatch, license_name): assert f"-p {license_name}" in args["additional_switches"] -# @requires("local") @pytest.mark.parametrize("license_name", LICENSES) def test_license_type_additional_switch(license_name): args = launch_mapdl( @@ -721,7 +788,7 @@ def test_get_slurm_options(set_env_var_context, validation): ], ) def test_slurm_ram(monkeypatch, ram, expected, context): - monkeypatch.setenv("SLURM_MEM_PER_NODE", ram) + monkeypatch.setenv("SLURM_MEM_PER_NODE", str(ram)) monkeypatch.setenv("PYMAPDL_MAPDL_EXEC", "asdf/qwer/poiu") args = { @@ -1208,7 +1275,7 @@ def test_launch_grpc(tmpdir, launch_on_hpc): @pytest.mark.parametrize("env", [None, 3, 10]) def test_get_cpus(monkeypatch, arg, env): if env: - monkeypatch.setenv("PYMAPDL_NPROC", env) + monkeypatch.setenv("PYMAPDL_NPROC", str(env)) context = NullContext() cores_machine = psutil.cpu_count(logical=False) # it is patched @@ -1440,7 +1507,7 @@ def test_launch_on_hpc_not_found_ansys(mck_sc, mck_lgrpc, mck_kj, monkeypatch): def test_launch_on_hpc_exception_launch_mapdl(monkeypatch): monkeypatch.delenv("PYMAPDL_START_INSTANCE", False) - exec_file = "path/to/mapdl/v242/executable/ansys242" + exec_file = "path/to/mapdl/v242/ansys/bin/executable/ansys242" process = get_fake_process("ERROR") @@ -1475,7 +1542,7 @@ def test_launch_on_hpc_exception_launch_mapdl(monkeypatch): def test_launch_on_hpc_exception_successfull_sbatch(monkeypatch): monkeypatch.delenv("PYMAPDL_START_INSTANCE", False) - exec_file = "path/to/mapdl/v242/executable/ansys242" + exec_file = "path/to/mapdl/v242/ansys/bin/executable/ansys242" def raise_exception(*args, **kwargs): raise Exception("Fake exception when launching MAPDL") @@ -1603,7 +1670,7 @@ def test_get_port(monkeypatch, port, port_envvar, start_instance, port_busy, res monkeypatch.delenv("PYMAPDL_PORT", False) if port_envvar: - monkeypatch.setenv("PYMAPDL_PORT", port_envvar) + monkeypatch.setenv("PYMAPDL_PORT", str(port_envvar)) # Testing if port_busy: @@ -1706,7 +1773,7 @@ def test_get_version_version_error(monkeypatch): @pytest.mark.parametrize("version", [211, 221, 232]) def test_get_version_env_var(monkeypatch, version): - monkeypatch.setenv("PYMAPDL_MAPDL_VERSION", version) + monkeypatch.setenv("PYMAPDL_MAPDL_VERSION", str(version)) assert version == get_version(None) assert version != get_version(241) @@ -1947,3 +2014,60 @@ def raising(): @patch("ansys.mapdl.core.launcher.check_valid_ansys", raising) def test_check_has_mapdl_failed(): assert check_has_mapdl() is False + + +@requires("local") +@patch("ansys.mapdl.core.launcher._is_ubuntu", lambda *args, **kwargs: True) +@patch("ansys.mapdl.core.launcher.check_mapdl_launch", lambda *args, **kwargs: None) +def test_mapdl_output_pass_arg(tmpdir): + def submitter(*args, **kwargs): + from _io import FileIO + + # Checking we are passing the arguments + assert isinstance(kwargs["stdout"], FileIO) + assert kwargs["stderr"] is subprocess.STDOUT + + return + + with patch("ansys.mapdl.core.launcher.submitter", submitter) as mck_sub: + mapdl_output = os.path.join(tmpdir, "apdl.txt") + args = launch_mapdl(just_launch=True, mapdl_output=mapdl_output) + + assert isinstance(args, list) + + +@requires("local") +@requires("nostudent") +def test_mapdl_output(tmpdir): + mapdl_output = os.path.join(tmpdir, "apdl.txt") + mapdl = launch_mapdl(mapdl_output=mapdl_output, port=50058) + + assert os.path.exists(mapdl_output) + + mapdl.prep7() + mapdl.exit(force=True) + + with open(mapdl_output, "r") as fid: + content = fid.read() + + assert "Beta activation of the GRPC server." in content + assert "### START GRPC SERVER ###" in content + assert "Server listening on" in content + + +def test_check_server_is_alive_no_queue(): + from ansys.mapdl.core.launcher import _check_server_is_alive + + assert _check_server_is_alive(None, 30) is None + + +def test_get_std_output_no_queue(): + from ansys.mapdl.core.launcher import _get_std_output + + assert _get_std_output(None, 30) == [None] + + +def test_create_queue_for_std_no_queue(): + from ansys.mapdl.core.launcher import _create_queue_for_std + + assert _create_queue_for_std(None) == (None, None) diff --git a/tests/test_logging.py b/tests/test_logging.py index a28871b28e..35069035d4 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -287,7 +287,12 @@ def test_log_to_file(tmpdir): LOG.logger.setLevel("ERROR") LOG.std_out_handler.setLevel("ERROR") - if not LOG.file_handler: + if LOG.file_handler is None: + old_logger = None + LOG.log_to_file(file_path) + else: + # the logger has been already instantiated + old_logger = LOG.file_handler.baseFilename LOG.log_to_file(file_path) LOG.error(file_msg_error) @@ -313,6 +318,9 @@ def test_log_to_file(tmpdir): assert file_msg_debug in text + if old_logger is not None: + LOG.log_to_file(old_logger) + def test_log_instance_name(mapdl, cleared): # verify we can access via an instance name diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index eaba199f9e..3455e816d2 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -2219,7 +2219,7 @@ def test_inquire_invalid(mapdl, cleared): def test_inquire_default(mapdl, cleared): mapdl.title("heeeelloo") - assert Path(mapdl.directory) == Path(mapdl.inquire()) + assert str(Path(mapdl.directory)) == str(Path(mapdl.inquire())) def test_vwrite_error(mapdl, cleared): @@ -2576,3 +2576,19 @@ def func(*args, **kwargs): reload(pymapdl) pymapdl.helpers.run_first_time() + + +def test_max_cmd_len(mapdl): + with pytest.raises( + ValueError, match="Maximum command length must be less than 640 characters" + ): + cmd = "a" * 640 + mapdl.run(cmd) + + +def test_max_cmd_len_mapdlgrpc(mapdl): + with pytest.raises( + ValueError, match="Maximum command length must be less than 640 characters" + ): + cmd = "a" * 640 + mapdl._run(cmd) diff --git a/tests/test_mesh_grpc.py b/tests/test_mesh_grpc.py index a0aaf421f7..e914974b22 100644 --- a/tests/test_mesh_grpc.py +++ b/tests/test_mesh_grpc.py @@ -317,3 +317,42 @@ def test_nodal_rotation(mapdl, cleared): ] ) assert np.allclose(nrotation_ref, nrotations[:7, :]) + + +def test_esln(mapdl, two_dimensional_mesh): + mapdl.nsel("S", "LOC", "X", 0) + selected_ids = mapdl.esln("S", 0) + expected_selected_ids = np.array([1, 41, 81, 121, 161, 201, 241, 281, 321, 361]) + assert all(selected_ids == expected_selected_ids) + + +def test_nsle(mapdl, two_dimensional_mesh): + mapdl.esel("S", "CENT", "X", 0, 0.1) + selected_ids = mapdl.nsle("S") + expected_selected_ids = np.array( + [ + 1, + 3, + 52, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + ] + ) + assert all(selected_ids == expected_selected_ids) diff --git a/tests/test_parameters.py b/tests/test_parameters.py index ca5660286a..f797b89173 100644 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -20,7 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import logging import re +from unittest.mock import patch import numpy as np import pytest @@ -28,22 +30,21 @@ from ansys.mapdl.core.errors import MapdlRuntimeError from ansys.mapdl.core.parameters import interp_star_status -parm_status = """PARAMETER STATUS- PORT ( 12 PARAMETERS DEFINED) +GOLDEN_TESTS = { + "parameter status": """PARAMETER STATUS- PORT ( 12 PARAMETERS DEFINED) (INCLUDING 3 INTERNAL PARAMETERS) NAME VALUE TYPE DIMENSIONS - PORT 50054.0000 SCALAR""" - -arr_status = """PARAMETER STATUS- ASDF ( 5 PARAMETERS DEFINED) + PORT 50054.0000 SCALAR""", + "array status": """PARAMETER STATUS- ASDF ( 5 PARAMETERS DEFINED) (INCLUDING 3 INTERNAL PARAMETERS) LOCATION VALUE 1 1 1 1.00000000 2 1 1 2.00000000 3 1 1 3.00000000 - 4 1 1 4.00000000""" - -arr3d_status = """ + 4 1 1 4.00000000""", + "array 3d status": """ PARAMETER STATUS- MYARR ( 6 PARAMETERS DEFINED) (INCLUDING 3 INTERNAL PARAMETERS) @@ -74,17 +75,15 @@ 3 2 3 3.00000000 1 3 3 0.00000000 2 3 3 0.00000000 - 3 3 3 0.00000000""" - -strarr_status = """PARAMETER STATUS- MYSTR3 ( 12 PARAMETERS DEFINED) + 3 3 3 0.00000000""", + "string array status": """PARAMETER STATUS- MYSTR3 ( 12 PARAMETERS DEFINED) (INCLUDING 3 INTERNAL PARAMETERS) 96 1 1 aqzzzxcv zx zxcv zxcv 96 2 1 qwer wer qwer - 96 3 1 zxcv""" - -gen_status = """ABBREVIATION STATUS- + 96 3 1 zxcv""", + "general status": """ABBREVIATION STATUS- ABBREV STRING SAVE_DB SAVE @@ -104,7 +103,8 @@ PGFZJK_DIM 20.0000000 SCALAR PGFZJK_ROWDIM 20.0000000 SCALAR PORT 50054.0000 SCALAR - STRARRAY STRING ARRAY 96 1 1""" + STRARRAY STRING ARRAY 96 1 1""", +} @pytest.mark.parametrize( @@ -339,12 +339,12 @@ def test_parameter_delete_raise(mapdl, cleared): @pytest.mark.parametrize( - "status,check", + "status_key,check", [ - (parm_status, 50054), - (arr_status, np.array([1, 2, 3, 4])), + ("parameter status", 50054), + ("array status", np.array([1, 2, 3, 4])), ( - arr3d_status, + "array 3d status", np.array( [ [[1.0, 1.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 0.0]], @@ -353,11 +353,12 @@ def test_parameter_delete_raise(mapdl, cleared): ] ), ), - (strarr_status, ["aqzzzxcv zx zxcv zxcv", "qwer wer qwer", "zxcv"]), - (gen_status, None), + ("string array status", ["aqzzzxcv zx zxcv zxcv", "qwer wer qwer", "zxcv"]), + ("general status", None), ], ) -def test_interp_star_status(status, check): +def test_interp_star_status(status_key, check): + status = GOLDEN_TESTS[status_key] output = interp_star_status(status) if len(output) == 1: name = list(output.keys())[0] @@ -470,3 +471,25 @@ def test_non_interactive(mapdl, cleared): mapdl.parameters["qwer"] = 3 assert mapdl.parameters["qwer"] == 3 + + +@pytest.mark.parametrize("value", [121, 299]) +def test_failing_get_routine(mapdl, caplog, value): + from ansys.mapdl.core.parameters import ROUTINE_MAP + + prev_level = mapdl.logger.logger.level + mapdl.logger.setLevel(logging.INFO) + + with patch("ansys.mapdl.core.mapdl_extended._MapdlExtended.get_value") as mck: + mck.return_value = value + with caplog.at_level(logging.INFO): + routine = mapdl.parameters.routine + + mck.assert_called_once() + + txt = str(caplog.text) + assert f"Getting a valid routine number failed." in txt + assert f"Routine obtained is {value}. Executing 'FINISH'." in txt + assert routine == ROUTINE_MAP[0] + + mapdl.logger.setLevel(prev_level) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index feea3c8bd2..d9e8a6a29d 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -876,7 +876,7 @@ def debug_orders(pl, point): def test_plotter_input(mapdl, make_block): import pyvista as pv - pl = MapdlPlotter(off_screen=False) + pl = MapdlPlotter() pl2 = mapdl.eplot(return_plotter=True, plotter=pl) assert pl is pl2 pl2.show() # plotting for catching diff --git a/tests/test_post.py b/tests/test_post.py index 42619bb036..489123e589 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -55,9 +55,10 @@ def test_repr(mapdl, cleared): assert "Enable routine POST1 to see a table of available results" in repr_ mapdl.post1() - repr_ = mapdl.post_processing.__repr__() - assert "Enable routine POST1 to see a table of available results" not in repr_ - assert mapdl.set("LIST") in repr_ + assert ( + "Enable routine POST1 to see a table of available results" + not in mapdl.post_processing.__repr__() + ) class Test_static_solve(TestClass): @@ -770,6 +771,16 @@ def resume(mapdl, plastic_solve): nsigfig = 10 mapdl.format("", "E", nsigfig + 9, nsigfig) + @staticmethod + def test_list_in_repr(mapdl, resume): + mapdl.finish() + assert "Enable routine POST1 to see a table of available results" in str( + mapdl.post_processing + ) + + mapdl.post1() + assert mapdl.set("LIST") in mapdl.post_processing.__str__() + @staticmethod @pytest.mark.parametrize("comp", COMPONENT_STRESS_TYPE) def test_nodal_plastic_component_strain(mapdl, resume, comp): diff --git a/tests/test_xpl.py b/tests/test_xpl.py index 979b58d594..639e6f9a3e 100644 --- a/tests/test_xpl.py +++ b/tests/test_xpl.py @@ -39,114 +39,143 @@ def check_supports_extract(mapdl): pytest.skip("command not supported") -@pytest.fixture(scope="function") -def xpl(mapdl, cube_solve): - xpl = mapdl.xpl - xpl.open("file.full") - return xpl +class Test_xpl: + + @staticmethod + @pytest.fixture(scope="class") + def cube_solve(mapdl): + from conftest import clear + + clear(mapdl) + + # set up the full file + mapdl.block(0, 1, 0, 1, 0, 1) + mapdl.et(1, 186) + mapdl.esize(0.5) + mapdl.vmesh("all") + + # Define a material (nominal steel in SI) + mapdl.mp("EX", 1, 210e9) # Elastic modulus in Pa (kg/(m*s**2)) + mapdl.mp("DENS", 1, 7800) # Density in kg/m3 + mapdl.mp("NUXY", 1, 0.3) # Poisson's Ratio + + # solve first 10 non-trivial modes + mapdl.modal_analysis(nmode=10, freqb=1) + mapdl.save("cube_solve_xpl") + + @staticmethod + @pytest.fixture(scope="function") + def xpl(mapdl, cube_solve): + mapdl.prep7() + mapdl.resume("cube_solve_xpl") + + xpl = mapdl.xpl + xpl.open("file.full") + + return xpl + + @staticmethod + def test_close(xpl): + xpl.close() + with pytest.raises(MapdlCommandIgnoredError): + xpl.list() + + @staticmethod + def test_xpl_str(xpl): + assert "file.full" in str(xpl) + + @staticmethod + @requires("ansys-math-core") + def test_read_int32(xpl): + vec = xpl.read("MASS") + arr = vec.asarray() + assert arr.size + assert arr.dtype == np.int32 + + @staticmethod + @requires("ansys-math-core") + def test_read_double(xpl): + vec = xpl.read("DIAGK") + arr = vec.asarray() + assert arr.size + assert arr.dtype == np.double + + @staticmethod + @requires("ansys-math-core") + def test_read_asarray(xpl): + vec1 = xpl.read("MASS", asarray=True) + vec2 = xpl.read("MASS") + assert np.allclose(vec1, vec2.asarray()) + + @staticmethod + def test_save(xpl): + xpl.save() + with pytest.raises(MapdlCommandIgnoredError): + xpl.list() + + @staticmethod + def test_copy(mapdl, cleared, xpl): + filename = "tmpfile.full" + xpl.copy(filename) + assert filename in mapdl.list_files() + + @staticmethod + def test_list(xpl): + assert "::FULL::" in xpl.list(1) + + @staticmethod + def test_help(xpl): + assert "SAVE" in xpl.help() + + @staticmethod + def test_step_where(xpl): + xpl.step("MASS") + assert "FULL::MASS" in xpl.where() + + with pytest.raises(MapdlRuntimeError): + xpl.step("notarecord") + + @staticmethod + def test_info(xpl): + assert "Record Size" in xpl.info("NGPH") + + @staticmethod + def test_print(xpl): + assert "10" in xpl.print("MASS") + + @staticmethod + def test_json(xpl): + json_out = xpl.json() + assert json_out["name"] == "FULL" + assert "children" in json_out + + @staticmethod + def test_up(xpl): + xpl.step("MASS") + xpl.up() + assert "Current Location : FULL" in xpl.where() + + xpl.up("TOP") + assert "Current Location : FULL" in xpl.where() + + @staticmethod + def test_goto(xpl): + xpl.goto("MASS") + assert "Current Location : FULL::MASS" in xpl.where() + + @staticmethod + @requires("ansys-math-core") + @pytest.mark.usefixtures("check_supports_extract") + def test_extract(xpl): + # expecting fixture to already have a non-result file open + assert xpl._filename[-3:] != "rst" + with pytest.raises(MapdlRuntimeError, match="result files"): + mat = xpl.extract("NSL") + + xpl.open("file.rst") + + with pytest.raises(ValueError, match="the only supported recordname is 'NSL'"): + xpl.extract("NOD") - -def test_close(xpl): - xpl.close() - with pytest.raises(MapdlCommandIgnoredError): - xpl.list() - - -def test_xpl_str(xpl): - assert "file.full" in str(xpl) - - -@requires("ansys-math-core") -def test_read_int32(xpl): - vec = xpl.read("MASS") - arr = vec.asarray() - assert arr.size - assert arr.dtype == np.int32 - - -@requires("ansys-math-core") -def test_read_double(xpl): - vec = xpl.read("DIAGK") - arr = vec.asarray() - assert arr.size - assert arr.dtype == np.double - - -@requires("ansys-math-core") -def test_read_asarray(xpl): - vec1 = xpl.read("MASS", asarray=True) - vec2 = xpl.read("MASS") - assert np.allclose(vec1, vec2.asarray()) - - -def test_save(xpl): - xpl.save() - with pytest.raises(MapdlCommandIgnoredError): - xpl.list() - - -def test_copy(mapdl, cleared, xpl): - filename = "tmpfile.full" - xpl.copy(filename) - assert filename in mapdl.list_files() - - -def test_list(xpl): - assert "::FULL::" in xpl.list(1) - - -def test_help(xpl): - assert "SAVE" in xpl.help() - - -def test_step_where(xpl): - xpl.step("MASS") - assert "FULL::MASS" in xpl.where() - - with pytest.raises(MapdlRuntimeError): - xpl.step("notarecord") - - -def test_info(xpl): - assert "Record Size" in xpl.info("NGPH") - - -def test_print(xpl): - assert "10" in xpl.print("MASS") - - -def test_json(xpl): - json_out = xpl.json() - assert json_out["name"] == "FULL" - assert "children" in json_out - - -def test_up(xpl): - xpl.step("MASS") - xpl.up() - assert "Current Location : FULL" in xpl.where() - - xpl.up("TOP") - assert "Current Location : FULL" in xpl.where() - - -def test_goto(xpl): - xpl.goto("MASS") - assert "Current Location : FULL::MASS" in xpl.where() - - -@requires("ansys-math-core") -@pytest.mark.usefixtures("check_supports_extract") -def test_extract(xpl): - # expecting fixture to already have a non-result file open - assert xpl._filename[-3:] != "rst" - with pytest.raises(MapdlRuntimeError, match="result files"): mat = xpl.extract("NSL") - - xpl.open("file.rst") - - with pytest.raises(ValueError, match="the only supported recordname is 'NSL'"): - xpl.extract("NOD") - - mat = xpl.extract("NSL") - assert mat.shape == (243, 10) + assert mat.shape == (243, 10)