diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b5d1656aa..99bf37ef3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: "pip" directory: "/" schedule: - interval: "daily" + interval: "weekly" labels: - "maintenance" - "dependencies" @@ -11,6 +11,6 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" labels: - "maintenance" diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index bc49b07e3..1815e935a 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -1,6 +1,7 @@ name: GitHub CI on: pull_request: + merge_group: workflow_dispatch: push: tags: @@ -12,10 +13,8 @@ env: MAIN_PYTHON_VERSION: "3.10" DOCUMENTATION_CNAME: "additive.docs.pyansys.com" LIBRARY_NAME: "ansys-additive" - LIBRARY_NAMESPACE: "ansys.additive" ANSYS_PRODUCT_IMAGE: "ghcr.io/ansys/additive:latest" ANSYS_PRODUCT_CONTAINER: "ansys-additive-container" - PYANSYS_PYPI_PRIVATE_PAT: ${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }} PIP_INDEX_URL: "https://${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }}@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/" @@ -35,24 +34,25 @@ jobs: name: "Running documentation style checks" runs-on: ubuntu-latest steps: - - name: "Checkout code" - uses: actions/checkout@v3 - - name: "Running documentation style checks" uses: ansys/actions/doc-style@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - checkout: false smoke-tests: name: "Build wheelhouse for Python versions" runs-on: ${{ matrix.os }} needs: [code-style] strategy: + fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - # python-version: ["3.8", "3.9", "3.10", "3.11"] - python-version: ["3.10"] + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.8", "3.9", "3.10", "3.11"] + should-release: + - ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags') }} + exclude: + - should-release: false + os: macos-latest steps: - name: "Build a wheelhouse of the Python library" uses: ansys/actions/build-wheelhouse@v4 @@ -73,8 +73,6 @@ jobs: steps: - name: "Run pytest" uses: ansys/actions/tests-pytest@v4 - with: - pytest-extra-args: "--cov=${{ env.LIBRARY_NAMESPACE }} --cov-report=term --cov-report=html:.cov/html" doc-build: name: "Building library documentation" @@ -115,6 +113,7 @@ jobs: uses: ansys/actions/doc-build@v4 with: checkout: false + sphinxopts: "-j auto" env: GENERATING_DOCS: 1 ANSYS_ADDITIVE_ADDRESS: "localhost:50052" @@ -125,6 +124,7 @@ jobs: uses: ansys/actions/doc-build@v4 with: checkout: false + sphinxopts: "-j auto" env: GENERATING_DOCS: 1 ANSYS_ADDITIVE_ADDRESS: "localhost:50052" @@ -142,21 +142,9 @@ jobs: name: docker-logs.txt path: docker-logs.txt - doc-deploy-dev: - name: "Deploy developers documentation" - runs-on: ubuntu-latest - needs: doc-build - if: github.event_name == 'push' - steps: - - name: "Deploy the latest documentation" - uses: ansys/actions/doc-deploy-dev@v4 - with: - cname: ${{ env.DOCUMENTATION_CNAME }} - token: ${{ secrets.GITHUB_TOKEN }} - build-library: name: "Build library artifacts" - needs: [tests, doc-deploy-dev] + needs: [tests, doc-build] runs-on: ubuntu-latest steps: - name: "Build library source and wheel artifacts" @@ -183,6 +171,18 @@ jobs: with: library-name: ${{ env.LIBRARY_NAME }} + upload_dev_docs: + name: "Deploy developers documentation" + runs-on: ubuntu-latest + needs: [build-library] + if: github.event_name == 'push' + steps: + - name: "Deploy the latest documentation" + uses: ansys/actions/doc-deploy-dev@v4 + with: + cname: ${{ env.DOCUMENTATION_CNAME }} + token: ${{ secrets.GITHUB_TOKEN }} + upload_docs_release: name: "Upload release documentation" if: github.event_name == 'push' && contains(github.ref, 'refs/tags') diff --git a/.gitignore b/.gitignore index 779697c62..6c1479b0e 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ instance/ # Sphinx documentation doc/source/examples _autosummary/ +doc/_build # PyBuilder .pybuilder/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31617bcb4..e4c2da926 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,23 +1,36 @@ repos: - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 23.7.0 # IF VERSION CHANGES --> MODIFY "blacken-docs" MANUALLY AS WELL!! hooks: - id: black + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==23.7.0] + - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.1.0 hooks: - id: flake8 + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.5 + hooks: + - id: docformatter + additional_dependencies: [tomli] + - repo: https://github.com/codespell-project/codespell - rev: v2.2.1 + rev: v2.2.5 hooks: - id: codespell + args: ["--ignore-words", "doc/styles/Vocab/ANSYS/accept.txt"] exclude_types: ["jupyter"] # To be activated after quick dev cycles @@ -30,13 +43,15 @@ repos: # exclude: "tests/" - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-merge-conflict - id: debug-statements + - id: check-yaml + - id: trailing-whitespace # this validates our github workflow files - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.22.0 + rev: 0.24.1 hooks: - id: check-github-workflows diff --git a/README.rst b/README.rst index 440edc837..edf3391f0 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,9 @@ ########## PyAdditive ########## + +.. readme_start + |pyansys| |python| |pypi| |GH-CI| |codecov| |MIT| |black| .. |pyansys| image:: https://img.shields.io/badge/Py-Ansys-ffc107.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDklEQVQ4jWNgoDfg5mD8vE7q/3bpVyskbW0sMRUwofHD7Dh5OBkZGBgW7/3W2tZpa2tLQEOyOzeEsfumlK2tbVpaGj4N6jIs1lpsDAwMJ278sveMY2BgCA0NFRISwqkhyQ1q/Nyd3zg4OBgYGNjZ2ePi4rB5loGBhZnhxTLJ/9ulv26Q4uVk1NXV/f///////69du4Zdg78lx//t0v+3S88rFISInD59GqIH2esIJ8G9O2/XVwhjzpw5EAam1xkkBJn/bJX+v1365hxxuCAfH9+3b9/+////48cPuNehNsS7cDEzMTAwMMzb+Q2u4dOnT2vWrMHu9ZtzxP9vl/69RVpCkBlZ3N7enoDXBwEAAA+YYitOilMVAAAAAElFTkSuQmCC @@ -34,7 +37,7 @@ PyAdditive :alt: Black -A Python client library for the Ansys additive service. +A Python client library for the Ansys Additive service. Installation ============ @@ -59,13 +62,14 @@ Basic Usage .. code:: python import ansys.additive as pyadditive + additive = pyadditive.Additive() input = pyadditive.SingleBeadInput( - machine=pyadditive.AdditiveMachine(), - material=additive.get_material("Ti64"), - id="bead1", - bead_length=0.001, # meters + machine=pyadditive.AdditiveMachine(), + material=additive.get_material("Ti64"), + id="bead1", + bead_length=0.001, # meters ) summary = additive.simulate(input) diff --git a/doc/make.bat b/doc/make.bat index 42241b7fa..070d81e15 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -33,6 +33,7 @@ goto end :clean rmdir /s /q %BUILDDIR% > /NUL 2>&1 +rmdir /s /q %SOURCEDIR%\examples > /NUL 2>&1 for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d" goto end diff --git a/doc/source/_autoapi_templates/index.rst b/doc/source/_autoapi_templates/index.rst new file mode 100644 index 000000000..bc32fd0a2 --- /dev/null +++ b/doc/source/_autoapi_templates/index.rst @@ -0,0 +1,15 @@ +API reference +============= + +This section provides descriptions of PyAdditive subpackages, submodules, classes, +methods, and attributes. Use the search feature or click links to view API documentation. + +.. toctree:: + :titlesonly: + :maxdepth: 2 + + {% for page in pages %} + {% if (page.top_level_object or page.name.split('.') | length == 2) and page.display %} + {{ page.include_path }} + {% endif %} + {% endfor %} \ No newline at end of file diff --git a/doc/source/api/additive.rst b/doc/source/api/additive.rst deleted file mode 100644 index 9019237b8..000000000 --- a/doc/source/api/additive.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _ref_additive_api: - -Additive --------- - -.. currentmodule:: ansys.additive.additive - -.. autosummary:: - :toctree: _autosummary - - Additive \ No newline at end of file diff --git a/doc/source/api/geometry_file.rst b/doc/source/api/geometry_file.rst deleted file mode 100644 index 77b803fa6..000000000 --- a/doc/source/api/geometry_file.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _ref_geometry_api: - -Geometry files --------------- - -.. currentmodule:: ansys.additive.geometry_file - -.. autosummary:: - :toctree: _autosummary - - MachineType - StlFile - BuildFile \ No newline at end of file diff --git a/doc/source/api/index.rst b/doc/source/api/index.rst deleted file mode 100644 index 26e76b001..000000000 --- a/doc/source/api/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _ref_index_api: - -============= -API reference -============= -This section gives an overview of the API of PyAdditive. - -.. toctree:: - :hidden: - :maxdepth: 2 - - additive - machine - material - single_bead - porosity - microstructure - thermal_history - geometry_file \ No newline at end of file diff --git a/doc/source/api/machine.rst b/doc/source/api/machine.rst deleted file mode 100644 index 67b635073..000000000 --- a/doc/source/api/machine.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _ref_machine_api: - -Additive machine ----------------- - -.. currentmodule:: ansys.additive.machine - -.. autosummary:: - :toctree: _autosummary - - AdditiveMachine \ No newline at end of file diff --git a/doc/source/api/material.rst b/doc/source/api/material.rst deleted file mode 100644 index d8cc8ea46..000000000 --- a/doc/source/api/material.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _ref_material_api: - -Additive material ------------------ - -.. currentmodule:: ansys.additive.material - -.. autosummary:: - :toctree: _autosummary - - AdditiveMaterial - CharacteristicWidthDataPoint - ThermalPropertiesDataPoint diff --git a/doc/source/api/microstructure.rst b/doc/source/api/microstructure.rst deleted file mode 100644 index c231d560e..000000000 --- a/doc/source/api/microstructure.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _ref_microstructure_api: - -Microstructure --------------- - -.. currentmodule:: ansys.additive.microstructure - -.. autosummary:: - :toctree: _autosummary - - MicrostructureInput - MicrostructureSummary \ No newline at end of file diff --git a/doc/source/api/porosity.rst b/doc/source/api/porosity.rst deleted file mode 100644 index 11febe6cb..000000000 --- a/doc/source/api/porosity.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _ref_porosity_api: - -Porosity --------- - -.. currentmodule:: ansys.additive.porosity - -.. autosummary:: - :toctree: _autosummary - - PorosityInput - PorositySummary \ No newline at end of file diff --git a/doc/source/api/single_bead.rst b/doc/source/api/single_bead.rst deleted file mode 100644 index aa29385b9..000000000 --- a/doc/source/api/single_bead.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _ref_single_bead_api: - -Single bead ------------ - -.. currentmodule:: ansys.additive.single_bead - -.. autosummary:: - :toctree: _autosummary - - MeltPool - SingleBeadInput - SingleBeadSummary diff --git a/doc/source/api/thermal_history.rst b/doc/source/api/thermal_history.rst deleted file mode 100644 index 295593322..000000000 --- a/doc/source/api/thermal_history.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _ref_thermal_history_api: - -Thermal history ---------------- - -.. currentmodule:: ansys.additive.thermal_history - -.. autosummary:: - :toctree: _autosummary - - Range - CoaxialAverageSensorInputs - ThermalHistoryInput - ThermalHistorySummary \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index c12bcb612..91730daeb 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -136,7 +136,8 @@ numpydoc_validation_checks = { "GL06", # Found unknown section "GL07", # Sections are in the wrong order. - "GL08", # The object does not have a docstring + # TODO: GL08 should be reactivated since it detects undocumented members + # "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 @@ -188,7 +189,24 @@ # -- Declare the Jinja context ----------------------------------------------- BUILD_API = True if os.environ.get("BUILD_API", "true") == "true" else False if not BUILD_API: - exclude_patterns.append("api") + exclude_patterns.append("autoapi") +else: + # Configuration for Sphinx autoapi + extensions.append("autoapi.extension") + autoapi_type = "python" + autoapi_dirs = ["../../src/ansys"] + autoapi_options = [ + "members", + "undoc-members", + "show-inheritance", + "show-module-summary", + "special-members", + ] + autoapi_template_dir = "_autoapi_templates" + suppress_warnings = ["autoapi.python_import_resolution"] + exclude_patterns.append("_autoapi_templates/index.rst") + autoapi_python_use_implicit_namespaces = True + BUILD_EXAMPLES = True if os.environ.get("BUILD_EXAMPLES", "true") == "true" else False BUILD_EXAMPLES_LONG = True if os.environ.get("BUILD_EXAMPLES_LONG", "true") == "true" else False @@ -242,7 +260,7 @@ linkcheck_ignore = [ r"https://pypi.org/project/ansys-additive.*", # TODO: remove once pyadditive is published - r"https://github.com/ansys-internal/.*", - r"https://ansyshelp.ansys.com/.*", # TODO: remove once pyadditive docs are updated - r"https://ansysproducthelpqa.win.ansys.com/.*", # TODO: remove once pyadditive docs are updated + r"https://github.com/ansys-internal/.*", # TODO: remove once pyadditive is published + r"https://ansyshelp.ansys.com/.*", + r"https://ansysproducthelpqa.win.ansys.com/.*", ] diff --git a/doc/source/getting_started/index.rst b/doc/source/getting_started/index.rst index a2204ccc3..0084753e2 100644 --- a/doc/source/getting_started/index.rst +++ b/doc/source/getting_started/index.rst @@ -6,7 +6,7 @@ Getting started ############### -PyAdditive is a Python client library for the Ansys additive service. +PyAdditive is a Python client library for the Ansys Additive service. .. note:: @@ -38,7 +38,7 @@ cloud environment. Once logged in to Ansys Lab, create a new jupyter notebook and connect to the additive service using: -.. code:: python +.. code:: pycon >>> import ansys.additive as pyadditive >>> additive = pyadditive.Additive() @@ -65,7 +65,7 @@ you can start the additive service locally using ``docker`` with: Next, connect to the service with: -.. code:: python +.. code:: pycon >>> import ansys.additive as pyadditive >>> additive = pyadditive.Additive() diff --git a/doc/source/index.rst b/doc/source/index.rst index 36c37d27d..174b9a31e 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -3,7 +3,11 @@ Provide any documentation specific to your online documentation here. +PyAdditive documentation |version| +=================================== + .. include:: ../../README.rst + :start-after: .. readme_start .. jinja:: main_toctree @@ -13,7 +17,7 @@ getting_started/index {% if build_api %} - api/index + autoapi/index {% endif %} {% if build_examples %} examples/gallery_examples/index diff --git a/doc/styles/Vocab/ANSYS/accept.txt b/doc/styles/Vocab/ANSYS/accept.txt index 569dd4e98..4936a6c13 100644 --- a/doc/styles/Vocab/ANSYS/accept.txt +++ b/doc/styles/Vocab/ANSYS/accept.txt @@ -19,4 +19,5 @@ Jupyter Makefile isort pandoc -Ansys Lab \ No newline at end of file +Ansys Lab +subpackages \ No newline at end of file diff --git a/compose.yaml b/docker/compose.yaml similarity index 100% rename from compose.yaml rename to docker/compose.yaml diff --git a/examples/00_additive_single_bead.py b/examples/00_additive_single_bead.py index 7199451a3..5aedf8562 100644 --- a/examples/00_additive_single_bead.py +++ b/examples/00_additive_single_bead.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Single Bead Analysis ==================== diff --git a/examples/01_additive_porosity.py b/examples/01_additive_porosity.py index 10fd41aa2..63fc72ca6 100644 --- a/examples/01_additive_porosity.py +++ b/examples/01_additive_porosity.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Porosity Analysis ================= diff --git a/examples/02_additive_microstructure.py b/examples/02_additive_microstructure.py index 411a0c87f..758b32216 100644 --- a/examples/02_additive_microstructure.py +++ b/examples/02_additive_microstructure.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Microstructure Analysis ####################### diff --git a/examples/03_additive_thermal_history.py b/examples/03_additive_thermal_history.py index 2caf1919e..38d206b05 100644 --- a/examples/03_additive_thermal_history.py +++ b/examples/03_additive_thermal_history.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Thermal History Analysis ======================== diff --git a/examples/04_additive_doe.py b/examples/04_additive_doe.py index 3d166291d..5d4a2df80 100644 --- a/examples/04_additive_doe.py +++ b/examples/04_additive_doe.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Design Of Experiments ===================== diff --git a/examples/05_advanced_optimization_workflow.py b/examples/05_advanced_optimization_workflow.py index a3dbe5117..8759ca984 100644 --- a/examples/05_advanced_optimization_workflow.py +++ b/examples/05_advanced_optimization_workflow.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Optimization Workflow Analysis ============================== diff --git a/examples/06_advanced_custom_material_tuning.py b/examples/06_advanced_custom_material_tuning.py index 468cdcc8e..fa018fe91 100644 --- a/examples/06_advanced_custom_material_tuning.py +++ b/examples/06_advanced_custom_material_tuning.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Custom Material Tuning ====================== diff --git a/examples/07_using_a_custom_material.py b/examples/07_using_a_custom_material.py index 86a475d5a..0db8b5f25 100644 --- a/examples/07_using_a_custom_material.py +++ b/examples/07_using_a_custom_material.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. """ Using A Custom Material ======================= diff --git a/pyproject.toml b/pyproject.toml index 2423a7ad5..949267f5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,15 +5,13 @@ build-backend = "flit_core.buildapi" [project] # Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections name = "ansys-additive" -version = "0.14.0.dev4" -description = "A python client for the Ansys additive service" +version = "0.14.dev4" +description = "A python client for the Ansys Additive service" readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.8,<4" license = { file = "LICENSE" } -authors = [{ name = "Ansys, Inc.", email = "pyansys.support@ansys.com" }] -maintainers = [ - { name = "PyAnsys developers", email = "pyansys.support@ansys.com" }, -] +authors = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}] +maintainers = [{name = "ANSYS, Inc.", email = "pyansys.core@ansys.com"}] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Manufacturing", @@ -47,7 +45,26 @@ dependencies = [ ] [project.optional-dependencies] -tests = ["pytest==7.4.0", "pytest-cov==4.0.0", "callee==0.3.1"] +tests = [ + "ansys-platform-instancemanagement==1.1.2", + "dill==0.3.7", + "google-api-python-client==2.97.0", + "googleapis-common-protos==1.60.0", + "grpcio-health-checking==1.57.0", + "grpcio==1.57.0", + "numpy==1.24.4; python_version=='3.8'", + "numpy==1.25.2; python_version>'3.8'", + "pandas==2.0.3", + "panel==1.2.1", + "platformdirs==3.10.0", + "protobuf==4.24.1", + "six==1.16.0", + "tqdm==4.66.1", + # Test specific dependencies + "pytest==7.4.0", + "pytest-cov==4.0.0", + "callee==0.3.1" +] doc = [ "ansys-sphinx-theme==0.10.3", @@ -69,8 +86,11 @@ doc = [ ] [project.urls] -Source = "https://github.com/ansys-interal/pyadditive" +Source = "https://github.com/ansys-internal/pyadditive" +Issues = "https://github.com/ansys-internal/pyadditive/issues" Documentation = "https://additive.docs.pyansys.com" +Discussions = "https://github.com/ansys-internal/pyadditive/discussions" +Releases = "https://github.com/ansys-internal/pyadditive/releases" [tool.flit.module] name = "ansys.additive" diff --git a/update-copyright.py b/scripts/update-copyright.py similarity index 77% rename from update-copyright.py rename to scripts/update-copyright.py index 15afe53be..280bb61b4 100644 --- a/update-copyright.py +++ b/scripts/update-copyright.py @@ -1,11 +1,14 @@ -# (c) 2022 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. import argparse import codecs from datetime import date import glob import os +import shutil import tempfile +REPO_ROOT = os.path.dirname(os.path.dirname(__file__)) + copyright_substr = "ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited." @@ -18,9 +21,9 @@ def get_args(): if __name__ == "__main__": _ = get_args() - dirs = ["src", "tests"] + dirs = ["src", "tests", "scripts", "examples"] for d in dirs: - source_files = glob.glob(os.path.join(d, "**", "*.py"), recursive=True) + source_files = glob.glob(os.path.join(REPO_ROOT, d, "**", "*.py"), recursive=True) for f in source_files: lines = [] with open(f, "r") as file: @@ -37,4 +40,4 @@ def get_args(): tmp.writelines(lines) tmp_name = tmp.name - os.replace(tmp_name, f) + shutil.move(tmp_name, f) diff --git a/src/ansys/additive/__init__.py b/src/ansys/additive/__init__.py index ccd264dd8..a09339a8a 100644 --- a/src/ansys/additive/__init__.py +++ b/src/ansys/additive/__init__.py @@ -1,7 +1,5 @@ # (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. -""" -PyAdditive is a Python client for the Ansys additive service. -""" +"""PyAdditive is a Python client for the Ansys Additive service.""" import os import platformdirs @@ -26,14 +24,37 @@ if not os.path.exists(EXAMPLES_PATH): # pragma: no cover os.makedirs(EXAMPLES_PATH) -from .additive import * -from .geometry_file import * -from .machine import * -from .material import * -from .material_tuning import * -from .microstructure import * -from .porosity import * -from .server_utils import * -from .simulation import * -from .single_bead import * -from .thermal_history import * +from ansys.additive.additive import ( + DEFAULT_ADDITIVE_SERVICE_PORT, + LOCALHOST, + MAX_MESSAGE_LENGTH, + Additive, +) +from ansys.additive.geometry_file import BuildFile, MachineType, StlFile +from ansys.additive.machine import AdditiveMachine, MachineConstants +from ansys.additive.material import ( + AdditiveMaterial, + CharacteristicWidthDataPoint, + ThermalPropertiesDataPoint, +) +from ansys.additive.material_tuning import MaterialTuningInput, MaterialTuningSummary +from ansys.additive.microstructure import ( + CircleEquivalenceColumnNames, + MicrostructureInput, + MicrostructureSummary, +) +from ansys.additive.porosity import PorosityInput, PorositySummary +from ansys.additive.server_utils import find_open_port, launch_server +from ansys.additive.simulation import SimulationError, SimulationStatus, SimulationType +from ansys.additive.single_bead import ( + MeltPool, + MeltPoolColumnNames, + SingleBeadInput, + SingleBeadSummary, +) +from ansys.additive.thermal_history import ( + CoaxialAverageSensorInputs, + Range, + ThermalHistoryInput, + ThermalHistorySummary, +) diff --git a/src/ansys/additive/additive.py b/src/ansys/additive/additive.py index 24811c2d8..b1b39e0c8 100644 --- a/src/ansys/additive/additive.py +++ b/src/ansys/additive/additive.py @@ -1,10 +1,6 @@ # (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. -""" - additive.py - ----------- - - This module contains the Additive class which interacts with the Additive service. -""" +"""This module contains the Additive class which interacts with the Additive +service.""" import concurrent.futures import hashlib import logging @@ -42,8 +38,9 @@ class Additive: - """ - Client interface to Additive service. The method :meth:`simulate` is + """Client interface to Additive service. + + The method :meth:`simulate` is used to execute simulations. """ @@ -89,7 +86,7 @@ def __del__(self): self._server_process.kill() def _create_logger(self, log_file, loglevel) -> logging.Logger: - """Create the logger for this module""" + """Instantiate the logger.""" numeric_level = getattr(logging, loglevel.upper(), None) if not isinstance(numeric_level, int): raise ValueError("Invalid log level: %s" % loglevel) @@ -111,8 +108,7 @@ def _create_channel( port: typing.Optional[int] = None, product_version: typing.Optional[str] = None, ): - """ - Create an insecure grpc channel. + """Create an insecure gRPC channel. A channel connection will be established using one of the following methods. The methods are listed in order of precedence. @@ -139,7 +135,6 @@ def _create_channel( ------- channel: grpc.Channel Insecure grpc channel. - """ if port: misc.check_valid_port(port) @@ -179,7 +174,6 @@ def _channel_str(self): """Return the target string. Generally of the form of "ip:port", like "127.0.0.1:50052". - """ if self._channel is not None: return self._channel._channel.target().decode() @@ -222,7 +216,6 @@ def simulate(self, inputs, nproc: typing.Optional[int] = None): :class:`MicrostructureSummary`, :class:`ThermalHistorySummary`, :class:`SimulationError`, or, if a list of inputs was provided, a list of these types. - """ if type(inputs) is not list: result = self._simulate(inputs, show_progress=True) @@ -261,7 +254,6 @@ def _simulate(self, input, show_progress: bool = False): :class:`SingleBeadSummary`, :class:`PorositySummary`, :class:`MicrostructureSummary`, :class:`ThermalHistorySummary`, :class:`SimulationError` - """ logger = None if show_progress: @@ -302,7 +294,6 @@ def get_materials_list(self) -> list[str]: ------- list[str] Names of available additive materials. - """ return self._materials_stub.GetMaterialsList(Empty()) @@ -318,7 +309,6 @@ def get_material(self, name: str) -> AdditiveMaterial: Returns ------- AdditiveMaterial - """ request = GetMaterialRequest() request.name = name @@ -329,8 +319,7 @@ def get_material(self, name: str) -> AdditiveMaterial: def load_material( parameters_file: str, thermal_lookup_file: str, characteristic_width_lookup_file: str ) -> AdditiveMaterial: - """ - Load a user provided material definition. + """Load a user provided material definition. Parameters ---------- @@ -346,7 +335,6 @@ def load_material( characteristic_width_lookup_file: str Name of `CSV file containing a lookup table for characteristic melt pool width `_ - """ material = AdditiveMaterial() material._load_parameters(parameters_file) @@ -357,8 +345,7 @@ def load_material( def tune_material( self, input: MaterialTuningInput, out_dir: str = USER_DATA_PATH ) -> MaterialTuningSummary: - """ - Tune a custom material for use with additive simulations. + """Tune a custom material for use with additive simulations. Parameters ---------- @@ -371,7 +358,6 @@ def tune_material( MaterialTuningSummary Summary of material tuning. - """ if input.id == "": input.id = misc.short_uuid() @@ -404,7 +390,7 @@ def tune_material( def __file_upload_reader( self, file_name: str, chunk_size=2 * 1024 * 1024 ) -> typing.Iterator[UploadFileRequest]: - """Read a file and return an iterator of UploadFileRequests""" + """Read a file and return an iterator of UploadFileRequests.""" file_size = os.path.getsize(file_name) short_name = os.path.basename(file_name) with open(file_name, mode="rb") as f: @@ -425,8 +411,7 @@ def _simulate_thermal_history( out_dir: str, logger: typing.Optional[ProgressLogger] = None, ) -> typing.Optional[ThermalHistorySummary]: - """ - Execute a thermal history simulation. + """Execute a thermal history simulation. Parameters ---------- @@ -444,7 +429,6 @@ def _simulate_thermal_history( ------- :class:`ThermalHistorySummary` - """ if input.geometry == None or input.geometry.path == "": raise ValueError("Geometry path not defined") diff --git a/src/ansys/additive/download.py b/src/ansys/additive/download.py index c32a0eaa8..a30dff400 100644 --- a/src/ansys/additive/download.py +++ b/src/ansys/additive/download.py @@ -15,7 +15,7 @@ def download_file( local_folder: str, logger: ProgressLogger = None, ): - """Download a file from the server to the local host + """Download a file from the server to the local host. Parameters ---------- @@ -28,7 +28,6 @@ def download_file( logger: ProgressLogger Log message handler - """ if not os.path.isdir(local_folder): diff --git a/src/ansys/additive/examples/__init__.py b/src/ansys/additive/examples/__init__.py index 7c6b5aaef..e77e4cc6a 100644 --- a/src/ansys/additive/examples/__init__.py +++ b/src/ansys/additive/examples/__init__.py @@ -1,6 +1,12 @@ # (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. -""" -Utilities for use with PyAdditive examples. -""" +"""Utilities for use with PyAdditive examples.""" -from .downloads import * +from ansys.additive.examples.downloads import ( + decompress, + delete_downloads, + download_10mm_cube, + download_custom_material, + download_material_tuning_input, + download_small_wedge_slm_build_file, + get_ext, +) diff --git a/src/ansys/additive/examples/downloads.py b/src/ansys/additive/examples/downloads.py index e9c796782..868fd0c5b 100644 --- a/src/ansys/additive/examples/downloads.py +++ b/src/ansys/additive/examples/downloads.py @@ -1,5 +1,5 @@ -"""Functions to download sample datasets from the pyansys data repository. -""" +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. +"""Functions to download sample datasets from the pyansys data repository.""" from http.client import HTTPMessage import os import shutil @@ -16,20 +16,21 @@ def get_ext(filename): - """Extract the extension of the filename""" + """Extract the extension of the filename.""" ext = os.path.splitext(filename)[1].lower() return ext def delete_downloads(): - """Delete all downloaded examples to free space or update the files""" + """Delete all downloaded examples to free space or update the files.""" shutil.rmtree(EXAMPLES_PATH) os.makedirs(EXAMPLES_PATH) return True def decompress(filename, subdir=None) -> str: - """Decompress a zip file into the examples directory and return the path""" + """Decompress a zip file into the examples directory and return the + path.""" outdir = EXAMPLES_PATH zip_ref = zipfile.ZipFile(filename, "r") if subdir: @@ -47,15 +48,13 @@ def _get_file_url(filename, directory=None): def _retrieve_file(url, filename) -> tuple[str, HTTPMessage]: - """ - Retrieve an example data file either from local storage or - from the given url. + """Retrieve an example data file either from local storage or from the + given url. Returns ------- str: local file path HttpMessage: http status message if any - """ # First check if file has already been downloaded local_path = os.path.join(EXAMPLES_PATH, os.path.basename(filename)) diff --git a/src/ansys/additive/geometry_file.py b/src/ansys/additive/geometry_file.py index 4361b0a06..5ad6f0b99 100644 --- a/src/ansys/additive/geometry_file.py +++ b/src/ansys/additive/geometry_file.py @@ -58,8 +58,8 @@ def type(self, value: MachineType): @property def path(self) -> str: - """Path of zip archive containing build instruction file, geometry stl file and - optional support stl files.""" + """Path of zip archive containing build instruction file, geometry stl + file and optional support stl files.""" return self._path @path.setter diff --git a/src/ansys/additive/machine.py b/src/ansys/additive/machine.py index f132ceada..dc540336f 100644 --- a/src/ansys/additive/machine.py +++ b/src/ansys/additive/machine.py @@ -51,7 +51,6 @@ class AdditiveMachine: Units are SI (m, kg, s, K) unless otherwise noted. Exceptions include angles, which are in degrees, and ``heater_temperature``, which is in degrees Celsius. - """ def __init__( @@ -105,7 +104,9 @@ def __validate_range(self, value, min, max, name): @property def laser_power(self) -> float: """Scanning laser power (W). - Valid values are from 50 to 700 Watts.""" + + Valid values are from 50 to 700 Watts. + """ return self._laser_power @laser_power.setter @@ -118,7 +119,9 @@ def laser_power(self, value: float): @property def scan_speed(self) -> float: """Laser scanning speed in (m/s). - Valid values are from 0.35 to 2.5 m/s.""" + + Valid values are from 0.35 to 2.5 m/s. + """ return self._scan_speed @scan_speed.setter @@ -131,7 +134,9 @@ def scan_speed(self, value: float): @property def heater_temperature(self) -> float: """Temperature of machine build chamber heater (°C). - Valid values are from 20 to 500 °C.""" + + Valid values are from 20 to 500 °C. + """ return self._heater_temperature @heater_temperature.setter @@ -146,8 +151,11 @@ def heater_temperature(self, value: float): @property def layer_thickness(self) -> float: - """The thickness of the powder layer deposited with each pass of the recoater blade (m). - Valid values are from 1e-5 to 1e-4 m (10 and 100 µm).""" + """The thickness of the powder layer deposited with each pass of the + recoater blade (m). + + Valid values are from 1e-5 to 1e-4 m (10 to 100 µm). + """ return self._layer_thickness @layer_thickness.setter @@ -163,9 +171,12 @@ def layer_thickness(self, value: float): @property def beam_diameter(self) -> float: """The width of the laser on the powder or substrate surface defined - using the D4σ beam diameter definition (m). Usually this value is provided by the - machine manufacturer. Sometimes called laser spot diameter. - Valid values are from 2e-5 to 1.4e-4 m (20 and 140 µm).""" + using the D4σ beam diameter definition (m). + + Usually this value is provided by the machine manufacturer. + Sometimes called laser spot diameter. Valid values are from 2e-5 + to 1.4e-4 m (20 and 140 µm). + """ return self._beam_diameter @beam_diameter.setter @@ -180,10 +191,12 @@ def beam_diameter(self, value: float): @property def starting_layer_angle(self) -> float: - """The angle at which the first layer will be scanned (°). It is measured - counter clockwise from the X axis, such that a value of 90° results in scan lines - parallel to the Y axis. - Valid values are from 0 to 180°.""" + """The angle at which the first layer will be scanned (°). + + It is measured counter clockwise from the X axis, such that a + value of 90° results in scan lines parallel to the Y axis. Valid + values are from 0 to 180°. + """ return self._starting_layer_angle @starting_layer_angle.setter @@ -200,7 +213,9 @@ def starting_layer_angle(self, value: float): def layer_rotation_angle(self) -> float: """The angle, in degrees, at which scan vector orientation changes from layer to layer (°). - Valid values are from 0 to 180°.""" + + Valid values are from 0 to 180°. + """ return self._layer_rotation_angle @layer_rotation_angle.setter @@ -215,11 +230,14 @@ def layer_rotation_angle(self, value: float): @property def hatch_spacing(self) -> float: - """The distance between adjacent scan vectors, or hatches when rastering - back and forth with the laser (m). Hatch spacing should allow for a slight overlap of - scan vector tracks such that some of the material re-melts to ensure full coverage - of solid material. - Valid values are from 6e-5 to 2e-4 m (0.06 and 0.2 mm).""" + """The distance between adjacent scan vectors, or hatches when + rastering back and forth with the laser (m). + + Hatch spacing should allow for a slight overlap of scan vector + tracks such that some of the material re-melts to ensure full + coverage of solid material. Valid values are from 6e-5 to 2e-4 m + (0.06 and 0.2 mm). + """ return self._hatch_spacing @hatch_spacing.setter @@ -234,8 +252,11 @@ def hatch_spacing(self, value: float): @property def slicing_stripe_width(self) -> float: - """The width of a stripe (m). A stripe is a section of scan lines within a layer. - Valid values must be between 0.001 and 0.1 m (1 and 100 mm).""" + """The width of a stripe (m). + + A stripe is a section of scan lines within a layer. Valid values + must be between 0.001 and 0.1 m (1 and 100 mm). + """ return self._slicing_stripe_width @slicing_stripe_width.setter @@ -250,7 +271,8 @@ def slicing_stripe_width(self, value: float): @staticmethod def _from_machine_message(msg: MachineMessage): - """Create an ``AdditiveMachine`` from a machine message received from the Additive service.""" + """Create an ``AdditiveMachine`` from a machine message received from + the Additive service.""" if isinstance(msg, MachineMessage): return AdditiveMachine( laser_power=msg.laser_power, @@ -267,7 +289,8 @@ def _from_machine_message(msg: MachineMessage): raise ValueError("Invalid message type passed to _from_machine_message()") def _to_machine_message(self) -> MachineMessage: - """Create a machine message from this ``AdditiveMachine`` to send to the Additive service.""" + """Create a machine message from this ``AdditiveMachine`` to send to + the Additive service.""" return MachineMessage( laser_power=self.laser_power, scan_speed=self.scan_speed, diff --git a/src/ansys/additive/material.py b/src/ansys/additive/material.py index 4e192f220..fb06e9f66 100644 --- a/src/ansys/additive/material.py +++ b/src/ansys/additive/material.py @@ -16,13 +16,12 @@ class CharacteristicWidthDataPoint: """Container for a characteristic width data point. - Additive material definitions include a file containing a characteristic width - lookup table which allows a given laser speed and power to be correlated to a - characteristic melt pool width. This class represents a single row in the - lookup table. + Additive material definitions include a file containing a + characteristic width lookup table which allows a given laser speed + and power to be correlated to a characteristic melt pool width. This + class represents a single row in the lookup table. Units are SI (m, kg, s, K) unless otherwise noted. - """ def __init__( @@ -49,7 +48,8 @@ def __eq__(self, other: object) -> bool: @property def characteristic_width(self) -> float: - """Characteristic melt pool width for a given laser power and scan speed (m).""" + """Characteristic melt pool width for a given laser power and scan + speed (m).""" return self._characteristic_width @characteristic_width.setter @@ -85,8 +85,8 @@ def scan_speed(self, value: float): @staticmethod def _from_characteristic_width_data_point_message(msg: CharacteristicWidthDataPointMessage): - """Create a ``CharacteristicWidthDataPoint`` from a characteristic data point message - received from Additive service""" + """Create a ``CharacteristicWidthDataPoint`` from a characteristic data + point message received from Additive service.""" if not isinstance(msg, CharacteristicWidthDataPointMessage): raise ValueError( "Invalid message object passed to from_characteristic_width_data_point_message()" @@ -100,7 +100,7 @@ def _to_characteristic_width_data_point_message( self, ) -> CharacteristicWidthDataPointMessage: """Create a characteristic width data point message from this - ``CharacteristicWidthDataPoint`` to send to the Additive service""" + ``CharacteristicWidthDataPoint`` to send to the Additive service.""" msg = CharacteristicWidthDataPointMessage() for p in self.__dict__: setattr(msg, p.replace("_", "", 1), getattr(self, p)) @@ -108,15 +108,14 @@ def _to_characteristic_width_data_point_message( class ThermalPropertiesDataPoint: - """ - Container for a temperature dependent properties. + """Container for a temperature dependent properties. - Additive material definitions include a file containing a lookup table - which describes the material's thermal properties at different temperatures. - This class represents a single row in the lookup table. + Additive material definitions include a file containing a lookup + table which describes the material's thermal properties at different + temperatures. This class represents a single row in the lookup + table. Units are SI (m, kg, s, K) unless otherwise noted. - """ def __init__( @@ -128,7 +127,7 @@ def __init__( specific_heat_ratio: float = 0, temperature: float = 0, thermal_conductivity: float = 0, - thermal_conductivity_ratio: float = 0 + thermal_conductivity_ratio: float = 0, ): """Create a ``ThermalPropertiesDataPoint``.""" self._density = density @@ -231,8 +230,8 @@ def thermal_conductivity_ratio(self, value: float): @staticmethod def _from_thermal_properties_data_point_message(msg: ThermalPropertiesDataPointMessage): - """Create a ``ThermalPropertiesDataPoint`` from a thermal characteristic - data point message received from the Additive service. + """Create a ``ThermalPropertiesDataPoint`` from a thermal + characteristic data point message received from the Additive service. :meta private: """ @@ -248,8 +247,8 @@ def _from_thermal_properties_data_point_message(msg: ThermalPropertiesDataPointM def _to_thermal_properties_data_point_message( self, ) -> ThermalPropertiesDataPointMessage: - """Create a thermal characteristic data point message from this ``ThermalPropertiesDataPoint`` - object to send to the Additive service. + """Create a thermal characteristic data point message from this + ``ThermalPropertiesDataPoint`` object to send to the Additive service. :meta private: """ @@ -260,7 +259,8 @@ def _to_thermal_properties_data_point_message( class AdditiveMaterial: - """Container for material properties used during additive manufacturing simulation.""" + """Container for material properties used during additive manufacturing + simulation.""" def __init__( self, @@ -299,7 +299,7 @@ def __init__( thermal_expansion_coefficient: float = 0, vaporization_temperature: float = 0, characteristic_width_data: list[CharacteristicWidthDataPoint] = None, - thermal_properties_data: list[ThermalPropertiesDataPoint] = None + thermal_properties_data: list[ThermalPropertiesDataPoint] = None, ): """Create an ``AdditiveMaterial``.""" self._absorptivity_maximum = absorptivity_maximum @@ -474,7 +474,8 @@ def elastic_modulus(self, value: float): @property def hardening_factor(self) -> float: """Factor relating the elastic modulus to the tangent modulus for - plasticity simulations (tangent modulus = elastic modulus * hardening factor ).""" + plasticity simulations (tangent modulus = elastic modulus * hardening + factor ).""" return self._hardening_factor @hardening_factor.setter @@ -484,7 +485,8 @@ def hardening_factor(self, value: float): @property def liquidus_temperature(self) -> float: - """Minimum temperature at which the material is completely liquid (K).""" + """Minimum temperature at which the material is completely liquid + (K).""" return self._liquidus_temperature @liquidus_temperature.setter @@ -514,8 +516,8 @@ def name(self, value: str): @property def nucleation_constant_bulk(self) -> float: - """Controls the homogeneous nucleation rate (in bulk of the microstructure - simulation domain) during solidification (1/m^2/K^2).""" + """Controls the homogeneous nucleation rate (in bulk of the + microstructure simulation domain) during solidification (1/m^2/K^2).""" return self._nucleation_constant_bulk @nucleation_constant_bulk.setter @@ -525,8 +527,8 @@ def nucleation_constant_bulk(self, value: float): @property def nucleation_constant_interface(self) -> float: - """Controls the heterogeneous nucleation rate (on existing solid interfaces) - during solidification (1/m/K^2).""" + """Controls the heterogeneous nucleation rate (on existing solid + interfaces) during solidification (1/m/K^2).""" return self._nucleation_constant_interface @nucleation_constant_interface.setter @@ -616,7 +618,8 @@ def powder_packing_density(self, value: float): @property def purging_gas_convection_coefficient(self) -> float: - """Convection coefficient between the solid and gas during processing.""" + """Convection coefficient between the solid and gas during + processing.""" return self._purging_gas_convection_coefficient @purging_gas_convection_coefficient.setter @@ -636,7 +639,8 @@ def solid_density_at_room_temperature(self, value: float): @property def solid_specific_heat_at_room_temperature(self) -> float: - """Specific heat of bulk material at room temperature, 298 K (J/kg/K).""" + """Specific heat of bulk material at room temperature, 298 K + (J/kg/K).""" return self._solid_specific_heat_at_room_temperature @solid_specific_heat_at_room_temperature.setter @@ -646,7 +650,8 @@ def solid_specific_heat_at_room_temperature(self, value: float): @property def solid_thermal_conductivity_at_room_temperature(self) -> float: - """Thermal conductivity of bulk material at room temperature, 298 K (W/m/K).""" + """Thermal conductivity of bulk material at room temperature, 298 K + (W/m/K).""" return self._solid_thermal_conductivity_at_room_temperature @solid_thermal_conductivity_at_room_temperature.setter @@ -656,7 +661,8 @@ def solid_thermal_conductivity_at_room_temperature(self, value: float): @property def solidus_temperature(self) -> float: - """Maximum temperature at which the material is completely solid (K).""" + """Maximum temperature at which the material is completely solid + (K).""" return self._solidus_temperature @solidus_temperature.setter @@ -676,7 +682,8 @@ def strain_scaling_factor(self, value: float): @property def support_yield_strength_ratio(self) -> float: - """Factor to reduce the yield strength and elastic modulus of support material.""" + """Factor to reduce the yield strength and elastic modulus of support + material.""" return self._support_yield_strength_ratio @support_yield_strength_ratio.setter @@ -696,7 +703,8 @@ def thermal_expansion_coefficient(self, value: float): @property def vaporization_temperature(self) -> float: - """Temperature at which material has completely changed from liquid to vapor (K).""" + """Temperature at which material has completely changed from liquid to + vapor (K).""" return self._vaporization_temperature @vaporization_temperature.setter @@ -734,8 +742,8 @@ def thermal_properties_data(self, value: list[ThermalPropertiesDataPoint]): @staticmethod def _from_material_message(msg: MaterialMessage): - """Create an ``AdditiveMaterial`` object from a material message received from - the Additive service.""" + """Create an ``AdditiveMaterial`` object from a material message + received from the Additive service.""" if not isinstance(msg, MaterialMessage): raise ValueError("Invalid message object passed to from_material_message()") material = AdditiveMaterial() @@ -753,7 +761,8 @@ def _from_material_message(msg: MaterialMessage): return material def _to_material_message(self) -> MaterialMessage: - """Create a material message from this ``AdditiveMaterial`` to send to the Additive service.""" + """Create a material message from this ``AdditiveMaterial`` to send to + the Additive service.""" msg = MaterialMessage() for p in self.__dict__: if p != "_characteristic_width_data" and p != "_thermal_properties_data": diff --git a/src/ansys/additive/material_tuning.py b/src/ansys/additive/material_tuning.py index 829429e56..02bf8911f 100644 --- a/src/ansys/additive/material_tuning.py +++ b/src/ansys/additive/material_tuning.py @@ -13,15 +13,16 @@ class MaterialTuningInput: """Input parameters for tuning a custom material. - ``id: string`` + Parameters + ---------- + id: string User provided identifier for this simulation. - ``machine: AdditiveMachine`` + machine: AdditiveMachine Machine related parameters. - ``material: AdditiveMaterial`` + material: AdditiveMaterial Material used during simulation. - ``bead_length: float`` + bead_length: float Length of bead to simulate (m). - """ def __init__( @@ -36,39 +37,30 @@ def __init__( max_iterations: int = 15, base_plate_temperature: float = 353.15, ): - """ - Initialize a ``MaterialTuningInput`` object. + """Initialize a ``MaterialTuningInput`` object. Parameters ---------- - id: str Identifier for this set of tuning simulations. - experiment_data_file: str Name of CSV file containing experimental results data. - material_parameters_file: str Name of JSON file containing material parameters. - thermal_properties_lookup_file: str Name of CSV file containing a lookup table for thermal dependent properties. - characteristic_width_lookup_file: str Optional: Name of CSV file containing a lookup table for the characteristic melt pool width at a given temperature. If not provided, the characteristic width will be calculated and ``base_plate_temperature`` must be provided. Default is None. - allowable_error: float Maximum allowable error between experimental and simulated results. Default is 0.05 (5%). - max_iterations: int Maximum number of iterations to perform when trying to match simulation results to an experiment if the allowable error is not met. Default is 15. - base_plate_temperature: float Temperature of the base plate in Kelvin. This is only required if ``characteristic_width_lookup_file`` is ``None``. It is ignored otherwise. @@ -95,7 +87,7 @@ def __init__( self.base_plate_temperature = base_plate_temperature def _to_request(self) -> TuneMaterialRequest: - """Convert this object into a material tuning request message""" + """Convert this object into a material tuning request message.""" input = MaterialTuningInputMessage( allowable_error=self.allowable_error, max_iterations=self.max_iterations, diff --git a/src/ansys/additive/microstructure.py b/src/ansys/additive/microstructure.py index 54a6d23da..426657027 100644 --- a/src/ansys/additive/microstructure.py +++ b/src/ansys/additive/microstructure.py @@ -19,7 +19,6 @@ class MicrostructureInput: """Input parameters for microstructure simulation. Units are SI (m, kg, s, K) unless otherwise noted. - """ #: Default minimum x, y, z, position coordinate (m). @@ -228,7 +227,9 @@ def sample_min_z(self, value: float): @property def sample_size_x(self) -> float: """Size of the geometry sample in the x direction (m). - Valid values are 0.001 to 0.01.""" + + Valid values are 0.001 to 0.01. + """ return self._sample_size_x @sample_size_x.setter @@ -244,7 +245,9 @@ def sample_size_x(self, value: float): @property def sample_size_y(self) -> float: """Size of the geometry sample in the y direction (m). - Valid values are 0.001 to 0.01.""" + + Valid values are 0.001 to 0.01. + """ return self._sample_size_y @sample_size_y.setter @@ -260,7 +263,9 @@ def sample_size_y(self, value: float): @property def sample_size_z(self) -> float: """Size of the geometry sample in the z direction (m). - Valid values are 0.001 to 0.01.""" + + Valid values are 0.001 to 0.01. + """ return self._sample_size_z @sample_size_z.setter @@ -276,7 +281,9 @@ def sample_size_z(self, value: float): @property def sensor_dimension(self) -> float: """Dimension of the sensor (m). - Valid values are 0.0001 to 0.001.""" + + Valid values are 0.0001 to 0.001. + """ return self._sensor_dimension @sensor_dimension.setter @@ -310,8 +317,8 @@ def sensor_dimension(self, value: float): @property def use_provided_thermal_parameters(self) -> bool: - """If ``True``, indicates that cooling_rate, thermal_gradient, melt_pool_depth and - melt_pool_width have been provided by the user.""" + """If ``True``, indicates that cooling_rate, thermal_gradient, + melt_pool_depth and melt_pool_width have been provided by the user.""" return self._use_provided_thermal_parameters @use_provided_thermal_parameters.setter @@ -321,7 +328,9 @@ def use_provided_thermal_parameters(self, value: bool): @property def cooling_rate(self) -> float: """Material cooling rate (K/s). - Valid values are 1e5 to 1e7.""" + + Valid values are 1e5 to 1e7. + """ return self._cooling_rate @cooling_rate.setter @@ -334,7 +343,9 @@ def cooling_rate(self, value: float): @property def thermal_gradient(self) -> float: """Material thermal gradient (K/m). - Valid values are 1e5 to 1e8.""" + + Valid values are 1e5 to 1e8. + """ return self._thermal_gradient @thermal_gradient.setter @@ -347,7 +358,12 @@ def thermal_gradient(self, value: float): @property def melt_pool_width(self) -> float: """Melt pool width (m). - Valid values are 7.5e-5 to 8e-4.""" + + This is the width of the melt pool measured at the top of the powder layer + which corresponds to the ``WIDTH``value in + :class:`MeltPoolColumnNames `. + Valid values are 7.5e-5 to 8e-4. + """ return self._melt_pool_width @melt_pool_width.setter @@ -360,7 +376,12 @@ def melt_pool_width(self, value: float): @property def melt_pool_depth(self) -> float: """Melt pool depth (m). - Valid values are 1.5e-5 to 8e-4.""" + + This is the depth of the melt pool as measured from the top of the powder layer + which corresponds to the ``DEPTH``value in + :class:`MeltPoolColumnNames `. + Valid values are 1.5e-5 to 8e-4. + """ return self._melt_pool_depth @melt_pool_depth.setter @@ -373,7 +394,9 @@ def melt_pool_depth(self, value: float): @property def random_seed(self) -> int: """Random seed for the simulation. - Valid values are 1 to 4294967295.""" + + Valid values are 1 to 4294967295. + """ return self._random_seed @random_seed.setter @@ -382,7 +405,7 @@ def random_seed(self, value: int): self._random_seed = value def _to_simulation_request(self) -> SimulationRequest: - """Convert this object into a simulation request message""" + """Convert this object into a simulation request message.""" input = MicrostructureInputMessage( machine=self.machine._to_machine_message(), material=self.material._to_material_message(), @@ -420,9 +443,8 @@ class CircleEquivalenceColumnNames: class MicrostructureSummary: """Summary of a microstructure simulation. - Units are typically SI (m, kg, s, K), however, some of the values listed - below do not use SI units. See descriptions for details. - + Units are typically SI (m, kg, s, K), however, some of the values + listed below do not use SI units. See descriptions for details. """ def __init__( @@ -489,8 +511,7 @@ def yz_vtk(self) -> str: @property def xy_circle_equivalence(self) -> pd.DataFrame: - """ - Circle equivalence data for XY plane. + """Circle equivalence data for XY plane. See :class:`CircleEquivalenceColumnNames` for data frame column names. """ @@ -498,8 +519,7 @@ def xy_circle_equivalence(self) -> pd.DataFrame: @property def xz_circle_equivalence(self) -> pd.DataFrame: - """ - Circle equivalence data for XZ plane. + """Circle equivalence data for XZ plane. See :class:`CircleEquivalenceColumnNames` for data frame column names. """ @@ -507,8 +527,7 @@ def xz_circle_equivalence(self) -> pd.DataFrame: @property def yz_circle_equivalence(self) -> pd.DataFrame: - """ - Circle equivalence data for YZ plane. + """Circle equivalence data for YZ plane. See :class:`CircleEquivalenceColumnNames` for data frame column names. """ diff --git a/src/ansys/additive/parametric_study/__init__.py b/src/ansys/additive/parametric_study/__init__.py index 8ec127599..4bcee7924 100644 --- a/src/ansys/additive/parametric_study/__init__.py +++ b/src/ansys/additive/parametric_study/__init__.py @@ -1,3 +1,10 @@ # (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. -from .parametric_study import * +from ansys.additive.parametric_study.constants import ( + DEFAULT_ITERATION, + DEFAULT_PRIORITY, + ColumnNames, +) +from ansys.additive.parametric_study.parametric_runner import ParametricRunner +from ansys.additive.parametric_study.parametric_study import ParametricStudy +from ansys.additive.parametric_study.parametric_utils import build_rate, energy_density diff --git a/src/ansys/additive/parametric_study/constants.py b/src/ansys/additive/parametric_study/constants.py index b7536d8a8..8e6142539 100644 --- a/src/ansys/additive/parametric_study/constants.py +++ b/src/ansys/additive/parametric_study/constants.py @@ -1,8 +1,9 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. class ColumnNames: """Column names for the parametric study data frame. - Values are stored internally as a :class:`Pandas DataFrame `. - The column names are defined here. + Values are stored internally as a :class:`Pandas DataFrame + `. The column names are defined here. """ #: Name of the parametric summary project. diff --git a/src/ansys/additive/parametric_study/parametric_runner.py b/src/ansys/additive/parametric_study/parametric_runner.py index b8a205112..428271335 100644 --- a/src/ansys/additive/parametric_study/parametric_runner.py +++ b/src/ansys/additive/parametric_study/parametric_runner.py @@ -29,8 +29,8 @@ def simulate( type: Optional[List[SimulationType]] = None, priority: Optional[int] = None, ) -> List[Union[MicrostructureSummary, PorositySummary, SingleBeadSummary]]: - """Run the simulations in the parametric study with ``SimulationStatus.PENDING`` in the - ``ColumnNames.STATUS`` column. + """Run the simulations in the parametric study with + ``SimulationStatus.PENDING`` in the ``ColumnNames.STATUS`` column. Execution order is determined by the values in the ``ColumnNames.PRIORITY`` column. Lower values are interpreted as having higher priority and will be run first. @@ -50,7 +50,6 @@ def simulate( ------- List[Union[MicrostructureSummary, PorositySummary, SingleBeadSummary]] A list of simulation summaries. - """ if type is None: type = [ diff --git a/src/ansys/additive/parametric_study/parametric_study.py b/src/ansys/additive/parametric_study/parametric_study.py index 6e2fab599..d0ea9e752 100644 --- a/src/ansys/additive/parametric_study/parametric_study.py +++ b/src/ansys/additive/parametric_study/parametric_study.py @@ -49,9 +49,12 @@ def __eq__(self, other): ) def data_frame(self) -> pd.DataFrame: - """Return a copy of the internal parametric study :class:`DataFrame `. + """Return a copy of the internal parametric study :class:`DataFrame + `. + See :class:`ColumnNames` for the column names used in the returned ``DataFrame``. - .. note:: Updating the returned ``DataFrame`` will not update the internal ``DataFrame``.""" + .. note:: Updating the returned ``DataFrame`` will not update the internal ``DataFrame``. + """ return self._data_frame.copy() def run_simulations( @@ -62,10 +65,12 @@ def run_simulations( # workers: int = 1, # threads: int = 4, ): - """Run the simulations in the parametric study with ``SimulationStatus.PENDING`` in the - ``ColumnNames.STATUS`` column. - Execution order is determined by the values in the ``ColumnNames.PRIORITY`` column. - Lower values are interpreted as having higher priority and will be run first. + """Run the simulations in the parametric study with + ``SimulationStatus.PENDING`` in the ``ColumnNames.STATUS`` column. + Execution order is determined by the values in the + ``ColumnNames.PRIORITY`` column. Lower values are interpreted as having + higher priority and will be run first. + Parameters ---------- additive: Additive @@ -101,7 +106,6 @@ def save(self, filename): ---------- filename : str Name of file to save the parametric study to. - """ with open(filename, "wb") as f: dill.dump(self, f) @@ -119,7 +123,6 @@ def load(filename): ------- ParametricStudy The loaded parametric study. - """ with open(filename, "rb") as f: return dill.load(f) @@ -129,7 +132,8 @@ def add_summaries( summaries: List[Union[SingleBeadSummary, PorositySummary, MicrostructureSummary]], iteration: int = DEFAULT_ITERATION, ): - """Add summaries of previously executed simulations to the parametric study. + """Add summaries of previously executed simulations to the parametric + study. This function adds new rows to the parametric study data frame. To update existing rows, use :meth:`update`. @@ -138,7 +142,6 @@ def add_summaries( ---------- summaries : list[SingleBeadSummary | PorositySummary | MicrostructureSummary] List of simulation result summaries to add to the parametric study. - """ for summary in summaries: if isinstance(summary, SingleBeadSummary): @@ -285,7 +288,6 @@ def _common_param_to_dict( ------- Dict[str, Any] The dictionary of common parameters. - """ return { ColumnNames.PROJECT: self._project_name, @@ -868,7 +870,6 @@ def update( ---------- summaries : List[Union[SingleBeadSummary, PorositySummary, MicrostructureSummary, SimulationError]] The list of simulation summaries to use for updating parametric study data frame. - """ for summary in summaries: if isinstance(summary, SingleBeadSummary): @@ -885,7 +886,8 @@ def update( raise TypeError(f"Invalid simulation summary type: {type(summary)}") def _update_single_bead(self, summary: SingleBeadSummary): - """Update the results of a single bead simulation in the parametric study data frame.""" + """Update the results of a single bead simulation in the parametric + study data frame.""" median_df = summary.melt_pool.data_frame().median() idx = self._data_frame[ (self._data_frame[ColumnNames.ID] == summary.input.id) @@ -920,7 +922,8 @@ def _update_single_bead(self, summary: SingleBeadSummary): ) def _update_porosity(self, summary: PorositySummary): - """Update the results of a porosity simulation in the parametric study data frame.""" + """Update the results of a porosity simulation in the parametric study + data frame.""" idx = self._data_frame[ (self._data_frame[ColumnNames.ID] == summary.input.id) & (self._data_frame[ColumnNames.TYPE] == SimulationType.POROSITY) @@ -930,7 +933,8 @@ def _update_porosity(self, summary: PorositySummary): self._data_frame.loc[idx, ColumnNames.RELATIVE_DENSITY] = summary.relative_density def _update_microstructure(self, summary: MicrostructureSummary): - """Update the results of a microstructure simulation in the parametric study data frame.""" + """Update the results of a microstructure simulation in the parametric + study data frame.""" idx = self._data_frame[ (self._data_frame[ColumnNames.ID] == summary.input.id) & (self._data_frame[ColumnNames.TYPE] == SimulationType.MICROSTRUCTURE) @@ -948,7 +952,8 @@ def add_inputs( priority: int = DEFAULT_PRIORITY, status: SimulationStatus = SimulationStatus.PENDING, ): - """Add new rows to the parametric study data frame for the specified simulation inputs. + """Add new rows to the parametric study data frame for the specified + simulation inputs. Parameters ---------- @@ -960,7 +965,6 @@ def add_inputs( priority : int The priority for the simulations. - """ for input in inputs: dict = {} @@ -1076,7 +1080,6 @@ def table(self, max_height: int = 300) -> pn.pane.DataFrame: Returns ------- :class:`panel.pane.DataFrame ` - """ return pn.pane.DataFrame( self._data_frame, sizing_mode="stretch_both", max_height=max_height diff --git a/src/ansys/additive/parametric_study/parametric_utils.py b/src/ansys/additive/parametric_study/parametric_utils.py index 8b285742d..484cfa4a6 100644 --- a/src/ansys/additive/parametric_study/parametric_utils.py +++ b/src/ansys/additive/parametric_study/parametric_utils.py @@ -26,7 +26,6 @@ def build_rate( The volumetric build rate if hatch spacing is provided, otherwise an area build rate. If input units are m/s, m, m, the output units are m^3/s or m^2/s. - """ if hatch_spacing is None: return scan_speed * layer_thickness @@ -42,7 +41,7 @@ def energy_density( """Calculate the energy density. This is an approximate value useful for comparison. The returned value is simply - the laser power divided by the build rate. See :method:`build_rate`. + the laser power divided by the build rate. See :meth:`build_rate`. Parameters ---------- @@ -61,7 +60,6 @@ def energy_density( The volumetric energy density if hatch spacing is provided, otherwise an area energy density. If input units are W, m/s, m, m, the output units are J/m^3 or J/m^2. - """ br = build_rate(scan_speed, layer_thickness, hatch_spacing) return laser_power / br if br else float("nan") diff --git a/src/ansys/additive/porosity.py b/src/ansys/additive/porosity.py index eb3b4fd4d..343ee6ea6 100644 --- a/src/ansys/additive/porosity.py +++ b/src/ansys/additive/porosity.py @@ -10,19 +10,20 @@ class PorosityInput: """Input parameters for porosity simulation. - ``id: string`` + Parameters + ---------- + id: string User provided identifier for this simulation. - ``size_x: float`` + size_x: float Size of simulated sample in x dimension (m), valid values: 0.001 to 0.01. - ``size_y: float`` + size_y: float Size of simulated sample in y dimension (m), valid values: 0.001 to 0.01. - ``size_z: float`` + size_z: float Size of simulated sample in z dimension (m), valid values: 0.001 to 0.01. - ``machine: AdditiveMachine`` + machine: AdditiveMachine Machine related parameters. - ``material: AdditiveMaterial`` + material: AdditiveMaterial Material used during simulation. - """ #: Default sample size (m) in each dimension. @@ -102,7 +103,9 @@ def material(self, value): @property def size_x(self): """Size of simulated sample in x dimension (m). - Valid values are from 1e-3 to 1e-2 m (1 to 10 mm).""" + + Valid values are from 1e-3 to 1e-2 m (1 to 10 mm). + """ return self._size_x @size_x.setter @@ -113,7 +116,9 @@ def size_x(self, value): @property def size_y(self): """Size of simulated sample in y dimension (m). - Valid values are from 1e-3 to 1e-2 m (1 to 10 mm).""" + + Valid values are from 1e-3 to 1e-2 m (1 to 10 mm). + """ return self._size_y @size_y.setter @@ -124,7 +129,9 @@ def size_y(self, value): @property def size_z(self): """Size of simulated sample in z dimension (m). - Valid values are from 1e-3 to 1e-2 m (1 to 10 mm).""" + + Valid values are from 1e-3 to 1e-2 m (1 to 10 mm). + """ return self._size_z @size_z.setter @@ -133,7 +140,7 @@ def size_z(self, value): self._size_z = value def _to_simulation_request(self) -> SimulationRequest: - """Convert this object into a simulation request message""" + """Convert this object into a simulation request message.""" input = PorosityInputMessage( machine=self.machine._to_machine_message(), material=self.material._to_material_message(), @@ -148,7 +155,6 @@ class PorositySummary: """Summary of a porosity simulation. Units are SI unless otherwise noted. - """ def __init__( @@ -170,7 +176,8 @@ def input(self) -> PorosityInput: @property def relative_density(self) -> float: - """Ratio of the density of the simulated sample to a completely solid sample.""" + """Ratio of the density of the simulated sample to a completely solid + sample.""" return self._relative_density def __repr__(self): diff --git a/src/ansys/additive/server_utils.py b/src/ansys/additive/server_utils.py index 8411d2bb0..3b08b1c0e 100644 --- a/src/ansys/additive/server_utils.py +++ b/src/ansys/additive/server_utils.py @@ -1,3 +1,4 @@ +# (c) 2023 ANSYS, Inc. Unauthorized use, distribution, or duplication is prohibited. from datetime import datetime import os import subprocess @@ -14,10 +15,8 @@ def launch_server(port: int, cwd: str = USER_DATA_PATH) -> subprocess.Popen: Parameters ---------- - port: int Port number to use for gRPC connections. - cwd: str Current working directory to use for the server process. @@ -25,7 +24,6 @@ def launch_server(port: int, cwd: str = USER_DATA_PATH) -> subprocess.Popen: ------- process: subprocess.Popen Server process. To stop the server, call ``kill()`` on the returned object. - """ ver = DEFAULT_ANSYS_VERSION server_exe = "" @@ -80,7 +78,6 @@ def find_open_port() -> int: ------- port: int Open port number. *Note:* this port may be taken by the time you try to use it. - """ import socket diff --git a/src/ansys/additive/single_bead.py b/src/ansys/additive/single_bead.py index 77a0c1e9e..5fd67133a 100644 --- a/src/ansys/additive/single_bead.py +++ b/src/ansys/additive/single_bead.py @@ -81,7 +81,9 @@ def material(self, value): @property def bead_length(self): """Length of bead to simulate (m). - Valid values are from 1e-3 to 1e-2 m (1 to 10 mm).""" + + Valid values are from 1e-3 to 1e-2 m (1 to 10 mm). + """ return self._bead_length @bead_length.setter @@ -90,7 +92,7 @@ def bead_length(self, value): self._bead_length = value def _to_simulation_request(self) -> SimulationRequest: - """Convert this object into a simulation request message""" + """Convert this object into a simulation request message.""" input = SingleBeadInputMessage( machine=self.machine._to_machine_message(), material=self.material._to_material_message(), @@ -118,7 +120,6 @@ class MeltPool: """Container for the melt pool evolution during a single bead simulation. Units are SI unless otherwise noted. - """ def __init__(self, msg: MeltPoolMessage): @@ -141,7 +142,8 @@ def __init__(self, msg: MeltPoolMessage): self._df.index.name = "bead_length" def data_frame(self) -> DataFrame: - """Return :class:`Pandas DataFrame ` containing melt pool data. + """Return :class:`Pandas DataFrame ` containing melt + pool data. Values are in meters. diff --git a/src/ansys/additive/thermal_history.py b/src/ansys/additive/thermal_history.py index f958c771f..2ebcef475 100644 --- a/src/ansys/additive/thermal_history.py +++ b/src/ansys/additive/thermal_history.py @@ -22,7 +22,6 @@ class Range: Minimum value. max: float Maximum value. - """ def __init__(self, **kwargs): @@ -49,7 +48,8 @@ def __eq__(self, other: object) -> bool: return True def _to_range_message(self) -> RangeMessage: - """Create a range message to send to the server based upon this object.""" + """Create a range message to send to the server based upon this + object.""" return RangeMessage(min=self.min, max=self.max) @@ -62,7 +62,6 @@ class CoaxialAverageSensorInputs: z_heights: Range[] (meters) Array of ranges along the z axis of the geometry. The simulated sensor will follow the scan path for each deposit layer within each range. - """ def __init__(self, **kwargs): @@ -91,7 +90,8 @@ def __eq__(self, other: object) -> bool: return True def _to_coaxial_average_sensor_inputs_message(self) -> CoaxialAverageSensorInputsMessage: - """Create a coaxial average sensor input message to send to the server based upon this object.""" + """Create a coaxial average sensor input message to send to the server + based upon this object.""" msg = CoaxialAverageSensorInputsMessage(sensor_radius=self.radius) for z in self.z_heights: msg.z_heights.append(z._to_range_message()) @@ -111,7 +111,6 @@ class ThermalHistoryInput: Geometry to use in simulation. coax_ave_sensor_inputs: CoaxialAverageSensorInputs Coaxial average sensor definition. - """ def __init__(self, **kwargs): @@ -159,7 +158,7 @@ def geometry(self, value): self._geometry = value def _to_simulation_request(self, remote_geometry_path: str) -> SimulationRequest: - """Convert this object into a simulation request message""" + """Convert this object into a simulation request message.""" if not self.geometry: raise ValueError("Attempted to create simulation request without defining geometry") diff --git a/tests/test_utils.py b/tests/test_utils.py index e8cace9d5..490cb4129 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,7 +12,6 @@ def get_test_material() -> AdditiveMaterial: - return AdditiveMaterial( absorptivity_maximum=101, absorptivity_minimum=102, diff --git a/tox.ini b/tox.ini index b4454c851..7a0b15c02 100644 --- a/tox.ini +++ b/tox.ini @@ -44,4 +44,4 @@ passenv = BUILD_EXAMPLES_LONG extras = doc commands = - sphinx-build -d "{toxworkdir}/doc_doctree" doc/source "{toxworkdir}/doc_out" --color -vW -bhtml + sphinx-build -d "{toxworkdir}/doc_doctree" doc/source "{toxworkdir}/_build/html" --color -vW -bhtml