diff --git a/.ci/run_vms.py b/.ci/run_vms.py new file mode 100644 index 0000000000..087513ff83 --- /dev/null +++ b/.ci/run_vms.py @@ -0,0 +1,14 @@ +"""Run some vm manuals""" +from ansys.mapdl.core import launch_mapdl +from ansys.mapdl.core.examples import vmfiles + +mapdl = launch_mapdl() + +vms = list(vmfiles.keys()) + +for i, vm in enumerate(vms[:2]): + mapdl.clear() + print(f"Running the vm {i}: {vm}") + output = mapdl.input(vmfiles[vm]) + print(f"Running the vm {i}: Successfully completed") +mapdl.exit() diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee14c00bba..ac96543dc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,11 +47,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - defaults: run: shell: bash +permissions: + contents: write + packages: read + pull-requests: write + jobs: doc-style: @@ -62,6 +66,7 @@ jobs: uses: ansys/actions/doc-style@v4 with: token: ${{ secrets.GITHUB_TOKEN }} + version: 2.29.6 smoke-tests: name: "Build and smoke tests" @@ -260,7 +265,7 @@ jobs: run: cat log.txt build-test: - name: "Remote: Build and unit testing" + name: "Remote: Build & test" runs-on: ubuntu-latest if: github.ref != 'refs/heads/main' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' timeout-minutes: 35 @@ -455,7 +460,7 @@ jobs: if compgen -G './logs-${{ matrix.mapdl-version }}/*.out' > /dev/null ;then for f in ./logs-${{ matrix.mapdl-version }}/*.out; do echo "::group:: Output file $f" && cat $f && echo "::endgroup::" ; done; fi || echo "Failed to display the 'log' files." build-test-ubuntu: - name: "Local: Build and unit testing on Ubuntu" + name: "Local: Build & test on Ubuntu" runs-on: ubuntu-latest if: github.ref != 'refs/heads/main' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' timeout-minutes: 55 @@ -481,6 +486,7 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} + - name: "Setup Python" uses: actions/setup-python@v4 with: @@ -576,10 +582,89 @@ jobs: with: name: ubuntu-v22.2.0-local.xml path: ./ubuntu-v22.2.0-local.xml + + - name: 'Upload minimal requirements file' + uses: actions/upload-artifact@v3 + with: + name: minimum_requirements.txt + path: ./minimum_requirements.txt + + build-test-ubuntu-minimal: + name: "Local: Build & test minimal package on Ubuntu" + runs-on: ubuntu-latest + if: github.ref != 'refs/heads/main' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + timeout-minutes: 55 + container: + image: ghcr.io/ansys/mapdl:v22.2-ubuntu + options: "-u=0:0 --entrypoint /bin/bash" + credentials: + username: ${{ secrets.GH_USERNAME }} + password: ${{ secrets.GITHUB_TOKEN }} + env: + ON_LOCAL: true + ON_UBUNTU: true + # Because there is no 'ansys-tools-path' we need to input the executable path. + PYMAPDL_MAPDL_EXEC: /ansys_inc/v222/ansys/bin/ansys222 + + steps: + - name: "Install Git and checkout project" + uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: "Setup Python" + uses: actions/setup-python@v4 + 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: "Running some verification manual examples" + run: | + unset PYMAPDL_START_INSTANCE + export ANSYSLMD_LICENSE_FILE=1055@${{ secrets.LICENSE_SERVER }} + python .ci/run_vms.py + + - name: "Unit testing requirements installation" + run: | + python -m pip install pytest pytest-rerunfailures pytest-cov + + - name: "Unit testing" + run: | + unset PYMAPDL_START_INSTANCE + export ANSYSLMD_LICENSE_FILE=1055@${{ secrets.LICENSE_SERVER }} + pytest -k "not test_dpf" \ + ${{ env.PYTEST_ARGUMENTS }} \ + --skip-regression-check \ + --cov-report=xml:ubuntu-v22.2.0-local-minimal.xml + + - uses: codecov/codecov-action@v3 + name: "Upload coverage to Codecov" + with: + token: ${{ secrets.CODECOV_TOKEN }} + root_dir: ${{ github.workspace }} + name: ubuntu-v22.2.0-local-minimal.xml + flags: ubuntu,local,v22.2.0,minimal + + - name: 'Upload coverage artifacts' + uses: actions/upload-artifact@v3 + with: + name: ubuntu-v22.2.0-local-minimal.xml + path: ./ubuntu-v22.2.0-local-minimal.xml test-windows: if: github.repository == '' - name: "Local: Build and unit testing on Windows" + name: "Local: Build & test on Windows" runs-on: [self-hosted, Windows, pymapdl] timeout-minutes: 30 env: @@ -671,6 +756,7 @@ jobs: ./**/*.tar.gz ./**/*pymapdl-Documentation-*.pdf ./**/ansys-mapdl-core*.zip + ./minimum_requirements.txt - name: Upload to Public PyPi env: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6036af92c2..ec1c50a2f6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,8 +7,21 @@ repos: hooks: - id: isort +- repo: https://github.com/numpy/numpydoc + rev: v1.6.0 + hooks: + - id: numpydoc-validation + exclude: | + (?x)( + tests/| + examples| + doc/source/| + src/ansys/mapdl/core/_commands| + src/ansys/mapdl/core/commands + ) + - repo: https://github.com/psf/black - rev: 23.9.1 # IF VERSION CHANGES --> MODIFY "blacken-docs" MANUALLY AS WELL!! + rev: 23.10.1 # IF VERSION CHANGES --> MODIFY "blacken-docs" MANUALLY AS WELL!! hooks: - id: black args: @@ -18,7 +31,7 @@ repos: rev: 1.16.0 hooks: - id: blacken-docs - additional_dependencies: [black==23.9.1] + additional_dependencies: [black==23.10.1] - repo: https://github.com/PyCQA/flake8 rev: 6.1.0 diff --git a/README.rst b/README.rst index 7952f1a9d6..1dbb9c915a 100644 --- a/README.rst +++ b/README.rst @@ -73,7 +73,11 @@ you can create issues to report bugs and request new features. On the `PyMAPDL D `_ page or the `Discussions `_ page on the Ansys Developer portal, you can post questions, share ideas, and get community feedback. -To reach the project support team, email `pyansys.core@ansys.com `_. +To reach the project support team, email `PyAnsys Core team `_. +Unfortunately, this team cannot answer specific library questions or issues. You must +use the `PyMAPDL Issues `_ +and `PyMAPDL Discussions `_ pages +for raising issues, request new features, and asking questions. Project transition - legacy support ----------------------------------- @@ -110,6 +114,14 @@ Install the latest release from `PyPi pip install ansys-mapdl-core +If you are planning to use PyMAPDL together with `Jupyter lab `_, +you can install both in one step: + +.. code:: console + + pip install 'ansys-mapdl-core[jupyter]' + + Alternatively, install the latest from `PyMAPDL GitHub `_ via: diff --git a/doc/.vale.ini b/doc/.vale.ini index 7d2d583897..4383ab4c6d 100644 --- a/doc/.vale.ini +++ b/doc/.vale.ini @@ -27,7 +27,8 @@ Vocab = ANSYS # Apply the following styles BasedOnStyles = Vale, Google -TokenIgnores = (:(func|class|meth|attr|py):`(?:.|\n)*?`)|(<.*>)|(.. code::.*\n| .*) +TokenIgnores = (:(func|class|meth|attr|py):`(?:.|\n)*?`)|(<.*>) # Removing Google-specific rule - Not applicable under some circumstances Google.Colons = NO +Google.Headings = NO diff --git a/doc/Makefile b/doc/Makefile index c39e30d919..b2bf4c21e1 100755 --- a/doc/Makefile +++ b/doc/Makefile @@ -23,6 +23,7 @@ help: # customized clean due to examples gallery clean: + @echo "Cleaning everything." rm -rf $(BUILDDIR)/* rm -rf images/auto-generated rm -rf source/examples/gallery_examples @@ -30,11 +31,13 @@ clean: # customized clean due to examples gallery clean-except-examples: + @echo "Cleaning everything except the examples." rm -rf $(BUILDDIR)/* find . -type d -name "_autosummary" -exec rm -rf {} + # clean only examples clean-examples: + @echo "Cleaning only the examples." rm -rf source/examples/gallery_examples # customized to build the pdf rather than using latexpdf due to various issues @@ -51,6 +54,7 @@ linkcheck: @echo "Check finished. Report is in $(LINKCHECKDIR)." html-noexamples: - @$(SPHINXBUILD) -D plot_gallery=0 -b html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @echo "Building gallery without running examples." + @$(SPHINXBUILD) -D plot_gallery=0 -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(O) @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)." \ No newline at end of file + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." \ No newline at end of file diff --git a/doc/source/api/unit_testing.rst b/doc/source/api/unit_testing.rst index a8a2bb33d5..74ac56865b 100644 --- a/doc/source/api/unit_testing.rst +++ b/doc/source/api/unit_testing.rst @@ -119,7 +119,7 @@ Most PyMAPDL tests require a connection to a running instance of MAPDL, which makes them integration tests. If your test requires a running MAPDL instance, you can use the PyMAPDL `mapdl `_ method in your function signature. -It will be executed upstream of each test and not within all tests. +It is executed upstream of each test and not within all tests. .. code:: python @@ -160,7 +160,7 @@ Here are some examples of how you use ``pytest``: def test_dunder_methods_keys(mapdl, basic_components): - assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.list()) + assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.names()) def test_dunder_methods_types(mapdl, basic_components): diff --git a/doc/source/conf.py b/doc/source/conf.py index 02b9e8ce4b..8656edc17e 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -86,6 +86,7 @@ "sphinx.ext.graphviz", "sphinx_reredirects", "ansys_sphinx_theme.extension.linkcode", + "sphinx_design", ] # Intersphinx mapping @@ -243,6 +244,7 @@ "thumbnail_size": (350, 350), "remove_config_comments": True, "default_thumb_file": pyansys_logo_white, + "show_signature": False, } # --- @@ -258,6 +260,7 @@ "show_breadcrumbs": True, "collapse_navigation": True, "use_edit_page_button": True, + "navigation_with_keys": False, "additional_breadcrumbs": [ ("PyAnsys", "https://docs.pyansys.com/"), ], diff --git a/doc/source/examples/devportal.rst b/doc/source/examples/devportal.rst new file mode 100644 index 0000000000..7176e618eb --- /dev/null +++ b/doc/source/examples/devportal.rst @@ -0,0 +1,42 @@ +.. _ref_devportal_examples: + + +PyMAPDL Developer Portal articles +================================= + +In this section, you'll find informative and insightful blog +articles crafted by the expert developers at Ansys and published +in the `Developer Portal `_. +Whether you're a seasoned pro or just getting started with PyMAPDL, the blog +articles cover a wide range of topics, from best practices and tips to in-depth +tutorials and real-world use cases. The aim is to provide you with a valuable +resource that can help you harness the full potential of PyMAPDL for your +engineering and simulation needs. + +Because the Developer Portal is your hub for all things related to Ansys +products, if you have questions or suggestions, you can go there to +connect to Ansys experts and the growing Ansys developer community. + +.. Limiting the amount of lines to 2 at 80 chars +.. grid:: 2 + :gutter: 2 + + .. grid-item-card:: Biomedical catheter design analysis + :img-top: https://developer.ansys.com/sites/default/files/inline-images/BMCatheter_1_0.png + :link: https://developer.ansys.com/blog/biomedical-catheter-design-analysis + :text-align: center + :class-title: pyansys-card-title + + Analyzing biomedical catheter flexibility using PyMAPDL scripting to + assess its flexural rigidity in various designs. + + + .. grid-item-card:: Postprocessing of simplified bolted connections with the help of PyAnsys + :img-top: https://developer.ansys.com/sites/default/files/inline-images/Postprocessing_of_simplified_bolted_connections_with_the_help_of_PyAnsys_image_04.jpg + :link: https://developer.ansys.com/blog/postprocessing-simplified-bolted-connections-help-pyansys + :text-align: center + :class-title: pyansys-card-title + + Illustrating PyMAPDL's GUI possibilities for pre-tensioned bolted + connections, adhering to industry standards, and data extraction to Excel + for analysis. diff --git a/doc/source/examples/extended_examples/executable/executable.rst b/doc/source/examples/extended_examples/executable/executable.rst index b68b19b501..6f2b41dc4a 100644 --- a/doc/source/examples/extended_examples/executable/executable.rst +++ b/doc/source/examples/extended_examples/executable/executable.rst @@ -227,7 +227,7 @@ using this approach (non-tested): run: | python rotor.py 4 --density 7000 - - name: "Postprocess images" + - name: "Postprocessing images" run: | COMPOSITE=/usr/bin/composite mogrify -mattecolor #f1ce80 -frame 10x10 volume.jpg diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index 9fc3514f27..de835a6ad2 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -1,5 +1,16 @@ .. _ref_examples: + +.. toctree:: + :hidden: + :maxdepth: 3 + + devportal.rst + Verification Manual + Technology Showcases + extended_examples/index.rst + + ======== Examples ======== @@ -17,22 +28,18 @@ Here are a series of examples using MAPDL with the ``ansys-mapdl-core`` library. :start-line: 2 -.. === PYMAPDL EXAMPLES === +.. === Developer Portal examples == -.. toctree:: - :hidden: - :maxdepth: 3 +.. include:: devportal.rst + :start-line: 2 - Verification Manual - Technology Showcases - extended_examples/index.rst +.. === PyMAPDL examples === .. include:: pymapdl_examples_repo/index.rst :start-line: 2 -.. === EXTENDED EXAMPLES === - +.. === Extended examples === .. include:: extended_examples/index.rst :start-line: 2 @@ -54,5 +61,4 @@ These links refers to the following GitHub repository where you can find all of `GitHub Example Data Repository `_ If you find a missing or broken link, open an issue in -GitHub (`PyMAPDL Issues `_) -or send an email to `PyAnsys Core team `_. +GitHub (`PyMAPDL Issues `_). diff --git a/doc/source/getting_started/contribution.rst b/doc/source/getting_started/contribution.rst index 2377fe7085..5be0ac5c19 100644 --- a/doc/source/getting_started/contribution.rst +++ b/doc/source/getting_started/contribution.rst @@ -33,12 +33,13 @@ Use the `PyMAPDL Issues `_ page to submit questions, report bugs, and request new features. When possible, use these issue templates: -* Bug report template -* Feature request template +* **🐞 Bug, problem, or error**: Fill a bug report here +* **📖 Documentation issue**: Modifications to the documentation only +* **🎓 Adding an example**: Proposing a new example for the library +* **💡 New feature**: Enhancements to the code -If your issue does not fit into one of these categories, create your own issue. +If your issue does not fit into one of these categories, click in `Open a blank issue `_. -To reach the project support team, email `PyAnsys Core team `_. Viewing PyMAPDL documentation ============================= diff --git a/doc/source/getting_started/learning.rst b/doc/source/getting_started/learning.rst index d76de57073..b421f92dbd 100644 --- a/doc/source/getting_started/learning.rst +++ b/doc/source/getting_started/learning.rst @@ -58,10 +58,10 @@ Course content -Getting Started with PyMAPDL +Getting started with PyMAPDL ---------------------------- -The `Getting Started with PyMAPDL `_ course teaches +The `Getting started with PyMAPDL `_ course teaches you about PyMAPDL, the Pythonic way to access Ansys MAPDL. Course content @@ -76,10 +76,10 @@ Course content -Intro to Ansys Mechanical APDL Scripting +Intro to Ansys Mechanical APDL scripting ---------------------------------------- -The `Intro to Ansys Mechanical APDL Scripting `_ +The `Intro to Ansys Mechanical APDL scripting `_ course teaches you how APDL, the Mechanical solver syntax language, works. Course content @@ -174,5 +174,5 @@ Recommended articles .. vale on -Feel free to email any educational or learning resource to -`PyAnsys Core team `_. \ No newline at end of file +Feel free to share any educational or learning resources by `opening an issue `_ +or `starting a discussion `_ on GitHub. \ No newline at end of file diff --git a/doc/source/getting_started/versioning.rst b/doc/source/getting_started/versioning.rst index 2a543bc93d..10e12e1325 100644 --- a/doc/source/getting_started/versioning.rst +++ b/doc/source/getting_started/versioning.rst @@ -86,8 +86,7 @@ it is recommended exploring alternative interfaces available in PyMAPDL. PyMAPDL maintainers greatly appreciate your support and understanding during this transition. If you have any questions or concerns regarding this change, -email `pyansys.core@ansys.com `_ or -post them on the `PyMAPDL Discussions `_ page. +post them on the `PyMAPDL Discussions `_ page. Console interface ----------------- diff --git a/doc/source/index.rst b/doc/source/index.rst index 187352b145..e6186ff38b 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -181,7 +181,12 @@ you can create issues to report bugs and request new features. On the `PyMAPDL D `_ page or the `Discussions `_ page on the Ansys Developer portal, you can post questions, share ideas, and get community feedback. -To reach the project support team, email `pyansys.core@ansys.com `_. +To reach the PyAnsys project support team, email `PyAnsys Core team `_. +Unfortunately no specific library questions or issues are answered through this email. +Please refer to `PyMAPDL Issues `_, +or `PyMAPDL Discussions `_ for raising issues, +request new features, or asking questions. + Project index ------------- diff --git a/doc/source/links.rst b/doc/source/links.rst index 5949e5276f..984799e0de 100644 --- a/doc/source/links.rst +++ b/doc/source/links.rst @@ -54,6 +54,9 @@ .. _ansys_introductory_mapdl_courses: https://ansyshelp.ansys.com/account/secured?returnurl=/Views/Secured/corp/%%VERSION%%/en/ans_tut/Hlp_UI_Tutorials.html .. _ansys_current_supported_os: https://www.ansys.com/content/dam/it-solutions/platform-support/2023-r2/ansys-2023-r2-platform-support-by-application.pdf +.. # Developer portal +.. _developer_portal: https://developer.ansys.com + .. # Ansys guides .. _ansys_command_reference_guide: https://ansyshelp.ansys.com/account/secured?returnurl=/Views/Secured/corp/%%VERSION%%/en/ans_cmd/Hlp_C_CmdTOC.html .. _ansys_techdemo_guide: https://ansyshelp.ansys.com/Views/Secured/corp/%%VERSION%%/en/ans_tec/tecintro.html @@ -120,6 +123,8 @@ .. _pymapdl_releases: https://github.com/ansys/pymapdl/releases .. _pymapdl_run_ubuntu: https://mapdl.docs.pyansys.com/version/dev/user_guide/troubleshoot.html#ubuntu .. _pymapdl_issues: https://github.com/ansys/pymapdl/issues +.. _pymapdl_new_issue: https://github.com/ansys/pymapdl/issues/new/choose +.. _pymapdl_new_blank_issue: https://github.com/ansys/pymapdl/issues/new .. _pymapdl_repo: https://github.com/ansys/pymapdl .. _pymapdl_docs: https://mapdl.docs.pyansys.com/version/stable/ .. _pymapdl_dev_docs: https://mapdl.docs.pyansys.com/version/dev/ diff --git a/doc/source/user_guide/database.rst b/doc/source/user_guide/database.rst index f0e87b65c8..1eb4b1ea12 100644 --- a/doc/source/user_guide/database.rst +++ b/doc/source/user_guide/database.rst @@ -1,7 +1,7 @@ Access MAPDL database ===================== -.. warning:: This feature is still in beta. Report any errors or suggestions to `PyAnsys Core team `_. +.. warning:: This feature is still in beta. To report any errors or suggestions, `open an issue on GitHub `_. In PyMAPDL v0.61.2 and later, you can access elements and nodes data from the MAPDL database using the DB module. diff --git a/doc/source/user_guide/index.rst b/doc/source/user_guide/index.rst index 330af6785b..e94fbbc485 100644 --- a/doc/source/user_guide/index.rst +++ b/doc/source/user_guide/index.rst @@ -79,16 +79,16 @@ example, if you input an invalid command: >>> mapdl.run("AL, 1, 2, 3") - apdlRuntimeError: - L, 1, 2, 3 + MapdlRuntimeError: + AL, 1, 2, 3 - EFINE AREA BY LIST OF LINES - INE LIST = 1 2 3 + DEFINE AREA BY LIST OF LINES + LINE LIST = 1 2 3 TRAVERSED IN SAME DIRECTION AS LINE 1) - ** ERROR *** CP = 0.338 TIME= 09:45:36 - eypoint 1 is referenced by only one line. Improperly connected line - et for AL command. + *** ERROR *** CP = 0.338 TIME= 09:45:36 + Keypoint 1 is referenced by only one line. Improperly connected line + set for AL command. This ``MapdlRuntimeError`` was caught immediately. This means that you can write your MAPDL scripts in Python, run them interactively, and diff --git a/doc/source/user_guide/krylov.rst b/doc/source/user_guide/krylov.rst index 4788b87884..61604bdd21 100644 --- a/doc/source/user_guide/krylov.rst +++ b/doc/source/user_guide/krylov.rst @@ -93,8 +93,8 @@ using Mechanical APDL: >>> mapdl = launch_mapdl() >>> mapdl.prep7() - # Generate the FEA model (mesh, constraints, loads) - # ... + # Generate the FEA model (mesh, constraints, loads) + # ... >>> mapdl.run("/SOLU") >>> mapdl.antype("HARMIC") # HARMONIC ANALYSIS diff --git a/doc/source/user_guide/launcher.rst b/doc/source/user_guide/launcher.rst index d7b2028a79..cfc3c61941 100644 --- a/doc/source/user_guide/launcher.rst +++ b/doc/source/user_guide/launcher.rst @@ -6,7 +6,7 @@ the location of MAPDL must be provided for non-standard installations. When running for the first time, ``ansys-mapdl-core`` requests the location of the MAPDL executable if it cannot automatically find it. You can test your installation of PyMAPDL and set it up by running -the :func:`launch_mapdl() ` function: +the :func:`launch_mapdl() ` function: .. code:: python @@ -81,4 +81,4 @@ keyword argument: API reference ~~~~~~~~~~~~~ For more information on controlling how MAPDL launches locally, see the -description of the :func:`launch_mapdl() ` function. +description of the :func:`launch_mapdl() ` function. diff --git a/doc/source/user_guide/mesh_geometry.rst b/doc/source/user_guide/mesh_geometry.rst index 6f62aa7ae1..9e1fbd9860 100644 --- a/doc/source/user_guide/mesh_geometry.rst +++ b/doc/source/user_guide/mesh_geometry.rst @@ -235,7 +235,8 @@ One of the most important is that you no longer need to call the entities like y Y Bounds: 0.000e+00, 1.974e+00 Z Bounds: 5.500e-01, 5.500e-01 N Arrays: 4, - ... + ... + **New API** diff --git a/doc/styles/Vocab/ANSYS/accept.txt b/doc/styles/Vocab/ANSYS/accept.txt index 5c3da5e154..de0ee6a543 100644 --- a/doc/styles/Vocab/ANSYS/accept.txt +++ b/doc/styles/Vocab/ANSYS/accept.txt @@ -16,7 +16,9 @@ [Pp]ythonically [Ss]uperelements [Vv]on Mises +2D 3-D +3D aadd aas AAS @@ -25,6 +27,7 @@ ALIS AMLs ans ANSYS +Ansys Mechanical APDL APDL Math APDLMath APIs @@ -60,8 +63,9 @@ extrem filname Fortran FSW -Getting Started with PyMAPDL +GitHub Gmsh +GPa GUI hexahedral hostname @@ -69,7 +73,6 @@ Imagemagick ImageMagick imagin importlib -Intro to Ansys Mechanical APDL Scripting ist Julia Krylov @@ -80,6 +83,7 @@ LDHI levl Linux MacOS +mapdl MAPDL mater MATLAB @@ -89,6 +93,7 @@ midside Mises mkdir MSc +Multi-[Ff]ield multipoint nce Newton-Raphson @@ -140,6 +145,7 @@ trian UCnvrg uncompress unconverged +unpause UNSYM unsymmetric UPF diff --git a/minimum_requirements.txt b/minimum_requirements.txt new file mode 100644 index 0000000000..be7a8b7caa --- /dev/null +++ b/minimum_requirements.txt @@ -0,0 +1,6 @@ +ansys-api-mapdl==0.5.1 +importlib-metadata==6.8.0 +numpy==1.26.1 +platformdirs==3.11.0 +psutil==5.9.6 +pyansys-tools-versioning==0.5.0 diff --git a/pyproject.toml b/pyproject.toml index 900e10a19f..a7e2f1fb0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ name = "ansys-mapdl-core" version = "0.68.dev0" description = "A Python wrapper for Ansys MAPDL." readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.8,<3.12" license = {file = "LICENSE"} authors = [ {name = "Ansys, Inc.", email = "pyansys.maintainers@ansys.com"}, @@ -55,15 +55,21 @@ classifiers = [ ] [project.optional-dependencies] +jupyter = [ + "jupyterlab>=3", + "ipywidgets", + "pyvista[jupyter]", +] + tests = [ - "ansys-dpf-core==0.9.0", + "ansys-dpf-core==0.10.0", "autopep8==2.0.4", - "matplotlib==3.8.0", + "matplotlib==3.8.1", "scipy==1.10.1; python_version < '3.9'", # to support python 3.8 "scipy==1.11.3; python_version > '3.8'", - "pandas==2.1.1", + "pandas==2.1.2", "pyiges[full]==0.3.1", - "pytest==7.4.2", + "pytest==7.4.3", "pytest-cov==4.1.0", "pyvista==0.42.3", "pyansys-tools-report==0.7.0", @@ -73,31 +79,32 @@ tests = [ ] doc = [ "sphinx==7.2.6", - "ansys-dpf-core==0.9.0", + "ansys-dpf-core==0.10.0", "ansys-mapdl-reader==0.52.20", - "ansys-sphinx-theme==0.12.3", + "ansys-sphinx-theme==0.12.4", "grpcio==1.51.1", "imageio-ffmpeg==0.4.9", - "imageio==2.31.5", + "imageio==2.31.6", "jupyter_sphinx==0.4.0", "jupyterlab>=3.2.8", - "matplotlib==3.8.0", + "matplotlib==3.8.1", "numpydoc==1.6.0", - "pandas==2.1.1", - "plotly==5.17.0", + "pandas==2.1.2", + "plotly==5.18.0", "pyiges[full]==0.3.1", - "pypandoc==1.11", + "pypandoc==1.12", "pytest-sphinx==0.5.0", "pythreejs==2.4.2", "pyvista[trame]==0.42.3", "sphinx-autobuild==2021.3.14", - "sphinx-autodoc-typehints==1.24.0", + "sphinx-autodoc-typehints==1.24.1", "sphinx-copybutton==0.5.2", "sphinx-gallery==0.14.0", "sphinxcontrib-websupport==1.2.6", "sphinxemoji==0.2.0", "sphinx-reredirects==0.1.2", "vtk==9.2.6", + "sphinx_design==0.5.0", ] [tool.flit.module] @@ -155,4 +162,71 @@ show_missing = true skip = '*.pyc,*.txt,*.gif,*.png,*.jpg,*.js,*.html,*.doctree,*.ttf,*.woff,*.woff2,*.eot,*.mp4,*.inv,*.pickle,*.ipynb,flycheck*,./.git/*,./.hypothesis/*,*.yml,./doc/build/*,./doc/images/*,./dist/*,*~,.hypothesis*,./doc/source/examples/*,*cover,*.dat,*.mac,*.cdb,*.CDB,build,./docker/mapdl/v*,./factory/*,./ansys/mapdl/core/mapdl_functions.py,PKG-INFO,*.mypy_cache/*,./docker/mapdl/*,./_unused/*' ignore-words = "doc/styles/Vocab/ANSYS/accept.txt" quiet-level = 3 -ignore-regex=".*codespell-ignore$|NORML|POIN" \ No newline at end of file +ignore-regex=".*codespell-ignore$|NORML|POIN" + +[tool.numpydoc_validation] +checks = [ + #"all", # report on all checks, except the below + # "GL01", # "Docstring text (summary) should start in the line immediately " + # "after the opening quotes (not in the same line, or leaving a " + # "blank line in between)", + # "GL02", # "Closing quotes should be placed in the line after the last text " + # "in the docstring (do not close the quotes in the same line as " + # "the text, or leave a blank line between the last text and the " + # "quotes)", + # "GL03", # "Double line break found; please use only one blank line to " + # "separate sections or paragraphs, and do not leave blank lines " + # "at the end of docstrings", + "GL05", # 'Tabs found at the start of line "{line_with_tabs}", please use ' + # "whitespace only", + # "GL06", # 'Found unknown section "{section}". Allowed sections are: ' + # "{allowed_sections}", + "GL07", # "Sections are in the wrong order. Correct order is: {correct_sections}", + # "GL08", # "The object does not have a docstring", + # "GL09", # "Deprecation warning should precede extended summary", + "GL10", # "reST directives {directives} must be followed by two colons", + # "SS01", # "No summary found (a short summary in a single line should be " + # "present at the beginning of the docstring)", + "SS02", # "Summary does not start with a capital letter", + # "SS03", # "Summary does not end with a period", + "SS04", # "Summary contains heading whitespaces", + # "SS05", # "Summary must start with infinitive verb, not third person " + # '(e.g. use "Generate" instead of "Generates")', + # "SS06", # "Summary should fit in a single line", + # "ES01", # "No extended summary found", + # "PR01", # "Parameters {missing_params} not documented", + # "PR02", # "Unknown parameters {unknown_params}", + "PR03", # "Wrong parameters order. Actual: {actual_params}. " + # "Documented: {documented_params}", + # "PR04", # 'Parameter "{param_name}" has no type', + "PR05", # 'Parameter "{param_name}" type should not finish with "."', + "PR06", # 'Parameter "{param_name}" type should use "{right_type}" instead ' + # 'of "{wrong_type}"', + "PR07", # 'Parameter "{param_name}" has no description', + # "PR08", # 'Parameter "{param_name}" description should start with a ' + # "capital letter", + # "PR09", # 'Parameter "{param_name}" description should finish with "."', + # "PR10", # 'Parameter "{param_name}" requires a space before the colon ' + # "separating the parameter name and type", + # "RT01", # "No Returns section found", + # "RT02", # "The first line of the Returns section should contain only the " + # "type, unless multiple values are being returned", + # "RT03", # "Return value has no description", + # "RT04", # "Return value description should start with a capital letter", + # "RT05", # 'Return value description should finish with "."', + "YD01", # "No Yields section found", + # "SA01", # "See Also section not found", + "SA02", # "Missing period at end of description for See Also " + # '"{reference_name}" reference', + "SA03", # "Description should be capitalized for See Also " + # '"{reference_name}" reference', + "SA04", # 'Missing description for See Also "{reference_name}" reference', + # "EX01", # "No examples section found", +] + + +override_SS05 = [ # override SS05 to allow docstrings starting with these words + # '^Process ', + # '^Assess ', + # '^Access ', +] diff --git a/src/ansys/mapdl/core/__init__.py b/src/ansys/mapdl/core/__init__.py index bea5894f70..67c2982687 100644 --- a/src/ansys/mapdl/core/__init__.py +++ b/src/ansys/mapdl/core/__init__.py @@ -73,14 +73,18 @@ __version__ = importlib_metadata.version(__name__.replace(".", "-")) - -from ansys.tools.path.path import ( - change_default_ansys_path, - find_ansys, - get_ansys_path, - get_available_ansys_installations, - save_ansys_path, -) +try: + from ansys.tools.path.path import ( + change_default_ansys_path, + find_ansys, + get_ansys_path, + get_available_ansys_installations, + save_ansys_path, + ) +except: + # We don't really use these imports in the library. They are here for + # convenience. + pass from ansys.mapdl.core._version import SUPPORTED_ANSYS_VERSIONS from ansys.mapdl.core.convert import convert_apdl_block, convert_script diff --git a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py index f459e70486..0504307d11 100644 --- a/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py +++ b/src/ansys/mapdl/core/_commands/apdl/parameter_definition.py @@ -518,13 +518,15 @@ def parres(self, lab="", fname="", ext="", **kwargs): >>> mapdl.parres('parm.PARM') """ - if ext: - fname = fname + "." + ext - elif not fname: - fname = "." + "PARM" + if not fname: + fname = self.jobname - if "Grpc" in self.__class__.__name__: # grpc mode - if self._local: + fname = self._get_file_name( + fname=fname, ext=ext, default_extension="parm" + ) # Although documentation says `PARM` + + if self._mode == "grpc": # grpc mode + if self.is_local: # It must be a file! if os.path.isfile(fname): # And it exist! diff --git a/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py b/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py index 7201c54824..0ac25dfa89 100644 --- a/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py +++ b/src/ansys/mapdl/core/_commands/aux12_/radiosity_solver.py @@ -9,8 +9,11 @@ def hemiopt(self, hres="", **kwargs): hres Hemicube resolution. Increase value to increase the accuracy of the view factor calculation. Defaults to 10. + + tolerance + Tolerance value that controls whether or not facets are subdivided in view factor calculations to increase view factor accuracy. TOLERANCE is closely related to the spacing between facets. Defaults to 1e-6. """ - command = f"HEMIOPT,{hres}" + command = f"HEMIOPT,{hres},{tolerance}" return self.run(command, **kwargs) def radopt( diff --git a/src/ansys/mapdl/core/_commands/graphics_/annotation.py b/src/ansys/mapdl/core/_commands/graphics_/annotation.py index 4c6c859444..0492ab648c 100644 --- a/src/ansys/mapdl/core/_commands/graphics_/annotation.py +++ b/src/ansys/mapdl/core/_commands/graphics_/annotation.py @@ -185,6 +185,78 @@ def anum(self, num="", type_="", xhot="", yhot="", **kwargs): command = f"/ANUM,{num},{type_},{xhot},{yhot}" return self.run(command, **kwargs) + def slashlarc(self, xcentr="", ycentr="", xlrad="", angle1="", angle2="", **kwargs): + """Creates annotation arcs (GUI). + + APDL Command: /LARC + + Parameters + ---------- + xcentr + Arc X center location (-1.0 < X < 1.0). + + ycentr + Arc Y center location (-1.0 < Y < 1.0). + + xlrad + Arc radius length. + + angle1 + Starting angle of arc. + + angle2 + Ending angle of arc. The arc is drawn counterclockwise from the + starting angle, `angle1`, to the ending angle, `angle2`. + + Notes + ----- + This command defines annotation arcs to be written directly onto the + display at a specified location. The command is generated by the Graphical + User Interface (GUI) and will appear in the log file (`Jobname.LOG`) if + annotation is used. It is not intended to be typed in directly in a + Mechanical APDL session (although it can be included in an input file + for batch input or for use with the `/INPUT` command). + All arcs are shown on subsequent displays unless the annotation is + turned off or deleted. Issueu `/LSPEC` to set the attributes + of the arc. + + This command is valid in any processor. + """ + command = f"/LARC, {xcentr}, {ycentr}, {xlrad}, {angle1}, {angle2}" + return self.run(command, **kwargs) + + def slashline(self, x1="", y1="", x2="", y2="", **kwargs): + """Creates annotation lines (GUI). + + APDL Command: /LINE + + Parameters + ---------- + X1 + Line X starting location (-1.0 < X < 2.0). + Y1 + Line Y starting location (-1.0 < Y < 1.0). + X2 + Line X ending location (-1.0 < X < 2.0). + Y2 + Line Y ending location (-1.0 < Y < 1.0). + + Notes + ----- + This command defines annotation lines to be written directly onto the + display at a specified location. The command is generated by the Graphical + User Interface (GUI) and appears in the log file (`Job-name.LOG`) if + annotation is used. It is not intended to be typed in directly in a + Mechanical APDL session (although it can be included in an input file + for batch input or for use with the `/INPUT` command). All lines are + shown on subsequent displays unless the annotation is turned off or + deleted. Issue `/LSPEC` to set the attributes of the line. + + This command is valid in any processor. + """ + command = f"/LINE, {x1}, {y1}, {x2}, {y2}" + return self.run(command, **kwargs) + def lspec(self, lcolor="", linstl="", xlnwid="", **kwargs): """Specifies annotation line attributes (GUI). diff --git a/src/ansys/mapdl/core/_commands/graphics_/scaling.py b/src/ansys/mapdl/core/_commands/graphics_/scaling.py index 18179be876..5f0d76a842 100644 --- a/src/ansys/mapdl/core/_commands/graphics_/scaling.py +++ b/src/ansys/mapdl/core/_commands/graphics_/scaling.py @@ -1,4 +1,69 @@ class Scaling: + def slashdscale(self, wn="", dmult="", **kwargs): + """Sets the displacement multiplier for displacement displays. + + APDL Command: /DSCALE + + Parameters + ---------- + wn + Window number (or ALL) to which command applies (defaults to 1). + + dmult + AUTO or 0 + Scale displacements automatically so that maximum displacement + (vector amplitude) displays as 5 percent of the maximum model + length, as measured in the global Cartesian X, Y, or Z + directions. + + 1 + Do not scale displacements (that is, scale displacements by 1.0, + true to geometry). Often used with large deflection results. + + FACTOR + Scale displacements by numerical value input for `FACTOR`. + + OFF + Remove displacement scaling (that is, scale displacements by 0.0, + no distortion). + + USER + Set `dmult` to that used for last display (useful when last `dmult` + value was automatically calculated). + + Notes + ----- + + **Command Default** + + The default value is AUTO or 0 except when: + + * Large deflection effects are included (`NLGEOM,ON`) and it is not a + modal analysis; then the default is 1. + + * It is a spectrum analysis (`ANTYPE,SPECTR`); then the default is `OFF`. + + * The amplitude of a time-harmonic solution is computed using the `HRCPLX` + command (`OMEGAT ≥ 360°`); then the default is `OFF`. + + * The amplitude of a complex modal or harmonic solution is stored into + the database using the `SET` command (`KIMG = AMPL`); then the default + is `OFF`. + + If Multi-Plots are not being displayed, and the current device is a 3D + device (`/SHOW,3D`), then the displacement scale in all active windows + will be the same, even if separate `/DSCALE` commands are issued for + each active window. For efficiency, the program maintains a single data + structure (segment) containing only one displacement scale. The program + displays the same segment (displacement scale) in all windows. Only the + view settings will be different in each of the active windows. + + This command is valid in any processor. + + """ + command = f"/DSCALE, {wn}, {dmult}" + return self.run(command, **kwargs) + def iclwid(self, factor="", **kwargs): """Scales the line width of circuit builder icons. diff --git a/src/ansys/mapdl/core/_commands/graphics_/style.py b/src/ansys/mapdl/core/_commands/graphics_/style.py index 6c083a0b2b..7ce840e2b2 100644 --- a/src/ansys/mapdl/core/_commands/graphics_/style.py +++ b/src/ansys/mapdl/core/_commands/graphics_/style.py @@ -531,6 +531,116 @@ def shade(self, wn="", type_="", **kwargs): command = f"/SHADE,{wn},{type_}" return self.run(command, **kwargs) + def slashtype(self, wn="", type_="", **kwargs): + """Defines the type of display. + + APDL Command: /TYPE + + Parameters + ---------- + wn + Window number (or ALL) to which command applies (defaults to 1). + + type_ + Display type. Defaults to `ZBUF` for raster mode displays or `BASIC` + for vector mode displays: + + BASIC or 0 + Basic display (no hidden or section operations). + + SECT or 1 + Section display (plane view). Use the `/CPLANE` command to + define the cutting plane. + + HIDC or 2 + Centroid hidden display (based on item centroid sort). + + HIDD or 3 + Face hidden display (based on face centroid sort). + + HIDP or 4 + Precise hidden display (like `HIDD` but with more precise checking). + Because all facets are sorted, this mode can be extremely slow, + especially for large models. + + CAP or 5 + Capped hidden display (same as combined `SECT` and `HIDD` with + model in front of section plane removed). + + ZBUF or 6 + Z-buffered display (like `HIDD` but using software Z-buffering). + + ZCAP or 7 + Capped Z-buffered display (same as combined `SECT` and `ZBUF` + with model in front of section plane removed). + + ZQSL or 8 + `QSLICE` Z-buffered display (same as `SECT` but the edge lines + of the remaining 3D model are shown). + + HQSL or 9 + `QSLICE` precise hidden display (like `ZQSL` but using precise hidden). + + Notes + ----- + **Command Default:** `ZBUF` for raster mode displays; `BASIC` for vector + mode displays. + + Defines the type of display, such as section display or hidden-line + display. Use the `/DEVICE` command to specify either raster or + vector mode. + + The `SECT`, `CAP`, `ZCAP`, `ZQSL`, and `HQSL` options produce section + displays. The section or "cutting" plane is specified on the `/CPLANE ` + command as either normal to the viewing vector at the focus point + (default), or as the working plane. + + When you use PowerGraphics, the section display options (`Section`, + `Slice`, and `Capped`) use different averaging techniques for the + interior and exterior results. Because of the different averaging + schemes, anomalies may appear at the transition areas. In many cases, + the automatically computed `MIN` and `MAX` values will differ from the + full range of interior values. You can lessen the effect of these + anomalies by issuing` AVRES,,FULL` (Main Menu> General Post Proc> + Options for Outp). This command sets your legend's automatic contour + interval range according to the minimum and maximum results found + throughout the entire model. + + With PowerGraphics active (`/GRAPHICS,POWER`), the averaging scheme for + surface data with interior element data included (`AVRES,,FULL`) and + multiple facets per edge (`/EFACET,2` or `/EFACET,4`) will yield + differing minimum and maximum contour values depending on the Z- + Buffering options (`/TYPE,,6` or `/TYPE,,7`). When the Section data is + not included in the averaging schemes (`/TYPE,,7`), the resulting + absolute value for the midside node is significantly smaller. + + The `HIDC`, `HIDD`, `HIDP`, `ZBUF`, `ZQSL`, and `HQSL` options produce + displays with "hidden" lines removed. Hidden lines are lines obscured + from view by another element, area, etc. The choice of non-Z-buffered + hidden-line procedure types is available only for raster mode + (`/DEVICE`) displays. For vector mode displays, all non-Z-buffered + "hidden-line" options use the same procedure (which is slightly + different from the raster procedures). Both geometry and postprocessing + displays may be of the hidden- line type. Interior stress contour lines + within solid elements can also be removed as hidden lines, leaving only + the stress contour lines and element outlines on the visible surfaces. + Midside nodes of elements are ignored on postprocessing displays. + Overlapping elements will not be displayed. + + The `ZBUF`, `ZCAP`, and `ZQSL` options use a specific hidden-line + technique called software Z-buffering. This technique allows a more + accurate display of overlapping surfaces (common when using Boolean + operations or `/ESHAPE` on element displays), and allows smooth shaded + displays on all interactive graphics displays. Z-buffered displays can + be performed faster than `HIDP` and `CAP` type displays for large + models. See also the `/LIGHT`, `/SHADE`, and `/GFILE` commands for + additional options when Z-buffering is used. + + This command is valid in any processor. + """ + command = f"/TYPE,{wn},{type_}" + return self.run(command, **kwargs) + def trlcy(self, lab="", tlevel="", n1="", n2="", ninc="", **kwargs): """Specifies the level of translucency. diff --git a/src/ansys/mapdl/core/_commands/post1_/special.py b/src/ansys/mapdl/core/_commands/post1_/special.py index 74659aed3b..25e8831ea6 100644 --- a/src/ansys/mapdl/core/_commands/post1_/special.py +++ b/src/ansys/mapdl/core/_commands/post1_/special.py @@ -1748,6 +1748,7 @@ def prcamp( stabval="", keyallfreq="", keynegfreq="", + keywhirl="", **kwargs, ): """Prints Campbell diagram data for applications involving rotating @@ -1768,19 +1769,18 @@ def prcamp( slope The slope of the line to be printed. This value must be positive. - SLOPE > 0 - The line represents the number of excitations per revolution of the rotor. For - example, SLOPE = 1 represents one excitation per - revolution, usually resulting from unbalance. + SLOPE > 0 - The line represents the number of excitations per + revolution of the rotor. For example, SLOPE = 1 represents one + excitation per revolution, usually resulting from unbalance. - SLOPE = 0 - The line represents the stability threshold for stability values or logarithmic - decrements printout (STABVAL = 1 or 2) + SLOPE = 0 - The line represents the stability threshold for stability + values or logarithmic decrements printout (STABVAL = 1 or 2) unit Specifies the unit of measurement for rotational angular velocities: - RDS - Rotational angular velocities in radians per second (rad/s). This value is the - default. + RDS - Rotational angular velocities in radians per second (rad/s). This value is the default. RPM - Rotational angular velocities in revolutions per minute (RPMs). @@ -1794,8 +1794,8 @@ def prcamp( stabval Flag to print the stability values: - 0 (OFF or NO) - Print the frequencies (the imaginary parts of the eigenvalues in Hz). This - value is the default. + 0 (OFF or NO) - Print the frequencies (the imaginary parts of the + eigenvalues in Hz). This value is the default. 1 (ON or YES) - Print the stability values (the real parts of the eigenvalues in Hz). @@ -1804,9 +1804,8 @@ def prcamp( keyallfreq Key to specify if all frequencies above FREQB are printed out: - 0 (OFF or NO) - A maximum of 10 frequencies are printed out. They correspond to the frequencies - displayed via the PLCAMP command. This value is the - default. + 0 (OFF or NO) - A maximum of 10 frequencies are printed out. They + correspond to the frequencies displayed via the PLCAMP command. This value is the default. 1 (ON or YES) - All frequencies are printed out. @@ -1819,6 +1818,12 @@ def prcamp( 1 (ON or YES) - Negative and positive frequencies are printed out. + keywhirl + Flag to print the whirl and instability keys for each load step: + + 0 (OFF or NO) - Print the whirl for the last load step. This value is the default. + 1 (ON or YES) - Print the whirl and instability keys for each load step. + Notes ----- The following items are required when generating a Campbell diagram: diff --git a/src/ansys/mapdl/core/_commands/post26_/special.py b/src/ansys/mapdl/core/_commands/post26_/special.py index 37e75e9d3d..3a899bf393 100644 --- a/src/ansys/mapdl/core/_commands/post26_/special.py +++ b/src/ansys/mapdl/core/_commands/post26_/special.py @@ -330,7 +330,7 @@ def rpsd( See POST26 - Response Power Spectral Density in the Mechanical APDL Theory Reference for more information on these equations. """ - command = f"RPSD,{ir},{ia},{ib},{itype},{datum},{name},{signif}" + command = f"RPSD,{ir},{ia},{ib},{itype},{datum},{name},,{signif}" return self.run(command, **kwargs) def smooth( diff --git a/src/ansys/mapdl/core/_commands/preproc/elements.py b/src/ansys/mapdl/core/_commands/preproc/elements.py index d9645fc769..3fc1304ccf 100644 --- a/src/ansys/mapdl/core/_commands/preproc/elements.py +++ b/src/ansys/mapdl/core/_commands/preproc/elements.py @@ -1241,7 +1241,7 @@ def eplot(self, **kwargs): APDL Command: EPLOT Notes - ------ + ----- Produces an element display of the selected elements. In full graphics, only those elements faces with all of their corresponding nodes selected are plotted. In PowerGraphics, all element faces of the selected diff --git a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py index 23c962db2d..7f80d5b4b4 100644 --- a/src/ansys/mapdl/core/_commands/preproc/special_purpose.py +++ b/src/ansys/mapdl/core/_commands/preproc/special_purpose.py @@ -969,7 +969,9 @@ def sstate( command = f"SSTATE,{action},{cm_name},{val1},{val2},{val3},{val4},{val5},{val6},{val7},{val8},{val9}" return self.run(command, **kwargs) - def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): + def xfdata( + self, enrichmentid="", lsm="", elemnum="", nodenum="", phi="", psi="", **kwargs + ): """Defines a crack in the model by specifying nodal level set values APDL Command: XFDATA @@ -993,6 +995,10 @@ def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): phi Signed normal distance of the node from the crack. + psi + Signed normal distance of the node from the crack tip (or crack front). + Used only in the singularity- based XFEM method. + Notes ----- Issue the XFDATA command multiple times as needed to specify nodal @@ -1000,10 +1006,19 @@ def xfdata(self, enrichmentid="", elemnum="", nodenum="", phi="", **kwargs): This command is valid in PREP7 (/PREP7) only. """ - command = f"XFDATA,{enrichmentid},{elemnum},{nodenum},{phi}" + command = f"XFDATA,{enrichmentid},{lsm},{elemnum},{nodenum},{phi},{psi}" return self.run(command, **kwargs) - def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): + def xfenrich( + self, + enrichmentid="", + compname="", + matid="", + method="", + radius="", + snaptoler="", + **kwargs, + ): """Defines parameters associated with crack propagation using XFEM APDL Command: XFENRICH @@ -1025,6 +1040,19 @@ def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): the initial crack. If 0 or not specified, the initial crack is assumed to be free of cohesive zone behavior. + method + PHAN -- Use phantom-node-based XFEM (default). + SING -- Use singularity-based XFEM. + + radius + Radius defining the region around the crack tip encompassing the + set of elements to be influenced by the crack-tip singularity effects. + Default = 0.0. Used only in singularity-based XFEM. + + snaptoler + Snap tolerance to snap the crack tip to the closest crack face along + the extension direction. Default = 1.0E-6. Used only in singularity-based XFEM. + Notes ----- If MatID is specified, the cohesive zone behavior is described by the @@ -1032,7 +1060,7 @@ def xfenrich(self, enrichmentid="", compname="", matid="", **kwargs): This command is valid in PREP7 (/PREP7) only. """ - command = f"XFENRICH,{enrichmentid},{compname},{matid}" + command = f"XFENRICH,{enrichmentid},{compname},{matid}, {method}, {radius}, {snaptoler}" return self.run(command, **kwargs) def xflist(self, enrichmentid="", **kwargs): diff --git a/src/ansys/mapdl/core/_commands/solution/analysis_options.py b/src/ansys/mapdl/core/_commands/solution/analysis_options.py index 585c9b42ea..deed5ba9e5 100644 --- a/src/ansys/mapdl/core/_commands/solution/analysis_options.py +++ b/src/ansys/mapdl/core/_commands/solution/analysis_options.py @@ -1105,25 +1105,27 @@ def cutcontrol(self, lab="", value="", option="", **kwargs): command = f"CUTCONTROL,{lab},{value},{option}" return self.run(command, **kwargs) - def ddoption(self, decomp="", **kwargs): + def ddoption(self, decomp="", nprocpersol="", numsolforlp="", **kwargs): """Sets domain decomposer option for Distributed ANSYS. APDL Command: DDOPTION Parameters ---------- - decomp + Decomp Controls which domain decomposition algorithm to use. - AUTO - Use the default domain decomposition algorithm when splitting the model into - domains for Distributed ANSYS (default). + * AUTO - Automatically selects the optimal domain decomposition method (default). + * MESH - Decompose the FEA mesh. + * FREQ - Decompose the frequency domain for harmonic analyses. + * CYCHI -Decompose the harmonic indices for cyclic symmetry modal analyses. - GREEDY - Use the "greedy" domain decomposition algorithm. + nprocpersol + Number of processes to be used for mesh-based decomposition in conjunction with each frequency solution (`Decomp = FREQ`) or harmonic index solution (`Decomp = CYCHI`). Defaults to 1. This field + is ignored when `Decomp = MESH`. - METIS - Use the METIS graph partitioning domain decomposition algorithm. + numsolforlp + Number of frequency or harmonic index solutions in a subsequent linear perturbation harmonic or linear perturbation cyclic modal analysis. This field is ignored when `Decomp = MESH` Notes ----- @@ -2016,7 +2018,7 @@ def essolv( command = f"ESSOLV,{electit},{strutit},{dimn},{morphopt},{mcomp},{xcomp},{electol},{strutol},{mxloop},,{ruseky},{restky},{eiscomp}" return self.run(command, **kwargs) - def expass(self, key="", **kwargs): + def expass(self, key="", keystat="", **kwargs): """Specifies an expansion pass of an analysis. APDL Command: EXPASS @@ -2025,10 +2027,13 @@ def expass(self, key="", **kwargs): ---------- key Expansion pass key: + * OFF - No expansion pass will be performed (default). + * ON - An expansion pass will be performed. - OFF - No expansion pass will be performed (default). - - ON - An expansion pass will be performed. + keystat + Static correction vectors key: + * ON - Include static correction vectors in the expanded displacements (default). + * OFF - Do not include static correction vectors in the expanded displacements. Notes ----- @@ -2040,7 +2045,7 @@ def expass(self, key="", **kwargs): This command is also valid in PREP7. """ - command = f"EXPASS,{key}" + command = f"EXPASS,{key},,,{keystat}" return self.run(command, **kwargs) def gauge(self, opt="", freq="", **kwargs): diff --git a/src/ansys/mapdl/core/_commands/solution/spectrum_options.py b/src/ansys/mapdl/core/_commands/solution/spectrum_options.py index ff65b77274..55ecbf445b 100644 --- a/src/ansys/mapdl/core/_commands/solution/spectrum_options.py +++ b/src/ansys/mapdl/core/_commands/solution/spectrum_options.py @@ -669,7 +669,7 @@ def psdfrq( command = f"PSDFRQ,{tblno1},{tblno2},{freq1},{freq2},{freq3},{freq4},{freq5},{freq6},{freq7}" return self.run(command, **kwargs) - def psdgraph(self, tblno1="", tblno2="", **kwargs): + def psdgraph(self, tblno1="", tblno2="", displaykey="", **kwargs): """Displays input PSD curves APDL Command: PSDGRAPH @@ -683,6 +683,14 @@ def psdgraph(self, tblno1="", tblno2="", **kwargs): Second PSD table number to display. TBLNO2 is used only in conjunction with the COVAL or the QDVAL commands. + displaykey + Key to display the points markers and numbering: + + 0 - Display points markers and numbering (default). + 1 - Display points numbering only. + 2 - Display points markers only. + 3 - No points markers or numbering. + Notes ----- The input PSD tables are displayed in log-log format as dotted lines. @@ -699,7 +707,7 @@ def psdgraph(self, tblno1="", tblno2="", **kwargs): This command is valid in any processor. """ - command = f"PSDGRAPH,{tblno1},{tblno2}" + command = f"PSDGRAPH,{tblno1},{tblno2},{displaykey}" return self.run(command, **kwargs) def psdres(self, lab="", relkey="", **kwargs): diff --git a/src/ansys/mapdl/core/common_grpc.py b/src/ansys/mapdl/core/common_grpc.py index 16c242f1e4..62408fc188 100644 --- a/src/ansys/mapdl/core/common_grpc.py +++ b/src/ansys/mapdl/core/common_grpc.py @@ -104,7 +104,7 @@ def check_vget_input(entity: str, item: str, itnum: str) -> str: Returns ------- - command : str + str MAPDL formatted vget command after the "VGET, " in the format of: "ENTITY, , ITEM, ITNUM" """ @@ -158,7 +158,7 @@ def parse_chunks(chunks, dtype=None): Returns ------- - array : np.ndarray + np.ndarray Deserialized numpy array. """ diff --git a/src/ansys/mapdl/core/component.py b/src/ansys/mapdl/core/component.py index 2ff7d353a0..ef67f93458 100644 --- a/src/ansys/mapdl/core/component.py +++ b/src/ansys/mapdl/core/component.py @@ -441,7 +441,8 @@ def __iter__(self): """ yield from self._comp.keys() - def list(self): + @property + def names(self): """ Return a tuple that contains the components. @@ -453,6 +454,7 @@ def list(self): """ return tuple(self._comp.keys()) + @property def types(self): """ Return the types of the components. @@ -476,3 +478,24 @@ def items(self): """ return self._comp.items() + + def select(self, names: Union[str, list[str], tuple[str]], mute=False) -> None: + """Select Select components given their names + + Select components given their names. + + Parameters + ---------- + names : Union[str, list[str], tuple[str]] + Name(s) of the components + mute : bool, optional + Whether to mute the `/CMSEL` command output or not, by default False. + """ + if isinstance(names, str): + names = [names] + + for i, each_name in enumerate(names): + if i == 0: + self._mapdl.cmsel("S", each_name, mute=mute) + else: + self._mapdl.cmsel("A", each_name, mute=mute) diff --git a/src/ansys/mapdl/core/convert.py b/src/ansys/mapdl/core/convert.py index 0c8530a468..3a1f3d1f41 100644 --- a/src/ansys/mapdl/core/convert.py +++ b/src/ansys/mapdl/core/convert.py @@ -15,58 +15,97 @@ "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 + + # This commands have "--" as one or some arguments -COMMANDS_WITH_EMPTY_ARGS = [ - "/CMA", # "/CMAP, - "/NER", # "/NERR, - "/PBF", # "/PBF, - "/PMO", # "/PMORE, - "ANTY", # ANTYPE, - "ASBL", # ASBL, - "ATAN", # ATAN, - "BCSO", # BCSOPTION, - "CLOG", # CLOG, - "CONJ", # CONJUG, - "DERI", # DERIV, - "DSPO", # DSPOPTION, - "ENER", # ENERSOL, - "ENSY", # ENSYM, - "ESYM", # ESYM, - "EXP", # EXP, - "EXPA", # EXPAND, - "FCLI", # FCLIST, - "FILE", # FILEAUX2, - "FLUR", # FLUREAD, - "GMAT", # GMATRIX, - "IMAG", # IMAGIN, - "INT1", # INT1, - "LARG", # LARGE, - "LATT", # LATT, - "MAP", # MAP, - "MORP", # MORPH, - "MPCO", # MPCOPY, - "NLOG", # NLOG, - "PLMA", # PLMAP, - "PRED", # PRED, - "PROD", # PROD, - "QRDO", # QRDOPT, - "QUOT", # QUOT, - "RACE", # RACE, - "REAL", # REALVAR, - "REME", # REMESH, - "SESY", # SESYMM, - "SETF", # SETFGAP, - "SETR", # SETRAN, - "SMAL", # SMALL, - "SNOP", # SNOPTION, - "SURE", # SURESU, - "THOP", # THOPT, - "TINT", # TINTP, -] +COMMANDS_WITH_EMPTY_ARGS = { + "/CMA": (), # "/CMAP, + "/NER": (), # "/NERR, + "/PBF": (), # "/PBF, + "/PMO": (), # "/PMORE, + "ADD": (), # "ADD" + "ANTY": (), # ANTYPE, + "ASBL": (), # ASBL, + "ATAN": (), # ATAN, + "BCSO": (), # BCSOPTION, + "CORI": (), # CORIOLIS + "CDRE": (), # CDREAD + "CLOG": (), # CLOG, + "CONJ": (), # CONJUG, + "DERI": (), # DERIV, + "DSPO": (), # DSPOPTION, + "ENER": (), # ENERSOL, + "ENSY": (), # ENSYM, + "ESYM": (), # ESYM, + "EXP": (), # EXP, + "EXPA": (), # EXPAND, + "FCLI": (), # FCLIST, + "FILE": (), # FILEAUX2, + "FLUR": (), # FLUREAD, + "GMAT": (), # GMATRIX, + "IMAG": (), # IMAGIN, + "INT1": (), # INT1, + "LARG": (), # LARGE, + "LATT": (), # LATT, + "MAP": (), # MAP, + "MORP": (), # MORPH, + "MPCO": (), # MPCOPY, + "NLOG": (), # NLOG, + "PLMA": (), # PLMAP, + "PRED": (), # PRED, + "PROD": (), # PROD, + "QRDO": (), # QRDOPT, + "QUOT": (), # QUOT, + "RACE": (), # RACE, + "RDEC": (), # RDEC + "REAL": (), # REALVAR, + "REME": (), # REMESH, + "RPSD": (), # RPSD + "SECR": (), # SECREAD + "SECW": (), # SECWRITE + "SESY": (), # SESYMM, + "SETF": (), # SETFGAP, + "SETR": (), # SETRAN, + "SMAL": (), # SMALL, + "SNOP": (), # SNOPTION, + "SQRT": (), # SQRT + "SURE": (), # SURESU, + "THOP": (), # THOPT, + "TINT": (), # TINTP, + "XFDA": (), # XFDATA +} -COMMANDS_TO_NOT_BE_CONVERTED = [] -COMMANDS_TO_NOT_BE_CONVERTED.extend(COMMANDS_WITH_EMPTY_ARGS) +COMMANDS_TO_NOT_BE_CONVERTED = [ + "CMPL", # CMPLOT default behaviour does not match the `mapdl.cmplot`'s at the moemnt + "MODE", # Until we merge #2431 + "/LIN", # Until we merge 2432 + "/LAR", # Until we merge 2432 + "/TYP", # Until we merge 2432 + "/DSC", # Until we merge 2432 + # CDREAD # commented above +] + +FORCED_MAPPING = { + # Forced mapping between MAPDL and PyMAPDL + "SECT": "sectype", # Because it is shadowed by `sectinqr` +} def convert_script( @@ -85,6 +124,9 @@ def convert_script( header=True, print_com=True, only_commands=False, + use_vtk=None, + clear_at_start=False, + check_parameter_names=True, ): """Converts an ANSYS input file to a python PyMAPDL script. @@ -156,6 +198,13 @@ def convert_script( and exit commands are NOT included (``auto_exit=False``). Overrides ``header``, ``add_imports`` and ``auto_exit``. + use_vtk : bool, optional + It sets the `mapdl.use_vtk` argument equals True or False depending on + this value. + + clear_at_start : bool, optional + Add a `mapdl.clear()` after the Mapdl object initialization. + Returns ------- list @@ -211,6 +260,9 @@ def convert_script( header=header, print_com=print_com, only_commands=only_commands, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, ) translator.save(filename_out) @@ -232,6 +284,9 @@ def convert_apdl_block( header=True, print_com=True, only_commands=False, + use_vtk=None, + clear_at_start=False, + check_parameter_names=False, ): """Converts an ANSYS input string to a python PyMAPDL string. @@ -299,6 +354,17 @@ def convert_apdl_block( and exit commands are NOT included (``auto_exit=False``). Overrides ``header``, ``add_imports`` and ``auto_exit``. + use_vtk : bool, optional + It sets the `mapdl.use_vtk` argument equals True or False depending on + this value. Defaults to `None` which is Mapdl class default. + + clear_at_start : bool, optional + Add a `mapdl.clear()` after the Mapdl object initialization. Defaults to + `False`. + + check_parameter_names : bool, optional + Set MAPDL object to avoid parameter name checks (do not raise leading underscored parameter exceptions). Defaults to `False`. + Returns ------- list @@ -335,6 +401,9 @@ def convert_apdl_block( header=header, print_com=print_com, only_commands=only_commands, + use_vtk=use_vtk, + clear_at_start=clear_at_start, + check_parameter_names=check_parameter_names, ) if isinstance(apdl_strings, str): @@ -357,6 +426,9 @@ def _convert( 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 @@ -375,6 +447,9 @@ def _convert( 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): @@ -437,6 +512,9 @@ def __init__( cleanup_output=True, header=True, print_com=True, + use_vtk=None, + clear_at_start=False, + check_parameter_names=False, ): self._non_interactive_level = 0 self.lines = Lines(mute=not show_log) @@ -454,12 +532,17 @@ def __init__( self.cleanup_output = cleanup_output self._header = header self.print_com = print_com + self.verification_example = False + self.use_vtk = use_vtk + self.clear_at_start = clear_at_start + self.check_parameter_names = check_parameter_names + self.macros_names = [] self.write_header() if self._add_imports: self.initialize_mapdl_object(loglevel, exec_file) - self._valid_commands = dir(Commands) + self._valid_commands = self._get_valid_pymapdl_methods_short() self._block_commands = { "NBLO": "NBLOCK", "EBLO": "EBLOCK", @@ -474,7 +557,6 @@ def __init__( } # Commands where you need to count the number of lines. _NON_INTERACTIVE_COMMANDS = { - "*CRE": "*CREATE", "*VWR": "*VWRITE", "*VRE": "*VREAD", } @@ -553,17 +635,26 @@ def initialize_mapdl_object(self, loglevel, exec_file): core_module = "ansys.mapdl.core" # shouldn't change self.lines.append(f"from {core_module} import launch_mapdl") + mapdl_arguments = [f'loglevel="{loglevel}"'] + if exec_file: - exec_file_parameter = f'exec_file="{exec_file}", ' - else: - exec_file_parameter = "" + mapdl_arguments.append(f'exec_file="{exec_file}"') if self.print_com: - line = f'{self.obj_name} = launch_mapdl({exec_file_parameter}loglevel="{loglevel}", print_com=True)\n' - else: - line = f'{self.obj_name} = launch_mapdl({exec_file_parameter}loglevel="{loglevel}")\n' + mapdl_arguments.append("print_com=True") + + if self.use_vtk is not None: + mapdl_arguments.append(f"use_vtk={bool(self.use_vtk)}") + + if self.check_parameter_names is not None and not self.check_parameter_names: + mapdl_arguments.append("check_parameter_names=False") + + line = f'{self.obj_name} = launch_mapdl({", ".join(mapdl_arguments)})' self.lines.append(line) + if self.clear_at_start: + self.lines.append(f"{self.obj_name}.clear() # Clearing session") + @property def line_ending(self): return self._line_ending @@ -611,6 +702,8 @@ def translate_line(self, line): self.comment = self.comment.lstrip() if not line: + # Keeping empty lines + self.lines.append("") return # Cleaning ending empty arguments. @@ -623,6 +716,8 @@ def translate_line(self, line): line = ",".join(line_[ind:][::-1]) + line_with_trailing_commas = line + # remove trailing comma line = line[:-1] if line[-1] == "," else line line_upper = line.upper() @@ -630,7 +725,7 @@ def translate_line(self, line): cmd_caps = line.split(",")[0].upper() cmd_caps_short = cmd_caps[:4] - items = line.split(",") + items = self._get_items(line.strip()) if cmd_caps_short in ["SOLV", "LSSO"] and self._comment_solve: self.store_command( @@ -640,11 +735,19 @@ def translate_line(self, line): self.store_command("com", [line]) return + if cmd_caps_short == "/VER": + self.verification_example = True + if cmd_caps_short == "/COM": # It is a comment self.store_command("com", [line[5:]]) return + if cmd_caps_short == "C***": + # It is an old style comment + self.store_command("com", [line[5:]]) + return + if cmd_caps == "*DO": self.start_non_interactive() self.store_run_command(line) @@ -655,9 +758,16 @@ def translate_line(self, line): self.end_non_interactive() return + if "/EXI" in cmd_caps.upper() and self.non_interactive: + self.store_command("com", [f"Skipping: {line}"]) + return + if self.output_to_file(line): - self.start_non_interactive() - self.store_run_command(line) + if self.verification_example and "SCRATCH" in line.upper(): + self.store_command("com", [f"Skipping: {line}"]) + else: + self.start_non_interactive() + self.store_run_command(line) return if self.output_to_default(line): @@ -669,7 +779,6 @@ def translate_line(self, line): if cmd_caps == "/VERIFY": self.store_run_command("FINISH") self.store_run_command(line) - self.store_run_command("/PREP7") return if cmd_caps_short == "*REP": @@ -677,7 +786,7 @@ def translate_line(self, line): prev_cmd = self.lines.pop(-1) self.start_non_interactive() new_prev_cmd = ( - " " + prev_cmd + self.indent + prev_cmd ) # Since we are writing in self.lines we need to add the indentation by ourselves. self.lines.append(new_prev_cmd) self.store_run_command( @@ -686,13 +795,27 @@ def translate_line(self, line): self.end_non_interactive() return + # Skipping conversion if command has empty arguments and there is ",," in the call + if ( + cmd_caps_short in COMMANDS_WITH_EMPTY_ARGS + and ",," in line_with_trailing_commas.replace(" ", "") + ): + self.store_run_command(line.strip()) + return + + # Skipping commands to not be converted if cmd_caps_short in COMMANDS_TO_NOT_BE_CONVERTED: - self.store_run_command(line) + self.store_run_command(line.strip()) return if cmd_caps_short == "/TIT": # /TITLE parameters = line.split(",")[1:] - return self.store_command("title", ["".join(parameters).strip()]) + return self.store_command("title", [",".join(parameters).strip()]) + + if cmd_caps_short == "/AXL": # /AXLAB + parameters = line.split(",")[1:] + parameters_ = [parameters[0], ",".join(parameters[1:])] + return self.store_command("axlab", parameters_) if cmd_caps_short == "*GET": if self.non_interactive: # gives error @@ -710,12 +833,29 @@ def translate_line(self, line): return if cmd_caps_short == "*CRE": # creating a function + self.macros_names.append(items[1]) if self.macros_as_functions: self.start_function(items[1].strip()) return else: self.start_non_interactive() + ## Treating functions + if items[0] in self.macros_names and self.macros_as_functions: + # We are calling the function/macro created before. + func_name = items[0].strip() + + args = self._parse_arguments(items[1:]) + self.store_python_command(f"{func_name}({args})") + return + + if cmd_caps_short == "*USE" and self.macros_as_functions: + func_name = items[1].strip() + if func_name in self._functions: + args = self._parse_arguments(items[2:]) + self.store_python_command(f"{func_name}({args})") + return + if cmd_caps == "/PREP7": return self.store_command("prep7", []) @@ -749,17 +889,7 @@ def translate_line(self, line): + "The previous line is: \n%s\n\n" % self.lines[-1] ) self.store_run_command(line) - if ( - not self._in_block - ): # To escape cmds that require (XX) but they are not in block - self.end_non_interactive() return - elif cmd_caps_short == "*USE" and self.macros_as_functions: - func_name = items[1].strip() - if func_name in self._functions: - args = ", ".join(items[2:]) - self.lines.append(f"{func_name}({args})") - return # check if a line is setting a variable if "=" in items[0]: # line sets a variable: @@ -781,52 +911,65 @@ def translate_line(self, line): # check valid command if ( self._pymapdl_command(command) not in self._valid_commands - or cmd_caps_short in self._non_interactive_commands + and cmd_caps_short in self._non_interactive_commands ): - if cmd_caps_short in self._non_interactive_commands: - if cmd_caps_short in self._block_commands: - self._in_block = True - self._block_count = 0 - self._block_count_target = 0 - - elif cmd_caps_short in self._enum_block_commands: - self._in_block = True - self._block_count = 0 - if cmd_caps_short == "CMBL": # In cmblock - # CMBLOCK,Cname,Entity,NUMITEMS,,,,,KOPT - numitems = int(line.split(",")[3]) - _block_count_target = ( - numitems // 8 + 1 if numitems % 8 != 0 else numitems // 8 - ) - self._block_count_target = ( - _block_count_target + 2 - ) # because the cmd_caps_short line and option line. - - self._block_current_cmd = cmd_caps_short - self.start_non_interactive() + if cmd_caps_short in self._block_commands: + self._in_block = True + self._block_count = 0 + self._block_count_target = 0 + + elif cmd_caps_short in self._enum_block_commands: + self._in_block = True + self._block_count = 0 + if cmd_caps_short == "CMBL": # In cmblock + # CMBLOCK,Cname,Entity,NUMITEMS,,,,,KOPT + numitems = int(line.split(",")[3]) + _block_count_target = ( + numitems // 8 + 1 if numitems % 8 != 0 else numitems // 8 + ) + self._block_count_target = ( + _block_count_target + 2 + ) # because the cmd_caps_short line and option line. + + self._block_current_cmd = cmd_caps_short + self.start_non_interactive() + self.store_run_command(line.strip()) + + elif self.use_function_names: + # Takign into account the leading characters + if command.upper() in FORCED_MAPPING: + # Checking exceptions/forced mapping + command = FORCED_MAPPING[command.upper()] - if self._in_block and cmd_caps_short not in self._non_interactive_commands: - self.store_run_command(original_line) else: - self.store_run_command(line) + # Looking for a suitable candidate. + if command[0] == "/": + slash_command = f"slash{command[1:4]}" + if slash_command in self._valid_commands: + command = slash_command + else: + command = command[1:] + elif command[0] == "*": + star_command = f"star{command[1:4]}" + if star_command in self._valid_commands: + command = star_command + else: + command = command[1:] + + # Some commands are abbreviated (only 4 letters) + from ansys.mapdl.core import Mapdl + + if command not in dir(Mapdl): + command = self.find_match(command) + + # Storing + if command: + self.store_command(command, parameters) + else: # find_match can return None + self.store_run_command(line.strip()) - elif self.use_function_names: - if command[0] == "/": - slash_command = f"slash{command[1:]}" - if slash_command in dir(Commands): - command = slash_command - else: - command = command[1:] - elif command[0] == "*": - star_command = f"star{command[1:]}" - if star_command in dir(Commands): - command = star_command - else: - command = command[1:] - - self.store_command(command, parameters) else: - self.store_run_command(line) + self.store_run_command(line.strip()) def _pymapdl_command(self, command): if command[0] in ["/", "*"]: @@ -839,14 +982,16 @@ def start_function(self, func_name): self.store_empty_line() self._infunction = True spacing = " " * (len(func_name) + 5) - line = "def %s(%s," % ( + line = self.indent + "def %s(%s," % ( func_name, ", ".join(["ARG%d=''" % i for i in range(1, 7)]), ) + line += "\n" line += "%s%s," % ( spacing, ", ".join(["ARG%d=''" % i for i in range(7, 13)]), ) + line += "\n" line += "%s%s):" % ( spacing, ", ".join(["ARG%d=''" % i for i in range(13, 19)]), @@ -866,22 +1011,24 @@ def store_run_command(self, command, run_underscored=False): else: underscore = "" + # Removing trailing/leading spaces + command = command.strip() + if self._infunction and "ARG" in command: args = [] for i in range(1, 19): arg = "ARG%d" % i c = 0 if arg in command: - command = command.replace(arg, "{%d:s}" % c) + command = command.replace(arg, "{" + f"{arg}" + "}") args.append(arg) c += 1 - line = '%s%s.%srun("%s".format(%s))' % ( + line = '%s%s.%srun(f"%s")' % ( self.indent, self.obj_name, underscore, command, - ", ".join(args), ) elif self.comment: @@ -910,12 +1057,15 @@ def store_empty_line(self): """Stores an empty line""" self.lines.append("") - def store_command(self, function, parameters): - """Stores a valid pyansys function with parameters""" + def store_python_command(self, command): + line = f"{self.indent}{command}" + self.lines.append(line) + + def _parse_arguments(self, parameters): parsed_parameters = [] for parameter in parameters: parameter = parameter.strip() - if is_float(parameter): + if is_float(parameter) and parameter.upper() != "INF": parsed_parameters.append(parameter) elif "ARG" in parameter and self._infunction: parsed_parameters.append("%s" % parameter) @@ -927,7 +1077,12 @@ def store_command(self, function, parameters): parameter = parameter[1:-1] parsed_parameters.append(f'"{parameter}"') - parameter_str = ", ".join(parsed_parameters) + return ", ".join(parsed_parameters) + + def store_command(self, function, parameters): + """Stores a valid pyansys function with parameters""" + parameter_str = self._parse_arguments(parameters) + if self.comment: line = "%s%s.%s(%s) # %s" % ( self.indent, @@ -957,9 +1112,9 @@ def start_non_interactive(self): def end_non_interactive(self): self._non_interactive_level -= 1 - if self._non_interactive_level == 0: - self.non_interactive = False + if self._non_interactive_level <= 0: self.indent = self.indent[4:] + self.non_interactive = False def output_to_file(self, line): """Return if an APDL line is redirecting to a file.""" @@ -1001,120 +1156,130 @@ def output_to_default(self, line): return False + def _get_items(self, line_): + """Parse the line items (comma separated elements) but ignoring the ones inside parenthesis, or brackets""" -import click - - -@click.command() -@click.argument("filename_in") -@click.option("-o", default=None, help="Name of the output Python script.") -@click.option("--filename_out", default=None, help="Name of the output Python script.") -@click.option( - "--loglevel", - default="WARNING", - help="Logging level of the ansys object within the script.", -) -@click.option( - "--auto_exit", - default=True, - help="Adds a line to the end of the script to exit MAPDL. Default ``True``", -) -@click.option("--line_ending", default=None, help="When None, automatically is ``\n.``") -@click.option( - "--exec_file", - default=None, - help="Specify the location of the ANSYS executable and include it in the converter output ``launch_mapdl`` call.", -) -@click.option( - "--macros_as_functions", - default=True, - help="Attempt to convert MAPDL macros to python functions.", -) -@click.option( - "--use_function_names", - default=True, - help="Convert MAPDL functions to ansys.mapdl.core.Mapdl class methods. When ``True``, the MAPDL command ``K`` will be converted to ``mapdl.k``. When ``False``, it will be converted to ``mapdl.run('k')``.", -) -@click.option( - "--show_log", - default=False, - help="Print the converted commands using a logger (from ``logging`` Python module).", -) -@click.option( - "--add_imports", - default=True, - help='If ``True``, add the lines ``from ansys.mapdl.core import launch_mapdl`` and ``mapdl = launch_mapdl(loglevel="WARNING")`` to the beginning of the output file. This option is useful if you are planning to use the output script from another mapdl session. See examples section. This option overrides ``auto_exit``.', -) -@click.option( - "--comment_solve", - default=False, - help='If ``True``, it will pythonically comment the lines that contain ``"SOLVE"`` or ``"/EOF"``.', -) -@click.option( - "--cleanup_output", - default=True, - help="If ``True`` the output is formatted using ``autopep8`` before writing the file or returning the string. This requires ``autopep8`` to be installed.", -) -@click.option( - "--header", - default=True, - help="If ``True``, the default header is written in the first line of the output. If a string is provided, this string will be used as header.", -) -@click.option( - "--print_com", - default=True, - help="Print command ``/COM`` arguments to python console. Defaults to ``True``.", -) -def cli( - filename_in, - o, - filename_out, - loglevel, - auto_exit, - line_ending, - exec_file, - macros_as_functions, - use_function_names, - show_log, - add_imports, - comment_solve, - cleanup_output, - header, - print_com, -): - """PyMAPDL CLI tool for converting MAPDL scripts to PyMAPDL scripts. + parenthesis_count = 0 - USAGE: + items = [] + begining_substring = 0 - This example demonstrates the main use of this tool: + for ind, each_char in enumerate(line_): + if each_char in ["(", "[", "{"]: + parenthesis_count += 1 - $ pymapdl_convert_script mapdl.dat -o python.py + if each_char == "," and parenthesis_count == 0: + items.append(line_[begining_substring:ind]) + begining_substring = ind + 1 - File mapdl.dat successfully converted to python.py. + if ind == len(line_) - 1: # reaching ending of line + items.append(line_[begining_substring : ind + 1]) - The output argument is optional, in which case the "py" extension is used: + if each_char in [")", "]", "}"]: + parenthesis_count -= 1 - $ pymapdl_convert_script mapdl.dat + return items - File mapdl.dat successfully converted to mapdl.py. + def _get_valid_pymapdl_methods_short(self): + pymethods = dir(Commands) - You can use any option from ``ansys.mapdl.core.convert.convert_script`` function: + reduced_list = [] + for each_method in pymethods: + if not re.match(r"^[\*~/A-Za-z]\w*$", each_method): + continue + if each_method.startswith("slash"): + reduced_list.append(each_method[:8]) + elif each_method.startswith("star"): + reduced_list.append(each_method[:7]) + else: + reduced_list.append(each_method[:4]) + return reduced_list - $ pymapdl_convert_script mapdl.dat --auto-exit False + def find_match(self, cmd): + pymethods = sorted(dir(Commands)) - File mapdl.dat successfully converted to mapdl.py. + for each in pymethods: + if each.startswith(cmd): + return each - $ pymapdl_convert_script.exe mapdl.dat --filename_out mapdl.out --add_imports False - File mapdl.dat successfully converted to mapdl.out. +try: + import click + _HAS_CLICK = True +except ModuleNotFoundError: + _HAS_CLICK = False - """ - if o: - filename_out = o +if _HAS_CLICK: + # Loading CLI - convert_script( + @click.command() + @click.argument("filename_in") + @click.option("-o", default=None, help="Name of the output Python script.") + @click.option( + "--filename_out", default=None, help="Name of the output Python script." + ) + @click.option( + "--loglevel", + default="WARNING", + help="Logging level of the ansys object within the script.", + ) + @click.option( + "--auto_exit", + default=True, + help="Adds a line to the end of the script to exit MAPDL. Default ``True``", + ) + @click.option( + "--line_ending", default=None, help="When None, automatically is ``\n.``" + ) + @click.option( + "--exec_file", + default=None, + help="Specify the location of the ANSYS executable and include it in the converter output ``launch_mapdl`` call.", + ) + @click.option( + "--macros_as_functions", + default=True, + help="Attempt to convert MAPDL macros to python functions.", + ) + @click.option( + "--use_function_names", + default=True, + help="Convert MAPDL functions to ansys.mapdl.core.Mapdl class methods. When ``True``, the MAPDL command ``K`` will be converted to ``mapdl.k``. When ``False``, it will be converted to ``mapdl.run('k')``.", + ) + @click.option( + "--show_log", + default=False, + help="Print the converted commands using a logger (from ``logging`` Python module).", + ) + @click.option( + "--add_imports", + default=True, + help='If ``True``, add the lines ``from ansys.mapdl.core import launch_mapdl`` and ``mapdl = launch_mapdl(loglevel="WARNING")`` to the beginning of the output file. This option is useful if you are planning to use the output script from another mapdl session. See examples section. This option overrides ``auto_exit``.', + ) + @click.option( + "--comment_solve", + default=False, + help='If ``True``, it will pythonically comment the lines that contain ``"SOLVE"`` or ``"/EOF"``.', + ) + @click.option( + "--cleanup_output", + default=True, + help="If ``True`` the output is formatted using ``autopep8`` before writing the file or returning the string. This requires ``autopep8`` to be installed.", + ) + @click.option( + "--header", + default=True, + help="If ``True``, the default header is written in the first line of the output. If a string is provided, this string will be used as header.", + ) + @click.option( + "--print_com", + default=True, + help="Print command ``/COM`` arguments to python console. Defaults to ``True``.", + ) + def cli( filename_in, + o, filename_out, loglevel, auto_exit, @@ -1128,11 +1293,63 @@ def cli( cleanup_output, header, print_com, - ) + ): + """PyMAPDL CLI tool for converting MAPDL scripts to PyMAPDL scripts. + + USAGE: + + This example demonstrates the main use of this tool: + + $ pymapdl_convert_script mapdl.dat -o python.py + + File mapdl.dat successfully converted to python.py. + + The output argument is optional, in which case the "py" extension is used: + + $ pymapdl_convert_script mapdl.dat - if filename_out: - print(f"File {filename_in} successfully converted to {filename_out}.") - else: - print( - f"File {filename_in} successfully converted to {os.path.splitext(filename_in)[0] + '.py'}." + File mapdl.dat successfully converted to mapdl.py. + + You can use any option from ``ansys.mapdl.core.convert.convert_script`` function: + + $ pymapdl_convert_script mapdl.dat --auto-exit False + + File mapdl.dat successfully converted to mapdl.py. + + $ pymapdl_convert_script.exe mapdl.dat --filename_out mapdl.out --add_imports False + + File mapdl.dat successfully converted to mapdl.out. + + + """ + if o: + filename_out = o + + convert_script( + filename_in, + filename_out, + loglevel, + auto_exit, + line_ending, + exec_file, + macros_as_functions, + use_function_names, + show_log, + add_imports, + comment_solve, + cleanup_output, + header, + print_com, ) + + if filename_out: + print(f"File {filename_in} successfully converted to {filename_out}.") + else: + print( + f"File {filename_in} successfully converted to {os.path.splitext(filename_in)[0] + '.py'}." + ) + +else: + + def cli(): + print("PyMAPDL CLI requires click to be installed.") diff --git a/src/ansys/mapdl/core/errors.py b/src/ansys/mapdl/core/errors.py index e61fffbaad..023e99b455 100644 --- a/src/ansys/mapdl/core/errors.py +++ b/src/ansys/mapdl/core/errors.py @@ -1,4 +1,4 @@ -"""pymapdl specific errors""" +"""PyMAPDL specific errors""" from functools import wraps import signal diff --git a/src/ansys/mapdl/core/examples/downloads.py b/src/ansys/mapdl/core/examples/downloads.py index bdae6c01e8..8cf32be77f 100644 --- a/src/ansys/mapdl/core/examples/downloads.py +++ b/src/ansys/mapdl/core/examples/downloads.py @@ -6,7 +6,12 @@ import urllib.request import zipfile -import requests +try: + import requests + + _HAS_REQUESTS = True +except ModuleNotFoundError: + _HAS_REQUESTS = False from ansys.mapdl import core as pymapdl @@ -56,6 +61,9 @@ def _get_file_url(filename, directory=None): def _check_url_exist(url): + if not _HAS_REQUESTS: + raise ModuleNotFoundError("Examples module requires request module") + response = requests.get(url) if response.status_code == 200: return [True] diff --git a/src/ansys/mapdl/core/examples/verif/vm132.dat b/src/ansys/mapdl/core/examples/verif/vm132.dat index 85cc37dbcb..98c7483d6c 100755 --- a/src/ansys/mapdl/core/examples/verif/vm132.dat +++ b/src/ansys/mapdl/core/examples/verif/vm132.dat @@ -22,7 +22,7 @@ D,ALL,ALL ! FIX ALL DOFS FINISH /SOLU NSUBST,100,100,100 -RATE,ON,ON +RATE,ON OUTPR,BASIC,10 ! PRINT BASIC SOLUTION FOR EVERY 10TH SUBSTEP OUTRES,ESOL,1 ! STORE ELEMENT SOLUTION FOR EVERY SUBSTEP /OUT,SCRATCH diff --git a/src/ansys/mapdl/core/examples/verif/vm139.dat b/src/ansys/mapdl/core/examples/verif/vm139.dat index 863ef1c87c..cf126a7f0b 100755 --- a/src/ansys/mapdl/core/examples/verif/vm139.dat +++ b/src/ansys/mapdl/core/examples/verif/vm139.dat @@ -25,7 +25,7 @@ D,1,UX,,,,,UY,UZ D,11,UX,,,,,UZ D,ALL,ROTZ NSEL,S,LOC,X,22.5 -DSYM,SYMM,X,0,22.5 ! SYMMETRY B.C.'S AT CENTERLINE +DSYM,SYMM,X,0 ! SYMMETRY B.C.'S AT CENTERLINE NSEL,ALL FINISH /SOLU @@ -100,7 +100,7 @@ NSEL,R,LOC,Y,0 D,ALL,UY ALLSEL,ALL NSEL,S,LOC,X,22.5 -DSYM,SYMM,X,0,22.5 ! SYMMETRY B.C.'S AT CENTERLINE +DSYM,SYMM,X,0 ! SYMMETRY B.C.'S AT CENTERLINE NSEL,ALL FINISH /SOLU diff --git a/src/ansys/mapdl/core/examples/verif/vm147.dat b/src/ansys/mapdl/core/examples/verif/vm147.dat index 0b34f0f978..29676c2de1 100755 --- a/src/ansys/mapdl/core/examples/verif/vm147.dat +++ b/src/ansys/mapdl/core/examples/verif/vm147.dat @@ -26,7 +26,7 @@ VTYPE,1 ! NON-HIDDEN (FAST) METHOD GEOM,1,50 ! 2-D AXISYMMETRIC GEOM WITH 50 FACETS MPRINT,1 STEF,5.6696E-8 ! STEFAN-BOLTZMANN CONSTANT IN MKS UNITS -WRITE,CONE,SUB ! WRITE RADIATION SUBSTRUCTURE MATRIX +WRITE,CONE ! WRITE RADIATION SUBSTRUCTURE MATRIX FINISH /CLEAR,NOSTART ! CLEAR DATABASE; DO NOT READ START.ANS FILE /PREP7 diff --git a/src/ansys/mapdl/core/examples/verif/vm221.dat b/src/ansys/mapdl/core/examples/verif/vm221.dat index 523bbcbb5e..07f4b5ac29 100755 --- a/src/ansys/mapdl/core/examples/verif/vm221.dat +++ b/src/ansys/mapdl/core/examples/verif/vm221.dat @@ -24,7 +24,7 @@ C6=70E3 !MPA, [MARTENSITE MODULUS] C7=0 !M = 0, SYMMETRICAL BEHAVIOR TB,SMA,1,,7,MEFF -TBDATA,1,C1,C2,C3,C4,C5,C6,C7 +TBDATA,1,C1,C2,C3,C4,C5,C6 BLOCK,0.00,5.00,0.00,5.00,0.00,5.00 ESIZE,5 diff --git a/src/ansys/mapdl/core/examples/verif/vm227.dat b/src/ansys/mapdl/core/examples/verif/vm227.dat index eacfe91d18..7f8fec704c 100755 --- a/src/ansys/mapdl/core/examples/verif/vm227.dat +++ b/src/ansys/mapdl/core/examples/verif/vm227.dat @@ -28,7 +28,7 @@ SFL,8,RDSF,1, ,1, FINISH /AUX12 STEF,0.119E-10 !SET STEFAN-BOLTZMAN CONSTANT FOR MODEL -hemiopt,,,,,,,,,,,,,,,,,,,0 +hemiopt TOFFST,100 !SET TEMPERATURE OFFSET RADOPT,,0.1,2,1000,0.1,0.1 !SET RADIOSITY OPTIONS SPCTEMP,1,0.E+00 !SET TEMPERATURE FOR RADIATION TO SPACE diff --git a/src/ansys/mapdl/core/examples/verif/vm273.dat b/src/ansys/mapdl/core/examples/verif/vm273.dat index a4f5ed1dcf..f0145eed29 100755 --- a/src/ansys/mapdl/core/examples/verif/vm273.dat +++ b/src/ansys/mapdl/core/examples/verif/vm273.dat @@ -27,7 +27,7 @@ C6=70E3 !MPA, [MARTENSITE MODULUS] C7=0 ! M = 0, SYMMETRICAL BEHAVIOR TB,SMA,1,,7,MEFF -TBDATA,1,C1,C2,C3,C4,C5,C6,C7 +TBDATA,1,C1,C2,C3,C4,C5,C6 BLOCK,0.00,10.00,0.00,10.00,0.00,10.00 ESIZE,10 diff --git a/src/ansys/mapdl/core/examples/verif/vm285.dat b/src/ansys/mapdl/core/examples/verif/vm285.dat index 8c06bd0da1..c33f584b02 100644 --- a/src/ansys/mapdl/core/examples/verif/vm285.dat +++ b/src/ansys/mapdl/core/examples/verif/vm285.dat @@ -46,7 +46,7 @@ A,1,2,3,4,7 A,7,4,5,6 AGLUE,ALL -KSCON,2,A/20,0,12,,10 ! CRACK TIP ELEMENTS +KSCON,2,A/20,0,12,, ! CRACK TIP ELEMENTS ESIZE,0.3 AMESH,1 diff --git a/src/ansys/mapdl/core/examples/verif/vm287.dat b/src/ansys/mapdl/core/examples/verif/vm287.dat index 8fec708652..a3aec45a2d 100644 --- a/src/ansys/mapdl/core/examples/verif/vm287.dat +++ b/src/ansys/mapdl/core/examples/verif/vm287.dat @@ -70,7 +70,7 @@ ALLSEL XFENRICH,ENRICH1,TESTCMP,1 FLST,3,2,8 FITEM,3,0.0,5.0,0 -FITEM,3,5.0,5.0,0,0 +FITEM,3,5.0,5.0,0 XFDATA,ENRICH1,P51X ALLSEL /OUT diff --git a/src/ansys/mapdl/core/examples/verif/vm309.dat b/src/ansys/mapdl/core/examples/verif/vm309.dat index 0eae3c8044..ae5776c976 100644 --- a/src/ansys/mapdl/core/examples/verif/vm309.dat +++ b/src/ansys/mapdl/core/examples/verif/vm309.dat @@ -95,7 +95,7 @@ FINISH OUTRES,ALL,ALL ANTYPE,1 BUCOPT,LANB,3 -SUBOPT,0,0,0,0,0,ALL +SUBOPT,0,0 MXPAND,3,,,YES SOLVE FINISH diff --git a/src/ansys/mapdl/core/examples/verif/vm51.dat b/src/ansys/mapdl/core/examples/verif/vm51.dat index cc02967b5e..69cbeafcaa 100755 --- a/src/ansys/mapdl/core/examples/verif/vm51.dat +++ b/src/ansys/mapdl/core/examples/verif/vm51.dat @@ -1,5 +1,4 @@ /COM,ANSYS MEDIA REL. 2023R2 (05/12/2023) REF. VERIF. MANUAL: REL. 2023R2 -/OUT,SCRATCH /VERIFY,VM51 JPGPRF,500,100,1 ! MACRO TO SET PREFS FOR JPEG PLOTS /SHOW,JPEG diff --git a/src/ansys/mapdl/core/launcher.py b/src/ansys/mapdl/core/launcher.py index a50a4758d9..9a23856417 100644 --- a/src/ansys/mapdl/core/launcher.py +++ b/src/ansys/mapdl/core/launcher.py @@ -20,7 +20,12 @@ except ModuleNotFoundError: # pragma: no cover _HAS_PIM = False -from ansys.tools.path import find_ansys, get_ansys_path, version_from_path +try: + from ansys.tools.path import find_ansys, get_ansys_path, version_from_path + + _HAS_ATP = True +except ModuleNotFoundError: + _HAS_ATP = False from ansys.mapdl import core as pymapdl from ansys.mapdl.core import LOG @@ -32,6 +37,7 @@ VersionError, ) from ansys.mapdl.core.licensing import ALLOWABLE_LICENSES, LicenseChecker +from ansys.mapdl.core.mapdl import _ALLOWED_START_PARM from ansys.mapdl.core.mapdl_grpc import MAX_MESSAGE_LENGTH, MapdlGrpc from ansys.mapdl.core.misc import ( check_valid_ip, @@ -412,8 +418,9 @@ def launch_grpc( raise IOError('Unable to write to ``run_location`` "%s"' % run_location) # verify version - if version_from_path("mapdl", exec_file) < 202: - raise VersionError("The MAPDL gRPC interface requires MAPDL 20.2 or later") + if _HAS_ATP: + if version_from_path("mapdl", exec_file) < 202: + raise VersionError("The MAPDL gRPC interface requires MAPDL 20.2 or later") # verify lock file does not exist check_lock_file(run_location, jobname, override) @@ -871,11 +878,23 @@ def _validate_MPI(add_sw, exec_path, force_intel=False): LOG.debug("Ubuntu system detected. Adding 'I_MPI_SHM_LMT' env var.") os.environ["I_MPI_SHM_LMT"] = "shm" - if ( - os.name == "nt" - and not force_intel - and (222 > version_from_path("mapdl", exec_path) >= 210) - ): + if _HAS_ATP: + condition = ( + os.name == "nt" + and not force_intel + and (222 > version_from_path("mapdl", exec_path) >= 210) + ) + else: + if os.name == "nt": + warnings.warn( + "Because 'ansys-tools-path' is not installed, PyMAPDL cannot check\n" + "if this Ansys version requires the MPI fix, so if you are on Windows,\n" + "the fix is applied by default.\n" + "Use 'force_intel=True' to not apply the fix." + ) + condition = os.name == "nt" and not force_intel + + if condition: # Workaround to fix a problem when launching ansys in 'dmp' mode in the # recent windows version and using VPN. # This is due to the intel compiler, and only affects versions between @@ -1079,15 +1098,15 @@ def launch_mapdl( ``log_apdl='pymapdl_log.txt'``). By default this is disabled. remove_temp_files : bool, optional - .. deprecated:: 0.64.0 - Use argument ``remove_temp_dir_on_exit`` instead. - When ``run_location`` is ``None``, this launcher creates a new MAPDL working directory within the user temporary directory, obtainable with ``tempfile.gettempdir()``. When this parameter is ``True``, this directory will be deleted when MAPDL is exited. Default ``False``. + .. deprecated:: 0.64.0 + Use argument ``remove_temp_dir_on_exit`` instead. + remove_temp_dir_on_exit : bool, optional When ``run_location`` is ``None``, this launcher creates a new MAPDL working directory within the user temporary directory, obtainable with @@ -1355,6 +1374,15 @@ def launch_mapdl( # Extract arguments: force_intel = kwargs.pop("force_intel", False) broadcast = kwargs.pop("log_broadcast", False) + use_vtk = kwargs.pop("use_vtk", None) + + # Transferring MAPDL arguments to start_parameters: + start_parm = {} + + kwargs_keys = list(kwargs.keys()) + for each_par in kwargs_keys: + if each_par in _ALLOWED_START_PARM: + start_parm[each_par] = kwargs.pop(each_par) # Raising error if using non-allowed arguments if kwargs: @@ -1449,6 +1477,7 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + **start_parm, ) GALLERY_INSTANCE[0] = {"ip": mapdl._ip, "port": mapdl._port} return mapdl @@ -1461,6 +1490,8 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1474,6 +1505,8 @@ def launch_mapdl( cleanup_on_exit=False, loglevel=loglevel, set_no_abort=set_no_abort, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1489,6 +1522,8 @@ def launch_mapdl( loglevel=loglevel, set_no_abort=set_no_abort, log_apdl=log_apdl, + use_vtk=use_vtk, + **start_parm, ) if clear_on_connect: mapdl.clear() @@ -1499,6 +1534,11 @@ def launch_mapdl( exec_file = os.getenv("PYMAPDL_MAPDL_EXEC", None) if exec_file is None: + if not _HAS_ATP: + raise ModuleNotFoundError( + "If you don't have 'ansys-tools-path' library installed, you need to input the executable path ('exec_path')." + ) + LOG.debug("Using default executable.") # Load cached path exec_file = get_ansys_path(version=version) @@ -1542,8 +1582,11 @@ def launch_mapdl( # verify no lock file and the mode is valid check_lock_file(run_location, jobname, override) - mode = check_mode(mode, version_from_path("mapdl", exec_file)) - LOG.debug("Using mode %s", mode) + if _HAS_ATP: + mode = check_mode(mode, version_from_path("mapdl", exec_file)) + LOG.debug("Using mode %s", mode) + else: + mode = "grpc" # Setting SMP by default if student version is used. additional_switches = _force_smp_student_version(additional_switches, exec_file) @@ -1556,14 +1599,16 @@ def launch_mapdl( additional_switches = _check_license_argument(license_type, additional_switches) LOG.debug(f"Using additional switches {additional_switches}.") - start_parm = { - "exec_file": exec_file, - "run_location": run_location, - "additional_switches": additional_switches, - "jobname": jobname, - "nproc": nproc, - "print_com": print_com, - } + start_parm.update( + { + "exec_file": exec_file, + "run_location": run_location, + "additional_switches": additional_switches, + "jobname": jobname, + "nproc": nproc, + "print_com": print_com, + } + ) if mode in ["console", "corba"]: start_parm["start_timeout"] = start_timeout @@ -1585,7 +1630,9 @@ def launch_mapdl( if mode == "console": from ansys.mapdl.core.mapdl_console import MapdlConsole - mapdl = MapdlConsole(loglevel=loglevel, log_apdl=log_apdl, **start_parm) + mapdl = MapdlConsole( + loglevel=loglevel, log_apdl=log_apdl, use_vtk=use_vtk, **start_parm + ) elif mode == "corba": try: # pending deprecation to ansys-mapdl-corba @@ -1601,6 +1648,7 @@ def launch_mapdl( log_apdl=log_apdl, log_broadcast=broadcast, verbose=verbose_mapdl, + use_vtk=use_vtk, **start_parm, ) elif mode == "grpc": @@ -1621,11 +1669,15 @@ def launch_mapdl( remove_temp_dir_on_exit=remove_temp_dir_on_exit, log_apdl=log_apdl, process=process, + use_vtk=use_vtk, **start_parm, ) if run_location is None: mapdl._path = actual_run_location + # Setting launched property + mapdl._launched = True + except Exception as exception: # Failed to launch for some reason. Check if failure was due # to the license check @@ -1640,9 +1692,6 @@ def launch_mapdl( LOG.debug("Stopping license server check.") lic_check.is_connected = True - # Setting launched property - mapdl._launched = True - return mapdl diff --git a/src/ansys/mapdl/core/licensing.py b/src/ansys/mapdl/core/licensing.py index 1fb4916123..3a540d3347 100644 --- a/src/ansys/mapdl/core/licensing.py +++ b/src/ansys/mapdl/core/licensing.py @@ -9,6 +9,15 @@ from ansys.mapdl.core.errors import LicenseServerConnectionError from ansys.mapdl.core.misc import threaded_daemon +try: + from ansys.tools.path import get_ansys_path, version_from_path + + _HAS_ATP = True + +except ModuleNotFoundError: + _HAS_ATP = False + + LOCALHOST = "127.0.0.1" LIC_PATH_ENVAR = "ANSYSLIC_DIR" LIC_FILE_ENVAR = "ANSYSLMD_LICENSE_FILE" @@ -121,7 +130,7 @@ def start(self, license_file=True, checkout_license=False): disabled. """ - if license_file: + if license_file and _HAS_ATP: self._lic_file_thread = self.check_license_file() if checkout_license: self._checkout_thread = self.checkout_license() @@ -464,8 +473,6 @@ def get_ansys_license_debug_file_name(): # pragma: no cover # - For version 22.1 and above: `licdebug.$hostname.$appname.$version.out` # - For version 21.2 and below: `licdebug.$appname.$version.out` - from ansys.mapdl.core.launcher import get_ansys_path, version_from_path - name = "licdebug" hostname = socket.gethostname() appname = APP_NAME diff --git a/src/ansys/mapdl/core/logging.py b/src/ansys/mapdl/core/logging.py index 2c9d38c3f9..e695181047 100644 --- a/src/ansys/mapdl/core/logging.py +++ b/src/ansys/mapdl/core/logging.py @@ -10,8 +10,8 @@ outputs and it is used to track the different MAPDL instances. -Usage ------ +How to use +========== Global logger ~~~~~~~~~~~~~ diff --git a/src/ansys/mapdl/core/mapdl.py b/src/ansys/mapdl/core/mapdl.py index e6b310f312..1a526b2aaa 100644 --- a/src/ansys/mapdl/core/mapdl.py +++ b/src/ansys/mapdl/core/mapdl.py @@ -16,7 +16,6 @@ from warnings import warn import weakref -from matplotlib.colors import to_rgba import numpy as np from numpy.typing import DTypeLike, NDArray @@ -193,7 +192,9 @@ def setup_logger(loglevel="INFO", log_file=True, mapdl_instance=None): "process", "ram", "run_location", - "start_timeout" "timeout", + "start_timeout", + "timeout", + "check_parameter_names", ] @@ -267,6 +268,7 @@ def __init__( self._jobname: str = start_parm.get("jobname", "file") self._path: Union[str, pathlib.Path] = start_parm.get("run_location", None) self._print_com: bool = print_com # print the command /COM input. + self.check_parameter_names = start_parm.get("check_parameter_names", True) # Setting up loggers self._log: logging.Logger = logger.add_instance_logger( @@ -325,8 +327,8 @@ def print_com(self, value): ) @property - def mode(self): - """Return the type of instance, namely: grpc, corba or console.""" + def connection(self): + """Return the type of connection to the instance, namely: grpc, corba or console.""" return self._mode @property @@ -366,6 +368,9 @@ def check_status(self): @property def file_type_for_plots(self): """Returns the current file type for plotting.""" + if not self._file_type_for_plots: + self._run("/show, PNG") + self._file_type_for_plots = "PNG" return self._file_type_for_plots @file_type_for_plots.setter @@ -457,6 +462,10 @@ def wrap_xsel_function(func): def wrap_xsel_function_output(method): # Injecting doc string modification name = method.__func__.__name__.upper() + if not self.geometry: + # Cases where the geometry module is not loaded + return None + if name == "NSEL": return self.mesh.nnum elif name == "ESEL": @@ -475,6 +484,10 @@ def wrap_xsel_function_output(method): @wraps(func) def inner_wrapper(*args, **kwargs): # in interactive mode (item='p'), the output is not suppressed + if self._store_commands: + # In non-interactive mode, we do not need to check anything. + return + is_interactive_arg = ( True if len(args) >= 2 @@ -638,6 +651,17 @@ def force_output(self): """ return self._force_output(self) + @property + def save_selection(self): + """Save selection + + Save the current selection (nodes, elements, keypoints, lines, areas, + volumes and components) before entering in the context manager, and + when exit returns to that selection. + + """ + return self._save_selection(self) + @property def solution(self) -> "Solution": """Solution parameters of MAPDL. @@ -806,6 +830,50 @@ def __exit__(self, *args): self._parent()._flush_stored() self._parent()._store_commands = False + class _save_selection: + """Save the selection and returns to it when exiting""" + + def __init__(self, parent): + self._parent = weakref.ref(parent) + self.selection_sets = [] + self.selection_sets_comps = [] + + def __enter__(self): + self._parent()._log.debug("Entering saving selection context") + + selection_set_name = random_string(10) + self.selection_sets.append(selection_set_name) + self.selection_sets_comps.append(self._parent().components.names) + + mapdl = self._parent() + + prev_ier = mapdl.ignore_errors + mapdl.ignore_errors = True + for entity in ["kp", "lines", "area", "volu", "node", "elem"]: + mapdl.cm(f"_{selection_set_name}_{entity}_", f"{entity}", mute=True) + mapdl.ignore_errors = prev_ier + + def __exit__(self, *args): + self._parent()._log.debug("Exiting saving selection context") + last_selection_name = self.selection_sets.pop() + last_selection_cmps = self.selection_sets_comps.pop() + + mapdl = self._parent() + + # probably this is redundant + prev_ier = mapdl.ignore_errors + mapdl.ignore_errors = True + for entity in ["kp", "lines", "area", "volu", "node", "elem"]: + cmp_name = f"_{last_selection_name}_{entity}_" + mapdl.cmsel("s", cmp_name, f"{entity}", mute=True) + mapdl.cmdele(cmp_name) + + mapdl.ignore_errors = prev_ier + + # mute to avoid getting issues when the component wasn't created in + # first place because there was no entities. + self._parent().components.select(last_selection_cmps, mute=True) + class _chain_commands: """Store MAPDL commands and send one chained command.""" @@ -930,7 +998,6 @@ def info(self): return self._info @property - @requires_package("pyvista", softerror=True) def geometry(self) -> "Geometry": """Geometry information. @@ -976,15 +1043,17 @@ def geometry(self) -> "Geometry": def _create_geometry(self) -> Union["Geometry", "LegacyGeometry"]: """Return geometry cache""" - from ansys.mapdl.core.mapdl_geometry import Geometry, LegacyGeometry if self.legacy_geometry: + from ansys.mapdl.core.mapdl_geometry import LegacyGeometry + return LegacyGeometry else: + from ansys.mapdl.core.mapdl_geometry import Geometry + return Geometry(self) @property - @requires_package("pyvista", softerror=True) def mesh(self): """Mesh information. @@ -1031,7 +1100,6 @@ def mesh(self): return self._mesh @property - @requires_package("ansys.mapdl.reader", softerror=True) @supress_logging def _mesh(self) -> "Archive": """Write entire archive to ASCII and read it in as an @@ -1708,45 +1776,39 @@ def vplot( ) return general_plotter([], [], [], **kwargs) - cm_name_area = "__tmp_area2__" - cm_name_volu = "__tmp_volu2__" - self.cm(cm_name_area, "AREA", mute=True) - self.cm(cm_name_volu, "VOLU", mute=True) - - volumes = self.geometry.vnum - meshes = [] - points = [] - labels = [] - - return_plotter = kwargs.pop("return_plotter", False) - color_areas = True - - for each_volu in volumes: - self.vsel("S", vmin=each_volu) - self.aslv("S", mute=True) # select areas attached to active volumes - - pl = self.aplot( - vtk=True, - color_areas=color_areas, - quality=quality, - show_area_numbering=show_area_numbering, - show_line_numbering=show_line_numbering, - show_lines=show_lines, - return_plotter=True, - **kwargs, - ) - - meshes_ = get_meshes_from_plotter(pl) + # Storing entities selection + with self.save_selection: + volumes = self.geometry.vnum + meshes = [] + points = [] + labels = [] + + return_plotter = kwargs.pop("return_plotter", False) + color_areas = True + + for each_volu in volumes: + self.vsel("S", vmin=each_volu) + self.aslv("S", mute=True) # select areas attached to active volumes + + pl = self.aplot( + vtk=True, + color_areas=color_areas, + quality=quality, + show_area_numbering=show_area_numbering, + show_line_numbering=show_line_numbering, + show_lines=show_lines, + return_plotter=True, + **kwargs, + ) - for each_mesh in meshes_: - each_mesh.cell_data["entity_num"] = int(each_volu) + meshes_ = get_meshes_from_plotter(pl) - meshes.extend(meshes_) + for each_mesh in meshes_: + each_mesh.cell_data["entity_num"] = int(each_volu) - meshes = [{"mesh": meshes}] + meshes.extend(meshes_) - self.cmsel("S", cm_name_area, "AREA", mute=True) - self.cmsel("S", cm_name_volu, "VOLU", mute=True) + meshes = [{"mesh": meshes}] return general_plotter( meshes, points, labels, return_plotter=return_plotter, **kwargs @@ -1868,6 +1930,8 @@ def aplot( ) if vtk: + from matplotlib.colors import to_rgba + kwargs.setdefault("show_scalar_bar", False) kwargs.setdefault("title", "MAPDL Area Plot") kwargs.setdefault("scalar_bar_args", {"title": "Scalar Bar Title"}) @@ -1963,11 +2027,10 @@ def aplot( if show_lines or show_line_numbering: kwargs.setdefault("line_width", 2) # subselect lines belonging to the current areas - self.cm("__area__", "AREA", mute=True) - self.lsla("S", mute=True) - lines = self.geometry.get_lines() - self.cmsel("S", "__area__", "AREA", mute=True) + with self.save_selection: + self.lsla("S", mute=True) + lines = self.geometry.get_lines() if show_lines: meshes.append( @@ -2013,33 +2076,37 @@ def __enter__(self) -> None: self._parent()._log.debug("Entering in 'WithInterativePlotting' mode") if not self._parent()._has_matplotlib: # pragma: no cover - raise ImportError( + raise ModuleNotFoundError( "Install matplotlib to display plots from MAPDL ," "from Python. Otherwise, plot with vtk with:\n" "``vtk=True``" ) - if not self._parent()._png_mode: - self._parent().show("PNG", mute=True) - self._parent().gfile(self._pixel_res, mute=True) + if not self._parent()._store_commands: + if not self._parent()._png_mode: + self._parent().show("PNG", mute=True) + self._parent().gfile(self._pixel_res, mute=True) - self.previous_device = self._parent().file_type_for_plots + self.previous_device = self._parent().file_type_for_plots - if self._parent().file_type_for_plots not in ["PNG", "TIFF", "PNG", "VRML"]: - self._parent().show(self._parent().default_file_type_for_plots) + if self._parent().file_type_for_plots not in [ + "PNG", + "TIFF", + "PNG", + "VRML", + ]: + self._parent().show(self._parent().default_file_type_for_plots) def __exit__(self, *args) -> None: self._parent()._log.debug("Exiting in 'WithInterativePlotting' mode") self._parent().show("close", mute=True) - if not self._parent()._png_mode: - self._parent().show("PNG", mute=True) - self._parent().gfile(self._pixel_res, mute=True) - self._parent().file_type_for_plots = self.previous_device + if not self._parent()._store_commands: + if not self._parent()._png_mode: + self._parent().show("PNG", mute=True) + self._parent().gfile(self._pixel_res, mute=True) - def __exit__(self, *args) -> None: - self._parent()._log.debug("Exiting in 'WithInterativePlotting' mode") - self._parent().show("close", mute=True) + self._parent().file_type_for_plots = self.previous_device @property def _has_matplotlib(self): @@ -2047,13 +2114,14 @@ def _has_matplotlib(self): import matplotlib # noqa: F401 return True - except ImportError: + except ModuleNotFoundError: return False @property def _png_mode(self): """Returns True when MAPDL is set to write plots as png to file.""" - return "PNG" in self.show(mute=False) + with self.force_output: + return "PNG" in self.show(mute=False) def set_log_level(self, loglevel: DEBUG_LEVELS) -> None: """Sets log level @@ -2786,6 +2854,10 @@ def get( with self.force_output: response = self.run(command, **kwargs) + if self._store_commands: + # Return early in non_interactive + return + value = response.split("=")[-1].strip() if item3: self._log.info( @@ -3002,6 +3074,8 @@ def run_multiline(self, commands) -> str: This function is being deprecated. Please use `input_strings` instead. + Allows to run multiple mapdl commands in different lines in one go. + Parameters ---------- commands : str @@ -3245,7 +3319,7 @@ def run( command = "/CLE,NOSTART" # Tracking output device - if command[:4].upper() == "/SHO": + if command[:4].upper() == "/SHO" and "," in command: self._file_type_for_plots = command.split(",")[1].upper() # Invalid commands silently ignored. @@ -3288,8 +3362,17 @@ def run( # Edge case. `\title, 'par=1234' ` self._check_parameter_name(param_name) + short_cmd = parse_to_short_cmd(command) text = self._run(command, verbose=verbose, mute=mute) + if ( + "Display device has not yet been specified with the /SHOW command" in text + and short_cmd in PLOT_COMMANDS + ): + # Reissuing the command to make sure we get output. + self.show(self.default_file_type_for_plots) + text = self._run(command, verbose=verbose, mute=mute) + if command[:4].upper() == "/CLE" and self.is_grpc: # We have reset the database, so we need to create a new session id self._create_session() @@ -3309,15 +3392,18 @@ def run( self._raise_errors(text) # special returns for certain geometry commands - short_cmd = parse_to_short_cmd(command) - if short_cmd in PLOT_COMMANDS: self._log.debug("It is a plot command.") plot_path = self._get_plot_name(text) + if save_fig: return self._download_plot(plot_path, save_fig) - else: + elif self._has_matplotlib: return self._display_plot(plot_path) + else: + self._log.debug( + "Since matplolib is not installed, images are not shown." + ) return self._response @@ -3642,7 +3728,7 @@ def directory(self, path: Union[str, pathlib.Path]) -> None: @property def _lockfile(self): - """lockfile path""" + """Lockfile path""" path = self.directory if path is not None: return os.path.join(path, self.jobname + ".lock").replace("\\", "/") @@ -3885,7 +3971,7 @@ def _screenshot_path(self): return filenames[-1] def _set_log_level(self, level): - """alias for set_log_level""" + """Alias for set_log_level""" self.set_log_level(level) def list(self, filename, ext=""): @@ -3979,6 +4065,9 @@ def get_nodal_constrains(self, label=None): def _check_parameter_name(self, param_name): """Checks if a parameter name is allowed or not.""" + if not self.check_parameter_names: + return + param_name = param_name.strip() match_valid_parameter_name = r"^[a-zA-Z_][a-zA-Z\d_\(\),\s\%]{0,31}$" @@ -4050,7 +4139,7 @@ def mpwrite( lib="", mat="", download_file=False, - progress_bar=True, + progress_bar=False, **kwargs, ): fname_ = fname + "." + ext @@ -4902,6 +4991,7 @@ def cmplot(self, label: str = "", entity: str = "", keyword: str = "", **kwargs) if entity[:4] not in ["NODE", "ELEM", "KP", "LINE", "AREA", "VOLU"]: raise ValueError(f"The entity '{entity}' is not allowed.") + cmps_names = self.components.names self.cm("__tmp_cm__", entity=entity) if label == "ALL": self.cmsel("ALL", entity=entity) @@ -4923,4 +5013,5 @@ def cmplot(self, label: str = "", entity: str = "", keyword: str = "", **kwargs) # returning to previous selection self.cmsel("s", "__tmp_cm__", entity=entity) + self.components.select(cmps_names) return output diff --git a/src/ansys/mapdl/core/mapdl_console.py b/src/ansys/mapdl/core/mapdl_console.py index 084d7df0a5..9d73dece14 100644 --- a/src/ansys/mapdl/core/mapdl_console.py +++ b/src/ansys/mapdl/core/mapdl_console.py @@ -7,9 +7,8 @@ import time from ansys.mapdl.core.errors import MapdlExitedError, MapdlRuntimeError - -# from ansys.mapdl.core.misc import kill_process from ansys.mapdl.core.mapdl import _MapdlCore +from ansys.mapdl.core.misc import requires_package ready_items = [ rb"BEGIN:", @@ -200,6 +199,54 @@ def _run(self, command, **kwargs): # return last response and all preceding responses return full_response + @property + @requires_package("ansys.mapdl.reader", softerror=True) + @requires_package("pyvista", softerror=True) + def mesh(self): + """Mesh information. + + Returns + ------- + :class:`Mapdl.Mesh ` + + Examples + -------- + Return an array of the active nodes + + >>> mapdl.mesh.nodes + array([[ 1., 0., 0.], + [ 2., 0., 0.], + [ 3., 0., 0.], + [ 4., 0., 0.], + [ 5., 0., 0.], + [ 6., 0., 0.], + [ 7., 0., 0.], + [ 8., 0., 0.], + [ 9., 0., 0.], + [10., 0., 0.]]) + + Return an array of the node numbers of the active nodes + + >>> mapdl.mesh.nnum + array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=int32) + + Simply query and print the geometry + + >>> print(mapdl.mesh) + ANSYS Mapdl Mesh + Number of Nodes: 321 + Number of Elements: 40 + Number of Element Types: 1 + Number of Node Components: 2 + Number of Element Components: 2 + + Access the geometry as a VTK object + + >>> mapdl.mesh.grid + + """ + return self._mesh + def exit(self, close_log=True, timeout=3): """Exit MAPDL process. diff --git a/src/ansys/mapdl/core/mapdl_corba.py b/src/ansys/mapdl/core/mapdl_corba.py index 39f1bf3f0b..0f611419fe 100644 --- a/src/ansys/mapdl/core/mapdl_corba.py +++ b/src/ansys/mapdl/core/mapdl_corba.py @@ -10,7 +10,7 @@ from ansys.mapdl.core.errors import MapdlExitedError, MapdlRuntimeError from ansys.mapdl.core.mapdl import _MapdlCore -from ansys.mapdl.core.misc import random_string, threaded +from ansys.mapdl.core.misc import random_string, requires_package, threaded if sys.version_info[1] > 8: raise ImportError( @@ -220,6 +220,54 @@ def __init__( INSTANCES.append(weakref.ref(self)) self._corba_key = None + @property + @requires_package("ansys.mapdl.reader", softerror=True) + @requires_package("pyvista", softerror=True) + def mesh(self): + """Mesh information. + + Returns + ------- + :class:`Mapdl.Mesh ` + + Examples + -------- + Return an array of the active nodes + + >>> mapdl.mesh.nodes + array([[ 1., 0., 0.], + [ 2., 0., 0.], + [ 3., 0., 0.], + [ 4., 0., 0.], + [ 5., 0., 0.], + [ 6., 0., 0.], + [ 7., 0., 0.], + [ 8., 0., 0.], + [ 9., 0., 0.], + [10., 0., 0.]]) + + Return an array of the node numbers of the active nodes + + >>> mapdl.mesh.nnum + array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=int32) + + Simply query and print the geometry + + >>> print(mapdl.mesh) + ANSYS Mapdl Mesh + Number of Nodes: 321 + Number of Elements: 40 + Number of Element Types: 1 + Number of Node Components: 2 + Number of Element Components: 2 + + Access the geometry as a VTK object + + >>> mapdl.mesh.grid + + """ + return self._mesh + def _launch(self, start_parm, verbose): """Launch CORBA.""" corba_key, process = launch_corba(verbose=verbose, **start_parm) @@ -414,7 +462,7 @@ def _run(self, command, **kwargs): return response def _close_output(self): - """closes the output file""" + """Closes the output file""" self._output = "" if self._outfile: self._outfile.close() diff --git a/src/ansys/mapdl/core/mapdl_geometry.py b/src/ansys/mapdl/core/mapdl_geometry.py index a70b3cfef7..2dbd4e44d9 100644 --- a/src/ansys/mapdl/core/mapdl_geometry.py +++ b/src/ansys/mapdl/core/mapdl_geometry.py @@ -1,15 +1,13 @@ """Module to support MAPDL CAD geometry""" -import contextlib from functools import wraps import re from typing import TYPE_CHECKING, Any, Iterable, List, Optional, Sequence, Tuple, Union import numpy as np from numpy.typing import NDArray -import pyvista as pv from ansys.mapdl.core import _HAS_PYVISTA, Mapdl -from ansys.mapdl.core.errors import ComponentNoData, VersionError +from ansys.mapdl.core.errors import VersionError if _HAS_PYVISTA: import pyvista as pv @@ -17,7 +15,7 @@ if TYPE_CHECKING: # pragma: no cover from pyiges import Iges -from ansys.mapdl.core.misc import run_as_prep7, supress_logging +from ansys.mapdl.core.misc import requires_package, run_as_prep7, supress_logging from ansys.mapdl.core.theme import MapdlTheme VALID_SELECTION_TYPE = ["S", "R", "A", "U"] @@ -57,28 +55,30 @@ """ -class Multiblock(pv.MultiBlock): - def __call__(self, *args, **kwargs): - raise VersionError(VERSION_ERROR) +if _HAS_PYVISTA: - @wraps(pv.MultiBlock.plot) - def plot(self, *args, **kwargs): - color = kwargs.pop("color", "white") - return super().plot(*args, color=color, theme=MapdlTheme(), **kwargs) + class Multiblock(pv.MultiBlock): + def __call__(self, *args, **kwargs): + raise VersionError(VERSION_ERROR) + @wraps(pv.MultiBlock.plot) + def plot(self, *args, **kwargs): + color = kwargs.pop("color", "white") + return super().plot(*args, color=color, theme=MapdlTheme(), **kwargs) -def merge_polydata(items: Iterable["pv.PolyData"]) -> "pv.PolyData": - """Merge list of polydata or unstructured grids""" + @requires_package("pyvista") + def merge_polydata(items: Iterable["pv.PolyData"]) -> "pv.PolyData": + """Merge list of polydata or unstructured grids""" - # lazy import here for faster module loading - from vtkmodules.vtkFiltersCore import vtkAppendPolyData + # lazy import here for faster module loading + from vtkmodules.vtkFiltersCore import vtkAppendPolyData - afilter = vtkAppendPolyData() - for item in items: - afilter.AddInputData(item) - afilter.Update() + afilter = vtkAppendPolyData() + for item in items: + afilter.AddInputData(item) + afilter.Update() - return pv.wrap(afilter.GetOutput()) + return pv.wrap(afilter.GetOutput()) def get_elements_per_area(resp: str) -> List[List[int]]: @@ -155,6 +155,7 @@ def __init__(self, mapdl: Mapdl): def _set_log_level(self, level: DEBUG_LEVELS) -> None: return self._mapdl.set_log_level(level) + @requires_package("pyiges") def _load_iges(self) -> "Iges": """Loads the iges file from MAPDL as a pyiges class""" # Lazy import here for speed and stability @@ -174,6 +175,7 @@ def _reset_cache(self) -> None: def __setitem__(self, key: Any, value: Any): raise NotImplementedError("This method has not been implemented yet") + @requires_package("pyvista") def __getitem__(self, name: str): name = name.lower() if "kp" in name: @@ -190,6 +192,7 @@ def __getitem__(self, name: str): ) @property + @requires_package("pyiges") def _keypoints(self) -> Tuple[NDArray, NDArray]: """Returns keypoints cache""" if self._keypoints_cache is None: @@ -197,7 +200,8 @@ def _keypoints(self) -> Tuple[NDArray, NDArray]: return self._keypoints_cache @property - def keypoints(self) -> pv.MultiBlock: + @requires_package("pyvista") + def keypoints(self) -> "pv.MultiBlock": """Obtain the keypoints geometry. Obtain the selected keypoints as a :class:`pyvista.MultiBlock` object. @@ -256,12 +260,13 @@ def keypoints(self) -> pv.MultiBlock: mb.set_block_name(index=ind, name=f"kp {mapdl_index}") return mb + @requires_package("pyvista") def get_keypoints( self, return_as_list: bool = False, return_as_array: bool = False, return_ids_in_array: bool = False, - ) -> Union[NDArray[Any], pv.PolyData, List[pv.PolyData]]: + ) -> Union[NDArray[Any], "pv.PolyData", List["pv.PolyData"]]: """Obtain the keypoints geometry. Obtain the selected keypoints as a :class:`pyvista.PolyData` object or @@ -357,14 +362,15 @@ def get_keypoints( return keypoints_pd @property - def _lines(self) -> List[pv.PolyData]: + def _lines(self) -> List["pv.PolyData"]: """Cache of the lines.""" if self._lines_cache is None: self._lines_cache = self._load_lines() return self._lines_cache @property - def lines(self) -> pv.MultiBlock: + @requires_package("pyvista") + def lines(self) -> "pv.MultiBlock": """Geometry of the lines. Obtain the selected lines as a :class:`pyvista.MultiBlock` object. @@ -436,9 +442,10 @@ def lines(self) -> pv.MultiBlock: mb.set_block_name(index=ind, name=f"line {mapdl_index}") return mb + @requires_package("pyvista") def get_lines( self, return_as_list: bool = False - ) -> Union[pv.PolyData, List[pv.PolyData]]: + ) -> Union["pv.PolyData", List["pv.PolyData"]]: """Obtain line geometry Obtain the active lines as a :class:`pyvista.PolyData` object or @@ -482,7 +489,8 @@ def get_lines( return merge_polydata(self._lines) @property - def areas(self) -> pv.MultiBlock: + @requires_package("pyvista") + def areas(self) -> "pv.MultiBlock": """Geometry of the areas. Obtain the selected areas as a :class:`pyvista.MultiBlock` object. @@ -551,9 +559,10 @@ def areas(self) -> pv.MultiBlock: mb.set_block_name(index=ind, name=f"area {mapdl_index}") return mb + @requires_package("pyvista") def get_areas( self, quality: int = 1, return_as_list: Optional[bool] = False - ) -> Union[List[pv.UnstructuredGrid], pv.PolyData]: + ) -> Union[List["pv.UnstructuredGrid"], "pv.PolyData"]: """Get active areas from MAPDL represented as :class:`pyvista.PolyData` or a list of :class:`pyvista.UnstructuredGrid`. Parameters @@ -628,13 +637,14 @@ def get_areas( @supress_logging @run_as_prep7 + @requires_package("pyvista") def generate_surface( self, density: int = 4, amin: Optional[int] = None, amax: Optional[int] = None, ninc: Optional[int] = None, - ) -> pv.PolyData: + ) -> "pv.PolyData": """ Generate an all-triangular surface of the active surfaces. @@ -655,75 +665,67 @@ def generate_surface( ninc : int, optional Steps to between amin and amax. """ - # store initially selected areas and elements - with contextlib.suppress(ComponentNoData): - # avoiding empty components exceptions - with self._mapdl.non_interactive: - self._mapdl.cm("__tmp_elem__", "ELEM") - self._mapdl.cm("__tmp_area__", "AREA") - - orig_anum = self.anum - - # reselect from existing selection to mimic APDL behavior - if amin or amax: - if amax is None: - amax = amin - - if amin is None: # amax is non-zero - amin = 1 - - if ninc is None: - ninc = "" - - self._mapdl.asel("R", "AREA", vmin=amin, vmax=amax, vinc=ninc) - - # duplicate areas to avoid affecting existing areas - a_num = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) - self._mapdl.numstr("AREA", a_num, mute=True) - self._mapdl.agen(2, "ALL", noelem=1, mute=True) - a_max = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) - - self._mapdl.asel("S", "AREA", vmin=a_num + 1, vmax=a_max, mute=True) - # necessary to reset element/area meshing association - self._mapdl.aatt(mute=True) - - # create a temporary etype - etype_max = int(self._mapdl.get(entity="ETYP", item1="NUM", it1num="MAX")) - etype_old = self._mapdl.parameters.type - etype_tmp = etype_max + 1 - - old_routine = self._mapdl.parameters.routine - - self._mapdl.et(etype_tmp, "MESH200", 6, mute=True) - self._mapdl.shpp("off", mute=True) - self._mapdl.smrtsize(density, mute=True) - self._mapdl.type(etype_tmp, mute=True) - - if old_routine != "PREP7": - self._mapdl.prep7(mute=True) - - # Mesh and get the number of elements per area - resp = self._mapdl.amesh("all") - groups = get_elements_per_area(resp) - - self._mapdl.esla("S") - grid = self._mapdl.mesh._grid.linear_copy() - pd = pv.PolyData(grid.points, grid.cells, n_faces=grid.n_cells) - - # pd['ansys_node_num'] = grid['ansys_node_num'] - # pd['vtkOriginalPointIds'] = grid['vtkOriginalPointIds'] - # pd.clean(inplace=True) # OPTIONAL - - # delete all temporary meshes and clean up settings - self._mapdl.aclear("ALL", mute=True) - self._mapdl.adele("ALL", kswp=1, mute=True) - self._mapdl.numstr("AREA", 1, mute=True) - self._mapdl.type(etype_old, mute=True) - self._mapdl.etdele(etype_tmp, mute=True) - self._mapdl.shpp("ON", mute=True) - self._mapdl.smrtsize("OFF", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_elem__", "ELEM", mute=True) + with self._mapdl.save_selection: + orig_anum = self.anum + + # reselect from existing selection to mimic APDL behavior + if amin or amax: + if amax is None: + amax = amin + + if amin is None: # amax is non-zero + amin = 1 + + if ninc is None: + ninc = "" + + self._mapdl.asel("R", "AREA", vmin=amin, vmax=amax, vinc=ninc) + + # duplicate areas to avoid affecting existing areas + a_num = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) + self._mapdl.numstr("AREA", a_num, mute=True) + self._mapdl.agen(2, "ALL", noelem=1, mute=True) + a_max = int(self._mapdl.get(entity="AREA", item1="NUM", it1num="MAXD")) + + self._mapdl.asel("S", "AREA", vmin=a_num + 1, vmax=a_max, mute=True) + # necessary to reset element/area meshing association + self._mapdl.aatt(mute=True) + + # create a temporary etype + etype_max = int(self._mapdl.get(entity="ETYP", item1="NUM", it1num="MAX")) + etype_old = self._mapdl.parameters.type + etype_tmp = etype_max + 1 + + old_routine = self._mapdl.parameters.routine + + self._mapdl.et(etype_tmp, "MESH200", 6, mute=True) + self._mapdl.shpp("off", mute=True) + self._mapdl.smrtsize(density, mute=True) + self._mapdl.type(etype_tmp, mute=True) + + if old_routine != "PREP7": + self._mapdl.prep7(mute=True) + + # Mesh and get the number of elements per area + resp = self._mapdl.amesh("all") + groups = get_elements_per_area(resp) + + self._mapdl.esla("S") + grid = self._mapdl.mesh._grid.linear_copy() + pd = pv.PolyData(grid.points, grid.cells, n_faces=grid.n_cells) + + # pd['ansys_node_num'] = grid['ansys_node_num'] + # pd['vtkOriginalPointIds'] = grid['vtkOriginalPointIds'] + # pd.clean(inplace=True) # OPTIONAL + + # delete all temporary meshes and clean up settings + self._mapdl.aclear("ALL", mute=True) + self._mapdl.adele("ALL", kswp=1, mute=True) + self._mapdl.numstr("AREA", 1, mute=True) + self._mapdl.type(etype_old, mute=True) + self._mapdl.etdele(etype_tmp, mute=True) + self._mapdl.shpp("ON", mute=True) + self._mapdl.smrtsize("OFF", mute=True) # store the area number used for each element entity_num = np.empty(grid.n_cells, dtype=np.int32) @@ -863,24 +865,18 @@ def vnum(self) -> NDArray[np.int32]: return self._mapdl.get_array("VOLU", item1="VLIST").astype(np.int32) @supress_logging - def _load_lines(self) -> List[pv.PolyData]: + @requires_package("pyvista") + @requires_package("pyiges") + def _load_lines(self) -> List["pv.PolyData"]: """Load lines from MAPDL using IGES""" # ignore volumes - self._mapdl.cm("__tmp_volu__", "VOLU", mute=True) - self._mapdl.cm("__tmp_line__", "LINE", mute=True) - self._mapdl.cm("__tmp_area__", "AREA", mute=True) - self._mapdl.cm("__tmp_keyp__", "KP", mute=True) - self._mapdl.ksel("ALL", mute=True) - self._mapdl.lsel("ALL", mute=True) - self._mapdl.asel("ALL", mute=True) - self._mapdl.vsel("NONE", mute=True) - - iges = self._load_iges() - - self._mapdl.cmsel("S", "__tmp_volu__", "VOLU", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_line__", "LINE", mute=True) - self._mapdl.cmsel("S", "__tmp_keyp__", "KP", mute=True) + with self._mapdl.save_selection: + self._mapdl.ksel("ALL", mute=True) + self._mapdl.lsel("ALL", mute=True) + self._mapdl.asel("ALL", mute=True) + self._mapdl.vsel("NONE", mute=True) + + iges = self._load_iges() selected_lnum = self.lnum lines = [] @@ -911,21 +907,15 @@ def _load_lines(self) -> List[pv.PolyData]: return lines_ + @requires_package("pyiges") def _load_keypoints(self) -> Tuple[NDArray, NDArray]: """Load keypoints from MAPDL using IGES.""" # write only keypoints - self._mapdl.cm("__tmp_volu__", "VOLU", mute=True) - self._mapdl.cm("__tmp_area__", "AREA", mute=True) - self._mapdl.cm("__tmp_line__", "LINE", mute=True) - self._mapdl.vsel("NONE", mute=True) - self._mapdl.asel("NONE", mute=True) - self._mapdl.lsel("NONE", mute=True) - - iges = self._load_iges() - - self._mapdl.cmsel("S", "__tmp_volu__", "VOLU", mute=True) - self._mapdl.cmsel("S", "__tmp_area__", "AREA", mute=True) - self._mapdl.cmsel("S", "__tmp_line__", "LINE", mute=True) + with self._mapdl.save_selection: + self._mapdl.vsel("NONE", mute=True) + self._mapdl.asel("NONE", mute=True) + self._mapdl.lsel("NONE", mute=True) + iges = self._load_iges() keypoints = [] kp_num = [] @@ -1191,7 +1181,8 @@ def area_select( return self.anum @property - def volumes(self) -> pv.MultiBlock: + @requires_package("pyvista") + def volumes(self) -> "pv.MultiBlock": """Obtain the volumes geometry Obtain the selected volumes as a :class:`pyvista.MultiBlock` object. @@ -1262,9 +1253,10 @@ def volumes(self) -> pv.MultiBlock: mb.set_block_name(index=ind, name=f"volume {mapdl_index}") return mb + @requires_package("pyvista") def get_volumes( self, return_as_list: bool = False, quality: int = 4 - ) -> Union[List[pv.PolyData], pv.PolyData]: + ) -> Union[List["pv.PolyData"], "pv.PolyData"]: """Get active volumes from MAPDL represented as a :class:`pyvista.PolyData` object or a list of :class:`pyvista.UnstructuredGrid` objects. @@ -1329,22 +1321,15 @@ def get_volumes( return surf # Cache current selection - with contextlib.suppress(ComponentNoData): - # avoiding empty components exceptions - self._mapdl.cm("__temp_volu__", "volu") - self._mapdl.cm("__temp_area__", "area") - - area_num = surf["entity_num"].astype(int) - - for each_volu in self.vnum: - self._mapdl.vsel("S", vmin=each_volu) - self._mapdl.aslv("S") - unstruct = surf.extract_cells(np.in1d(area_num, self.anum)) - unstruct.entity_num = int(each_volu) - volumes_.append(unstruct) + with self._mapdl.save_selection: + area_num = surf["entity_num"].astype(int) - self._mapdl.cmsel("S", "__temp_volu__") - self._mapdl.cmsel("S", "__temp_area__") + for each_volu in self.vnum: + self._mapdl.vsel("S", vmin=each_volu) + self._mapdl.aslv("S") + unstruct = surf.extract_cells(np.in1d(area_num, self.anum)) + unstruct.entity_num = int(each_volu) + volumes_.append(unstruct) return volumes_ @@ -1521,13 +1506,15 @@ def keypoints(self) -> np.array: # type: ignore """Keypoint coordinates""" return super().get_keypoints(return_as_array=True) - def lines(self) -> pv.PolyData: + @requires_package("pyvista") + def lines(self) -> "pv.PolyData": """Active lines as a ``pyvista.PolyData`` object.""" return super().get_lines() # type: ignore + @requires_package("pyvista") def areas( self, quality=1, merge=False - ) -> Union[pv.PolyData, List[pv.UnstructuredGrid]]: + ) -> Union["pv.PolyData", List["pv.UnstructuredGrid"]]: """List of areas from MAPDL represented as a ``pyvista.PolyData`` object. Parameters diff --git a/src/ansys/mapdl/core/mapdl_grpc.py b/src/ansys/mapdl/core/mapdl_grpc.py index dce09f08b2..3cdf013779 100644 --- a/src/ansys/mapdl/core/mapdl_grpc.py +++ b/src/ansys/mapdl/core/mapdl_grpc.py @@ -1,4 +1,4 @@ -"""gRPC specific class and methods for the MAPDL gRPC client """ +"""A gRPC specific class and methods for the MAPDL gRPC client """ import fnmatch from functools import wraps @@ -49,7 +49,6 @@ parse_chunks, ) from ansys.mapdl.core.errors import ( - DifferentSessionConnectionError, MapdlConnectionError, MapdlExitedError, MapdlRuntimeError, @@ -61,7 +60,6 @@ check_valid_ip, last_created, random_string, - requires_package, run_as_prep7, supress_logging, ) @@ -419,12 +417,9 @@ def __init__( # initialize mesh, post processing, and file explorer interfaces self._mesh_rep: Optional["MeshGrpc"] = None - try: - from ansys.mapdl.core.mesh_grpc import MeshGrpc + from ansys.mapdl.core.mesh_grpc import MeshGrpc - self._mesh_rep = MeshGrpc(self) - except ModuleNotFoundError: # pragma: no cover - self._mesh_rep = None + self._mesh_rep = MeshGrpc(self) # Run at connect self._run_at_connect() @@ -873,6 +868,7 @@ def _run_at_connect(self): self.show(self._file_type_for_plots) self.version # Caching version + self.file_type_for_plots # Setting /show,png and caching it. def _reset_cache(self): """Reset cached items.""" @@ -883,7 +879,6 @@ def _reset_cache(self): self._geometry._reset_cache() @property - @requires_package("pyvista") def _mesh(self): return self._mesh_rep @@ -1811,11 +1806,6 @@ def input( if not self._apdl_log.closed: self._apdl_log.write(tmp_dat) - # Escaping early if inside non_interactive context - if self._store_commands: - self._stored_commands.append(tmp_dat.splitlines()[1]) - return None - if self._local: local_path = self.directory tmp_name_path = os.path.join(local_path, tmp_name) @@ -1824,6 +1814,11 @@ def input( else: self._upload_raw(tmp_dat.encode(), tmp_name) + # Escaping early if inside non_interactive context + if self._store_commands: + self._stored_commands.append(tmp_dat.splitlines()[1]) + return None + request = pb_types.InputFileRequest(filename=tmp_name) # even though we don't care about the output, we still @@ -1896,9 +1891,13 @@ def _get_file_path(self, fname: str, progress_bar: bool = False) -> str: if os.path.isfile(fname): # And it exists filename = os.path.join(os.getcwd(), fname) - elif fname in self.list_files(): + elif not self._store_commands and fname in self.list_files(): # It exists in the Mapdl working directory filename = os.path.join(self.directory, fname) + elif self._store_commands: + # Assuming that in non_interactive we have uploaded the file + # manually. + filename = os.path.join(self.directory, fname) else: # Finally raise FileNotFoundError(f"Unable to locate filename '{fname}'") @@ -1909,10 +1908,15 @@ def _get_file_path(self, fname: str, progress_bar: bool = False) -> str: self.upload(ffullpath, progress_bar=progress_bar) filename = fname - elif fname in self.list_files(): + elif not self._store_commands and fname in self.list_files(): # It exists in the Mapdl working directory filename = fname + elif self._store_commands: + # Assuming that in non_interactive, the file exists already in + # the Mapdl working directory + filename = fname + else: raise FileNotFoundError(f"Unable to locate filename '{fname}'") @@ -2428,7 +2432,7 @@ def _download( ) @protect_grpc - def upload(self, file_name: str, progress_bar: bool = True) -> str: + def upload(self, file_name: str, progress_bar: bool = _HAS_TQDM) -> str: """Upload a file to the grpc instance file_name : str @@ -2473,7 +2477,7 @@ def _get_array( kloop="", **kwargs, ): - """gRPC VGET request. + """Do a gRPC VGET request. Send a vget request, receive a bytes stream, and return it as a numpy array. @@ -3034,7 +3038,8 @@ def vget( ) -> NDArray[np.float64]: """Wraps VGET""" super().vget(par=par, ir=ir, tstrt=tstrt, kcplx=kcplx, **kwargs) - return self.parameters[par] + if not self._store_commands: + return self.parameters[par] def get_variable( self, @@ -3336,9 +3341,6 @@ def _check_session_id(self): elif pymapdl.RUNNING_TESTS or self._strict_session_id_check: if pymapdl_session_id != self._mapdl_session_id: self._log.error("The session ids do not match") - raise DifferentSessionConnectionError( - f"Local MAPDL session ID '{pymapdl_session_id}' is different from MAPDL session ID '{self._mapdl_session_id}." - ) else: self._log.debug("The session ids match") diff --git a/src/ansys/mapdl/core/mesh_grpc.py b/src/ansys/mapdl/core/mesh_grpc.py index 2568606fe0..95dc61565c 100644 --- a/src/ansys/mapdl/core/mesh_grpc.py +++ b/src/ansys/mapdl/core/mesh_grpc.py @@ -9,7 +9,7 @@ from ansys.mapdl.core.common_grpc import DEFAULT_CHUNKSIZE, parse_chunks from ansys.mapdl.core.mapdl_grpc import MapdlGrpc -from ansys.mapdl.core.misc import supress_logging, threaded +from ansys.mapdl.core.misc import requires_package, supress_logging, threaded TMP_NODE_CM = "__NODE__" @@ -134,33 +134,33 @@ def _update_cache(self): """ self.logger.debug("Updating cache") # elements must have their underlying nodes selected to avoid - # VTK segfault - self._mapdl.cm(TMP_NODE_CM, "NODE", mute=True) - self._mapdl.nsle("S", mute=True) + # VTK segfaul + with self._mapdl.save_selection: + self._mapdl.nsle("S", mute=True) - # not thread safe - self._update_cache_elem() + # not thread safe + self._update_cache_elem() - threads = [ - self._update_cache_element_desc(), - self._update_cache_nnum(), - self._update_node_coord(), - ] + threads = [ + self._update_cache_element_desc(), + self._update_cache_nnum(), + self._update_node_coord(), + ] - for thread in threads: - thread.join() + for thread in threads: + thread.join() - # must occur after read - self._ignore_cache_reset = True + # must occur after read + self._ignore_cache_reset = True + + # somehow requesting path seems to help windows avoid an + # outright segfault prior to running CMSEL + if os.name == "nt": + _ = self._mapdl.path - # somehow requesting path seems to help windows avoid an - # outright segfault prior to running CMSEL - if os.name == "nt": - _ = self._mapdl.path + # TODO: flaky + time.sleep(0.05) - # TODO: flaky - time.sleep(0.05) - self._mapdl.cmsel("S", TMP_NODE_CM, "NODE", mute=True) self._ignore_cache_reset = False @threaded @@ -391,16 +391,15 @@ def nnum_all(self) -> np.ndarray: array([ 1, 2, 3, ..., 19998, 19999, 20000]) """ self._ignore_cache_reset = True - self._mapdl.cm(TMP_NODE_CM, "NODE", mute=True) - self._mapdl.nsel("all", mute=True) + with self._mapdl.save_selection: + self._mapdl.nsel("all", mute=True) - nnum = self._mapdl.get_array("NODE", item1="NLIST") - nnum = nnum.astype(np.int32) - if nnum.size == 1: - if nnum[0] == 0: - nnum = np.empty(0, np.int32) + nnum = self._mapdl.get_array("NODE", item1="NLIST") + nnum = nnum.astype(np.int32) + if nnum.size == 1: + if nnum[0] == 0: + nnum = np.empty(0, np.int32) - self._mapdl.cmsel("S", TMP_NODE_CM, "NODE", mute=True) self._ignore_cache_reset = False return nnum @@ -415,18 +414,16 @@ def enum_all(self) -> np.ndarray: array([ 1, 2, 3, ..., 19998, 19999, 20000]) """ self._ignore_cache_reset = True - self._mapdl.cm("__ELEM__", "ELEM", mute=True) - self._mapdl.esel("all", mute=True) + with self._mapdl.save_selection: + self._mapdl.esel("all", mute=True) - enum = self._mapdl.get_array("ELEM", item1="ELIST") - enum = enum.astype(np.int32) - if enum.size == 1: - if enum[0] == 0: - enum = np.empty(0, np.int32) + enum = self._mapdl.get_array("ELEM", item1="ELIST") + enum = enum.astype(np.int32) + if enum.size == 1: + if enum[0] == 0: + enum = np.empty(0, np.int32) - self._mapdl.cmsel("S", "__ELEM__", "ELEM", mute=True) self._ignore_cache_reset = False - return enum @property @@ -543,7 +540,7 @@ def _load_nodes(self, chunk_size=DEFAULT_CHUNKSIZE): Returns ------- - nodes : np.ndarray + np.ndarray Numpy array of nodes """ if self._chunk_size: @@ -655,7 +652,7 @@ def _load_element_types(self, chunk_size=DEFAULT_CHUNKSIZE): Parameters ---------- - chunk_size : int + int Size of the chunks to request from the server. """ request = anskernel.StreamRequest(chunk_size=chunk_size) @@ -714,6 +711,7 @@ def grid(self): return self._grid @property + @requires_package("pyvista") def _grid(self): if self._grid_cache is None: self._update_cache() diff --git a/src/ansys/mapdl/core/misc.py b/src/ansys/mapdl/core/misc.py index 4b526712e7..c2fa1b0f2c 100644 --- a/src/ansys/mapdl/core/misc.py +++ b/src/ansys/mapdl/core/misc.py @@ -13,10 +13,17 @@ import sys import tempfile from threading import Thread +from typing import Union from warnings import warn import weakref -from ansys.tools.path import get_available_ansys_installations +try: + from ansys.tools.path import get_available_ansys_installations + + _HAS_ATP = True +except ModuleNotFoundError: + _HAS_ATP = False + import numpy as np from ansys.mapdl import core as pymapdl @@ -197,14 +204,22 @@ def mapdl_info(self): # List installed Ansys lines = ["", "Ansys Environment Report", "-" * 79] lines = ["\n", "Ansys Installation", "******************"] - mapdl_install = get_available_ansys_installations() - if not mapdl_install: - lines.append("Unable to locate any Ansys installations") + if _HAS_ATP: + mapdl_install = get_available_ansys_installations() + + if not mapdl_install: + lines.append("Unable to locate any Ansys installations") + else: + lines.append("Version Location") + lines.append("------------------") + for key in sorted(mapdl_install.keys()): + lines.append(f"{abs(key)} {mapdl_install[key]}") else: - lines.append("Version Location") - lines.append("------------------") - for key in sorted(mapdl_install.keys()): - lines.append(f"{abs(key)} {mapdl_install[key]}") + mapdl_install = None + lines.append( + "Unable to locate any Ansys installations because 'ansys-tools-path is not installed." + ) + install_info = "\n".join(lines) env_info_lines = [ @@ -1066,7 +1081,7 @@ def _get_load_step_options(self): return self._get_between(init_, end_string) -def write_array(filename, array): +def write_array(filename: Union[str, bytes], array: np.ndarray): """ Write an array to a file. @@ -1080,6 +1095,8 @@ def write_array(filename, array): array : numpy.ndarray Array. """ + if isinstance(filename, bytes): + filename = filename.decode() np.savetxt(filename, array, fmt="%20.12f") @@ -1249,13 +1266,13 @@ def wrapper(self, *args, **kwargs): else: return original_sel_func( self, - type_=type_, - item=item, - comp=comp, - vmin=vmin, - vmax=vmax, - vinc=vinc, - kabs=kabs, + type_, + item, + comp, + vmin, + vmax, + vinc, + kabs, # ksel, esel, nsel uses kabs, but lsel, asel, vsel uses kswp **kwargs, ) diff --git a/src/ansys/mapdl/core/parameters.py b/src/ansys/mapdl/core/parameters.py index da1dda442c..3b7487184b 100644 --- a/src/ansys/mapdl/core/parameters.py +++ b/src/ansys/mapdl/core/parameters.py @@ -535,12 +535,12 @@ def _set_parameter_array(self, name, arr): Parameters ---------- - arr : np.ndarray or List - Array to send to MAPDL. Maximum of 3 dimensions. - name : str Name of the array to write to within MAPDL. + arr : np.ndarray or List + Array to send to MAPDL. Maximum of 3 dimensions. + Examples -------- Load a 1D numpy array into MAPDL diff --git a/src/ansys/mapdl/core/plotting.py b/src/ansys/mapdl/core/plotting.py index 27a7191ba1..490ecb9bbb 100644 --- a/src/ansys/mapdl/core/plotting.py +++ b/src/ansys/mapdl/core/plotting.py @@ -224,13 +224,13 @@ def _general_plotter( ``off_screen`` are evaluated to ``False`` and ``True`` respectively. - style : string, optional + style : str, optional Visualization style of the mesh. One of the following: ``style='surface'``, ``style='wireframe'``, ``style='points'``. Defaults to ``'surface'``. Note that ``'wireframe'`` only shows a wireframe of the outer geometry. - color : string or 3 item list, optional + color : str, list[int, int, int], optional Use to make the entire mesh have a single solid color. Either a string, RGB list, or hex color string. For example: ``color='white'``, ``color='w'``, ``color=[1, 1, 1]``, or @@ -241,9 +241,9 @@ def _general_plotter( Shows the edges of a mesh. Does not apply to a wireframe representation. - edge_color : string or 3 item list, optional, defaults to black + edge_color : str, list[int, int, int], optional The solid color to give the edges when ``show_edges=True``. - Either a string, RGB list, or hex color string. + Either a string, RGB list, or hex color string. Defaults to black. point_size : float, optional Point size of any nodes in the dataset plotted. Also applicable @@ -594,13 +594,13 @@ def general_plotter( savefig : str, optional Saves screenshot to a file path. - style : string, optional + style : str, optional Visualization style of the mesh. One of the following: ``style='surface'``, ``style='wireframe'``, ``style='points'``. Defaults to ``'surface'``. Note that ``'wireframe'`` only shows a wireframe of the outer geometry. - color : string or 3 item list, optional + color : str, list[int, int, int], optional Use to make the entire mesh have a single solid color. Either a string, RGB list, or hex color string. For example: ``color='white'``, ``color='w'``, ``color=[1, 1, 1]``, or @@ -611,7 +611,7 @@ def general_plotter( Shows the edges of a mesh. Does not apply to a wireframe representation. - edge_color : string or 3 item list, optional, + edge_color : str, list[int, int, int], optional The solid color to give the edges when ``show_edges=True``. Either a string, RGB list, or hex color string. Defaults to black. diff --git a/src/ansys/mapdl/core/pool.py b/src/ansys/mapdl/core/pool.py index a155838892..4a3b51f87c 100755 --- a/src/ansys/mapdl/core/pool.py +++ b/src/ansys/mapdl/core/pool.py @@ -6,15 +6,26 @@ from typing import Any, Dict, List, Optional import warnings -from ansys.mapdl.core import LOG, get_ansys_path, launch_mapdl +from ansys.mapdl.core import LOG, launch_mapdl from ansys.mapdl.core.errors import MapdlRuntimeError, VersionError -from ansys.mapdl.core.launcher import MAPDL_DEFAULT_PORT, port_in_use, version_from_path +from ansys.mapdl.core.launcher import MAPDL_DEFAULT_PORT, port_in_use from ansys.mapdl.core.mapdl_grpc import _HAS_TQDM from ansys.mapdl.core.misc import create_temp_dir, threaded, threaded_daemon +try: + from ansys.tools.path import get_ansys_path, version_from_path + + _HAS_ATP = True +except ModuleNotFoundError: + _HAS_ATP = False + if _HAS_TQDM: from tqdm import tqdm + DEFAULT_PROGRESS_BAR = True +else: + DEFAULT_PROGRESS_BAR = False + def available_ports(n_ports: int, starting_port: int = MAPDL_DEFAULT_PORT) -> List[int]: """Return a list the first ``n_ports`` ports starting from ``starting_port``.""" @@ -122,7 +133,7 @@ def __init__( wait: bool = True, run_location: Optional[str] = None, port: int = MAPDL_DEFAULT_PORT, - progress_bar: bool = True, + progress_bar: bool = DEFAULT_PROGRESS_BAR, restart_failed: bool = True, remove_temp_files: bool = True, names: Optional[str] = None, @@ -159,7 +170,14 @@ def __init__( if "exec_file" in kwargs: exec_file = kwargs["exec_file"] else: # get default executable - exec_file = get_ansys_path() + if _HAS_ATP: + exec_file = get_ansys_path() + else: + raise ValueError( + "Please use 'exec_file' argument to specify the location of the ansys installation.\n" + "Alternatively, PyMAPDL can detect your ansys installation if you install 'ansys-tools-path' library." + ) + if exec_file is None: raise FileNotFoundError( "Invalid exec_file path or cannot load cached " @@ -167,8 +185,9 @@ def __init__( "exec_file=" ) - if version_from_path("mapdl", exec_file) < 211: - raise VersionError("LocalMapdlPool requires MAPDL 2021R1 or later.") + if _HAS_ATP: + if version_from_path("mapdl", exec_file) < 211: + raise VersionError("LocalMapdlPool requires MAPDL 2021R1 or later.") # grab available ports ports = available_ports(n_instances, port) @@ -245,7 +264,7 @@ def map( self, func, iterable=None, - progress_bar=True, + progress_bar=DEFAULT_PROGRESS_BAR, close_when_finished=False, timeout=None, wait=True, @@ -439,7 +458,7 @@ def run_batch( self, files, clear_at_start=True, - progress_bar=True, + progress_bar=DEFAULT_PROGRESS_BAR, close_when_finished=False, timeout=None, wait=True, diff --git a/src/ansys/mapdl/core/post.py b/src/ansys/mapdl/core/post.py index 57022832d0..b2d4b596b2 100644 --- a/src/ansys/mapdl/core/post.py +++ b/src/ansys/mapdl/core/post.py @@ -3,6 +3,7 @@ import numpy as np +from ansys.mapdl.core.errors import MapdlRuntimeError from ansys.mapdl.core.misc import supress_logging from ansys.mapdl.core.plotting import general_plotter @@ -113,11 +114,11 @@ def _mapdl(self): @property def _log(self): - """alias for mapdl log""" + """Alias for mapdl log""" return self._mapdl._log def _set_log_level(self, level): - """alias for mapdl._set_log_level""" + """Alias for mapdl._set_log_level""" return self._mapdl._set_log_level(level) @supress_logging @@ -350,7 +351,7 @@ def element_values(self, item, comp="", option="AVG") -> np.ndarray: Returns ------- numpy.ndarray - Numpy array containing the requested element values for ta + Numpy array containing the requested element values for a given item and component. Notes @@ -631,27 +632,63 @@ def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **kwargs): surf = self._mapdl.mesh._surf - # as ``disp`` returns the result for all nodes, we need all node numbers + # as ``disp`` returns the result for all nodes/elems, we need all node/elem numbers # and to index to the output node numbers if hasattr(self._mapdl.mesh, "enum_all"): - enum = self._mapdl.mesh.enum + enum = self._mapdl.mesh.enum_all else: enum = self._all_enum - # it's possible that there are duplicated element numbers, - # therefore we need to get the unique values and a reverse index + ####################################################################### + # Bool operations + # =============== + # I'm going to explain this clearly because it can be confusing for the + # future developers (me). + # This explanation is based in scalars (`element_values`) NOT having the + # full elements (selected and not selected) size. + # + # First, it's possible that there are duplicated element numbers, + # in the surf object returned by Pyvista. + # Therefore we need to get the unique values and a reverse index, to + # later convert the MAPDL values to Pyvista values. uni, ridx = np.unique(surf["ansys_elem_num"], return_inverse=True) - mask = np.isin(enum, uni, assume_unique=True) - - if scalars.size != mask.size: - scalars = scalars[self.selected_elements] - scalars = scalars[mask][ridx] + # which means that, uni is the id of mapdl elements in the polydata + # object. These elements does not need to be in order, and there can be + # duplicated! + # Hence: + # uni[ridx] = surf["ansys_elem_num"] + # + # Let's notice that: + # * enum[self.selected_elements] is mapdl selected elements ids in MAPDL notation. + # + # Theoretical approach + # -------------------- + # The theoretical approach will be using an intermediate array of the + # size of the MAPDL total number of elements (we do not care about selected). + # + values = np.zeros(enum.shape) + # + # Then assign the MAPDL values for the selected element (scalars) + # + values[self.selected_elements] = scalars + # + # Because values are in order, but with python notation, then we can do: + # + surf_values = values[ + uni - 1 + ] # -1 to set MAPDL element indexing to python indexing + # + # Then to account for the original Pyvista object: + # + surf_values = surf_values[ridx] + # + ####################################################################### meshes = [ { "mesh": surf.copy(deep=False), # deep=False for ipyvtk-simple "scalar_bar_args": {"title": kwargs.pop("stitle", "")}, - "scalars": scalars, + "scalars": surf_values, } ] @@ -669,27 +706,27 @@ def _plot_cell_scalars(self, scalars, show_elem_numbering=False, **kwargs): @property @supress_logging def _all_nnum(self): - self._mapdl.cm("__TMP_NODE__", "NODE") - self._mapdl.allsel() - nnum = self._mapdl.get_array("NODE", item1="NLIST") - - # rerun if encountered weird edge case of negative first index. - if nnum[0] == -1: + with self._mapdl.save_selection: + self._mapdl.allsel() nnum = self._mapdl.get_array("NODE", item1="NLIST") - self._mapdl.cmsel("S", "__TMP_NODE__", "NODE") + + # rerun if encountered weird edge case of negative first index. + if nnum[0] == -1: + nnum = self._mapdl.get_array("NODE", item1="NLIST") + return nnum.astype(np.int32, copy=False) @property @supress_logging def _all_enum(self): - self._mapdl.cm("__TMP_ELEM__", "ELEM") - self._mapdl.allsel() - enum = self._mapdl.get_array("ELEM", item1="ELIST") - - # rerun if encountered weird edge case of negative first index. - if enum[0] == -1: + with self._mapdl.save_selection: + self._mapdl.allsel() enum = self._mapdl.get_array("ELEM", item1="ELIST") - self._mapdl.cmsel("S", "__TMP_ELEM__", "ELEM") + + # rerun if encountered weird edge case of negative first index. + if enum[0] == -1: + enum = self._mapdl.get_array("ELEM", item1="ELIST") + return enum.astype(np.int32, copy=False) @property @@ -1346,6 +1383,7 @@ def plot_element_stress( **kwargs : dict, optional Keyword arguments passed to :func:`general_plotter ` + Returns ------- pyvista.plotting.renderer.CameraPosition diff --git a/src/ansys/mapdl/core/theme.py b/src/ansys/mapdl/core/theme.py index f1c0f252df..e9c019b1ff 100644 --- a/src/ansys/mapdl/core/theme.py +++ b/src/ansys/mapdl/core/theme.py @@ -1,7 +1,15 @@ """Store parameters for a PyMAPDL-specific theme for pyvista""" -from matplotlib.colors import ListedColormap + import numpy as np +try: + from matplotlib.colors import ListedColormap + + _HAS_MATPLOTLIB = True +except ModuleNotFoundError: + _HAS_MATPLOTLIB = False + + from ansys.mapdl.core import _HAS_PYVISTA if _HAS_PYVISTA: @@ -46,10 +54,15 @@ class myEmptyClass: / 255 ) -PyMAPDL_cmap: ListedColormap = ListedColormap(MAPDL_colorbar, name="PyMAPDL", N=255) +if _HAS_MATPLOTLIB: + PyMAPDL_cmap: ListedColormap = ListedColormap(MAPDL_colorbar, name="PyMAPDL", N=255) def get_ansys_colors(N=9): + if not _HAS_MATPLOTLIB: + raise ModuleNotFoundError( + "'matplotlib' package is needed for 'get_ansys_colors'." + ) return np.array([PyMAPDL_cmap(i) for i in range(N)]) @@ -93,21 +106,23 @@ def __init__(self): self.background = "paraview" self.interactive = True - self.cmap = PyMAPDL_cmap + if _HAS_MATPLOTLIB: + self.cmap = PyMAPDL_cmap + + self.font.size = 18 + self.font.title_size = 18 + self.font.label_size = 18 + self.font.color = "black" + + self.axes.x_color = "tomato" + self.axes.y_color = "seagreen" + self.axes.z_color = "blue" - self.font.size = 18 - self.font.title_size = 18 - self.font.label_size = 18 - self.font.color = "black" self.show_edges = False self.color = "lightblue" self.outline_color = "black" self.edge_color = "black" - self.axes.x_color = "tomato" - self.axes.y_color = "seagreen" - self.axes.z_color = "blue" - self.color_cycler = get_cycler(MAPDL_colorbar.tolist()) self.render_points_as_spheres = True diff --git a/src/ansys/mapdl/core/xpl.py b/src/ansys/mapdl/core/xpl.py index 1d5b219877..273b9b4018 100644 --- a/src/ansys/mapdl/core/xpl.py +++ b/src/ansys/mapdl/core/xpl.py @@ -6,7 +6,6 @@ import weakref from ansys.api.mapdl.v0 import mapdl_pb2 -from ansys.math.core.math import AnsMath import numpy as np from .common_grpc import ANSYS_VALUE_TYPE @@ -362,8 +361,7 @@ def save(self): return response def extract(self, recordname, sets="ALL", asarray=False): - """ - Import a Matrix/Vector from a MAPDL result file. + """Import a Matrix/Vector from a MAPDL result file. At the moment, this only supports reading the displacement vectors from a result file. @@ -421,6 +419,8 @@ def extract(self, recordname, sets="ALL", asarray=False): 1.20642736e-02, 2.58299321e-11, 9.14504940e-04]]) """ + from ansys.math.core.math import AnsMath + if recordname.upper() != "NSL": raise ValueError("Currently, the only supported recordname is 'NSL'") @@ -473,6 +473,8 @@ def read(self, recordname, asarray=False): array([ 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 1], dtype=int32) """ + from ansys.math.core.math import AnsMath + rand_name = id_generator() response = self._mapdl.run(f"*XPL,READ,{recordname},{rand_name}") self._check_ignored(response) diff --git a/tests/.image_cache/bc_plot_options[False-False-False].png b/tests/.image_cache/bc_plot_options[False-False-False].png index 996436574d..6cd9a7ba4d 100644 Binary files a/tests/.image_cache/bc_plot_options[False-False-False].png and b/tests/.image_cache/bc_plot_options[False-False-False].png differ diff --git a/tests/.image_cache/bc_plot_options[False-False-True].png b/tests/.image_cache/bc_plot_options[False-False-True].png index 996436574d..6cd9a7ba4d 100644 Binary files a/tests/.image_cache/bc_plot_options[False-False-True].png and b/tests/.image_cache/bc_plot_options[False-False-True].png differ diff --git a/tests/.image_cache/bc_plot_options[False-True-False].png b/tests/.image_cache/bc_plot_options[False-True-False].png index ae4246015d..e688ff10f2 100644 Binary files a/tests/.image_cache/bc_plot_options[False-True-False].png and b/tests/.image_cache/bc_plot_options[False-True-False].png differ diff --git a/tests/.image_cache/bc_plot_options[False-True-True].png b/tests/.image_cache/bc_plot_options[False-True-True].png index ae4246015d..e688ff10f2 100644 Binary files a/tests/.image_cache/bc_plot_options[False-True-True].png and b/tests/.image_cache/bc_plot_options[False-True-True].png differ diff --git a/tests/.image_cache/bc_plot_options[True-False-False].png b/tests/.image_cache/bc_plot_options[True-False-False].png index 491c8d3f1f..b3c5ad397e 100644 Binary files a/tests/.image_cache/bc_plot_options[True-False-False].png and b/tests/.image_cache/bc_plot_options[True-False-False].png differ diff --git a/tests/.image_cache/bc_plot_options[True-False-True].png b/tests/.image_cache/bc_plot_options[True-False-True].png index 491c8d3f1f..b3c5ad397e 100644 Binary files a/tests/.image_cache/bc_plot_options[True-False-True].png and b/tests/.image_cache/bc_plot_options[True-False-True].png differ diff --git a/tests/.image_cache/bc_plot_options[True-True-False].png b/tests/.image_cache/bc_plot_options[True-True-False].png index 55ef2b4e34..bf02eb639b 100644 Binary files a/tests/.image_cache/bc_plot_options[True-True-False].png and b/tests/.image_cache/bc_plot_options[True-True-False].png differ diff --git a/tests/.image_cache/bc_plot_options[True-True-True].png b/tests/.image_cache/bc_plot_options[True-True-True].png index 55ef2b4e34..bf02eb639b 100644 Binary files a/tests/.image_cache/bc_plot_options[True-True-True].png and b/tests/.image_cache/bc_plot_options[True-True-True].png differ diff --git a/tests/.image_cache/components_selection_keep_between_plots.png b/tests/.image_cache/components_selection_keep_between_plots.png new file mode 100644 index 0000000000..0a8be97034 Binary files /dev/null and b/tests/.image_cache/components_selection_keep_between_plots.png differ diff --git a/tests/common.py b/tests/common.py index 91f102eded..a8d4102db3 100644 --- a/tests/common.py +++ b/tests/common.py @@ -3,8 +3,6 @@ import os from typing import Dict -from ansys.tools.path import find_mapdl - from ansys.mapdl.core.launcher import _is_ubuntu Node = namedtuple("Node", ["number", "x", "y", "z", "thx", "thy", "thz"]) @@ -35,6 +33,8 @@ def is_on_local(): os.environ.get("PYMAPDL_START_INSTANCE", "").lower() != "false" ) # default is false + from ansys.tools.path import find_mapdl + _, rver = find_mapdl() if rver: @@ -59,6 +59,19 @@ def is_on_ubuntu(): def has_grpc(): + envvar = os.environ.get("HAS_GRPC", None) + + if envvar is not None: + return envvar.lower().strip() == "true" + + if testing_minimal(): + return True + + try: + from ansys.tools.path import find_mapdl + except ModuleNotFoundError: + return True + _, rver = find_mapdl() if rver: @@ -76,6 +89,28 @@ def is_smp(): return os.environ.get("DISTRIBUTED_MODE", "smp").lower().strip() == "smp" +def support_plotting(): + envvar = os.environ.get("SUPPORT_PLOTTING", None) + + if envvar is not None: + return envvar.lower().strip() == "true" + + if testing_minimal(): + return False + + try: + import pyvista + + return pyvista.system_supports_plotting() + + except ModuleNotFoundError: + return False + + +def testing_minimal(): + return os.environ.get("TESTING_MINIMAL", "NO").upper().strip() in ["YES", "TRUE"] + + def is_float(s: str) -> bool: try: float(s) diff --git a/tests/conftest.py b/tests/conftest.py index 1316fb292c..364728c27f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,21 +1,12 @@ from collections import namedtuple +from importlib import import_module import os from pathlib import Path from sys import platform from _pytest.terminal import TerminalReporter # for terminal customization -from ansys.tools.path import get_available_ansys_installations import pytest -import pyvista -import ansys.mapdl.core as pymapdl - -pymapdl.RUNNING_TESTS = True - -from ansys.mapdl.core.errors import MapdlExitedError, MapdlRuntimeError -from ansys.mapdl.core.examples import vmfiles -from ansys.mapdl.core.launcher import get_start_instance, launch_mapdl -from ansys.mapdl.core.theme import _apply_default_theme from common import ( Element, Node, @@ -27,17 +18,17 @@ is_on_local, is_on_ubuntu, is_smp, + support_plotting, + testing_minimal, ) -_apply_default_theme() - ################################################################ # # Setting testing environment # --------------------------- # -# Necessary for CI plotting -pyvista.OFF_SCREEN = True + +TESTING_MINIMAL = testing_minimal() ON_LOCAL = is_on_local() ON_CI = is_on_ci() @@ -50,14 +41,11 @@ HAS_GRPC = has_grpc() HAS_DPF = has_dpf() -SUPPORT_PLOTTING = pyvista.system_supports_plotting() +SUPPORT_PLOTTING = support_plotting() IS_SMP = is_smp() QUICK_LAUNCH_SWITCHES = "-smp -m 100 -db 100" -# check if the user wants to permit pytest to start MAPDL -START_INSTANCE = get_start_instance() - ## Skip ifs skip_on_windows = pytest.mark.skipif(ON_WINDOWS, reason="Skip on Windows") skip_on_linux = pytest.mark.skipif(ON_LINUX, reason="Skip on Linux") @@ -86,6 +74,106 @@ reason="""Requires DPF.""", ) +requires_linux = pytest.mark.skipif(not ON_LINUX, reason="This test requires Linux") +requires_windows = pytest.mark.skipif( + not ON_WINDOWS, reason="This test requires Windows" +) +requires_on_cicd = pytest.mark.skipif( + not ON_CI, reason="This test requires to be on CICD" +) + + +def has_dependency(requirement): + try: + import_module(requirement) + return True + except ModuleNotFoundError: + return False + + +def requires(requirement: str): + """Check requirements""" + requirement = requirement.lower() + + if "grpc" == requirement: + return skip_if_no_has_grpc + + elif "dpf" == requirement: + return skip_if_no_has_dpf + + elif "local" == requirement: + return skip_if_not_local + + elif "cicd" == requirement: + return skip_if_on_cicd + + elif "nocicd" == requirement: + return skip_if_on_cicd + + elif "xserver" == requirement: + return skip_no_xserver + + elif "linux" == requirement: + return requires_linux + + elif "nolinux" == requirement: + return skip_on_linux + + elif "windows" == requirement: + return requires_windows + + elif "nowindows" == requirement: + return skip_on_windows + + elif "console" == requirement: + return pytest.mark.console + + elif "corba" == requirement: + return pytest.mark.corba + + else: + return requires_dependency(requirement) + + +def requires_dependency(dependency: str): + try: + import_module(dependency) + return pytest.mark.skipif( + False, reason="Never skip" + ) # faking a null skipif decorator + + except ModuleNotFoundError: + # package does not exist + return pytest.mark.skip(reason=f"Requires '{dependency}' package") + + +################ + +if has_dependency("ansys-tools-package"): + from ansys.tools.path import get_available_ansys_installations + + +if has_dependency("pyvista"): + import pyvista + + from ansys.mapdl.core.theme import _apply_default_theme + + _apply_default_theme() + + # Necessary for CI plotting + pyvista.OFF_SCREEN = True + +import ansys.mapdl.core as pymapdl + +pymapdl.RUNNING_TESTS = True + +from ansys.mapdl.core.errors import MapdlExitedError, MapdlRuntimeError +from ansys.mapdl.core.examples import vmfiles +from ansys.mapdl.core.launcher import get_start_instance, launch_mapdl + +# check if the user wants to permit pytest to start MAPDL +START_INSTANCE = get_start_instance() + ################ if os.name == "nt": os_msg = """SET PYMAPDL_START_INSTANCE=False @@ -107,6 +195,7 @@ alexander.kaszynski@ansys.com """ +MAPDL_VERSION = None # this is cached by mapdl fixture and used in the minimal testing if START_INSTANCE and not ON_LOCAL: raise MapdlRuntimeError(ERRMSG) @@ -209,24 +298,25 @@ def pytest_collection_modifyitems(config, items): # --------------------------- # +if has_dependency("pytest-pyvista"): -@pytest.fixture(autouse=True) -def wrapped_verify_image_cache(verify_image_cache, pytestconfig): - # Checking if we want to avoid the check using pytest cli. - skip_regression_check = pytestconfig.option.skip_regression_check - if skip_regression_check: - verify_image_cache.skip = True + @pytest.fixture(autouse=True) + def wrapped_verify_image_cache(verify_image_cache, pytestconfig): + # Checking if we want to avoid the check using pytest cli. + skip_regression_check = pytestconfig.option.skip_regression_check + if skip_regression_check: + verify_image_cache.skip = True - # Configuration - # default check - verify_image_cache.error_value = 500.0 - verify_image_cache.warning_value = 200.0 + # Configuration + # default check + verify_image_cache.error_value = 500.0 + verify_image_cache.warning_value = 200.0 - # High variance test - verify_image_cache.var_error_value = 1000.0 - verify_image_cache.var_warning_value = 1000.0 + # High variance test + verify_image_cache.var_error_value = 1000.0 + verify_image_cache.var_warning_value = 1000.0 - return verify_image_cache + return verify_image_cache class Running_test: @@ -276,6 +366,7 @@ def is_exited(mapdl): # Restoring the local configuration mapdl._local = local_ + mapdl.gopr() yield # this is where the testing happens # Teardown : fill with any logic you want @@ -374,6 +465,7 @@ def mapdl(request, tmpdir_factory): start_timeout=50, ) mapdl._show_matplotlib_figures = False # CI: don't show matplotlib figures + MAPDL_VERSION = mapdl.version # Caching version if ON_CI: mapdl._local = ON_LOCAL # CI: override for testing diff --git a/tests/test_commands.py b/tests/test_commands.py index 799261fd06..1e9ad4cfd8 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -14,15 +14,11 @@ StringWithLiteralRepr, ) from ansys.mapdl.core.examples import verif_files +from conftest import has_dependency, requires -try: +if has_dependency("pandas"): import pandas as pd - HAS_PANDAS = True - -except ModuleNotFoundError: - HAS_PANDAS = False - LIST_OF_INQUIRE_FUNCTIONS = [ "ndinqr", "elmiqr", @@ -570,7 +566,7 @@ def test_cmd_class_prnsol_short(): assert out_list assert isinstance(out_array, np.ndarray) and out_array.size != 0 - if HAS_PANDAS: + if has_dependency("pandas"): out_df = out.to_dataframe() assert isinstance(out_df, pd.DataFrame) and not out_df.empty @@ -631,7 +627,7 @@ def test_output_listing(mapdl, plastic_solve, func, args): assert isinstance(out_list, list) and out_list assert isinstance(out_array, np.ndarray) and out_array.size != 0 - if HAS_PANDAS: + if has_dependency("pandas"): out_df = out.to_dataframe() assert isinstance(out_df, pd.DataFrame) and not out_df.empty @@ -648,7 +644,7 @@ def test_bclist(mapdl, beam_solve, func): with pytest.raises(ValueError): out.to_array() - if HAS_PANDAS: + if has_dependency("pandas"): out_df = out.to_dataframe() assert isinstance(out_df, pd.DataFrame) and not out_df.empty @@ -679,6 +675,7 @@ def test_string_with_literal(): assert len(output.split()) == 2 +@requires("pandas") @pytest.mark.parametrize("output,last_element", [(set_list_0, 9), (set_list_1, 15)]) def test_magicwords(output, last_element): magicwords = ["SET"] diff --git a/tests/test_component.py b/tests/test_component.py index 4e99122596..b76f3d7140 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -119,12 +119,7 @@ def test_contains_entities(mapdl, cube_geom_and_mesh, func, entity, selector, im func_ = getattr(mapdl, func) func_("S", vmin=1, vmax=imax) - if entity in ["nodes", "elem"]: - count = getattr(mapdl.mesh, selector) - else: - count = getattr(mapdl.geometry, selector) - - assert len(count) == imax + assert mapdl.get_value(entity[:4], 0, "count") == imax mapdl.cm("mycomp", entity) @@ -264,11 +259,11 @@ def test_dunder_methods_iter(mapdl, basic_components): def test_dunder_methods_keys(mapdl, basic_components): - assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.list()) + assert ["MYCOMP1", "MYCOMP2"] == list(mapdl.components.names) def test_dunder_methods_types(mapdl, basic_components): - assert ["NODE", "KP"] == list(mapdl.components.types()) + assert ["NODE", "KP"] == list(mapdl.components.types) def test_dunder_methods_items(mapdl, basic_components): diff --git a/tests/test_console.py b/tests/test_console.py index 02ae2f8426..ee09169d52 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -6,17 +6,24 @@ import os import time -from ansys.mapdl.reader import examples +import pytest + +from conftest import has_dependency, requires + +# skip entire module unless --console is enabled +pytestmark = requires("console") + import numpy as np import pytest -import pyvista + +if has_dependency("pyvista"): + import pyvista + +if has_dependency("ansys-mapdl-reader"): + from ansys.mapdl.reader import examples from ansys.mapdl import core as pymapdl from ansys.mapdl.core.errors import MapdlRuntimeError -from conftest import skip_no_xserver - -# skip entire module unless --console is enabled -pytestmark = pytest.mark.console @pytest.fixture(scope="function") @@ -198,7 +205,7 @@ def test_invalid_area(mapdl_console): # mapdl_console.input('thisisnotafile') -@skip_no_xserver +@requires("xserver") def test_kplot(cleared, mapdl_console, tmpdir): with pytest.raises(MapdlRuntimeError): mapdl_console.kplot(vtk=True) @@ -216,7 +223,7 @@ def test_kplot(cleared, mapdl_console, tmpdir): mapdl_console.kplot(knum=True, vtk=False) # make sure legacy still works -@skip_no_xserver +@requires("xserver") def test_aplot(cleared, mapdl_console): k0 = mapdl_console.k("", 0, 0, 0) k1 = mapdl_console.k("", 1, 0, 0) @@ -237,7 +244,7 @@ def test_aplot(cleared, mapdl_console): mapdl_console.aplot(vtk=False) -@skip_no_xserver +@requires("xserver") @pytest.mark.parametrize("vtk", [True, False]) def test_vplot(cleared, mapdl_console, vtk): mapdl_console.block(0, 1, 0, 1, 0, 1) @@ -278,7 +285,7 @@ def test_lines(cleared, mapdl_console): assert mapdl_console.geometry.n_line == 4 -@skip_no_xserver +@requires("xserver") def test_lplot(cleared, mapdl_console, tmpdir): with pytest.raises(MapdlRuntimeError): mapdl_console.lplot(vtk=True) @@ -353,7 +360,7 @@ def test_enum(mapdl_console, make_block): @pytest.mark.parametrize("knum", [True, False]) -@skip_no_xserver +@requires("xserver") def test_nplot_vtk(cleared, mapdl_console, knum): with pytest.raises(MapdlRuntimeError): mapdl_console.nplot() @@ -364,7 +371,7 @@ def test_nplot_vtk(cleared, mapdl_console, knum): mapdl_console.nplot(vtk=True, knum=knum, background="w", color="k") -@skip_no_xserver +@requires("xserver") def test_nplot(cleared, mapdl_console): mapdl_console.n(1, 0, 0, 0) mapdl_console.n(11, 10, 0, 0) @@ -483,7 +490,7 @@ def test_eplot_fail(mapdl_console): mapdl_console.eplot() -@skip_no_xserver +@requires("xserver") def test_eplot(mapdl_console, make_block): init_elem = mapdl_console.mesh.n_elem mapdl_console.aplot() # check aplot and verify it doesn't mess up the element plotting @@ -492,7 +499,7 @@ def test_eplot(mapdl_console, make_block): assert mapdl_console.mesh.n_elem == init_elem -@skip_no_xserver +@requires("xserver") def test_eplot_screenshot(mapdl_console, make_block, tmpdir): filename = str(tmpdir.mkdir("tmpdir").join("tmp.png")) mapdl_console.eplot( diff --git a/tests/test_convert.py b/tests/test_convert.py index bfaceae501..c9947bec14 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -9,6 +9,7 @@ FileTranslator, convert_apdl_block, ) +from conftest import requires nblock = """nblock,3,,326253 (1i9,3e20.9e3) @@ -43,34 +44,34 @@ pynblock = """with mapdl.non_interactive: mapdl.run("nblock,3,,326253") mapdl.run("(1i9,3e20.9e3)") - mapdl.run(" 1 3.352881632E-03 1.110639271E-02 5.172433282E-03") - mapdl.run(" 2 3.485685736E-03 1.110981270E-02 4.999255638E-03") - mapdl.run(" 3 3.615164748E-03 1.111323677E-02 4.823719994E-03") - mapdl.run(" 4 3.673859471E-03 1.111439119E-02 4.740300611E-03") - mapdl.run(" 5 3.709417144E-03 1.111407057E-02 4.691582629E-03") + mapdl.run("1 3.352881632E-03 1.110639271E-02 5.172433282E-03") + mapdl.run("2 3.485685736E-03 1.110981270E-02 4.999255638E-03") + mapdl.run("3 3.615164748E-03 1.111323677E-02 4.823719994E-03") + mapdl.run("4 3.673859471E-03 1.111439119E-02 4.740300611E-03") + mapdl.run("5 3.709417144E-03 1.111407057E-02 4.691582629E-03") mapdl.run("-1")""" pyeblock = """with mapdl.non_interactive: mapdl.run("eblock,19,solid,,6240") mapdl.run("(19i9)") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38161 186586 186589 192999 193065 191265 191262 193063 193064") - mapdl.run(" 194712 194731 213866 194716 210305 210306 213993 210310 194715 194730 213865 213995") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38162 186586 193065 192999 186589 186781 193066 192935 186784") - mapdl.run(" 194716 213866 194731 194712 195560 213737 195572 195557 194714 213997 213736 194729") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38163 186781 193066 192935 186784 186976 193067 192871 186979") - mapdl.run(" 195560 213737 195572 195557 196210 213608 196222 196207 195559 213998 213607 195571") - mapdl.run(" 1 1 1 1 0 0 0 0 20 0 38164 186976 193067 192871 186979 187171 193068 192807 187174") - mapdl.run(" 196210 213608 196222 196207 196860 213479 196872 196857 196209 213999 213478 196221")""" + mapdl.run("1 1 1 1 0 0 0 0 20 0 38161 186586 186589 192999 193065 191265 191262 193063 193064") + mapdl.run("194712 194731 213866 194716 210305 210306 213993 210310 194715 194730 213865 213995") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38162 186586 193065 192999 186589 186781 193066 192935 186784") + mapdl.run("194716 213866 194731 194712 195560 213737 195572 195557 194714 213997 213736 194729") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38163 186781 193066 192935 186784 186976 193067 192871 186979") + mapdl.run("195560 213737 195572 195557 196210 213608 196222 196207 195559 213998 213607 195571") + mapdl.run("1 1 1 1 0 0 0 0 20 0 38164 186976 193067 192871 186979 187171 193068 192807 187174") + mapdl.run("196210 213608 196222 196207 196860 213479 196872 196857 196209 213999 213478 196221")""" pycmblock = """with mapdl.non_interactive: mapdl.run("CMBLOCK,PRESSURE_AREAS,NODE, 48") mapdl.run("(8i10)") - mapdl.run(" 1688 1689 1690 1691 1700 1701 1702 1703") - mapdl.run(" 1704 1705 1706 1707 1708 1709 1710 1711") - mapdl.run(" 1712 1721 1723 1731 1736 1754 1755 1756") - mapdl.run(" 1757 1758 1759 1760 1761 1762 1763 1764") - mapdl.run(" 1765 1766 1767 1768 1769 1802 1803 1804") - mapdl.run(" 1805 1806 1807 1808 1809 1831 1832 1833")""" + mapdl.run("1688 1689 1690 1691 1700 1701 1702 1703") + mapdl.run("1704 1705 1706 1707 1708 1709 1710 1711") + mapdl.run("1712 1721 1723 1731 1736 1754 1755 1756") + mapdl.run("1757 1758 1759 1760 1761 1762 1763 1764") + mapdl.run("1765 1766 1767 1768 1769 1802 1803 1804") + mapdl.run("1805 1806 1807 1808 1809 1831 1832 1833")""" block_commands = ["nblock", "eblock", "cmblock"] @@ -142,6 +143,25 @@ *END *USE,SLV""" +DO_CONVERSION = """with mapdl.non_interactive: + mapdl.run("*DO,I,1,81") # REPEAT MACRO EXECUTION""" + +GOLDEN_TESTS = { + "/DELETE,TABLE_1": 'mapdl.slashdelete("TABLE_1")', + "PROD,4,3, , ,FORCE , , ,-1.0,1,1,": 'mapdl.run("PROD,4,3, , ,FORCE , , ,-1.0,1,1")', + "ALLSEL,ALL": 'mapdl.allsel("ALL")', + "/EXIT,NOSAVE": 'mapdl.exit("NOSAVE")', + "": "", + "*DO,I,1,81 ! REPEAT MACRO EXECUTION": DO_CONVERSION, + " *USE,LOAD ! EXECUTE MACRO": 'mapdl.use("LOAD") # EXECUTE MACRO', + "*ENDDO": 'mapdl.run("*ENDDO")', + "SECT,1,SHELL": 'mapdl.sectype(1, "SHELL")', + "SECD,.00005,1 ! PLATE THICKNESS": "mapdl.secdata(.00005, 1) # PLATE THICKNESS", + "/show, asdf": 'mapdl.show("asdf")', + "*STAT,UXFEA2 ": 'mapdl.starstatus("UXFEA2")', + "/AXLAB,X,NORMALIZED TIME,TAU=ALPHA**2*D*t": 'mapdl.axlab("X", "NORMALIZED TIME,TAU=ALPHA**2*D*t")', +} + def test_convert_no_use_function_names(tmpdir): vm_file = examples.vmfiles["vm1"] @@ -318,9 +338,16 @@ def test_header(): assert '"""My header"""' in convert_apdl_block("/com", header="My header") -def test_com(): - converted_output = convert_apdl_block( +@pytest.mark.parametrize( + "cmd", + [ "/com, this is a comment !inline comment!", + "C***, this is a comment !inline comment!", + ], +) +def test_com(cmd): + converted_output = convert_apdl_block( + cmd, header=False, add_imports=False, ) @@ -360,13 +387,45 @@ def test_repeat(): ) -@pytest.mark.parametrize("cmd", COMMANDS_TO_NOT_BE_CONVERTED) -def test_commands_to_not_be_converted(cmd): - assert f'mapdl.run("{cmd}")' in convert_apdl_block( +@pytest.mark.parametrize( + "cmd", + [ + "/PMORE", # "/PMORE, + "ANTYPE", # ANTYPE, + "ASBL", # ASBL, + "ATAN", # ATAN, + ], +) +def test_empty_arguments_2(cmd): + # Checking trailing commas does not avoid conversion + assert f'mapdl.run("{cmd}")' not in convert_apdl_block( + cmd + ",,", header=False, add_imports=False + ) + + # Checking empty arguments avoid conversion + assert f'mapdl.run("{cmd.upper()},,OTHER_ARG")' in convert_apdl_block( + cmd + ",,OTHER_ARG", header=False, add_imports=False + ) + + # Checking default conversion + assert f'mapdl.run("{cmd}")' not in convert_apdl_block( cmd, header=False, add_imports=False ) +def test_commands_with_empty_arguments(): + cmd = """ANTYPE,STATIC ! STATIC ANALYSIS +ANTYPE,STATIC,,NON_EMPTY_ARGUMENT + +ANTYPE,STATIC,,,""" + pycmd = """mapdl.antype("STATIC") # STATIC ANALYSIS +mapdl.run("ANTYPE,STATIC,,NON_EMPTY_ARGUMENT") + +mapdl.antype("STATIC")""" + + assert pycmd in convert_apdl_block(cmd, header=False, add_imports=False) + + @pytest.mark.parametrize("ending", ["\n", "\r\n"]) def test_detect_line_ending(ending): assert ending in convert_apdl_block( @@ -387,7 +446,7 @@ def test_no_macro_as_functions(): APDL_MACRO, macros_as_functions=False, add_imports=False, header=False ) assert "with mapdl.non_interactive" in output - assert ' mapdl.run("*CREATE,SLV")' in output + assert ' mapdl.create("SLV")' in output assert ' mapdl.run("*END")' in output @@ -427,6 +486,54 @@ def test_only_commands(): assert "mapdl.exit" not in output +@pytest.mark.parametrize("vtk", [None, True, False]) +def test_use_vtk(vtk): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + use_vtk=vtk, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if vtk is None: + assert "use_vtk" not in output + else: + assert f"use_vtk={vtk}" in output + + +@pytest.mark.parametrize("check_parameter_names", [None, True, False]) +def test_check_parameter_names(check_parameter_names): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + check_parameter_names=check_parameter_names, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if check_parameter_names is not None and not check_parameter_names: + assert "check_parameter_names=False" in output + else: + assert f"check_parameter_names" not in output + + +@pytest.mark.parametrize("clear_at_start", [None, True, False]) +def test_clear_at_start(clear_at_start): + output = convert_apdl_block( + "/view,1,1,1", + only_commands=False, + add_imports=True, + clear_at_start=clear_at_start, + ) + assert "mapdl.view(1, 1, 1)" in output + assert "launch_mapdl" in output + if clear_at_start: + assert "mapdl.clear()" in output + else: + assert "mapdl.clear()" not in output + + @pytest.mark.parametrize( "parameters", [ @@ -452,6 +559,30 @@ def test_convert_star_slash(parameters): assert convert_apdl_block(parameters[0], only_commands=True) == parameters[1] +def test_parsing_items(): + cmd = "VGET,VALUE7(2,2),2,7E-2" + conv_cmd = convert_apdl_block(cmd, only_commands=True) + assert 'mapdl.vget("VALUE7(2,2)", 2, 7E-2)' in conv_cmd + + +def test_macros_call(): + cmd = """ +*create,myfunc +/prep7 +*end +myfunc +""" + conv_cmd = convert_apdl_block(cmd, only_commands=True) + assert "mapdl.prep7()" in conv_cmd + assert "def myfunc(" in conv_cmd + assert "myfunc()" in conv_cmd + + +@pytest.mark.parametrize("mapdl_cmd", GOLDEN_TESTS.keys()) +def test_golden(mapdl_cmd): + assert GOLDEN_TESTS[mapdl_cmd] == convert_apdl_block(mapdl_cmd, only_commands=True) + + ## CLI testing @@ -464,6 +595,7 @@ def do_run(*args): return do_run +@requires("click") def test_converter_cli(tmpdir, run_cli): input_file = tmpdir.join("mapdl.dat") output_file = tmpdir.join("mapdl.py") @@ -510,3 +642,32 @@ def test_converter_cli(tmpdir, run_cli): assert "mapdl.prep7()" in newcontent assert "mapdl.exit()" not in newcontent assert "launch_mapdl" not in newcontent + + +def test_exit_in_non_interactive(): + cmd = """ +*do,i,1,10 +/exit +*enddo""" + output = convert_apdl_block(cmd, only_commands=True) + + assert 'mapdl.run("*do,i,1,10")' in output + assert 'mapdl.com("Skipping: /exit")' in output + assert 'mapdl.run("*enddo")' in output + assert "mapdl.exit" not in output + + +@pytest.mark.parametrize("cmd", COMMANDS_TO_NOT_BE_CONVERTED) +def test_commands_to_not_be_converted(cmd): + assert f'mapdl.run("{cmd}")' in convert_apdl_block(cmd, only_commands=True) + + +def test_vwrite(): + cmd = """*VWRITE ! WRITE OUTPUT IN TABULAR FORMAT +(///T14,'MODE',T24,'COEFF',T34,'ISYM',/)""" + + pycmd = """with mapdl.non_interactive: + mapdl.run("*VWRITE") # WRITE OUTPUT IN TABULAR FORMAT + mapdl.run("(///T14,'MODE',T24,'COEFF',T34,'ISYM',/)")""" + + assert pycmd in convert_apdl_block(cmd, only_commands=True) diff --git a/tests/test_corba.py b/tests/test_corba.py index bac1525b38..64324107bc 100644 --- a/tests/test_corba.py +++ b/tests/test_corba.py @@ -6,17 +6,22 @@ import os import time -from ansys.mapdl.reader import examples import numpy as np import pytest -import pyvista -from ansys.mapdl import core as pymapdl -from ansys.mapdl.core.errors import MapdlRuntimeError -from conftest import skip_no_xserver +from conftest import has_dependency, requires # skip entire module unless --corba is enabled -pytestmark = pytest.mark.corba +pytestmark = requires("corba") + +if has_dependency("pyvista"): + import pyvista + +if has_dependency("ansys-mapdl-reader"): + from ansys.mapdl.reader import examples + +from ansys.mapdl import core as pymapdl +from ansys.mapdl.core.errors import MapdlRuntimeError @pytest.fixture(scope="function") @@ -186,7 +191,7 @@ def test_invalid_area(mapdl_corba): # mapdl_corba.input('thisisnotafile') -@skip_no_xserver +@requires("xserver") def test_kplot(cleared, mapdl_corba, tmpdir): mapdl_corba.k("", 0, 0, 0) mapdl_corba.k("", 1, 0, 0) @@ -201,7 +206,7 @@ def test_kplot(cleared, mapdl_corba, tmpdir): mapdl_corba.kplot(knum=True, vtk=False) # make sure legacy still works -@skip_no_xserver +@requires("xserver") def test_aplot(cleared, mapdl_corba): k0 = mapdl_corba.k("", 0, 0, 0) k1 = mapdl_corba.k("", 1, 0, 0) @@ -222,7 +227,7 @@ def test_aplot(cleared, mapdl_corba): mapdl_corba.aplot(vtk=False) -@skip_no_xserver +@requires("xserver") @pytest.mark.parametrize("vtk", [True, False]) def test_vplot(cleared, mapdl_corba, vtk): mapdl_corba.block(0, 1, 0, 1, 0, 1) @@ -263,7 +268,7 @@ def test_lines(cleared, mapdl_corba): assert mapdl_corba.geometry.n_line == 4 -@skip_no_xserver +@requires("xserver") def test_lplot(cleared, mapdl_corba, tmpdir): mapdl_corba.lplot(vtk=True) @@ -340,7 +345,7 @@ def test_enum(mapdl_corba, make_block): @pytest.mark.parametrize("nnum", [True, False]) -@skip_no_xserver +@requires("xserver") def test_nplot_vtk(cleared, mapdl_corba, nnum): mapdl_corba.nplot() @@ -350,7 +355,7 @@ def test_nplot_vtk(cleared, mapdl_corba, nnum): mapdl_corba.nplot(vtk=True, nnum=nnum, background="w", color="k") -@skip_no_xserver +@requires("xserver") def test_nplot(cleared, mapdl_corba): mapdl_corba.n(1, 0, 0, 0) mapdl_corba.n(11, 10, 0, 0) @@ -462,7 +467,7 @@ def test_builtin_parameters(mapdl_corba, cleared): assert mapdl_corba.parameters.real == 1 -@skip_no_xserver +@requires("xserver") def test_eplot(mapdl_corba, make_block): init_elem = mapdl_corba.mesh.n_elem mapdl_corba.aplot() # check aplot and verify it doesn't mess up the element plotting @@ -471,7 +476,7 @@ def test_eplot(mapdl_corba, make_block): assert mapdl_corba.mesh.n_elem == init_elem -@skip_no_xserver +@requires("xserver") def test_eplot_screenshot(mapdl_corba, make_block, tmpdir): filename = str(tmpdir.mkdir("tmpdir").join("tmp.png")) mapdl_corba.eplot( diff --git a/tests/test_database.py b/tests/test_database.py index fcd8dca351..9b17b3748e 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -46,6 +46,7 @@ def db(mapdl): ) mapdl.clear() + mapdl.db.start() return mapdl.db @@ -127,7 +128,8 @@ def test_clear(db): db._mapdl.prep7() db._mapdl.k(1, 1, 1, 1) db.clear() - assert db._mapdl.geometry.n_keypoint == 0 + with pytest.raises(ValueError, match="There are no KEYPOINTS defined."): + db._mapdl.get_value("KP", 0, "count") def test_nodes_repr(nodes): diff --git a/tests/test_dpf.py b/tests/test_dpf.py index c5b48b580c..46f9684272 100644 --- a/tests/test_dpf.py +++ b/tests/test_dpf.py @@ -3,23 +3,19 @@ import pytest -from conftest import skip_if_no_has_dpf +from conftest import HAS_DPF, has_dependency, requires -try: - from ansys.dpf import core as dpf - from ansys.dpf.core.server_types import DPF_DEFAULT_PORT -except ImportError: - skip_if_no_has_dpf = pytest.mark.skipif( - True, - reason="""DPF couldn't be imported.""", - ) - DPF_DEFAULT_PORT = None +if not has_dependency("ansys-dpf-core") or not HAS_DPF: + pytest.skip(allow_module_level=True) +from ansys.dpf import core as dpf +from ansys.dpf.core.server_types import DPF_DEFAULT_PORT DPF_PORT = os.environ.get("DPF_PORT", DPF_DEFAULT_PORT) # Set in ci.yaml -@skip_if_no_has_dpf +@requires("dpf") +@requires("ansys-dpf-core") def test_dpf_connection(): # uses 127.0.0.1 and port 50054 by default try: @@ -30,7 +26,8 @@ def test_dpf_connection(): assert False -@skip_if_no_has_dpf +@requires("dpf") +@requires("ansys-dpf-core") def test_upload(mapdl, solved_box, tmpdir): # Download RST file rst_path = mapdl.download_result(str(tmpdir.mkdir("tmpdir"))) diff --git a/tests/test_element.py b/tests/test_element.py index 6e088ee459..345e573a28 100644 --- a/tests/test_element.py +++ b/tests/test_element.py @@ -4,6 +4,7 @@ from ansys.mapdl.core import examples from ansys.mapdl.core._commands.parse import parse_e, parse_et +from conftest import requires @pytest.fixture @@ -33,7 +34,7 @@ def test_et(mapdl, cleared): assert n_plane183 == 17 -@pytest.mark.skip_grpc +@requires("grpc") def test_ewrite(mapdl, cleared): mapdl.et("", 183) n0 = mapdl.n("", 0, 0, 0) diff --git a/tests/test_examples.py b/tests/test_examples.py index cebebe8b87..588ee88baf 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -18,6 +18,7 @@ download_vtk_rotor, get_ext, ) +from conftest import requires def test_check_directory_exist(tmpdir): @@ -66,6 +67,7 @@ def test_load_verif(): assert os.path.isfile(filename) +@requires("requests") def test_bracket(mapdl, cleared, running_test): # note that this method just returns a file path with running_test(False): # To force downloading the file @@ -80,6 +82,7 @@ def test_bracket(mapdl, cleared, running_test): assert int(n_ent[0]) > 0 +@requires("requests") def test_download_example_data_true_download(): path = download_example_data("LatheCutter.anf", "geometry") assert os.path.exists(path) @@ -92,36 +95,43 @@ def test_failed_download(running_test): _download_file(filename, directory=None) +@requires("requests") def test_download_cfx_mapping_example_data(running_test): with running_test(): assert all(download_cfx_mapping_example_data().values()) +@requires("requests") def test_download_manifold_example_data(running_test): with running_test(): assert all(download_manifold_example_data().values()) +@requires("requests") def test_download_bracket(running_test): with running_test(): assert download_bracket() is True +@requires("requests") def test_download_vtk_rotor(running_test): with running_test(): assert download_vtk_rotor() is True +@requires("requests") def test__download_rotor_tech_demo_vtk(running_test): with running_test(): assert _download_rotor_tech_demo_vtk() is True +@requires("requests") def test_download_example_data(running_test): with running_test(): assert download_example_data("LatheCutter.anf", "geometry") is True +@requires("requests") def test_download_tech_demo_data(running_test): with running_test(): assert ( @@ -130,6 +140,7 @@ def test_download_tech_demo_data(running_test): ) +@requires("requests") def test_detach_examples_submodule(): cmd = """ import sys diff --git a/tests/test_files/sector.cdb b/tests/test_files/sector.cdb new file mode 100644 index 0000000000..82d041e41b --- /dev/null +++ b/tests/test_files/sector.cdb @@ -0,0 +1,835 @@ +/PREP7 +/NOPR +*IF,_CDRDOFF,EQ,1,THEN !if solid model was read in +_CDRDOFF= !reset flag, numoffs already performed +*ELSE !offset database for the following FE model +NUMOFF,NODE, 678 +NUMOFF,ELEM, 328 +NUMOFF,TYPE, 2 +*ENDIF +*SET,_BUTTON , 1.000000000000 +*SET,_GUI_CLR_BG,' #D4D0C8 ' +*SET,_GUI_CLR_FG,' #000000 ' +*SET,_GUI_CLR_INFOBG,' #FFFFDC ' +*SET,_GUI_CLR_SEL,' #FFFF80 ' +*SET,_GUI_CLR_SELBG,' #0A246A ' +*SET,_GUI_CLR_SELFG,' #FFFFFF ' +*SET,_GUI_CLR_WIN,' #FFFFFF ' +*SET,_GUI_FNT_FMLY,'helvetica ' +*SET,_GUI_FNT_PXLS, 10.00000000000 +*SET,_GUI_FNT_SLNT,'r ' +*SET,_GUI_FNT_WEGT,'medium ' +*SET,_NREAL , 0.000000000000 +*SET,_QUALKEY, 0.000000000000 +*SET,_RETURN , 0.000000000000 +*SET,_SAV1 , 20.00000000000 +*SET,_SAV2 , 10000.00000000 +*SET,_SAV3 , 0.000000000000 +*SET,_SAV4 , 5.000000000000 +*SET,_STATUS , 1.000000000000 +*SET,_UIQR , 1.000000000000 +*SET,_Z1 , 0.000000000000 +*SET,_ZRD , 2.000000000000 +DOF,DELETE +ET, 1,200 +KEYOP, 1, 1, 7 +ET, 2,185 +NBLOCK,6,SOLID, 678, 655 +(3i9,6e21.13e3) + 1 0 0-2.8749388208227E-001-7.7396874943990E-001-4.0554552665065E-002 + 2 0 0-3.0911290325736E-001-8.3123884941167E-001-3.3999817382376E-002 + 3 0 0-2.9830339266982E-001-8.0260379942578E-001-3.7277185023723E-002 + 4 0 0-2.9343895270591E-001-7.7173437034977E-001-1.0850000000000E-001 + 5 0 0-2.9046641739409E-001-7.7285155989483E-001-7.4527276332533E-002 + 6 0 0-3.1230136738781E-001-8.3004618405191E-001-1.0850000000000E-001 + 7 0 0-3.0287016004686E-001-8.0089027720084E-001-1.0850000000000E-001 + 8 0 0-3.1070713532258E-001-8.3064251673179E-001-7.1249908691188E-002 + 9 0 0-6.4106437325314E-001-5.2030455186348E-001-7.2000000000000E-001 + 10 0 0-6.8031539468484E-001-4.6781547761817E-001-7.7047094398237E-001 + 11 0 0-6.6068988396899E-001-4.9406001474082E-001-7.4523547199118E-001 + 12 0 0-7.1480401135042E-001-4.4620210783410E-001-7.8747094398236E-001 + 13 0 0-6.9755970301763E-001-4.5700879272613E-001-7.7897094398236E-001 + 14 0 0-7.6119802099886E-001-4.3525776058905E-001-7.8747094398236E-001 + 15 0 0-7.3800101617464E-001-4.4072993421158E-001-7.8747094398236E-001 + 16 0 0-7.6802731499679E-001-4.4344430896140E-001-7.7747094398236E-001 + 17 0 0-7.6461266799775E-001-4.3935103477525E-001-7.8247094398236E-001 + 18 0 0-7.2010697899418E-001-5.1764345889358E-001-7.2000000000000E-001 + 19 0 0-7.4406714699549E-001-4.8054388392749E-001-7.4873547199118E-001 + 20 0 0-6.8058567612366E-001-5.1897400537853E-001-7.2000000000000E-001 + 21 0 0-7.2684490224811E-001-4.5764450044740E-001-7.7380843657056E-001 + 22 0 0-3.7433509037666E-001-7.3590325278327E-001-3.2222856038608E-001 + 23 0 0-3.1000378801192E-001-7.6502193311952E-001-1.6363370400513E-001 + 24 0 0-3.2656862331793E-001-7.5830949588928E-001-2.1876740801025E-001 + 25 0 0-3.5045185684730E-001-7.4710637433627E-001-2.7049798419817E-001 + 26 0 0-3.7487641523837E-001-7.1875159113523E-001-3.3722856038608E-001 + 27 0 0-3.7460575280752E-001-7.2732742195925E-001-3.2972856038608E-001 + 28 0 0-2.8709654584008E-001-5.5466746970894E-001-3.3722856038608E-001 + 29 0 0-3.3098648053923E-001-6.3670953042208E-001-3.3722856038608E-001 + 30 0 0-2.8489860857260E-001-5.3888893632385E-001-3.5222856038608E-001 + 31 0 0-2.8599757720634E-001-5.4677820301639E-001-3.4472856038608E-001 + 32 0 0-3.0806562608361E-001-5.2598866231561E-001-4.2113844758148E-001 + 33 0 0-2.9648211732811E-001-5.3243879931973E-001-3.8668350398378E-001 + 34 0 0-3.2156329427311E-001-5.3542270811280E-001-4.3613844758148E-001 + 35 0 0-3.1481446017836E-001-5.3070568521421E-001-4.2863844758148E-001 + 36 0 0-3.7600763300706E-001-6.1603241889712E-001-4.3613844758148E-001 + 37 0 0-3.4878546364008E-001-5.7572756350496E-001-4.3613844758148E-001 + 38 0 0-3.8959461681388E-001-6.6216208765148E-001-4.1001170752194E-001 + 39 0 0-3.8280112491071E-001-6.3909725327465E-001-4.2307507755171E-001 + 40 0 0-4.1314538089745E-001-6.9745757627579E-001-4.1001170752194E-001 + 41 0 0-4.0136999885567E-001-6.7980983196363E-001-4.1001170752194E-001 + 42 0 0-4.3016310360769E-001-7.0472694118991E-001-4.2501170752194E-001 + 43 0 0-4.2165424225257E-001-7.0109225873285E-001-4.1751170752194E-001 + 44 0 0-4.6404461462219E-001-6.8166243024612E-001-4.7959361818188E-001 + 45 0 0-4.9792612563668E-001-6.5859791930233E-001-5.3417552884182E-001 + 46 0 0-5.3394540688984E-001-6.2797095323466E-001-5.8337418797363E-001 + 47 0 0-5.6996468814300E-001-5.9734398716698E-001-6.3257284710545E-001 + 48 0 0-6.0551453069807E-001-5.5882426951523E-001-6.7628642355272E-001 + 49 0 0-6.7566426154020E-001-5.7029580554594E-001-6.7504301677288E-001 + 50 0 0-6.3122154408623E-001-6.2294815219830E-001-6.3008603354576E-001 + 51 0 0-5.8417364096845E-001-6.6432206710988E-001-5.7686470238967E-001 + 52 0 0-5.3712573785067E-001-7.0569598202147E-001-5.2364337123358E-001 + 53 0 0-4.9298355164893E-001-7.3529113395669E-001-4.6085701573334E-001 + 54 0 0-4.4884136544719E-001-7.6488628589191E-001-3.9807066023310E-001 + 55 0 0-4.0875500832874E-001-7.8573793827451E-001-3.2929912553000E-001 + 56 0 0-3.6866865121030E-001-8.0658959065712E-001-2.6052759082690E-001 + 57 0 0-3.4048500929905E-001-8.1831788735451E-001-1.8451379541345E-001 + 58 0 0-3.0843998003228E-001-5.6428459821589E-001-3.7085265075605E-001 + 59 0 0-3.5289552361577E-001-5.9496707280998E-001-4.2030759435375E-001 + 60 0 0-3.2478946717900E-001-5.6205525768343E-001-4.0916751911838E-001 + 61 0 0-5.9899324838254E-001-6.0930550852781E-001-6.3233553646219E-001 + 62 0 0-5.6139122864364E-001-6.5960672810409E-001-5.7410199799256E-001 + 63 0 0-5.7781070378981E-001-6.0543073067684E-001-6.2856673592850E-001 + 64 0 0-4.0587882031398E-001-7.2061842393953E-001-3.8084550922150E-001 + 65 0 0-4.3384694973234E-001-7.1831170081573E-001-4.1801707985061E-001 + 66 0 0-4.8732826685383E-001-7.1879622123150E-001-4.6733291170643E-001 + 67 0 0-2.9883237111159E-001-5.4951707677169E-001-3.6984969557240E-001 + 68 0 0-3.9401089806791E-001-7.0810458370551E-001-3.7362013395401E-001 + 69 0 0-3.4761863726411E-001-7.8244954327320E-001-2.3964749941857E-001 + 70 0 0-5.1752593174368E-001-6.8214695066190E-001-5.2890945003770E-001 + 71 0 0-3.0931691123336E-001-5.3517767307503E-001-4.1180463917010E-001 + 72 0 0-3.1518185825831E-001-5.4728773623923E-001-4.0816456393473E-001 + 73 0 0-3.2067940720331E-001-5.4281574837841E-001-4.2499837234611E-001 + 74 0 0-3.8647367505359E-001-7.3984126219327E-001-3.3695393565357E-001 + 75 0 0-3.8364045547040E-001-7.7518443113019E-001-3.0610345087398E-001 + 76 0 0-3.3834558132698E-001-6.0841477868021E-001-3.7362013395401E-001 + 77 0 0-6.2177566070734E-001-6.1402084753361E-001-6.3509824085930E-001 + 78 0 0-6.2669707529080E-001-5.6269904736620E-001-6.8005522408642E-001 + 79 0 0-4.4318608065209E-001-7.4839137316672E-001-4.0454655620619E-001 + 80 0 0-4.1807152779375E-001-7.3783786602240E-001-3.8135088155017E-001 + 81 0 0-3.2978341422448E-001-5.7390172672285E-001-4.0447674112602E-001 + 82 0 0-5.8565671943661E-001-6.1351747418671E-001-6.2456062475155E-001 + 83 0 0-3.1056819638310E-001-5.4436668383445E-001-4.0247083075871E-001 + 84 0 0-3.1979552013352E-001-5.5020878864401E-001-4.1385829711074E-001 + 85 0 0-3.9861225973051E-001-7.4377927160327E-001-3.5167931092106E-001 + 86 0 0-6.1232977732846E-001-6.0509354286891E-001-6.4011044817283E-001 + 87 0 0-4.3753079585700E-001-7.3189646044154E-001-4.1102245217928E-001 + 88 0 0 5.2308702906449E-002-8.8530933087350E-001-1.0850000000000E-001 + 89 0 0 5.5706610534415E-002-8.8510201969841E-001-3.3999817382376E-002 + 90 0 0 5.4007656720432E-002-8.8520567528595E-001-7.1249908691188E-002 + 91 0 0 4.5822824802394E-002-8.2436680311098E-001-1.0850000000000E-001 + 92 0 0 4.9065763854421E-002-8.5483806699224E-001-1.0850000000000E-001 + 93 0 0 5.2162720916463E-002-8.2398993191112E-001-4.0554552665065E-002 + 94 0 0 4.8992772859427E-002-8.2417836751105E-001-7.4527276332533E-002 + 95 0 0 5.3934665725439E-002-8.5454597580476E-001-3.7277185023723E-002 + 96 0 0-4.4730589689635E-001-7.6578472584218E-001-7.2000000000000E-001 + 97 0 0-3.7401451943990E-001-7.3606623111812E-001-7.2000000000000E-001 + 98 0 0-4.1066020816812E-001-7.5092547848015E-001-7.2000000000000E-001 + 99 0 0-5.2126281534273E-001-7.1749138606146E-001-7.7747094398236E-001 + 100 0 0-4.8428435611954E-001-7.4163805595182E-001-7.4873547199118E-001 + 101 0 0-5.1835371403536E-001-7.0723487786684E-001-7.8747094398236E-001 + 102 0 0-5.1980826468897E-001-7.1236313196414E-001-7.8247094398236E-001 + 103 0 0-4.7151921020010E-001-6.9836289283619E-001-7.8747094398236E-001 + 104 0 0-4.9493646211773E-001-7.0279888535151E-001-7.8747094398236E-001 + 105 0 0-4.3122134163335E-001-7.0407990445988E-001-7.7047094398237E-001 + 106 0 0-4.5137027591672E-001-7.0122139864803E-001-7.7897094398236E-001 + 107 0 0-4.0261793053662E-001-7.2007306778900E-001-7.4523547199118E-001 + 108 0 0-4.7786507102537E-001-7.1371351017599E-001-7.7380843657056E-001 + 109 0 0-3.8528981551353E-001-7.9580855625427E-001-6.7504301677288E-001 + 110 0 0-3.2327373413071E-001-8.2583238666636E-001-6.3008603354576E-001 + 111 0 0-2.6346504868381E-001-8.4449323252085E-001-5.7686470238967E-001 + 112 0 0-2.0365636323692E-001-8.6315407837534E-001-5.2364337123358E-001 + 113 0 0-1.5129303679245E-001-8.7223635036028E-001-4.6085701573334E-001 + 114 0 0-9.8929710347990E-002-8.8131862234522E-001-3.9807066023310E-001 + 115 0 0-5.3827869796311E-002-8.8406296433620E-001-3.2929912553000E-001 + 116 0 0-8.7260292446313E-003-8.8680730632717E-001-2.6052759082690E-001 + 117 0 0 2.1791336830909E-002-8.8605831860033E-001-1.8451379541345E-001 + 118 0 0-1.0633527900159E-001-8.1876319273887E-001-4.2501170752194E-001 + 119 0 0-1.4666876124241E-001-8.1147356558579E-001-4.7959361818188E-001 + 120 0 0-1.8700224348323E-001-8.0418393843272E-001-5.3417552884182E-001 + 121 0 0-2.3236460362557E-001-7.9085517424313E-001-5.8337418797363E-001 + 122 0 0-2.7772696376791E-001-7.7752641005354E-001-6.3257284710545E-001 + 123 0 0-3.2587074160390E-001-7.5679632058583E-001-6.7628642355272E-001 + 124 0 0-9.3745532802687E-002-8.0520056603366E-001-4.1001170752194E-001 + 125 0 0-1.0004040590214E-001-8.1198187938627E-001-4.1751170752194E-001 + 126 0 0-8.6586807808907E-002-7.6337757400043E-001-4.1001170752194E-001 + 127 0 0-9.0166170305797E-002-7.8428907001705E-001-4.1001170752194E-001 + 128 0 0-9.2937107084464E-002-7.1570970046421E-001-4.3613844758148E-001 + 129 0 0-8.9761957446765E-002-7.3954363723274E-001-4.2307507755171E-001 + 130 0 0-7.5986651903432E-002-6.1992455776419E-001-4.3613844758148E-001 + 131 0 0-8.4461879493948E-002-6.6781712911420E-001-4.3613844758148E-001 + 132 0 0-6.7493090558325E-002-6.0581613183023E-001-4.2113844758148E-001 + 133 0 0-7.1739871230879E-002-6.1287034479721E-001-4.2863844758148E-001 + 134 0 0-4.1081952799151E-002-6.0817824362029E-001-3.5222856038608E-001 + 135 0 0-5.4287521678738E-002-6.0699718772526E-001-3.8668350398378E-001 + 136 0 0-3.6672160704326E-002-6.2348663274837E-001-3.3722856038608E-001 + 137 0 0-3.8877056751738E-002-6.1583243818433E-001-3.4472856038608E-001 + 138 0 0-5.0124036934622E-002-8.0908822595732E-001-3.3722856038608E-001 + 139 0 0-4.3398098819474E-002-7.1628742935285E-001-3.3722856038608E-001 + 140 0 0-4.2653302784203E-002-8.2453687188985E-001-3.2222856038608E-001 + 141 0 0-4.6388669859413E-002-8.1681254892359E-001-3.2972856038608E-001 + 142 0 0 2.7959900566102E-002-8.2497221207505E-001-1.6363370400513E-001 + 143 0 0 1.0096976329812E-002-8.2557762103912E-001-2.1876740801025E-001 + 144 0 0-1.6278163227196E-002-8.2505724646448E-001-2.7049798419817E-001 + 145 0 0-5.2258719496157E-002-6.4095347358651E-001-3.7085265075605E-001 + 146 0 0-8.0391192686226E-002-6.8706500744443E-001-4.2030759435375E-001 + 147 0 0-6.8101473698277E-002-6.4556680518760E-001-4.0916751911838E-001 + 148 0 0-2.9938068407226E-001-8.0026078270438E-001-6.3233553646219E-001 + 149 0 0-2.4457018054853E-001-8.3091911408070E-001-5.7410199799256E-001 + 150 0 0-2.8160548081402E-001-7.8810527991980E-001-6.2856673592850E-001 + 151 0 0-7.7686833959436E-002-8.2340347675360E-001-3.8084550922150E-001 + 152 0 0-1.0417522036822E-001-8.3267184339455E-001-4.1801707985061E-001 + 153 0 0-1.5283576248589E-001-8.5486728621278E-001-4.6733291170643E-001 + 154 0 0-4.9488224106617E-002-6.2355490485013E-001-3.6984969557240E-001 + 155 0 0-7.1934784868654E-002-8.0714439599549E-001-3.7362013395401E-001 + 156 0 0 6.8547354259039E-004-8.5619246368315E-001-2.3964749941857E-001 + 157 0 0-1.9532930336007E-001-8.3366900840403E-001-5.2890945003770E-001 + 158 0 0-6.4898689033617E-002-6.1471965439106E-001-4.1180463917010E-001 + 159 0 0-6.5330978308737E-002-6.2816823645123E-001-4.0816456393473E-001 + 160 0 0-7.2172160505998E-002-6.2631892685738E-001-4.2499837234611E-001 + 161 0 0-5.2140718950194E-002-8.3307162968169E-001-3.3695393565357E-001 + 162 0 0-3.5177082180408E-002-8.6420684690036E-001-3.0610345087398E-001 + 163 0 0-6.1629484256616E-002-6.9343210337440E-001-3.7362013395401E-001 + 164 0 0-3.1827555220754E-001-8.1383490114453E-001-6.3509824085930E-001 + 165 0 0-3.4364594486214E-001-7.6895182337041E-001-6.8005522408642E-001 + 166 0 0-1.0047243604143E-001-8.6394955819772E-001-4.0454655620619E-001 + 167 0 0-8.1821648425523E-002-8.4409344076188E-001-3.8135088155017E-001 + 168 0 0-6.7845278287989E-002-6.5842031442464E-001-4.0447674112602E-001 + 169 0 0-2.8548399786014E-001-7.9868414978606E-001-6.2456062475155E-001 + 170 0 0-6.2304287508909E-002-6.2362317695190E-001-4.0247083075871E-001 + 171 0 0-6.8357669108565E-002-6.3271329595056E-001-4.1385829711074E-001 + 172 0 0-6.1628135116184E-002-8.4160638747354E-001-3.5167931092106E-001 + 173 0 0-3.1327737028437E-001-8.0183741562269E-001-6.4011044817283E-001 + 174 0 0-1.0201516173486E-001-8.4658049405022E-001-4.1102245217928E-001 + 175 0 0-1.0102444899411E-001-8.8108051416251E-001-3.3999817382376E-002 + 176 0 0-9.0574200423374E-002-8.2065624445438E-001-4.0554552665054E-002 + 177 0 0-9.5799324708741E-002-8.5086837930844E-001-3.7277185023721E-002 + 178 0 0-1.1385009702910E-001-8.7951518869085E-001-1.1288303905988E-001 + 179 0 0-1.0743727301160E-001-8.8029785142668E-001-7.3441428221127E-002 + 180 0 0-8.1843769455740E-002-8.2157285419223E-001-1.1127946367209E-001 + 181 0 0-9.7846933242418E-002-8.5054402144154E-001-1.1208125136598E-001 + 182 0 0-8.6208984939557E-002-8.2111454932330E-001-7.5917008168569E-002 + 183 0 0-1.5334767836913E-001-8.7349487764398E-001-1.0849999999971E-001 + 184 0 0-1.3344121123607E-001-8.1478451168892E-001-1.0849999999971E-001 + 185 0 0-1.4339444480262E-001-8.4413969466644E-001-1.0849999999971E-001 + 186 0 0-2.1344008197099E-001-7.9325944101934E-001-1.0849999999985E-001 + 187 0 0-2.3282452287847E-001-8.5177053084793E-001-1.0849999999985E-001 + 188 0 0-1.3334232408605E-001-8.1480070075548E-001-4.0554552665054E-002 + 189 0 0-1.4368695909587E-001-8.7513591488249E-001-3.3999817382376E-002 + 190 0 0-1.3851464159096E-001-8.4496830781899E-001-3.7277185023721E-002 + 191 0 0-2.1041810308416E-001-7.9438472509769E-001-4.0554552665065E-002 + 192 0 0-2.2639993117662E-001-8.5318738214708E-001-3.3999817382376E-002 + 193 0 0-1.3339176766108E-001-8.1479260622220E-001-7.4527276332377E-002 + 194 0 0-1.4851731873249E-001-8.7431539626322E-001-7.1249908691043E-002 + 195 0 0-1.2235570404499E-001-8.7810821452250E-001-3.3999817382376E-002 + 196 0 0-1.1195826225471E-001-8.1772847260493E-001-4.0554552665065E-002 + 197 0 0-6.1886205191590E-002-8.8382102081460E-001-3.3999817382376E-002 + 198 0 0-2.2747961389069E-002-8.8656152746669E-001-3.3999817382376E-002 + 199 0 0 1.6479324572673E-002-8.8583177358255E-001-3.3999817382376E-002 + 200 0 0-5.4926068095909E-002-8.2303525765642E-001-4.0554552665065E-002 + 201 0 0-1.9277935768444E-002-8.2541427085846E-001-4.0554552665065E-002 + 202 0 0 1.6442392574009E-002-8.2470210138479E-001-4.0554552665065E-002 + 203 0 0-2.0390380339672E-002-8.3062682811040E-001-2.6103574364344E-002 + 204 0 0-1.3364790875408E-001-8.7649629514224E-001-1.1106509176900E-001 + 205 0 0-1.0764249034592E-001-8.1817868294057E-001-1.0988973183590E-001 + 206 0 0 1.3879323016609E-002-8.2490412547663E-001-1.0920374700457E-001 + 207 0 0-1.8064178769208E-002-8.2544144784227E-001-1.0990749400913E-001 + 208 0 0-4.9953974112474E-002-8.2350715101725E-001-1.1059347884061E-001 + 209 0 0-1.8671065331359E-002-8.2542822138796E-001-7.5231023323738E-002 + 210 0 0-7.2377902983277E-002-8.8291396919768E-001-1.1180642529748E-001 + 211 0 0-3.0905708937457E-002-8.8631274970452E-001-1.1072981153507E-001 + 212 0 0 1.0701496984496E-002-8.8581104028901E-001-1.0961490576754E-001 + 213 0 0-2.6827137727837E-002-8.8644746962962E-001-7.2364814408314E-002 + 214 0 0-7.1535024277687E-001-5.2419733106007E-001-7.7747094398238E-001 + 215 0 0-6.4396966055712E-001-6.0976379690285E-001-7.0744783914337E-001 + 216 0 0-6.7965995166699E-001-5.6698056398146E-001-7.4245939156287E-001 + 217 0 0-6.8208685627001E-001-5.6363609418497E-001-7.1377587244275E-001 + 218 0 0-7.4168877888683E-001-4.8382082001074E-001-7.7747094398236E-001 + 219 0 0-7.0956049321178E-001-5.1516565471549E-001-7.8747094398237E-001 + 220 0 0-7.1245536799432E-001-5.1968149288779E-001-7.8247094398237E-001 + 221 0 0-7.3537925710510E-001-4.7521170765255E-001-7.8747094398236E-001 + 222 0 0-6.6134504055286E-001-5.2217222543280E-001-7.8747094398236E-001 + 223 0 0-6.8545276688232E-001-5.1866894007415E-001-7.8747094398236E-001 + 224 0 0-6.8807452595186E-001-4.8418716663362E-001-7.8747094398236E-001 + 225 0 0-6.2200397372911E-001-5.4294697163673E-001-7.7047094398237E-001 + 226 0 0-6.4167450714137E-001-5.3255959853472E-001-7.7897094398237E-001 + 227 0 0-6.5115968420697E-001-5.0538122462745E-001-7.7047094398237E-001 + 228 0 0-5.5773840462986E-001-6.0877600917352E-001-7.0750446029015E-001 + 229 0 0-5.8987118917948E-001-5.7586149040512E-001-7.3898770213626E-001 + 230 0 0-5.9941071010573E-001-5.6452879554287E-001-7.1376372286983E-001 + 231 0 0-6.0085403259349E-001-6.0926990303818E-001-7.0747614971676E-001 + 232 0 0-2.4948162492130E-001-7.7129442840463E-001-4.1001170752194E-001 + 233 0 0-3.3131350290938E-001-7.3437600234021E-001-4.1001170752194E-001 + 234 0 0-2.1881607653197E-001-7.3645238843015E-001-4.1001170752194E-001 + 235 0 0-2.3414885072664E-001-7.5387340841739E-001-4.1001170752194E-001 + 236 0 0-3.0420534667293E-001-6.9930723804082E-001-4.1001170752194E-001 + 237 0 0-2.0677895139659E-001-6.9146232469482E-001-4.3613844758148E-001 + 238 0 0-2.1279751396428E-001-7.1395735656249E-001-4.2307507755171E-001 + 239 0 0-2.9139329220207E-001-6.5374737179632E-001-4.3613844758148E-001 + 240 0 0-1.6458233312307E-001-6.0248907393446E-001-4.3613844758148E-001 + 241 0 0-1.8568064225983E-001-6.4697569931464E-001-4.3613844758148E-001 + 242 0 0-2.4307281369808E-001-5.6895589102363E-001-4.3613844758148E-001 + 243 0 0-1.5410802713209E-001-5.8976200185525E-001-4.2113844758148E-001 + 244 0 0-1.5934518012758E-001-5.9612553789485E-001-4.2863844758148E-001 + 245 0 0-2.3108682660785E-001-5.5787533208543E-001-4.2113844758148E-001 + 246 0 0-1.3387717159131E-001-5.9468092771298E-001-3.5222856038608E-001 + 247 0 0-1.4399259936170E-001-5.9222146478411E-001-3.8668350398378E-001 + 248 0 0-2.0938789008196E-001-5.6678493201841E-001-3.5222856038608E-001 + 249 0 0-1.3383595359792E-001-6.1005603522231E-001-3.3722856038608E-001 + 250 0 0-1.3385656259462E-001-6.0236848146764E-001-3.4472856038608E-001 + 251 0 0-2.1046624971900E-001-5.8236175246562E-001-3.3722856038608E-001 + 252 0 0-2.0279985530099E-001-7.8486202300232E-001-3.3722856038608E-001 + 253 0 0-1.6831790444946E-001-6.9745902911231E-001-3.3722856038608E-001 + 254 0 0-2.8883813526968E-001-7.5180680706877E-001-3.3722856038608E-001 + 255 0 0-2.0422387290491E-001-7.9998310424659E-001-3.2222856038608E-001 + 256 0 0-2.0351186410295E-001-7.9242256362446E-001-3.2972856038608E-001 + 257 0 0-2.8927948164079E-001-7.6794317851493E-001-3.2222856038608E-001 + 258 0 0-1.4484396574081E-001-8.1275237937940E-001-1.6404863661761E-001 + 259 0 0-1.5624672024554E-001-8.1072024706989E-001-2.1959727323625E-001 + 260 0 0-1.8023529657522E-001-8.0535167565824E-001-2.7091291681117E-001 + 261 0 0-2.4282719163343E-001-7.8912312876237E-001-2.1918150292803E-001 + 262 0 0-2.4485360439811E-002-8.5587724042161E-001-1.1030169845691E-001 + 263 0 0-2.7346601789211E-001-7.7903574654988E-001-4.2501170752194E-001 + 264 0 0-3.5181456074990E-001-7.4188134386990E-001-4.2501170752194E-001 + 265 0 0-2.6147382140670E-001-7.7516508747725E-001-4.1751170752194E-001 + 266 0 0-5.9039383879783E-001-6.5784645827715E-001-6.6328771868877E-001 + 267 0 0-5.3681801703853E-001-7.0592911965145E-001-6.1912759823417E-001 + 268 0 0-4.8037006595866E-001-7.4245045560664E-001-5.6801249895676E-001 + 269 0 0-4.2392211487880E-001-7.7897179156183E-001-5.1689739967935E-001 + 270 0 0-3.6808968781243E-001-8.0451688454693E-001-4.5894764712686E-001 + 271 0 0-3.1225726074607E-001-8.3006197753203E-001-4.0099789457437E-001 + 272 0 0-2.6572645795333E-001-8.4470009898593E-001-3.3221530289165E-001 + 273 0 0-2.1919565516059E-001-8.5933822043983E-001-2.6343271120893E-001 + 274 0 0-1.8627166676485E-001-8.6641654904190E-001-1.8596635560432E-001 + 275 0 0-5.8554484615898E-001-6.6606759782179E-001-6.2464528947985E-001 + 276 0 0-4.8196578086693E-001-7.4445805644426E-001-5.2029767226718E-001 + 277 0 0-3.8198262372746E-001-8.0037371702331E-001-3.9954618573467E-001 + 278 0 0-2.9515462946486E-001-8.3629693078290E-001-2.6197148184264E-001 + 279 0 0-3.1971206578655E-001-7.5957051317524E-001-4.7815198592831E-001 + 280 0 0-3.6595811368099E-001-7.4010527980060E-001-5.3129226433468E-001 + 281 0 0-4.1472430448952E-001-7.1168698619648E-001-5.7771941797452E-001 + 282 0 0-4.6349049529806E-001-6.8326869259236E-001-6.2414657161435E-001 + 283 0 0-5.1061444996396E-001-6.4602235088294E-001-6.6582551595225E-001 + 284 0 0-5.1852030345229E-001-6.4250840636683E-001-6.2836638839575E-001 + 285 0 0-4.3386707557739E-001-7.0245264472007E-001-5.3273632046801E-001 + 286 0 0-1.1473280769097E-001-8.1762873003805E-001-3.2222856038608E-001 + 287 0 0-1.5947834029794E-001-8.0880591714232E-001-3.2222856038608E-001 + 288 0 0-9.8402391581270E-002-8.1958940958894E-001-2.7079114950967E-001 + 289 0 0-8.2071975471568E-002-8.2155008913983E-001-2.1935373863327E-001 + 290 0 0-8.1957872463655E-002-8.2156147166603E-001-1.6531660115267E-001 + 291 0 0-1.1930273350324E-001-8.1697442745961E-001-2.1947315861701E-001 + 292 0 0-1.1522640304801E-001-8.0240828292136E-001-3.3722856038608E-001 + 293 0 0-1.5901312917456E-001-7.9363515296221E-001-3.3722856038608E-001 + 294 0 0-1.1497960536965E-001-8.1001850648006E-001-3.2972856038608E-001 + 295 0 0-8.3032638609398E-002-6.1902020121494E-001-3.3722856038608E-001 + 296 0 0-1.0843429610366E-001-6.1453811821862E-001-3.3722856038608E-001 + 297 0 0-9.9129520828706E-002-7.1071424206815E-001-3.3722856038608E-001 + 298 0 0-8.4605956409759E-002-6.0366409119546E-001-3.5222856038608E-001 + 299 0 0-8.3819297509579E-002-6.1134214620520E-001-3.4472856038608E-001 + 300 0 0-1.0924156400053E-001-5.9917250945422E-001-3.5222856038608E-001 + 301 0 0-1.0305023066935E-001-6.0079044001869E-001-4.2113844758148E-001 + 302 0 0-1.2857912890072E-001-5.9527622093697E-001-4.2113844758148E-001 + 303 0 0-9.3828093539556E-002-6.0222726560707E-001-3.8668350398378E-001 + 304 0 0-1.1094907549245E-001-6.1463056483874E-001-4.3613844758148E-001 + 305 0 0-1.0699965308090E-001-6.0771050242871E-001-4.2863844758148E-001 + 306 0 0-1.3776570430776E-001-6.0855981938660E-001-4.3613844758148E-001 + 307 0 0-1.3473984388474E-001-7.0902951679246E-001-4.3613844758148E-001 + 308 0 0-1.2284445968859E-001-6.6183004081560E-001-4.3613844758148E-001 + 309 0 0-1.7075939764066E-001-7.0024592074364E-001-4.3613844758148E-001 + 310 0 0-1.3690768461904E-001-7.5597545043842E-001-4.1001170752194E-001 + 311 0 0-1.3582376425189E-001-7.3250248361544E-001-4.2307507755171E-001 + 312 0 0-1.7786188057551E-001-7.4621391943429E-001-4.1001170752194E-001 + 313 0 0-1.5848974338210E-001-7.9499508030180E-001-4.1001170752194E-001 + 314 0 0-2.0398568415170E-001-7.8314475435321E-001-4.1001170752194E-001 + 315 0 0-1.4769871400057E-001-7.7548526537011E-001-4.1001170752194E-001 + 316 0 0-1.7997121608768E-001-8.0578577719156E-001-4.2501170752194E-001 + 317 0 0-2.2671861698989E-001-7.9241076187072E-001-4.2501170752194E-001 + 318 0 0-1.6923047973489E-001-8.0039042874668E-001-4.1751170752194E-001 + 319 0 0-5.3394076122318E-001-6.2975203123825E-001-7.2466231097144E-001 + 320 0 0-5.4583958292652E-001-6.1926402020588E-001-7.1608338563079E-001 + 321 0 0-4.7496911772068E-001-6.7146416406828E-001-6.8249130833101E-001 + 322 0 0-4.1599747421817E-001-7.1317629689832E-001-6.4032030569059E-001 + 323 0 0-3.5463212562529E-001-7.4248804543915E-001-5.9169802661262E-001 + 324 0 0-2.9326677703241E-001-7.7179979397998E-001-5.4307574753466E-001 + 325 0 0-2.3661899656005E-001-7.8879278558578E-001-4.8404372752830E-001 + 326 0 0-3.2999612685614E-001-7.5682422900185E-001-5.3718316701278E-001 + 327 0 0-4.4000099399363E-001-6.9862685507280E-001-6.3223289784751E-001 + 328 0 0-6.2945669154883E-001-6.2473441172696E-001-7.2773259288742E-001 + 329 0 0-6.3651788280190E-001-6.1744960861403E-001-7.1743694693316E-001 + 330 0 0-1.2786506679017E-001-8.7747297840605E-001-2.0065491185641E-001 + 331 0 0-1.4188003655125E-001-8.7543076812126E-001-2.8842678465294E-001 + 332 0 0-1.9264795183973E-001-8.6411230181226E-001-3.6189807922094E-001 + 333 0 0-2.4341586712822E-001-8.5279383550325E-001-4.3536937378895E-001 + 334 0 0-3.0748594068339E-001-8.2903047405633E-001-4.9436665170324E-001 + 335 0 0-3.7155601423856E-001-8.0526711260942E-001-5.5336392961752E-001 + 336 0 0-4.3863579345030E-001-7.6689989932117E-001-6.0005230787698E-001 + 337 0 0-5.0571557266203E-001-7.2853268603292E-001-6.4674068613643E-001 + 338 0 0-5.6758613210543E-001-6.7663354887994E-001-6.8723663951192E-001 + 339 0 0-1.8078929023183E-001-8.6823040966607E-001-2.7617322967537E-001 + 340 0 0-2.7803724825233E-001-8.4214256531522E-001-4.1830821568519E-001 + 341 0 0-3.9781534745220E-001-7.9262334123019E-001-5.3517664462543E-001 + 342 0 0-5.2120137045098E-001-7.1753602221175E-001-6.3288353834915E-001 + 343 0 0-1.2435810175080E-001-7.5803425226511E-001-4.1001170752194E-001 + 344 0 0-1.1180851888257E-001-7.6009305409180E-001-4.1001170752194E-001 + 345 0 0-9.9197663345737E-002-7.6173531404612E-001-4.1001170752194E-001 + 346 0 0-1.4235528411000E-001-7.9787376317077E-001-4.1001170752194E-001 + 347 0 0-1.2622082483790E-001-8.0075244603974E-001-4.1001170752194E-001 + 348 0 0-1.0998317882029E-001-8.0297650603670E-001-4.1001170752194E-001 + 349 0 0-1.1901467186023E-001-7.8042275006577E-001-4.1001170752194E-001 + 350 0 0-1.2431365450827E-001-7.1085284452333E-001-4.3613844758148E-001 + 351 0 0-1.1388746513179E-001-7.1267617225420E-001-4.3613844758148E-001 + 352 0 0-1.0341228610818E-001-7.1419293635963E-001-4.3613844758148E-001 + 353 0 0-1.1284851128631E-001-7.3638800765971E-001-4.2308119353284E-001 + 354 0 0-1.0222720647896E-001-6.1607780461568E-001-4.3613844758148E-001 + 355 0 0-9.3505337465465E-002-6.1752504439261E-001-4.3613844758148E-001 + 356 0 0-8.4745994684449E-002-6.1872480107840E-001-4.3613844758148E-001 + 357 0 0-1.0369640129863E-001-6.6510060832340E-001-4.3613844758148E-001 + 358 0 0-5.8169872638601E-001-6.2724322148261E-001-7.2619745192943E-001 + 359 0 0-9.4179454206554E-002-6.0217781243329E-001-4.2113844758148E-001 + 360 0 0-8.5308677743755E-002-6.0356518484789E-001-4.2113844758148E-001 + 361 0 0-7.6400884151040E-002-6.0469065833906E-001-4.2113844758148E-001 + 362 0 0-8.8957202724308E-002-6.0747347426946E-001-4.3173974048170E-001 + 363 0 0-5.8836620685452E-001-5.7922842124323E-001-7.7047094398237E-001 + 364 0 0-6.0518509029181E-001-5.6108769643998E-001-7.7047094398237E-001 + 365 0 0-6.3086208771108E-001-5.5861804658197E-001-7.8747094398236E-001 + 366 0 0-6.0961414728280E-001-5.6892323391260E-001-7.7897094398236E-001 + 367 0 0-6.4610356413219E-001-5.4039513600756E-001-7.8747094398236E-001 + 368 0 0-6.8041238432377E-001-5.5309197479644E-001-7.8747094398236E-001 + 369 0 0-6.9498643876777E-001-5.3412881475597E-001-7.8747094398236E-001 + 370 0 0-6.5563723601743E-001-5.5585501068921E-001-7.8747094398236E-001 + 371 0 0-6.8567394244500E-001-5.6245893750865E-001-7.7747094398236E-001 + 372 0 0-6.8304316338439E-001-5.5777545615255E-001-7.8247094398236E-001 + 373 0 0-7.0051209261094E-001-5.4332813428436E-001-7.7747094398236E-001 + 374 0 0-7.3745215018422E-002-6.0498796498390E-001-3.5222856038608E-001 + 375 0 0-6.2884473627084E-002-6.0631183877234E-001-3.5222856038608E-001 + 376 0 0-5.1983213213118E-002-6.0724504119632E-001-3.5222856038608E-001 + 377 0 0-7.4109301391613E-002-6.0504240703119E-001-3.8668350398378E-001 + 378 0 0-7.1463343352781E-002-6.2035295945631E-001-3.3722856038608E-001 + 379 0 0-5.9894048096163E-002-6.2168571769768E-001-3.3722856038608E-001 + 380 0 0-4.8283104400244E-002-6.2258617522302E-001-3.3722856038608E-001 + 381 0 0-6.1080315015427E-002-6.1090878546684E-001-3.4162486871582E-001 + 382 0 0-6.5756531699692E-001-5.9359667461781E-001-7.5260176843489E-001 + 383 0 0-5.6115348403885E-001-6.0449022624074E-001-7.4756662747690E-001 + 384 0 0-1.2161120215260E+000-1.2303568262168E+000-7.2313585266101E-001 + 385 0 0-5.1055638863057E-001-1.6528879401662E+000-1.2351988013257E-001 + 386 0 0-1.1442683010779E+000-1.2938925615014E+000-6.8115254980496E-001 + 387 0 0-1.0724245806299E+000-1.3574282967860E+000-6.3916924694890E-001 + 388 0 0-9.9584969772137E-001-1.4114556026955E+000-5.9250231116497E-001 + 389 0 0-9.1927481481284E-001-1.4654829086049E+000-5.4583537538104E-001 + 390 0 0-8.4162689517780E-001-1.5087955800241E+000-4.9063505947769E-001 + 391 0 0-7.6397897554276E-001-1.5521082514434E+000-4.3543474357434E-001 + 392 0 0-6.9216155523057E-001-1.5835005121936E+000-3.6628417300183E-001 + 393 0 0-6.2034413491838E-001-1.6148927729438E+000-2.9713360242931E-001 + 394 0 0-5.6545026177448E-001-1.6338903565550E+000-2.1032674128094E-001 + 395 0 0-1.2012331424876E+000-1.2448876680679E+000-7.3985043715255E-001 + 396 0 0-1.2086725820068E+000-1.2376222471424E+000-7.3149314490678E-001 + 397 0 0-4.7551052766374E-001-1.6633087827578E+000-1.1587599339095E-001 + 398 0 0-5.0784521323467E-001-1.6533769191587E+000-2.1837515973677E-001 + 399 0 0-5.4017989880560E-001-1.6434450555595E+000-3.2087432608258E-001 + 400 0 0-6.1845415203486E-001-1.6134415352292E+000-3.9420279155853E-001 + 401 0 0-6.9672840526411E-001-1.5834380148989E+000-4.6753125703448E-001 + 402 0 0-7.8332083757754E-001-1.5393748442960E+000-5.2253739153807E-001 + 403 0 0-8.6991326989097E-001-1.4953116736932E+000-5.7754352604166E-001 + 404 0 0-9.5544093187297E-001-1.4385050568264E+000-6.2152269818174E-001 + 405 0 0-1.0409685938550E+000-1.3816984399596E+000-6.6550187032181E-001 + 406 0 0-1.1211008681713E+000-1.3132930540138E+000-7.0267615373718E-001 + 407 0 0-4.9303345814716E-001-1.6580983614620E+000-1.1969793676176E-001 + 408 0 0-5.8073579141095E-001-1.6295558625873E+000-3.0882944018442E-001 + 409 0 0-7.3068349489776E-001-1.5680586720535E+000-4.5133866854059E-001 + 410 0 0-8.9479253959923E-001-1.4805582324907E+000-5.6161276930758E-001 + 411 0 0-1.0567955467820E+000-1.3696311697023E+000-6.5230258546895E-001 + 412 0 0-9.8984530395514E-002-8.0440689103347E-001-3.3722856038608E-001 + 413 0 0-8.2742657742908E-002-8.0640549914484E-001-3.3722856038608E-001 + 414 0 0-6.6433347338765E-002-8.0774686255108E-001-3.3722856038608E-001 + 415 0 0-7.1318352919535E-002-7.1404560842126E-001-3.3722856038608E-001 + 416 0 0-4.3934392557184E-001-1.5849294957135E+000-1.1561163403002E-001 + 417 0 0-4.0317732347993E-001-1.5065502086693E+000-1.1534727466909E-001 + 418 0 0-3.6701072138803E-001-1.4281709216250E+000-1.1508291530816E-001 + 419 0 0-3.3084411929612E-001-1.3497916345807E+000-1.1481855594723E-001 + 420 0 0-2.9467751720422E-001-1.2714123475365E+000-1.1455419658630E-001 + 421 0 0-2.5851091511232E-001-1.1930330604922E+000-1.1428983722537E-001 + 422 0 0-2.2234431302041E-001-1.1146537734479E+000-1.1402547786444E-001 + 423 0 0-1.8617771092851E-001-1.0362744864037E+000-1.1376111850351E-001 + 424 0 0-1.5001110883660E-001-9.5789519935938E-001-1.1349675914258E-001 + 425 0 0-4.7484588084386E-001-1.5749468139465E+000-1.2205767049497E-001 + 426 0 0-4.3913537305714E-001-1.4970056877268E+000-1.2059546085736E-001 + 427 0 0-4.0342486527042E-001-1.4190645615071E+000-1.1913325121976E-001 + 428 0 0-3.6771435748370E-001-1.3411234352875E+000-1.1767104158215E-001 + 429 0 0-3.3200384969699E-001-1.2631823090678E+000-1.1620883194455E-001 + 430 0 0-2.9629334191027E-001-1.1852411828481E+000-1.1474662230694E-001 + 431 0 0-2.6058283412355E-001-1.1073000566284E+000-1.1328441266934E-001 + 432 0 0-2.2487232633684E-001-1.0293589304087E+000-1.1182220303173E-001 + 433 0 0-1.8916181855012E-001-9.5141780418906E-001-1.1035999339413E-001 + 434 0 0-4.2974572877798E-001-1.4979715504739E+000-1.0763884058617E-001 + 435 0 0-3.5604273380313E-001-1.3425164256957E+000-1.0405824231406E-001 + 436 0 0-2.8201492539630E-001-1.1872062899597E+000-1.0088103522092E-001 + 437 0 0-2.0739216662550E-001-1.0321627263107E+000-9.8114263774300E-002 + 438 0 0-1.1440320976020E+000-1.1828959102736E+000-7.3862165545361E-001 + 439 0 0-1.0868310527164E+000-1.1209041524793E+000-7.3739287375466E-001 + 440 0 0-1.0296300078307E+000-1.0589123946850E+000-7.3616409205572E-001 + 441 0 0-9.7242896294511E-001-9.9692063689073E-001-7.3493531035678E-001 + 442 0 0-9.1522791805948E-001-9.3492887909643E-001-7.3370652865783E-001 + 443 0 0-8.5802687317384E-001-8.7293712130213E-001-7.3247774695889E-001 + 444 0 0-8.0082582828821E-001-8.1094536350784E-001-7.3124896525995E-001 + 445 0 0-7.4362478340258E-001-7.4895360571354E-001-7.3002018356100E-001 + 446 0 0-6.8642373851694E-001-6.8696184791925E-001-7.2879140186206E-001 + 447 0 0-9.8742875060201E-001-1.3163861922324E+000-6.6362243020995E-001 + 448 0 0-8.2007312305776E-001-1.4262925220749E+000-5.7514556310021E-001 + 449 0 0-6.5140543326384E-001-1.5103506329990E+000-4.6436399445142E-001 + 450 0 0-5.0037207625118E-001-1.5666424117258E+000-3.1771788214799E-001 + 451 0 0-1.0103599800327E+000-1.1859890484923E+000-6.9956793192638E-001 + 452 0 0-9.3388890734904E-001-1.2510739445053E+000-6.6174299009810E-001 + 453 0 0-8.5206094178679E-001-1.3041736574810E+000-6.1724529512842E-001 + 454 0 0-7.7023297622455E-001-1.3572733704567E+000-5.7274760015875E-001 + 455 0 0-6.8815771874406E-001-1.3972683107779E+000-5.1697216601355E-001 + 456 0 0-6.0608246126357E-001-1.4372632510992E+000-4.6119673186836E-001 + 457 0 0-5.3332335748017E-001-1.4635515094956E+000-3.8787908504087E-001 + 458 0 0-4.6056425369676E-001-1.4898397678921E+000-3.1456143821339E-001 + 459 0 0-4.3187078858835E-001-1.4981949882807E+000-2.1495435644124E-001 + 460 0 0-8.8034748052063E-001-1.1857685744770E+000-6.5985951242591E-001 + 461 0 0-7.2038856003552E-001-1.2882683516454E+000-5.7033654348432E-001 + 462 0 0-5.6074980951251E-001-1.3641928020654E+000-4.5800515982789E-001 + 463 0 0-4.2073791431228E-001-1.4130430462629E+000-3.1136239681510E-001 + 464 0 0-8.9961750831867E-001-1.0586919206697E+000-6.9645567255525E-001 + 465 0 0-8.2680605369223E-001-1.1204632044487E+000-6.5797603475373E-001 + 466 0 0-7.4867509876936E-001-1.1698632686414E+000-6.1295076078181E-001 + 467 0 0-6.7054414384649E-001-1.2192633328341E+000-5.6792548680989E-001 + 468 0 0-5.9298065080397E-001-1.2551928429329E+000-5.1136953729865E-001 + 469 0 0-5.1541715776145E-001-1.2911223530316E+000-4.5481358778742E-001 + 470 0 0-4.4816436634463E-001-1.3136843388326E+000-3.8148847160211E-001 + 471 0 0-3.8091157492780E-001-1.3362463246337E+000-3.0816335541681E-001 + 472 0 0-3.5587784711196E-001-1.3430189796072E+000-2.1149095568202E-001 + 473 0 0-7.7326359728473E-001-1.0551642711358E+000-6.5608807654060E-001 + 474 0 0-6.2069807448006E-001-1.1502705976594E+000-5.6550023513674E-001 + 475 0 0-4.7008106563011E-001-1.2180650020741E+000-4.5159731818682E-001 + 476 0 0-3.4107794750117E-001-1.2594495084875E+000-3.0492714775151E-001 + 477 0 0-7.8887400702554E-001-9.3140122956251E-001-6.9333893264318E-001 + 478 0 0-7.1972114087723E-001-9.8986533782289E-001-6.5420011832748E-001 + 479 0 0-6.4528657299543E-001-1.0355716001538E+000-6.0863755089554E-001 + 480 0 0-5.7085200511363E-001-1.0812778624846E+000-5.6307498346359E-001 + 481 0 0-4.9779848930620E-001-1.1131427568006E+000-5.0572801602490E-001 + 482 0 0-4.2474497349877E-001-1.1450076511166E+000-4.4838104858621E-001 + 483 0 0-3.6299464678666E-001-1.1638301717290E+000-3.7503599433622E-001 + 484 0 0-3.0124432007454E-001-1.1826526923413E+000-3.0169094008622E-001 + 485 0 0-2.7987761759343E-001-1.1878428764168E+000-2.0799038865579E-001 + 486 0 0-6.6617834350521E-001-9.2457229100897E-001-6.5230723462256E-001 + 487 0 0-5.2100754473583E-001-1.0122950930176E+000-5.6063473817637E-001 + 488 0 0-3.7941289288978E-001-1.0719587631179E+000-4.4514113957369E-001 + 489 0 0-2.6141533865999E-001-1.1058492656500E+000-2.9842695466354E-001 + 490 0 0-6.7813016476788E-001-8.0411642495429E-001-6.9021726723932E-001 + 491 0 0-6.1263554613319E-001-8.5927924419504E-001-6.5041435091765E-001 + 492 0 0-5.4189931524561E-001-9.0129578387282E-001-6.0430442190340E-001 + 493 0 0-4.7116308435804E-001-9.4331232355059E-001-5.5819449288916E-001 + 494 0 0-4.0262194831941E-001-9.7111109933489E-001-5.0004786172517E-001 + 495 0 0-3.3408081228079E-001-9.9890987511920E-001-4.4190123056117E-001 + 496 0 0-2.7783358476312E-001-1.0139778570389E+000-3.6853209990102E-001 + 497 0 0-2.2158635724545E-001-1.0290458389586E+000-2.9516296924086E-001 + 498 0 0-2.0388203408698E-001-1.0326601626811E+000-2.0446204387219E-001 + 499 0 0-5.5917555939761E-001-7.9390596511398E-001-6.4857751852704E-001 + 500 0 0-4.2135954929830E-001-8.7428971808001E-001-5.5577921125334E-001 + 501 0 0-2.8874833970450E-001-9.2585185531122E-001-4.3863530217506E-001 + 502 0 0-1.8173319689835E-001-9.5223830353993E-001-2.9179487694690E-001 + 503 0 0-1.1588821265706E+000-1.1683140563054E+000-7.2155339476523E-001 + 504 0 0-1.1016522316153E+000-1.1062712863941E+000-7.1997093686945E-001 + 505 0 0-1.0444223366599E+000-1.0442285164827E+000-7.1838847897368E-001 + 506 0 0-9.8719244170458E-001-9.8218574657134E-001-7.1680602107790E-001 + 507 0 0-9.2996254674923E-001-9.2014297665997E-001-7.1522356318212E-001 + 508 0 0-8.7273265179388E-001-8.5810020674860E-001-7.1364110528634E-001 + 509 0 0-8.1550275683853E-001-7.9605743683723E-001-7.1205864739056E-001 + 510 0 0-7.5827286188318E-001-7.3401466692586E-001-7.1047618949478E-001 + 511 0 0-7.0104296692783E-001-6.7197189701450E-001-7.0889373159900E-001 + 512 0 0-1.0936439999928E+000-1.1139324017158E+000-7.3934887568216E-001 + 513 0 0-9.7966612903371E-001-9.8947420195442E-001-7.3691938067342E-001 + 514 0 0-8.6607461231774E-001-8.6465981618169E-001-7.3438228671653E-001 + 515 0 0-7.5419578692751E-001-7.3827694015112E-001-7.3089663013103E-001 + 516 0 0-2.6219058045181E-001-1.0198993753900E+000-1.9110586948926E-001 + 517 0 0-3.3799903549336E-001-1.1733991757405E+000-1.9592054719560E-001 + 518 0 0-4.1381314384853E-001-1.3268969003982E+000-2.0072945997865E-001 + 519 0 0-4.8963083147879E-001-1.4803936091918E+000-2.0553168131115E-001 + 520 0 0-2.5935224486369E-001-9.3488902040557E-001-2.6691112357786E-001 + 521 0 0-2.9950883456678E-001-1.0104398203713E+000-2.7038953594678E-001 + 522 0 0-3.3960678182162E-001-1.0859984945021E+000-2.7374200401552E-001 + 523 0 0-3.7970472907645E-001-1.1615571686329E+000-2.7709447208426E-001 + 524 0 0-4.1980832964491E-001-1.2371137670709E+000-2.8044117522970E-001 + 525 0 0-4.5991193021336E-001-1.3126703655089E+000-2.8378787837514E-001 + 526 0 0-5.0001911005691E-001-1.3882259480829E+000-2.8712789007005E-001 + 527 0 0-5.4012628990045E-001-1.4637815306568E+000-2.9046790176495E-001 + 528 0 0-5.8023521240942E-001-1.5393371518003E+000-2.9380075209713E-001 + 529 0 0-3.5106871734565E-001-9.9246718822212E-001-3.3920172159548E-001 + 530 0 0-4.3632133754617E-001-1.1402335658022E+000-3.4600353507290E-001 + 531 0 0-5.2159122826906E-001-1.2879925358645E+000-3.5278505726994E-001 + 532 0 0-6.0687288409593E-001-1.4357472178102E+000-3.5954528592405E-001 + 533 0 0-3.5744293043529E-001-9.0227826680248E-001-4.0450590090928E-001 + 534 0 0-4.0262860012451E-001-9.7449455607292E-001-4.0801390724418E-001 + 535 0 0-4.4778327307020E-001-1.0467022595223E+000-4.1146325265286E-001 + 536 0 0-4.9293794601589E-001-1.1189099629716E+000-4.1491259806154E-001 + 537 0 0-5.3810423617033E-001-1.1911123345959E+000-4.1834741711314E-001 + 538 0 0-5.8327052632476E-001-1.2633147062201E+000-4.2178223616475E-001 + 539 0 0-6.2844500230808E-001-1.3355138055918E+000-4.2520245312395E-001 + 540 0 0-6.7361947829140E-001-1.4077129049635E+000-4.2862267008315E-001 + 541 0 0-7.1879922691708E-001-1.4799105782034E+000-4.3202870682875E-001 + 542 0 0-4.6280289815938E-001-9.4540988282830E-001-4.6536413929742E-001 + 543 0 0-5.5748719148123E-001-1.0862653723531E+000-4.7171873369786E-001 + 544 0 0-6.5218938939736E-001-1.2271126079362E+000-4.7804839632799E-001 + 545 0 0-7.4690411837908E-001-1.3679550269508E+000-4.8435348054500E-001 + 546 0 0-4.7344965553652E-001-8.4764850057275E-001-5.1980588551500E-001 + 547 0 0-5.2297719619425E-001-9.1632520958367E-001-5.2271437135065E-001 + 548 0 0-5.7250681657041E-001-9.8497299565915E-001-5.2561962034242E-001 + 549 0 0-6.2203643694656E-001-1.0536207817346E+000-5.2852486933418E-001 + 550 0 0-6.7157234470826E-001-1.1222656456935E+000-5.3141971291271E-001 + 551 0 0-7.2110825246996E-001-1.1909105096524E+000-5.3431455649123E-001 + 552 0 0-7.7064850546836E-001-1.2595538292952E+000-5.3719942374903E-001 + 553 0 0-8.2018875846675E-001-1.3281971489381E+000-5.4008429100684E-001 + 554 0 0-8.6973178663980E-001-1.3968400287715E+000-5.4295983319394E-001 + 555 0 0-5.8341708430916E-001-8.7632673183561E-001-5.7291477476974E-001 + 556 0 0-6.8651271563773E-001-1.0101147014476E+000-5.7783930053745E-001 + 557 0 0-7.8961871503207E-001-1.1438973593392E+000-5.8274464787289E-001 + 558 0 0-8.9273188634190E-001-1.2776770343174E+000-5.8763186986168E-001 + 559 0 0-5.9033749473130E-001-7.7112868686950E-001-6.2112138821150E-001 + 560 0 0-6.4385697242407E-001-8.3632825408755E-001-6.2311517818884E-001 + 561 0 0-6.9742298337648E-001-9.0146843762402E-001-6.2513445496478E-001 + 562 0 0-7.5098899432890E-001-9.6660862116050E-001-6.2715373174073E-001 + 563 0 0-8.0455908596154E-001-1.0317464150933E+000-6.2916423549764E-001 + 564 0 0-8.5812917759417E-001-1.0968842090260E+000-6.3117473925454E-001 + 565 0 0-9.1170209590561E-001-1.1620205643613E+000-6.3317709398553E-001 + 566 0 0-9.6527501421705E-001-1.2271569196966E+000-6.3517944871653E-001 + 567 0 0-1.0188497974235E+000-1.2922926082413E+000-6.3717434783271E-001 + 568 0 0-7.0106491715363E-001-7.8517146050671E-001-6.6679568384181E-001 + 569 0 0-8.1186082306139E-001-9.1235441395455E-001-6.7039741851353E-001 + 570 0 0-9.2266080964937E-001-1.0395349777987E+000-6.7399038016622E-001 + 571 0 0-1.0334636229162E+000-1.1667141030453E+000-6.7757519279299E-001 + 572 0 0-9.6750815573310E-002-8.1975104803592E-001-3.2222856038608E-001 + 573 0 0-7.8768823455648E-002-8.2187336603379E-001-3.2222856038608E-001 + 574 0 0-6.0711063119926E-002-8.2320511896182E-001-3.2222856038608E-001 + 575 0 0-8.1062517151065E-002-8.1723220204343E-001-3.3283840645043E-001 + 576 0 0-2.7059014454680E-002-8.2514661945704E-001-1.6448773502902E-001 + 577 0 0-5.9062912805860E-002-8.2320094010582E-001-2.1921085734109E-001 + 578 0 0-3.6053850140151E-002-8.2485179107181E-001-2.1906797604891E-001 + 579 0 0-1.2978436905170E-002-8.2521470605546E-001-2.1891769202958E-001 + 580 0 0-5.7411336797900E-002-8.2336257855280E-001-2.7064826821750E-001 + 581 0 0-1.6163572250842E-001-8.0944712905244E-001-4.2501170752194E-001 + 582 0 0-1.4330022892915E-001-8.1310848091331E-001-4.2501170752194E-001 + 583 0 0-1.2481775396537E-001-8.1593583682609E-001-4.2501170752194E-001 + 584 0 0-1.3527385859121E-001-8.1000423442698E-001-4.1438137919602E-001 + 585 0 0-4.1555115330167E-001-7.1181760916279E-001-7.2092661208856E-001 + 586 0 0-4.5708778716345E-001-6.8756898720745E-001-7.2185322417713E-001 + 587 0 0-4.9551427419332E-001-6.5866050922285E-001-7.2325776757428E-001 + 588 0 0-2.1406978705669E-001-7.9691254400979E-001-5.3632002364552E-001 + 589 0 0-3.1326664930169E-001-7.6293378715574E-001-6.3434967683563E-001 + 590 0 0-1.9221877977965E-001-8.0137481525009E-001-4.8173811298558E-001 + 591 0 0-2.4113733063015E-001-7.8964114958686E-001-5.3846451844923E-001 + 592 0 0-2.9497183273281E-001-7.6899115692240E-001-5.8729551250752E-001 + 593 0 0-3.4880633483548E-001-7.4834116425795E-001-6.3612650656581E-001 + 594 0 0-4.0294706099946E-001-7.1795507573270E-001-6.7898986537147E-001 + 595 0 0-2.6720205383128E-001-7.8072047178342E-001-5.4077013299195E-001 + 596 0 0-3.8240190452682E-001-7.3075873057813E-001-6.3822340612820E-001 + 597 0 0-5.8621860435149E-001-6.6296703815166E-001-7.2580414382332E-001 + 598 0 0-5.4298051715414E-001-7.0119966457635E-001-7.2387569475922E-001 + 599 0 0-4.9514320702525E-001-7.3349219520926E-001-7.2193784737961E-001 + 600 0 0-5.3579569609242E-002-8.8494088852997E-001-1.9261377932869E-001 + 601 0 0-1.0906673341614E-001-8.7949989773834E-001-2.8146226588762E-001 + 602 0 0-7.6253430281026E-002-8.8356902735542E-001-2.7449774712231E-001 + 603 0 0-4.2489729762829E-002-8.8518816684130E-001-2.6751266897460E-001 + 604 0 0-1.2453223545589E-001-8.7671127646874E-001-3.4559981431835E-001 + 605 0 0-2.0811345387949E-001-8.6132368054266E-001-4.2603562765167E-001 + 606 0 0-1.7281104063075E-001-8.6985352558207E-001-4.1670188151440E-001 + 607 0 0-1.3587037548937E-001-8.7558607396364E-001-4.0738627087375E-001 + 608 0 0-2.3143588668661E-001-8.5396541943632E-001-4.7758704848439E-001 + 609 0 0-3.3080837349051E-001-8.2167221294999E-001-5.4591807253596E-001 + 610 0 0-2.9006073274247E-001-8.3807731329057E-001-5.3847221545439E-001 + 611 0 0-2.4685854798969E-001-8.5061569583295E-001-5.3105779334398E-001 + 612 0 0-3.5400588643523E-001-8.1013510887522E-001-5.8844027090310E-001 + 613 0 0-4.6183330639501E-001-7.5536279524639E-001-6.4257450624411E-001 + 614 0 0-4.1795104012799E-001-7.8219290445987E-001-6.3840832635180E-001 + 615 0 0-3.7061238712935E-001-8.0401264556311E-001-6.3424717994878E-001 + 616 0 0-4.8046577864107E-001-7.4169628451811E-001-6.8114201055551E-001 + 617 0 0-5.0003452467254E-001-6.9438407622149E-001-7.2284805481624E-001 + 618 0 0-6.4705023907536E-001-6.0384609975022E-001-7.7747094398236E-001 + 619 0 0-6.0842653570572E-001-6.4523326199179E-001-7.7747094398236E-001 + 620 0 0-5.6484467552422E-001-6.8136232402663E-001-7.7747094398236E-001 + 621 0 0-5.7638321525745E-001-6.7401127653839E-001-7.5067332036799E-001 + 622 0 0-5.5098393358228E-001-6.1283769827028E-001-7.7047094398237E-001 + 623 0 0-5.1360166031004E-001-6.4644697529733E-001-7.7047094398237E-001 + 624 0 0-4.7241150097169E-001-6.7526343987860E-001-7.7047094398237E-001 + 625 0 0-4.8578003729660E-001-6.6760625574391E-001-7.4616209329860E-001 + 626 0 0-6.4236511073185E-001-5.9422180369523E-001-7.8747094398236E-001 + 627 0 0-6.0431783713994E-001-6.3535163259402E-001-7.8747094398236E-001 + 628 0 0-5.6133577558762E-001-6.7129325523039E-001-7.8747094398236E-001 + 629 0 0-6.0779669428922E-001-6.4179664166133E-001-7.8454346697477E-001 + 630 0 0-5.9323191093920E-001-5.9606910942421E-001-7.8747094398236E-001 + 631 0 0-5.5560173416733E-001-6.3352017226645E-001-7.8747094398236E-001 + 632 0 0-5.1356047218371E-001-6.6594153255132E-001-7.8747094398236E-001 + 633 0 0-5.7995978565363E-001-6.3443590243024E-001-7.8747094398236E-001 + 634 0 0-5.3239313372469E-001-6.3733965331192E-001-7.8231900596618E-001 + 635 0 0-2.3693683525043E-001-8.0938185214099E-001-3.5341965270211E-001 + 636 0 0-2.8886190343290E-001-8.0220442444904E-001-4.1276010121468E-001 + 637 0 0-4.8103647784957E-001-6.9755983780197E-001-6.1519914511161E-001 + 638 0 0-5.1451516410053E-001-6.8941585848386E-001-6.2953028362370E-001 + 639 0 0-1.6622939248708E-001-6.4125852297212E-001-4.0526467843521E-001 + 640 0 0-1.6066650220735E-001-6.1599775139992E-001-4.1443809897408E-001 + 641 0 0-1.5348837820558E-001-6.0775930499092E-001-4.0305796233020E-001 + 642 0 0-1.0020831775449E-001-6.1887998085332E-001-4.0319573602594E-001 + 643 0 0-1.0550085540798E-001-6.2786921913603E-001-4.1446217003100E-001 + 644 0 0-1.0685923801371E-001-6.5388578516303E-001-4.0518924509059E-001 + 645 0 0-4.7797524087764E-001-7.1450501631542E-001-6.5280938390974E-001 + 646 0 0-4.3741494500198E-001-7.2518150000707E-001-6.3654812320263E-001 + 647 0 0-2.0494802080713E-001-8.2751045118032E-001-4.2862187220561E-001 + 648 0 0-1.4995186760851E-001-8.3009074150904E-001-3.6534203756880E-001 + 672 0 0-8.1304854507376E-002-6.2160489127241E-001-4.0276658242962E-001 + 673 0 0-8.6972813975529E-002-6.3063631159317E-001-4.1408447256282E-001 + 674 0 0-8.7389112642237E-002-6.5658844625118E-001-4.0467348802975E-001 + 675 0 0-3.9870421693107E-001-7.6270722901753E-001-6.4637054342821E-001 + 676 0 0-3.6409997419993E-001-7.6575391511015E-001-6.3037967227040E-001 + 677 0 0-1.5436091939736E-001-8.3914455015380E-001-4.1978446753563E-001 + 678 0 0-1.0661412144830E-001-8.3920115235609E-001-3.5847099282556E-001 +N,R5.3,LOC, -1, +EBLOCK,19,SOLID, 328, 105 +(19i9) + 1 2 1 1 0 0 0 0 8 0 224 96 97 105 99 598 586 623 619 + 1 2 1 1 0 0 0 0 8 0 225 598 586 623 619 328 319 363 371 + 1 2 1 1 0 0 0 0 8 0 226 99 105 103 101 619 623 631 627 + 1 2 1 1 0 0 0 0 8 0 227 619 623 631 627 371 363 365 368 + 1 2 1 1 0 0 0 0 8 0 228 168 136 170 171 674 379 672 673 + 1 2 1 1 0 0 0 0 8 0 229 674 379 672 673 644 295 642 643 + 1 2 1 1 0 0 0 0 8 0 230 136 168 128 126 379 674 351 344 + 1 2 1 1 0 0 0 0 8 0 231 379 674 351 344 295 644 307 310 + 1 2 1 1 0 0 0 0 8 0 232 130 128 168 171 355 351 674 673 + 1 2 1 1 0 0 0 0 8 0 233 355 351 674 673 304 307 644 643 + 1 2 1 1 0 0 0 0 8 0 234 169 173 110 112 676 675 614 610 + 1 2 1 1 0 0 0 0 8 0 235 676 675 614 610 646 645 337 335 + 1 2 1 1 0 0 0 0 8 0 236 173 169 122 97 675 676 593 586 + 1 2 1 1 0 0 0 0 8 0 237 675 676 593 586 645 646 322 319 + 1 2 1 1 0 0 0 0 8 0 238 169 112 120 122 676 610 591 593 + 1 2 1 1 0 0 0 0 8 0 239 676 610 591 593 646 335 324 322 + 1 2 1 1 0 0 0 0 8 0 240 124 118 174 172 347 582 677 678 + 1 2 1 1 0 0 0 0 8 0 241 347 582 677 678 313 316 647 648 + 1 2 1 1 0 0 0 0 8 0 242 138 124 172 140 413 347 678 573 + 1 2 1 1 0 0 0 0 8 0 243 413 347 678 573 292 313 648 286 + 1 2 1 1 0 0 0 0 8 0 244 118 120 112 174 582 591 610 677 + 1 2 1 1 0 0 0 0 8 0 245 582 591 610 677 316 324 335 647 + 1 2 1 1 0 0 0 0 8 0 246 112 114 174 174 610 606 677 677 + 1 2 1 1 0 0 0 0 8 0 247 610 606 677 677 335 333 647 647 + 1 2 1 1 0 0 0 0 8 0 248 174 114 116 172 677 606 602 678 + 1 2 1 1 0 0 0 0 8 0 249 677 606 602 678 647 333 331 648 + 1 2 1 1 0 0 0 0 8 0 250 172 116 143 140 678 602 578 573 + 1 2 1 1 0 0 0 0 8 0 251 678 602 578 573 648 331 289 286 + 1 2 1 1 0 0 0 0 8 0 252 110 173 97 96 614 675 586 598 + 1 2 1 1 0 0 0 0 8 0 253 614 675 586 598 337 645 319 328 + 1 2 1 1 0 0 0 0 8 0 254 143 116 88 91 578 602 211 207 + 1 2 1 1 0 0 0 0 8 0 255 578 602 211 207 289 331 178 180 + 1 2 1 1 0 0 0 0 8 0 256 138 136 126 124 413 379 344 347 + 1 2 1 1 0 0 0 0 8 0 257 413 379 344 347 292 295 310 313 + 1 2 1 1 0 0 0 0 8 0 258 171 170 132 130 673 672 360 355 + 1 2 1 1 0 0 0 0 8 0 259 673 672 360 355 643 642 301 304 + 1 2 1 1 0 0 0 0 8 0 260 132 170 136 134 360 672 379 375 + 1 2 1 1 0 0 0 0 8 0 261 360 672 379 375 301 642 295 298 + 1 2 1 1 0 0 0 0 8 0 262 198 211 178 175 201 207 180 176 + 1 2 1 1 0 0 0 0 8 0 263 89 88 211 198 93 91 207 201 + 1 2 1 1 0 0 0 0 8 0 264 432 521 273 183 423 497 331 178 + 1 2 1 1 0 0 0 0 8 0 265 521 534 271 273 497 495 333 331 + 1 2 1 1 0 0 0 0 8 0 266 534 547 269 271 495 493 335 333 + 1 2 1 1 0 0 0 0 8 0 267 547 560 267 269 493 491 337 335 + 1 2 1 1 0 0 0 0 8 0 268 560 510 215 267 491 445 328 337 + 1 2 1 1 0 0 0 0 8 0 269 430 523 521 432 421 484 497 423 + 1 2 1 1 0 0 0 0 8 0 270 523 536 534 521 484 482 495 497 + 1 2 1 1 0 0 0 0 8 0 271 536 549 547 534 482 480 493 495 + 1 2 1 1 0 0 0 0 8 0 272 549 562 560 547 480 478 491 493 + 1 2 1 1 0 0 0 0 8 0 273 562 508 510 560 478 443 445 491 + 1 2 1 1 0 0 0 0 8 0 274 428 525 523 430 419 471 484 421 + 1 2 1 1 0 0 0 0 8 0 275 525 538 536 523 471 469 482 484 + 1 2 1 1 0 0 0 0 8 0 276 538 551 549 536 469 467 480 482 + 1 2 1 1 0 0 0 0 8 0 277 551 564 562 549 467 465 478 480 + 1 2 1 1 0 0 0 0 8 0 278 564 506 508 562 465 441 443 478 + 1 2 1 1 0 0 0 0 8 0 279 426 527 525 428 417 458 471 419 + 1 2 1 1 0 0 0 0 8 0 280 527 540 538 525 458 456 469 471 + 1 2 1 1 0 0 0 0 8 0 281 540 553 551 538 456 454 467 469 + 1 2 1 1 0 0 0 0 8 0 282 553 566 564 551 454 452 465 467 + 1 2 1 1 0 0 0 0 8 0 283 566 504 506 564 452 439 441 465 + 1 2 1 1 0 0 0 0 8 0 284 385 393 527 426 397 399 458 417 + 1 2 1 1 0 0 0 0 8 0 285 393 391 540 527 399 401 456 458 + 1 2 1 1 0 0 0 0 8 0 286 391 389 553 540 401 403 454 456 + 1 2 1 1 0 0 0 0 8 0 287 389 387 566 553 403 405 452 454 + 1 2 1 1 0 0 0 0 8 0 288 387 384 504 566 405 395 439 452 + 1 2 1 1 0 0 0 0 8 0 289 365 368 371 363 222 219 214 225 + 1 2 1 1 0 0 0 0 8 0 290 363 371 328 319 225 214 215 228 + 1 2 1 1 0 0 0 0 8 0 291 295 298 301 642 249 246 243 641 + 1 2 1 1 0 0 0 0 8 0 292 301 304 643 642 243 240 640 641 + 1 2 1 1 0 0 0 0 8 0 293 310 313 292 295 234 232 252 249 + 1 2 1 1 0 0 0 0 8 0 294 178 180 289 331 183 184 259 273 + 1 2 1 1 0 0 0 0 8 0 295 319 328 337 645 228 215 267 638 + 1 2 1 1 0 0 0 0 8 0 296 289 286 648 331 259 255 635 273 + 1 2 1 1 0 0 0 0 8 0 297 331 648 647 333 273 635 636 271 + 1 2 1 1 0 0 0 0 8 0 298 335 333 647 647 269 271 636 636 + 1 2 1 1 0 0 0 0 8 0 299 335 647 316 324 269 636 263 280 + 1 2 1 1 0 0 0 0 8 0 300 648 286 292 313 635 255 252 232 + 1 2 1 1 0 0 0 0 8 0 301 647 648 313 316 636 635 232 263 + 1 2 1 1 0 0 0 0 8 0 302 324 322 646 335 280 282 637 269 + 1 2 1 1 0 0 0 0 8 0 303 322 319 645 646 282 228 638 637 + 1 2 1 1 0 0 0 0 8 0 304 337 335 646 645 267 269 637 638 + 1 2 1 1 0 0 0 0 8 0 305 644 643 304 307 639 640 240 237 + 1 2 1 1 0 0 0 0 8 0 306 307 310 295 644 237 234 249 639 + 1 2 1 1 0 0 0 0 8 0 307 642 643 644 295 641 640 639 249 + 1 2 1 1 0 0 0 0 8 0 308 81 84 83 28 639 640 641 249 + 1 2 1 1 0 0 0 0 8 0 309 28 38 36 81 249 234 237 639 + 1 2 1 1 0 0 0 0 8 0 310 34 84 81 36 240 640 639 237 + 1 2 1 1 0 0 0 0 8 0 311 82 52 50 86 637 269 267 638 + 1 2 1 1 0 0 0 0 8 0 312 86 9 47 82 638 228 282 637 + 1 2 1 1 0 0 0 0 8 0 313 82 47 45 52 637 282 280 269 + 1 2 1 1 0 0 0 0 8 0 314 40 85 87 42 232 635 636 263 + 1 2 1 1 0 0 0 0 8 0 315 26 22 85 40 252 255 635 232 + 1 2 1 1 0 0 0 0 8 0 316 42 87 52 45 263 636 269 280 + 1 2 1 1 0 0 0 0 8 0 317 52 87 54 54 269 636 271 271 + 1 2 1 1 0 0 0 0 8 0 318 87 85 56 54 636 635 273 271 + 1 2 1 1 0 0 0 0 8 0 319 85 22 24 56 635 255 259 273 + 1 2 1 1 0 0 0 0 8 0 320 50 18 9 86 267 215 228 638 + 1 2 1 1 0 0 0 0 8 0 321 24 4 6 56 259 184 183 273 + 1 2 1 1 0 0 0 0 8 0 322 26 40 38 28 252 232 234 249 + 1 2 1 1 0 0 0 0 8 0 323 84 34 32 83 640 240 243 641 + 1 2 1 1 0 0 0 0 8 0 324 32 30 28 83 243 246 249 641 + 1 2 1 1 0 0 0 0 8 0 325 18 16 10 9 215 214 225 228 + 1 2 1 1 0 0 0 0 8 0 326 16 14 12 10 214 219 222 225 + 1 2 1 1 0 0 0 0 8 0 327 178 180 184 183 175 176 188 189 + 1 2 1 1 0 0 0 0 8 0 328 189 183 6 2 188 184 4 1 + -1 +CMBLOCK,REFINE ,NODE, 7 ! users node component definition +(8i10) + 384 395 -396 438 -446 503 -515 +EXTOPT,ATTR, 0, 0, 0 +EXTOPT,ESIZE, 0, 0.0000 +EXTOPT,ACLEAR, 0 +TREF, 0.00000000 +IRLF, 0 +BFUNIF,TEMP,_TINY +ACEL, 0.00000000 , 0.00000000 , 0.00000000 +OMEGA, 0.00000000 , 0.00000000 , 0.00000000 +DOMEGA, 0.00000000 , 0.00000000 , 0.00000000 +CGLOC, 0.00000000 , 0.00000000 , 0.00000000 +CGOMEGA, 0.00000000 , 0.00000000 , 0.00000000 +DCGOMG, 0.00000000 , 0.00000000 , 0.00000000 + +KUSE, 0 +TIME, 0.00000000 +ALPHAD, 0.00000000 +BETAD, 0.00000000 +DMPRAT, 0.00000000 +DMPSTR, 0.00000000 + +CRPLIM, 0.100000000 , 0 +CRPLIM, 0.00000000 , 1 +NCNV, 1, 0.00000000 , 0, 0.00000000 , 0.00000000 + +NEQIT, 0 + +ERESX,DEFA + +/GO +FINISH diff --git a/tests/test_geometry.py b/tests/test_geometry.py index f597deab25..aca6243697 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -1,7 +1,11 @@ """Test geometry commands""" import numpy as np import pytest -import pyvista as pv + +from conftest import has_dependency, requires + +if has_dependency("pyvista"): + import pyvista as pv from ansys.mapdl.core.mapdl_geometry import Geometry, LegacyGeometry @@ -497,6 +501,7 @@ def test_empty_model(mapdl): assert mapdl.geometry.vnum.size == 0 +@requires("pyvista") @pytest.mark.parametrize( "entity,number", (["keypoints", 8], ["lines", 12], ["areas", 6], ["volumes", 1]) ) @@ -506,6 +511,7 @@ def test_entities_simple_cube(mapdl, cube_solve, entity, number): assert isinstance(entity, pv.MultiBlock) +@requires("pyvista") @pytest.mark.parametrize( "entity,number", (["keypoints", 26], ["lines", 45], ["areas", 28], ["volumes", 6]) ) @@ -519,10 +525,12 @@ def test_create_geometry(mapdl): assert isinstance(mapdl._create_geometry(), Geometry) +@requires("pyvista") def test_get_lines(mapdl, contact_geom_and_mesh): assert isinstance(mapdl.geometry.get_lines(), pv.PolyData) +@requires("pyvista") @pytest.mark.parametrize( "entity,number", (["keypoints", 26], ["lines", 45], ["areas", 28], ["volumes", 6]) ) @@ -548,6 +556,7 @@ def test_geometry_get_apis(mapdl, contact_geom_and_mesh, entity, number): assert len(as_an_array) == number +@requires("pyvista") @pytest.mark.parametrize( "entity,entity_name,number", ( @@ -573,6 +582,7 @@ def test_geometry_names(mapdl, contact_geom_and_mesh, entity, entity_name, numbe assert mb_names == names +@requires("pyvista") def test_geometry_get_item(mapdl, contact_geom_and_mesh): assert isinstance(mapdl.geometry["kp 2"], pv.PolyData) assert mapdl.geometry["kp 2"].n_points > 0 @@ -587,6 +597,7 @@ def test_geometry_get_item(mapdl, contact_geom_and_mesh): assert mapdl.geometry["volume 1"].n_cells > 0 +@requires("pyvista") def test_geometry_get_item_error(mapdl, contact_geom_and_mesh): with pytest.raises(ValueError): mapdl.geometry["l 0"] @@ -595,11 +606,13 @@ def test_geometry_get_item_error(mapdl, contact_geom_and_mesh): mapdl.geometry["kip 0"] +@requires("pyvista") def test_geometry_get_block_error(mapdl, contact_geom_and_mesh): with pytest.raises(KeyError): mapdl.geometry["kp 0"] +@requires("pyvista") def test_build_legacy_geometry(mapdl, contact_geom_and_mesh): leg_geo = LegacyGeometry(mapdl) diff --git a/tests/test_grpc.py b/tests/test_grpc.py index e4d9b95dd8..656f3cd1b0 100644 --- a/tests/test_grpc.py +++ b/tests/test_grpc.py @@ -16,10 +16,10 @@ PATH = os.path.dirname(os.path.abspath(__file__)) -# skip entire module unless HAS_GRPC installed or connecting to server -pytestmark = pytest.mark.skip_grpc +from conftest import has_dependency, requires -from conftest import skip_if_not_local +# skip entire module unless HAS_GRPC installed or connecting to server +pytestmark = requires("grpc") def write_tmp_in_mapdl_instance(mapdl, filename, ext="txt"): @@ -258,7 +258,10 @@ def test__download(mapdl, tmpdir): assert out_file.exists() out_file = tmpdir.join("out1_" + file_name) - mapdl._download(file_name, out_file_name=out_file, progress_bar=True) + if has_dependency("tqdm"): + mapdl._download(file_name, out_file_name=out_file, progress_bar=True) + else: + mapdl._download(file_name, out_file_name=out_file) assert out_file.exists() out_file = tmpdir.join("out2_" + file_name) @@ -355,7 +358,7 @@ def test_download_with_extension( os.remove(file) -@skip_if_not_local +@requires("local") def test_download_recursive(mapdl): if mapdl._local: temp_dir = os.path.join(mapdl.directory, "new_folder") @@ -418,7 +421,7 @@ def test__channel_str(mapdl): def test_mode_corba(mapdl): - assert mapdl.mode == "grpc" + assert mapdl.connection == "grpc" assert mapdl.is_grpc assert not mapdl.is_corba assert not mapdl.is_console diff --git a/tests/test_inline_functions/test_query.py b/tests/test_inline_functions/test_query.py index d7d240a8ff..c132a5a314 100644 --- a/tests/test_inline_functions/test_query.py +++ b/tests/test_inline_functions/test_query.py @@ -1,6 +1,7 @@ import pytest from ansys.mapdl.core.errors import MapdlCommandIgnoredError, MapdlRuntimeError +from conftest import requires class TestParseParameter: @@ -76,7 +77,7 @@ def test_interactive_mode_error(self, mapdl, line_geometry): with mapdl.non_interactive: q.kx(1) - @pytest.mark.skip_grpc # only works in gRPC mode + @requires("grpc") # only works in gRPC mode def test_nopr_mode(self, mapdl, line_geometry): try: # enter no printout mode diff --git a/tests/test_krylov.py b/tests/test_krylov.py index 29efa45a31..18de44fb48 100644 --- a/tests/test_krylov.py +++ b/tests/test_krylov.py @@ -5,6 +5,12 @@ import numpy as np import pytest +from conftest import has_dependency + +if not has_dependency("ansys-math-core"): + # Needs ansys-math-core + pytest.skip(allow_module_level=True) + PATH = os.path.dirname(os.path.abspath(__file__)) # Krylov Apdl Macro Files diff --git a/tests/test_launcher.py b/tests/test_launcher.py index 3cfc64763c..13c414f683 100644 --- a/tests/test_launcher.py +++ b/tests/test_launcher.py @@ -15,19 +15,11 @@ _parse_ip_route, _validate_MPI, _verify_version, - find_ansys, - get_default_ansys, launch_mapdl, update_env_vars, - version_from_path, ) from ansys.mapdl.core.licensing import LICENSES -from conftest import ( - QUICK_LAUNCH_SWITCHES, - skip_if_not_local, - skip_on_linux, - skip_on_windows, -) +from conftest import QUICK_LAUNCH_SWITCHES, requires try: import ansys_corba # noqa: F401 @@ -36,18 +28,28 @@ except: HAS_CORBA = False -# CORBA and console available versions -from ansys.tools.path import get_available_ansys_installations +try: + from ansys.tools.path import ( + find_ansys, + get_available_ansys_installations, + version_from_path, + ) -from ansys.mapdl.core._version import SUPPORTED_ANSYS_VERSIONS as versions + from ansys.mapdl.core.launcher import get_default_ansys -valid_versions = list(get_available_ansys_installations().keys()) + installed_mapdl_versions = list(get_available_ansys_installations().keys()) + try: + V150_EXEC = find_ansys("150")[0] + except ValueError: + V150_EXEC = "" +except: + from conftest import MAPDL_VERSION -try: - V150_EXEC = find_ansys("150")[0] -except ValueError: + installed_mapdl_versions = [MAPDL_VERSION] V150_EXEC = "" +from ansys.mapdl.core._version import SUPPORTED_ANSYS_VERSIONS as versions + paths = [ ("/usr/dir_v2019.1/slv/ansys_inc/v211/ansys/bin/ansys211", 211), ("C:/Program Files/ANSYS Inc/v202/ansys/bin/win64/ANSYS202.exe", 202), @@ -69,8 +71,8 @@ def fake_local_mapdl(mapdl): mapdl._local = False -@skip_if_not_local -@skip_on_linux +@requires("local") +@requires("windows") def test_validate_sw(): # ensure that windows adds msmpi # fake windows path @@ -85,21 +87,24 @@ def test_validate_sw(): assert "msmpi" in add_sw and "INTELMPI" not in add_sw -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") @pytest.mark.parametrize("path_data", paths) def test_version_from_path(path_data): exec_file, version = path_data assert version_from_path("mapdl", exec_file) == version -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") def test_catch_version_from_path(): with pytest.raises(RuntimeError): version_from_path("mapdl", "abc") -@skip_if_not_local -@skip_on_windows +@requires("ansys-tools-path") +@requires("local") +@requires("linux") def test_find_ansys_linux(): # assuming ansys is installed, should be able to find it on linux # without env var @@ -108,14 +113,16 @@ def test_find_ansys_linux(): assert isinstance(ver, float) -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") def test_invalid_mode(): with pytest.raises(ValueError): - exec_file = find_ansys(valid_versions[0])[0] + exec_file = find_ansys(installed_mapdl_versions[0])[0] pymapdl.launch_mapdl(exec_file, mode="notamode", start_timeout=start_timeout) -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") @pytest.mark.skipif(not os.path.isfile(V150_EXEC), reason="Requires v150") def test_old_version(): exec_file = find_ansys("150")[0] @@ -123,28 +130,31 @@ def test_old_version(): pymapdl.launch_mapdl(exec_file, mode="corba", start_timeout=start_timeout) -@skip_if_not_local -@skip_on_windows -@pytest.mark.console +@requires("ansys-tools-path") +@requires("local") +@requires("linux") +@requires("console") def test_failed_console(): - exec_file = find_ansys(valid_versions[0])[0] + exec_file = find_ansys(installed_mapdl_versions[0])[0] with pytest.raises(ValueError): pymapdl.launch_mapdl(exec_file, mode="console", start_timeout=start_timeout) -@skip_if_not_local -@pytest.mark.parametrize("version", valid_versions) -@pytest.mark.console -@skip_on_windows +@requires("ansys-tools-path") +@requires("local") +@requires("console") +@requires("linux") +@pytest.mark.parametrize("version", installed_mapdl_versions) def test_launch_console(version): exec_file = find_ansys(version)[0] mapdl = pymapdl.launch_mapdl(exec_file, mode="console", start_timeout=start_timeout) assert mapdl.version == int(version) / 10 -@skip_if_not_local -@pytest.mark.corba -@pytest.mark.parametrize("version", valid_versions) +@requires("ansys-tools-path") +@requires("local") +@requires("corba") +@pytest.mark.parametrize("version", installed_mapdl_versions) def test_launch_corba(version): mapdl = pymapdl.launch_mapdl( find_ansys(version)[0], mode="corba", start_timeout=start_timeout @@ -158,7 +168,7 @@ def test_launch_corba(version): assert mapdl_ref() is None -@skip_if_not_local +@requires("local") def test_license_type_keyword(): checks = [] for license_name, license_description in LICENSES.items(): @@ -176,7 +186,7 @@ def test_license_type_keyword(): assert any(checks) -@skip_if_not_local +@requires("local") def test_license_type_keyword_names(): # This test might became a way to check available licenses, which is not the purpose. @@ -198,7 +208,7 @@ def test_license_type_keyword_names(): assert successful_check # if at least one license is ok, this should be true. -@skip_if_not_local +@requires("local") def test_license_type_additional_switch(): # This test might became a way to check available licenses, which is not the purpose. successful_check = False @@ -217,7 +227,8 @@ def test_license_type_additional_switch(): assert successful_check # if at least one license is ok, this should be true. -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") def test_license_type_dummy(): dummy_license_type = "dummy" with pytest.raises(LicenseServerConnectionError): @@ -227,7 +238,7 @@ def test_license_type_dummy(): ) -@skip_if_not_local +@requires("local") def test_remove_temp_files(): """Ensure the working directory is removed when run_location is not set.""" mapdl = launch_mapdl( @@ -248,7 +259,7 @@ def test_remove_temp_files(): assert os.path.isdir(path) -@skip_if_not_local +@requires("local") def test_remove_temp_files_fail(tmpdir): """Ensure the working directory is not removed when the cwd is changed.""" mapdl = launch_mapdl( @@ -374,22 +385,23 @@ def test_license_product_argument_p_arg_warning(): assert "qwer -p asdf" in _check_license_argument(None, "qwer -p asdf") -valid_versions = [] -valid_versions.extend(list(versions.keys())) -valid_versions.extend([each / 10 for each in versions.keys()]) -valid_versions.extend([str(each) for each in list(versions.keys())]) -valid_versions.extend([str(each / 10) for each in versions.keys()]) -valid_versions.extend(list(versions.values())) +installed_mapdl_versions = [] +installed_mapdl_versions.extend(list(versions.keys())) +installed_mapdl_versions.extend([each / 10 for each in versions.keys()]) +installed_mapdl_versions.extend([str(each) for each in list(versions.keys())]) +installed_mapdl_versions.extend([str(each / 10) for each in versions.keys()]) +installed_mapdl_versions.extend(list(versions.values())) -@pytest.mark.parametrize("version", valid_versions) +@pytest.mark.parametrize("version", installed_mapdl_versions) def test__verify_version_pass(version): ver = _verify_version(version) assert isinstance(ver, int) assert min(versions.keys()) <= ver <= max(versions.keys()) -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") def test_find_ansys(mapdl): assert find_ansys() is not None @@ -405,7 +417,7 @@ def test_find_ansys(mapdl): assert find_ansys(version="11") -@skip_if_not_local +@requires("local") def test_version(mapdl): version = int(10 * mapdl.version) mapdl_ = launch_mapdl( @@ -416,7 +428,7 @@ def test_version(mapdl): mapdl_.exit() -@skip_if_not_local +@requires("local") def test_raise_exec_path_and_version_launcher(): with pytest.raises(ValueError): launch_mapdl( @@ -427,13 +439,14 @@ def test_raise_exec_path_and_version_launcher(): ) -@skip_on_windows -@skip_if_not_local +@requires("linux") +@requires("local") def test_is_ubuntu(): assert _is_ubuntu() -@skip_if_not_local +@requires("ansys-tools-path") +@requires("local") def test_get_default_ansys(): assert get_default_ansys() is not None diff --git a/tests/test_launcher_remote.py b/tests/test_launcher_remote.py index cdf4a23f87..86c1067652 100644 --- a/tests/test_launcher_remote.py +++ b/tests/test_launcher_remote.py @@ -1,4 +1,11 @@ """Test the PyPIM integration.""" +import pytest + +from conftest import has_dependency + +if not has_dependency("ansys-platform-instancemanagement"): + pytest.skip(allow_module_level=True) + from unittest.mock import create_autospec import ansys.platform.instancemanagement as pypim diff --git a/tests/test_licensing.py b/tests/test_licensing.py index b9950f82ed..ec62eed25e 100644 --- a/tests/test_licensing.py +++ b/tests/test_licensing.py @@ -9,7 +9,7 @@ from ansys.mapdl.core import errors, launch_mapdl, licensing from ansys.mapdl.core.misc import threaded from conftest import ON_LOCAL as IS_LOCAL -from conftest import QUICK_LAUNCH_SWITCHES, skip_if_not_local +from conftest import QUICK_LAUNCH_SWITCHES, requires try: LIC_INSTALLED = os.path.isfile(licensing.get_ansys_license_utility_path()) @@ -141,7 +141,7 @@ def test_get_ansys_license_debug_file_tail(tmpdir, license_checker): assert "CHECKOUT" in line -@skip_if_not_local +@requires("local") @skip_no_lic_bin def test_check_license_file_fail(license_checker): with pytest.raises(TimeoutError): @@ -158,7 +158,7 @@ def test_license_checker(tmpdir, license_checker): assert license_checker.check() -@skip_if_not_local +@requires("local") @skip_no_lic_bin def test_check_license_file(tmpdir): timeout = 15 @@ -238,6 +238,7 @@ def test_check_license_file_exception(license_checker): license_checker._check_license_file(0.01) +@requires("ansys-tools-path") def test_license_wait(): license_checker = licensing.LicenseChecker() assert not license_checker._lic_file_thread @@ -272,6 +273,7 @@ def test_license_check(): license_checker.check() +@requires("ansys-tools-path") def test_stop_license_checker(): license_checker = licensing.LicenseChecker() @@ -284,6 +286,7 @@ def test_stop_license_checker(): assert not license_checker._lic_file_thread.is_alive() +@requires("ansys-tools-path") def test_is_connected_license_checker(): license_checker = licensing.LicenseChecker() diff --git a/tests/test_logging.py b/tests/test_logging.py index 8ad8b81701..b788d5f097 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -7,7 +7,7 @@ from ansys.mapdl.core import LOG # Global logger from ansys.mapdl.core import logging -from conftest import skip_if_no_has_grpc +from conftest import requires ## Notes # Use the next fixtures for: @@ -172,7 +172,7 @@ def test_global_logger_debug_levels(caplog): ) -@skip_if_no_has_grpc +@requires("grpc") def test_global_logger_format(): # Since we cannot read the format of our logger, because pytest just dont show the console output or # if it does, it formats the logger with its own formatter, we are going to check the logger handlers @@ -195,7 +195,7 @@ def test_global_logger_format(): assert "This is a message" in log -@skip_if_no_has_grpc +@requires("grpc") def test_instance_logger_format(mapdl): # Since we cannot read the format of our logger, because pytest just dont show the console output or # if it does, it formats the logger with its own formatter, we are going to check the logger handlers diff --git a/tests/test_mapdl.py b/tests/test_mapdl.py index 063b878162..bab5602e29 100644 --- a/tests/test_mapdl.py +++ b/tests/test_mapdl.py @@ -6,18 +6,22 @@ import shutil import time -from ansys.mapdl.reader import examples -from ansys.mapdl.reader.rst import Result import grpc import numpy as np import psutil import pytest -from pyvista import MultiBlock + +from conftest import has_dependency + +if has_dependency("pyvista"): + from pyvista import MultiBlock + +if has_dependency("ansys-mapdl-reader"): + from ansys.mapdl.reader.rst import Result from ansys.mapdl import core as pymapdl from ansys.mapdl.core.commands import CommandListingOutput from ansys.mapdl.core.errors import ( - DifferentSessionConnectionError, IncorrectWorkingDirectory, MapdlCommandIgnoredError, MapdlConnectionError, @@ -26,14 +30,11 @@ from ansys.mapdl.core.launcher import launch_mapdl from ansys.mapdl.core.mapdl_grpc import SESSION_ID_NAME from ansys.mapdl.core.misc import random_string -from conftest import ( - IS_SMP, - ON_LOCAL, - QUICK_LAUNCH_SWITCHES, - skip_if_not_local, - skip_if_on_cicd, - skip_on_windows, -) +from conftest import IS_SMP, ON_CI, ON_LOCAL, QUICK_LAUNCH_SWITCHES, requires + +# Path to files needed for examples +PATH = os.path.dirname(os.path.abspath(__file__)) +test_files = os.path.join(PATH, "test_files") CMD_BLOCK = """/prep7 ! Mat @@ -156,7 +157,7 @@ def warns_in_cdread_error_log(mapdl, tmpdir): return any(warns) -@pytest.mark.skip_grpc +@requires("grpc") def test_internal_name_grpc(mapdl): assert str(mapdl._ip) in mapdl.name assert str(mapdl._port) in mapdl.name @@ -181,7 +182,7 @@ def test_jobname(mapdl, cleared): assert mapdl.jobname == other_jobname -@pytest.mark.skip_grpc +@requires("grpc") def test_server_version(mapdl): if mapdl.version == 20.2: assert mapdl._server_version == (0, 0, 0) @@ -196,7 +197,7 @@ def test_server_version(mapdl): assert mapdl._server_version[0] >= 0 -@pytest.mark.skip_grpc +@requires("grpc") def test_global_mute(mapdl): mapdl.mute = True assert mapdl.mute is True @@ -218,7 +219,7 @@ def test_parsav_parres(mapdl, cleared, tmpdir): assert np.allclose(mapdl.parameters["MYARR"], arr) -@pytest.mark.skip_grpc +@requires("grpc") def test_no_results(mapdl, cleared, tmpdir): pth = str(tmpdir.mkdir("tmpdir")) mapdl.jobname = random_string() @@ -306,7 +307,8 @@ def test_allow_ignore(mapdl): mapdl.allow_ignore = True assert mapdl.allow_ignore is True mapdl.k() - assert mapdl.geometry.n_keypoint == 0 + with pytest.raises(ValueError, match="There are no KEYPOINTS defined"): + mapdl.get_value("KP", 0, "count") mapdl.allow_ignore = False @@ -347,7 +349,7 @@ def test_ignore_error(mapdl): assert mapdl.ignore_error is False -@pytest.mark.skip_grpc +@requires("grpc") def test_list(mapdl, tmpdir): """Added for backwards compatibility""" fname = "tmp.txt" @@ -361,7 +363,7 @@ def test_list(mapdl, tmpdir): assert output == txt -@pytest.mark.skip_grpc +@requires("grpc") def test_invalid_input(mapdl): with pytest.raises(FileNotFoundError): mapdl.input("thisisnotafile") @@ -378,11 +380,14 @@ def test_keypoints(cleared, mapdl): knum.append(i + 1) assert mapdl.geometry.n_keypoint == 4 - assert isinstance(mapdl.geometry.keypoints, MultiBlock) - assert np.allclose(kps, mapdl.geometry.get_keypoints(return_as_array=True)) assert np.allclose(knum, mapdl.geometry.knum) + if has_dependency("pyvista"): + assert isinstance(mapdl.geometry.keypoints, MultiBlock) + assert np.allclose(kps, mapdl.geometry.get_keypoints(return_as_array=True)) + +@requires("pyvista") def test_lines(cleared, mapdl): assert mapdl.geometry.n_line == 0 @@ -396,12 +401,14 @@ def test_lines(cleared, mapdl): l3 = mapdl.l(k3, k0) lines = mapdl.geometry.lines - assert isinstance(lines, MultiBlock) assert np.allclose(mapdl.geometry.lnum, [l0, l1, l2, l3]) assert mapdl.geometry.n_line == 4 + if has_dependency("pyvista"): + assert isinstance(lines, MultiBlock) + -@skip_if_not_local +@requires("local") def test_apdl_logging_start(tmpdir): filename = str(tmpdir.mkdir("tmpdir").join("tmp.inp")) @@ -429,7 +436,7 @@ def test_apdl_logging_start(tmpdir): assert "K,4,0,1,0" in text -@pytest.mark.corba +@requires("corba") def test_corba_apdl_logging_start(tmpdir): filename = str(tmpdir.mkdir("tmpdir").join("tmp.inp")) @@ -692,6 +699,7 @@ def test_partial_mesh_nnum(mapdl, make_block): assert np.allclose(allsel_nnum_old, mapdl.mesh.nnum) +@requires("pyvista") def test_partial_mesh_nnum2(mapdl, make_block): mapdl.nsel("S", "NODE", vmin=1, vmax=10) mapdl.esel("S", "ELEM", vmin=10, vmax=20) @@ -702,7 +710,7 @@ def test_cyclic_solve(mapdl, cleared): # build the cyclic model mapdl.prep7() mapdl.shpp("off") - mapdl.cdread("db", examples.sector_archive_file) + mapdl.cdread("db", os.path.join(test_files, "sector.cdb")) mapdl.prep7() time.sleep(1.0) mapdl.cyclic() @@ -785,7 +793,7 @@ def test_load_array_failure_types(mapdl, array): assert mapdl.parameters["myarr"].ndim == array.ndim + 1 -@pytest.mark.skip_grpc +@requires("grpc") def test_lssolve(mapdl, cleared): mapdl.mute = True @@ -881,7 +889,7 @@ def test_cdread(mapdl, cleared): mapdl.cdread("test", "model2", "cdb") -@skip_if_not_local +@requires("local") def test_cdread_different_location(mapdl, cleared, tmpdir): random_letters = mapdl.directory.split("/")[0][-3:0] dirname = "tt" + random_letters @@ -1027,7 +1035,7 @@ def test_inval_commands_silent(mapdl, tmpdir, cleared): mapdl._run("/gopr") # getting settings back -@skip_if_not_local +@requires("local") def test_path_without_spaces(mapdl, path_tests): old_path = mapdl.directory try: @@ -1037,7 +1045,7 @@ def test_path_without_spaces(mapdl, path_tests): mapdl.directory = old_path -@skip_if_not_local +@requires("local") def test_path_with_spaces(mapdl, path_tests): old_path = mapdl.directory try: @@ -1047,7 +1055,7 @@ def test_path_with_spaces(mapdl, path_tests): mapdl.directory = old_path -@skip_if_not_local +@requires("local") def test_path_with_single_quote(mapdl, path_tests): with pytest.raises(MapdlRuntimeError): mapdl.cwd(path_tests.path_with_single_quote) @@ -1077,7 +1085,8 @@ def test_cwd(mapdl, tmpdir): mapdl.cwd(old_path) -@skip_if_on_cicd +@requires("nocicd") +@requires("local") def test_inquire(mapdl): # Testing basic functions (First block: Functions) assert "apdl" in mapdl.inquire("", "apdl").lower() @@ -1110,6 +1119,7 @@ def test_ksel(mapdl, cleared): mapdl.prep7() assert "SELECTED" in mapdl.ksel("S", "KP", vmin=1, return_mapdl_output=True) assert "SELECTED" in mapdl.ksel("S", "KP", "", 1, return_mapdl_output=True) + assert 1 in mapdl.ksel("S", "KP", vmin=1) @@ -1304,7 +1314,10 @@ def test_mpfunctions(mapdl, cube_solve, capsys): captured = capsys.readouterr() # To flush it output = mapdl.mpread(fname, ext) captured = capsys.readouterr() - assert f"Uploading {fname}.{ext}:" in captured.err + if has_dependency("tqdm"): + # Printing uploading requires tqdm + assert f"Uploading {fname}.{ext}:" in captured.err + assert "PROPERTY TEMPERATURE TABLE NUM. TEMPS= 1" in output assert "TEMPERATURE TABLE ERASED." in output assert "0.4000000" in output @@ -1359,7 +1372,7 @@ def test_result_file(mapdl, solved_box): assert isinstance(mapdl.result_file, str) -@skip_if_not_local +@requires("local") def test_file_command_local(mapdl, cube_solve, tmpdir): rst_file = mapdl.result_file @@ -1422,7 +1435,7 @@ def test_file_command_remote(mapdl, cube_solve, tmpdir): assert "DATA FILE CHANGED TO FILE" in output -@skip_on_windows +@requires("linux") def test_lgwrite(mapdl, cleared, tmpdir): filename = str(tmpdir.join("file.txt")) @@ -1493,7 +1506,7 @@ def test_non_interactive(mapdl, cleared): mapdl.k(1, 1, 1, 1) mapdl.k(2, 2, 2, 2) - assert len(mapdl.geometry.keypoints) == 2 + assert mapdl.get_value("KP", 0, "count") == 2 def test_ignored_command(mapdl, cleared): @@ -1578,7 +1591,7 @@ def test_set_list(mapdl, cube_solve): def test_mode(mapdl): - assert mapdl.mode == "grpc" + assert mapdl.connection == "grpc" assert mapdl.is_grpc assert not mapdl.is_corba assert not mapdl.is_console @@ -1720,7 +1733,7 @@ def test_get_file_name(mapdl): ) -@skip_if_not_local +@requires("local") def test_cache_pids(mapdl): assert mapdl._pids mapdl._cache_pids() # Recache pids @@ -1729,7 +1742,7 @@ def test_cache_pids(mapdl): assert "ansys" in "".join(psutil.Process(each).cmdline()) -@skip_if_not_local +@requires("local") def test_process_is_alive(mapdl): assert mapdl.process_is_alive @@ -1785,18 +1798,6 @@ def test_session_id(mapdl, running_test): mapdl._session_id_ = id_ -def test_session_id_different(mapdl, running_test): - # Assert it works - with running_test(): - assert mapdl.prep7() - - mapdl._run(f"{SESSION_ID_NAME}='1234'") - - with running_test(): - with pytest.raises(DifferentSessionConnectionError): - mapdl.prep7() - - def test_check_empty_session_id(mapdl): # it should run normal mapdl._session_id_ = None @@ -1805,6 +1806,7 @@ def test_check_empty_session_id(mapdl): assert mapdl.prep7() +@requires("requests") # Requires 'requests' package def test_igesin_whitespace(mapdl, cleared, tmpdir): bracket_file = pymapdl.examples.download_bracket() assert os.path.isfile(bracket_file) @@ -1822,7 +1824,7 @@ def test_igesin_whitespace(mapdl, cleared, tmpdir): assert int(n_ent[0]) > 0 -@skip_if_not_local +@requires("local") def test_save_on_exit(mapdl, cleared): mapdl2 = launch_mapdl( license_server_check=False, additional_switches=QUICK_LAUNCH_SWITCHES @@ -1920,6 +1922,7 @@ def num_(): assert [1, 2, 4] == mapdl.mesh.rlblock_num +@requires("ansys-mapdl-reader") def test_download_results_non_local(mapdl, cube_solve): assert mapdl.result is not None assert isinstance(mapdl.result, Result) @@ -1971,7 +1974,7 @@ def test_port(mapdl): def test_distributed(mapdl): - if IS_SMP and not ON_LOCAL: + if ON_CI and IS_SMP and not ON_LOCAL: assert not mapdl._distributed else: assert mapdl._distributed @@ -1995,3 +1998,122 @@ def test_non_valid_kwarg(mapdl): with pytest.warns(UserWarning): mapdl.cdwrite(options="DB", fname="test1", ext="cdb") + + +def test_check_parameter_names(mapdl): + with pytest.raises(ValueError): + mapdl.parameters["_dummy"] = 1 + + mapdl.check_parameter_names = False + mapdl.parameters["_dummy"] = 1 + mapdl.check_parameter_names = True # returning to default + + +@requires("pyvista") +def test_components_selection_keep_between_plots(mapdl, cube_solve): + mapdl.cm("mycm", "volu") + assert "MYCM" in mapdl.cmlist() + assert "mycm" in mapdl.components + + mapdl.vplot() + + assert "MYCM" in mapdl.cmlist() + assert "mycm" in mapdl.components + + +def test_saving_selection_context(mapdl, cube_solve): + mapdl.allsel() + + for i in range(1, 4): + mapdl.nsel("s", "", "", i) + mapdl.cm(f"nod_selection_{i}", "node") + + for i in range(1, 4): + mapdl.esel("s", "", "", i) + mapdl.cm(f"elem_selection_{i}", "elem") + + mapdl.cmsel("s", "nod_selection_1") + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + mapdl.cmsel("a", "elem_selection_1") + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + with mapdl.save_selection: + mapdl.cmsel("a", "nod_selection_2") + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + + mapdl.cmsel("a", "elem_selection_2") + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + assert "elem_selection_2".upper() in mapdl.cmlist() + assert "elem_selection_2" in mapdl.components + + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + mapdl.nsel("s", "", "", 4) + mapdl.cm("nod_selection_4", "node") + + with mapdl.save_selection: + mapdl.cmsel("s", "nod_selection_3") + assert "nod_selection_3".upper() in mapdl.cmlist() + assert "nod_selection_3" in mapdl.components + + mapdl.cmsel("a", "elem_selection_3") + assert "elem_selection_3".upper() in mapdl.cmlist() + assert "elem_selection_3" in mapdl.components + assert "nod_selection_3".upper() in mapdl.cmlist() + assert "nod_selection_3" in mapdl.components + + # Erased because the previous cmsel("s") + assert "nod_selection_4".upper() not in mapdl.cmlist() + assert "nod_selection_4" not in mapdl.components + + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + assert "elem_selection_2".upper() not in mapdl.cmlist() + assert "elem_selection_2" not in mapdl.components + + assert "elem_selection_1".upper() not in mapdl.cmlist() + assert "elem_selection_1" not in mapdl.components + assert "nod_selection_1".upper() not in mapdl.cmlist() + assert "nod_selection_1" not in mapdl.components + + # Checking correctly exiting contexts + assert "nod_selection_2".upper() in mapdl.cmlist() + assert "nod_selection_2" in mapdl.components + assert "elem_selection_2".upper() in mapdl.cmlist() + assert "elem_selection_2" in mapdl.components + + assert "elem_selection_3".upper() not in mapdl.cmlist() + assert "elem_selection_3" not in mapdl.components + assert "nod_selection_3".upper() not in mapdl.cmlist() + assert "nod_selection_3" not in mapdl.components + + assert "nod_selection_4".upper() in mapdl.cmlist() + assert "nod_selection_4" in mapdl.components + + assert "elem_selection_1".upper() in mapdl.cmlist() + assert "elem_selection_1" in mapdl.components + assert "nod_selection_1".upper() in mapdl.cmlist() + assert "nod_selection_1" in mapdl.components + + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + assert "nod_selection_2".upper() not in mapdl.cmlist() + assert "nod_selection_2" not in mapdl.components + + assert "elem_selection_3".upper() not in mapdl.cmlist() + assert "elem_selection_3" not in mapdl.components + assert "nod_selection_3".upper() not in mapdl.cmlist() + assert "nod_selection_3" not in mapdl.components + + assert "nod_selection_4".upper() not in mapdl.cmlist() + assert "nod_selection_4" not in mapdl.components diff --git a/tests/test_mesh_grpc.py b/tests/test_mesh_grpc.py index 4afc487f8c..40de300285 100644 --- a/tests/test_mesh_grpc.py +++ b/tests/test_mesh_grpc.py @@ -3,7 +3,11 @@ import numpy as np import pytest -import pyvista as pv + +from conftest import has_dependency, requires + +if has_dependency("pyvista"): + import pyvista as pv from ansys.mapdl.core import examples @@ -58,6 +62,7 @@ def test_local(mapdl): assert mapdl._local == mapdl.mesh.local +@pytest.mark.xfail(strict=False, reason="Flaky test. See #2435") def test_empty_mesh(mapdl, cleared): assert mapdl.mesh.n_node == 0 assert mapdl.mesh.n_elem == 0 @@ -93,9 +98,10 @@ def test_empty_mesh(mapdl, cleared): assert not mapdl.mesh._has_nodes # Others - assert mapdl.mesh.grid is None - with pytest.raises(ValueError): - mapdl.mesh.save("file.vtk") + if has_dependency("pyvista"): + assert mapdl.mesh.grid is None + with pytest.raises(ValueError): + mapdl.mesh.save("file.vtk") def test_non_empty_mesh(mapdl, contact_geom_and_mesh): @@ -136,9 +142,11 @@ def test_non_empty_mesh(mapdl, contact_geom_and_mesh): assert mapdl.mesh._has_nodes # Others - assert isinstance(mapdl.mesh.grid, pv.UnstructuredGrid) - assert mapdl.mesh.grid.n_cells > 0 - assert mapdl.mesh.grid.n_points > 0 + if has_dependency("pyvista"): + assert isinstance(mapdl.mesh.grid, pv.UnstructuredGrid) + + assert mapdl.mesh.grid.n_cells > 0 + assert mapdl.mesh.grid.n_points > 0 def test_tshape_key(mapdl, contact_geom_and_mesh): @@ -151,6 +159,7 @@ def test_tshape_key(mapdl, contact_geom_and_mesh): assert tshape.size > 0 +@requires("pyvista") def test_save(mapdl, cube_geom_and_mesh): # This test seems to fail when parallelized. fname = "mesh.vtk" diff --git a/tests/test_misc.py b/tests/test_misc.py index 3d7d9e32df..fc1bdd713d 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -4,7 +4,11 @@ import numpy as np import pytest -from pyvista.plotting import system_supports_plotting + +from conftest import has_dependency, requires + +if has_dependency("pyvista"): + from pyvista.plotting import system_supports_plotting from ansys.mapdl import core as pymapdl from ansys.mapdl.core.misc import ( @@ -20,6 +24,7 @@ ) +@requires("pyvista") def test_report(): report = pymapdl.Report( additional=["matplotlib", "pyvista", "pyiges", "tqdm"], @@ -291,7 +296,21 @@ def test_plain_report(): # There should be only one package not found ("ger") assert "Package not found" in rep_str - _rep_str = rep_str.replace("Package not found", "", 1) + not_found_packages = 1 + + # Plus the not additional packages + if not has_dependency("pyvista"): + not_found_packages += 1 + if not has_dependency("tqdm"): + not_found_packages += 1 + if not has_dependency("ansys.mapdl.reader"): + not_found_packages += 1 + if not has_dependency("scipy"): + not_found_packages += 1 + if not has_dependency("pexpect"): + not_found_packages += 1 + + _rep_str = rep_str.replace("Package not found", "", not_found_packages) assert "Package not found" not in _rep_str assert "\n" in rep_str diff --git a/tests/test_plotting.py b/tests/test_plotting.py index ab6199a04c..d0710e4679 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -3,6 +3,12 @@ import numpy as np import pytest + +from conftest import has_dependency + +if not has_dependency("pyvista"): + pytest.skip(allow_module_level=True) + from pyvista.plotting import Plotter from ansys.mapdl.core.errors import ComponentDoesNotExits diff --git a/tests/test_pool.py b/tests/test_pool.py index ae8cd83250..2e18ee4303 100644 --- a/tests/test_pool.py +++ b/tests/test_pool.py @@ -2,16 +2,28 @@ from pathlib import Path import time -from ansys.tools.path import find_ansys import numpy as np import pytest +from conftest import has_dependency + +if has_dependency("ansys-tools-path"): + from ansys.tools.path import find_ansys + + EXEC_FILE = find_ansys()[0] + +else: + EXEC_FILE = os.environ.get("PYMAPDL_MAPDL_EXEC") + +if not EXEC_FILE: + pytest.skip(allow_module_level=True) + from ansys.mapdl.core import LocalMapdlPool, examples from ansys.mapdl.core.errors import VersionError -from conftest import QUICK_LAUNCH_SWITCHES, skip_if_not_local +from conftest import QUICK_LAUNCH_SWITCHES, requires # skip entire module unless HAS_GRPC -pytestmark = pytest.mark.skip_grpc +pytestmark = requires("grpc") skip_if_ignore_pool = pytest.mark.skipif( @@ -31,7 +43,6 @@ @pytest.fixture(scope="module") def pool(tmpdir_factory): - EXEC_FILE = find_ansys()[0] run_path = str(tmpdir_factory.mktemp("ansys_pool")) mapdl_pool = LocalMapdlPool( @@ -77,7 +88,8 @@ def test_invalid_exec(): ) -@skip_if_not_local +@pytest.mark.xfail(strict=False, reason="Flaky test. See #2435") +@requires("local") def test_heal(pool): pool_sz = len(pool) pool[0].exit() @@ -95,7 +107,7 @@ def test_heal(pool): pool._verify_unique_ports() -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_simple_map(pool): pool_sz = len(pool) @@ -103,7 +115,7 @@ def test_simple_map(pool): assert len(pool) == pool_sz -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_map_timeout(pool): pool_sz = len(pool) @@ -130,7 +142,7 @@ def func(mapdl, tsleep): assert len(pool) == pool_sz -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_simple(pool): pool_sz = len(pool) @@ -144,7 +156,7 @@ def func(mapdl): # fails intermittently -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_batch(pool): input_files = [examples.vmfiles["vm%d" % i] for i in range(1, 11)] @@ -153,7 +165,7 @@ def test_batch(pool): # fails intermittently -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_map(pool): completed_indices = [] @@ -167,12 +179,12 @@ def func(mapdl, input_file, index): return mapdl.parameters.routine inputs = [(examples.vmfiles["vm%d" % i], i) for i in range(1, 11)] - outputs = pool.map(func, inputs, progress_bar=True, wait=True) + outputs = pool.map(func, inputs, wait=True) assert len(outputs) == len(inputs) -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_abort(pool, tmpdir): pool_sz = len(pool) # initial pool size @@ -207,7 +219,7 @@ def test_abort(pool, tmpdir): assert path_deleted -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_directory_names_default(pool): dirs_path_pool = os.listdir(pool._root_dir) @@ -217,11 +229,12 @@ def test_directory_names_default(pool): assert "Instance_3" in dirs_path_pool -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_directory_names_custom_string(tmpdir): pool = LocalMapdlPool( 2, + exec_file=EXEC_FILE, run_location=tmpdir, nproc=NPROC, names="my_instance", @@ -236,7 +249,7 @@ def test_directory_names_custom_string(tmpdir): pool.exit(block=True) -@skip_if_not_local +@requires("local") @skip_if_ignore_pool def test_directory_names_function(tmpdir): def myfun(i): @@ -249,6 +262,7 @@ def myfun(i): pool = LocalMapdlPool( 3, + exec_file=EXEC_FILE, nproc=NPROC, names=myfun, run_location=tmpdir, diff --git a/tests/test_post.py b/tests/test_post.py index 2edd736220..387f662fa6 100644 --- a/tests/test_post.py +++ b/tests/test_post.py @@ -4,8 +4,12 @@ import numpy as np import pytest -from pyvista import Plotter -from pyvista.plotting.renderer import CameraPosition + +from conftest import has_dependency, requires + +if has_dependency("pyvista"): + from pyvista import Plotter + from pyvista.plotting.renderer import CameraPosition from ansys.mapdl.core import examples from ansys.mapdl.core.post import ( @@ -151,12 +155,14 @@ def test_disp_norm_all(mapdl, static_solve): @pytest.mark.parametrize("comp", ["X", "Y", "z", "norm"]) # lowercase intentional +@requires("pyvista") def test_disp_plot(mapdl, static_solve, comp): assert ( mapdl.post_processing.plot_nodal_displacement(comp, smooth_shading=True) is None ) +@requires("pyvista") def test_disp_plot_subselection(mapdl, static_solve, verify_image_cache): verify_image_cache.skip = True # skipping image verification @@ -171,9 +177,7 @@ def test_disp_plot_subselection(mapdl, static_solve, verify_image_cache): mapdl.allsel() -def test_nodal_eqv_stress(mapdl, static_solve, verify_image_cache): - verify_image_cache.skip = True # skipping image verification - +def test_nodal_eqv_stress(mapdl, static_solve): mapdl.post1(mute=True) mapdl.set(1, 1, mute=True) @@ -187,6 +191,7 @@ def test_nodal_eqv_stress(mapdl, static_solve, verify_image_cache): assert np.allclose(seqv_ans, seqv_aligned) +@requires("pyvista") def test_plot_nodal_eqv_stress(mapdl, static_solve, verify_image_cache): verify_image_cache.skip = True # skipping image verification @@ -226,6 +231,7 @@ def test_rot(mapdl, static_solve, comp): @pytest.mark.parametrize("comp", ["X", "Y", "z"]) # lowercase intentional +@requires("pyvista") def test_plot_rot(mapdl, static_solve, comp): assert mapdl.post_processing.plot_nodal_rotation(comp) is None @@ -244,11 +250,13 @@ def test_element_temperature(mapdl, static_solve): assert np.allclose(values, 0) +@requires("pyvista") def test_plot_element_temperature(mapdl, static_solve): mapdl.set(1, 1, mute=True) assert mapdl.post_processing.plot_element_temperature() is None +@requires("pyvista") def test_plot_temperature(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_temperature() is None @@ -259,6 +267,7 @@ def test_pressure(mapdl, static_solve): assert np.allclose(from_grpc, 0) +@requires("pyvista") def test_plot_pressure(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_pressure() is None @@ -269,6 +278,7 @@ def test_voltage(mapdl, static_solve): assert np.allclose(from_grpc, 0) +@requires("pyvista") def test_plot_voltage(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_voltage() is None @@ -290,6 +300,7 @@ def test_nodal_component_stress(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_component_stress(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_component_stress("X") is None @@ -311,6 +322,7 @@ def test_nodal_principal_stress(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_principal_stress(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_principal_stress(1) is None @@ -329,6 +341,7 @@ def test_nodal_stress_intensity(mapdl, static_solve): assert np.allclose(sint_ans, sint_aligned) +@requires("pyvista") def test_plot_nodal_stress_intensity(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_stress_intensity() is None @@ -350,6 +363,7 @@ def test_nodal_total_component_strain(mapdl, static_solve, comp): assert np.allclose(data_ans, data) +@requires("pyvista") def test_plot_nodal_total_component_strain(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_total_component_strain("x") is None @@ -371,6 +385,7 @@ def test_nodal_principal_total_strain(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_principal_total_strain(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_total_principal_strain(1) is None @@ -389,6 +404,7 @@ def test_nodal_total_strain_intensity(mapdl, static_solve): assert np.allclose(sint_ans, sint_aligned) +@requires("pyvista") def test_plot_nodal_total_strain_intensity(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_total_strain_intensity() is None @@ -407,6 +423,7 @@ def test_nodal_total_eqv_strain(mapdl, static_solve): assert np.allclose(seqv_ans, seqv_aligned) +@requires("pyvista") def test_plot_nodal_total_eqv_strain(mapdl, static_solve): assert ( mapdl.post_processing.plot_nodal_total_eqv_strain(smooth_shading=True) is None @@ -431,6 +448,7 @@ def test_nodal_component_stress(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_component_stress(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_component_stress("X") is None @@ -451,6 +469,7 @@ def test_nodal_principal_stress(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns, atol=1e-5) +@requires("pyvista") def test_plot_nodal_principal_stress(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_principal_stress(1) is None @@ -469,6 +488,7 @@ def test_nodal_stress_intensity(mapdl, static_solve): assert np.allclose(sint_ans, sint_aligned) +@requires("pyvista") def test_plot_nodal_stress_intensity(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_stress_intensity() is None @@ -490,6 +510,7 @@ def test_nodal_elastic_component_strain(mapdl, static_solve, comp): assert np.allclose(data_ans, data) +@requires("pyvista") def test_plot_nodal_elastic_component_strain(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_elastic_component_strain("x") is None @@ -511,6 +532,7 @@ def test_nodal_elastic_principal_strain(mapdl, static_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_elastic_principal_strain(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_elastic_principal_strain(1) is None @@ -529,6 +551,7 @@ def test_nodal_elastic_strain_intensity(mapdl, static_solve): assert np.allclose(sint_ans, sint_aligned) +@requires("pyvista") def test_plot_nodal_elastic_strain_intensity(mapdl, static_solve): assert mapdl.post_processing.plot_nodal_elastic_strain_intensity() is None @@ -547,6 +570,7 @@ def test_nodal_elastic_eqv_strain(mapdl, static_solve): assert np.allclose(seqv_ans, seqv_aligned) +@requires("pyvista") def test_plot_nodal_elastic_eqv_strain(mapdl, static_solve): assert ( mapdl.post_processing.plot_nodal_elastic_eqv_strain(smooth_shading=True) is None @@ -595,6 +619,7 @@ def test_elem_disp_norm(mapdl, static_solve): @pytest.mark.parametrize("comp", ["X", "Y", "Z", "NORM"]) +@requires("pyvista") def test_elem_disp_plot(mapdl, static_solve, comp): mapdl.post1(mute=True) mapdl.set(1, 1, mute=True) @@ -616,12 +641,14 @@ def test_element_stress(mapdl, static_solve, component, option): @pytest.mark.parametrize("comp", ["X", "1", "INT", "EQV"]) +@requires("pyvista") def test_plot_element_stress(mapdl, static_solve, comp): mapdl.post1(mute=True) mapdl.set(1, 1, mute=True) assert mapdl.post_processing.plot_element_stress(comp) is None +@requires("pyvista") def test_plot_element_values(mapdl, static_solve, verify_image_cache): verify_image_cache.high_variance_test = 600 mapdl.post1(mute=True) @@ -646,6 +673,7 @@ def test_nodal_plastic_component_strain(mapdl, plastic_solve, comp): assert np.allclose(data_ans, data) +@requires("pyvista") def test_plot_nodal_plastic_component_strain(mapdl, plastic_solve): assert mapdl.post_processing.plot_nodal_plastic_component_strain("x") is None @@ -666,6 +694,7 @@ def test_nodal_plastic_principal_strain(mapdl, plastic_solve, comp): assert np.allclose(from_grpc, from_prns) +@requires("pyvista") def test_plot_nodal_plastic_principal_strain(mapdl, plastic_solve): assert mapdl.post_processing.plot_nodal_plastic_principal_strain(1) is None @@ -681,6 +710,7 @@ def test_nodal_plastic_strain_intensity(mapdl, plastic_solve): assert np.allclose(sint_ans, sint_aligned) +@requires("pyvista") def test_plot_nodal_plastic_strain_intensity(mapdl, plastic_solve): assert mapdl.post_processing.plot_nodal_plastic_strain_intensity() is None @@ -696,6 +726,7 @@ def test_nodal_plastic_eqv_strain(mapdl, plastic_solve): assert np.allclose(seqv_ans, seqv_aligned) +@requires("pyvista") def test_plot_nodal_plastic_eqv_strain(mapdl, plastic_solve): assert ( mapdl.post_processing.plot_nodal_plastic_eqv_strain(smooth_shading=True) is None @@ -721,6 +752,7 @@ def test_nodal_contact_friction_stress(mapdl, contact_solve): assert np.allclose(sfric_prn, sfric_nod) +@requires("pyvista") def test_plot_nodal_contact_friction_stress(mapdl, contact_solve): assert ( mapdl.post_processing.plot_nodal_contact_friction_stress(smooth_shading=True) @@ -728,16 +760,51 @@ def test_plot_nodal_contact_friction_stress(mapdl, contact_solve): ) +@requires("pyvista") def test_plot_incomplete_element_selection(mapdl, contact_solve): mapdl.esel("S", "ELEM", "", 1, mapdl.mesh.n_elem // 2) assert mapdl.post_processing.plot_element_displacement() is None + mapdl.nsel("S", "NODE", "", 1, mapdl.mesh.n_elem // 2, 2) + assert mapdl.post_processing.plot_element_displacement() is None + + mapdl.nsel("S", "NODE", "", 5, mapdl.mesh.n_elem // 2, 2) + assert mapdl.post_processing.plot_element_displacement() is None + + mapdl.vsel("s", "", "", 1) + mapdl.eslv("s") + assert mapdl.post_processing.plot_element_displacement() is None + + mapdl.vsel("s", "", "", 2) + mapdl.eslv("s") + assert mapdl.post_processing.plot_element_displacement() is None + + +@requires("pyvista") +def test_plot_incomplete_nodal_selection(mapdl, contact_solve, verify_image_cache): + verify_image_cache.skip = True -def test_plot_incomplete_nodal_selection(mapdl, contact_solve): mapdl.nsel("S", "NODE", "", 1, mapdl.mesh.n_node // 2) assert mapdl.post_processing.plot_nodal_displacement() is None + mapdl.nsel("S", "NODE", "", 1, mapdl.mesh.n_node // 2, 2) + assert mapdl.post_processing.plot_nodal_displacement() is None + + mapdl.nsel("S", "NODE", "", 5, mapdl.mesh.n_node // 2, 2) + assert mapdl.post_processing.plot_nodal_displacement() is None + + mapdl.vsel("s", "", "", 1) + mapdl.eslv("S") + mapdl.nsle("S") + assert mapdl.post_processing.plot_nodal_displacement() is None + + mapdl.vsel("s", "", "", 2) + mapdl.eslv("S") + mapdl.nsle("S") + assert mapdl.post_processing.plot_nodal_displacement() is None + +@requires("pyvista") def test_general_plotter_returns(mapdl, static_solve, verify_image_cache): verify_image_cache.skip = True # skipping image verification @@ -856,6 +923,7 @@ def test_meta_post_plot_docstrings(): ), f"Less than three complete one-liner general plotter link in {meth.__name__}" +@requires("pyvista") def test_cuadratic_beam(mapdl, cuadratic_beam_problem): # Display elements with their nodes numbers. mapdl.eplot(show_node_numbering=True, line_width=5, cpos="xy", font_size=40) @@ -886,6 +954,7 @@ def test_cuadratic_beam(mapdl, cuadratic_beam_problem): # assert np.allclose(data_ans, data) +# @requires("pyvista") # def test_plot_nodal_thermal_component_strain(mapdl, thermal_solve): # assert mapdl.post_processing.plot_nodal_thermal_component_strain('x') is None @@ -906,6 +975,7 @@ def test_cuadratic_beam(mapdl, cuadratic_beam_problem): # assert np.allclose(from_grpc, from_prns) +# @requires("pyvista") # def test_plot_nodal_thermal_principal_strain(mapdl, thermal_solve): # assert mapdl.post_processing.plot_nodal_thermal_principal_strain(1) is None @@ -921,6 +991,7 @@ def test_cuadratic_beam(mapdl, cuadratic_beam_problem): # assert np.allclose(sint_ans, sint_aligned) +# @requires("pyvista") # def test_plot_nodal_thermal_strain_intensity(mapdl, thermal_solve): # assert mapdl.post_processing.plot_nodal_thermal_strain_intensity() is None @@ -936,6 +1007,7 @@ def test_cuadratic_beam(mapdl, cuadratic_beam_problem): # assert np.allclose(seqv_ans, seqv_aligned) +# @requires("pyvista") # def test_plot_nodal_thermal_eqv_strain(mapdl, thermal_solve): # assert mapdl.post_processing.plot_nodal_thermal_eqv_strain(smooth_shading=True) is None diff --git a/tests/test_theme.py b/tests/test_theme.py index c6945938a1..ecbbaf6fab 100644 --- a/tests/test_theme.py +++ b/tests/test_theme.py @@ -1,3 +1,10 @@ +import pytest + +from conftest import has_dependency + +if not has_dependency("pyvista"): + pytest.skip(allow_module_level=True) + from ansys.mapdl.core.theme import MapdlTheme, _apply_default_theme diff --git a/tests/test_xpl.py b/tests/test_xpl.py index 26281b2086..6885ca112e 100644 --- a/tests/test_xpl.py +++ b/tests/test_xpl.py @@ -4,9 +4,10 @@ import pytest from ansys.mapdl.core.errors import MapdlCommandIgnoredError, MapdlRuntimeError +from conftest import requires # skip entire module unless HAS_GRPC -pytestmark = pytest.mark.skip_grpc +pytestmark = requires("grpc") @pytest.fixture(scope="module") @@ -33,6 +34,7 @@ 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() @@ -40,6 +42,7 @@ def test_read_int32(xpl): assert arr.dtype == np.int32 +@requires("ansys-math-core") def test_read_double(xpl): vec = xpl.read("DIAGK") arr = vec.asarray() @@ -47,6 +50,7 @@ def test_read_double(xpl): assert arr.dtype == np.double +@requires("ansys-math-core") def test_read_asarray(xpl): vec1 = xpl.read("MASS", asarray=True) vec2 = xpl.read("MASS") @@ -109,6 +113,7 @@ def test_goto(xpl): 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