diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 7ef0d5aaf8c..198e7b56f92 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -128,7 +128,7 @@ jobs: set -ex echo $(PAT) | docker login -u $(GH_USERNAME) --password-stdin docker.pkg.github.com docker pull $(DPF_IMAGE) - docker run --restart always --name dpf -v `pwd`:/dpf -p $(DPF_PORT):50054 $(DPF_IMAGE) > log.txt & + docker run --restart always --name dpf -v `pwd`:/dpf -v /tmp:/dpf/_cache -p $(DPF_PORT):50054 $(DPF_IMAGE) > log.txt & grep -q 'server started on ip' <(timeout 60 tail -f log.txt) python -c "from ansys.dpf import core; core.connect_to_server(port=$(DPF_PORT)); print('Python Connected')" displayName: Pull, launch, and validate DPF service diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index dd4a395cbae..9277deb1fb0 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -44,7 +44,7 @@ jobs: - name: Pull, launch, and validate DPF service run: | echo $PAT | docker login -u $GH_USERNAME --password-stdin docker.pkg.github.com - docker run --restart always --name dpf -v `pwd`:/dpf -p $DPF_PORT:50054 $DPF_IMAGE > log.txt & + docker run --restart always --name dpf -v `pwd`:/dpf -v /tmp:/dpf/_cache -p $DPF_PORT:50054 $DPF_IMAGE > log.txt & grep -q 'server started on ip' <(timeout 60 tail -f log.txt) python -c "import os; from ansys.dpf import core; core.connect_to_server(port=os.environ['DPF_PORT']); print('Python Connected')" env: diff --git a/ansys/dpf/core/__init__.py b/ansys/dpf/core/__init__.py index 6392f8c8ea6..e83124bf7b9 100644 --- a/ansys/dpf/core/__init__.py +++ b/ansys/dpf/core/__init__.py @@ -40,11 +40,10 @@ if os.environ.get('DPF_DOCKER', False): # pragma: no cover # Running DPF within docker (likely for CI) # path must be relative to DPF directory - _module_path = os.path.dirname(inspect.getfile(inspect.currentframe())) - EXAMPLES_PATH = os.path.join(_module_path, 'examples', '_cache') - if not os.path.isdir(EXAMPLES_PATH): - os.makedirs(EXAMPLES_PATH) - + # + # assumes the following docker mount: + # -v /tmp:/dpf/_cache + EXAMPLES_PATH = '/tmp' else: try: import appdirs diff --git a/ansys/dpf/core/_version.py b/ansys/dpf/core/_version.py index d04b399b8be..444ea2653f9 100644 --- a/ansys/dpf/core/_version.py +++ b/ansys/dpf/core/_version.py @@ -1,6 +1,6 @@ """Version for ansys-dpf-core""" # major, minor, patch -version_info = 0, 1, 2 +version_info = 0, 2, 0 # Nice string for the version __version__ = '.'.join(map(str, version_info)) diff --git a/ansys/dpf/core/collection.py b/ansys/dpf/core/collection.py index fb62b5ee296..22ef89cdce6 100644 --- a/ansys/dpf/core/collection.py +++ b/ansys/dpf/core/collection.py @@ -210,7 +210,7 @@ def __getitem__(self, index): if not self_len: raise IndexError('This collection contains no items') if index >= self_len: - raise IndexError(f'This collection contains only {self_len} entries') + raise IndexError(f'This collection contains only {self_len} entrie(s)') return self._get_entries(index) diff --git a/tests/testfiles/complex/fileComplex.rst b/ansys/dpf/core/examples/complex.rst similarity index 100% rename from tests/testfiles/complex/fileComplex.rst rename to ansys/dpf/core/examples/complex.rst diff --git a/ansys/dpf/core/examples/downloads.py b/ansys/dpf/core/examples/downloads.py index 2f1f3ae60f9..cdce5c2fd8f 100644 --- a/ansys/dpf/core/examples/downloads.py +++ b/ansys/dpf/core/examples/downloads.py @@ -23,7 +23,7 @@ def _retrieve_file(url, filename, directory): local_path = os.path.join(EXAMPLES_PATH, directory, os.path.basename(filename)) local_path_no_zip = local_path.replace('.zip', '') if os.path.isfile(local_path_no_zip) or os.path.isdir(local_path_no_zip): - return local_path_no_zip, None + return local_path_no_zip # grab the correct url retriever urlretrieve = urllib.request.urlretrieve @@ -44,13 +44,84 @@ def _download_file(directory, filename): if os.environ.get('DPF_DOCKER', False): # pragma: no cover # override path if running on docker as path must be relative # to docker mount - local_path = os.path.join('/dpf/ansys/dpf/core/examples/_cache/', directory, - filename) + # + # Assumes the following mapping in docker + # DWN_CSH=/tmp/dpf_cache + # -v $DWN_CSH:/dpf/_cache + local_path = os.path.join('/dpf/_cache', directory, filename) return local_path ############################################################################### # front-facing downloads -def download_transient_result(): - """Download an example transient result and return the download path""" +def download_transient_result() -> str: + """Download an example transient result file and return the download path. + + Examples files are downloaded to a persistent cache to avoid + re-downloading the same file twice. + + Returns + ------- + str + Path to the example file. + + Examples + -------- + Download an example result file and return the path of the file + + >>> from ansys.dpf.core import examples + >>> path = examples.transient_result + >>> path + 'C:/Users/user/AppData/local/temp/transient.rst' + + """ return _download_file('transient', 'transient.rst') + + +def download_all_kinds_of_complexity() -> str: + """Download an example static result and return the download path. + + Examples files are downloaded to a persistent cache to avoid + re-downloading the same file twice. + + Returns + ------- + str + Path to the example file. + + Examples + -------- + Download an example result file and return the path of the file + + >>> from ansys.dpf.core import examples + >>> path = examples.download_all_kinds_of_complexity + >>> path + 'C:/Users/user/AppData/local/temp/allKindOfComplexity.rst' + + """ + return _download_file('testing', 'allKindOfComplexity.rst') + + +def download_all_kinds_of_complexity_modal() -> str: + """Download an example result file from a static modal analsys and + return the download path. + + Examples files are downloaded to a persistent cache to avoid + re-downloading the same file twice. + + Returns + ------- + str + Path to the example file. + + Examples + -------- + Download an example result file and return the path of the file + + >>> from ansys.dpf.core import examples + >>> path = examples.download_all_kinds_of_complexity_modal + >>> path + 'C:/Users/user/AppData/local/temp/modal_allKindOfComplexity.rst' + + """ + return _download_file('testing', 'modal_allKindOfComplexity.rst') diff --git a/ansys/dpf/core/examples/examples.py b/ansys/dpf/core/examples/examples.py index e3d388a9987..13d65ed5d4c 100644 --- a/ansys/dpf/core/examples/examples.py +++ b/ansys/dpf/core/examples/examples.py @@ -11,3 +11,9 @@ # this files can be imported with from `ansys.dpf.core import examples`: simple_bar = os.path.join(_module_path, 'ASimpleBar.rst') static_rst = os.path.join(_module_path, 'static.rst') +complex_rst = os.path.join(_module_path, 'complex.rst') +multishells_rst = os.path.join(_module_path, 'model_with_ns.rst') +electric_therm = os.path.join(_module_path, 'rth', 'rth_electric.rth') +steady_therm = os.path.join(_module_path, 'rth', 'rth_steady.rth') +transient_therm = os.path.join(_module_path, 'rth', 'rth_transient.rth') +msup_transient = os.path.join(_module_path, 'msup_transient_plate1.rst') diff --git a/ansys/dpf/core/examples/model_with_ns.rst b/ansys/dpf/core/examples/model_with_ns.rst new file mode 100644 index 00000000000..acbb9cdefae Binary files /dev/null and b/ansys/dpf/core/examples/model_with_ns.rst differ diff --git a/tests/testfiles/msup_transient/plate1.rst b/ansys/dpf/core/examples/msup_transient_plate1.rst similarity index 100% rename from tests/testfiles/msup_transient/plate1.rst rename to ansys/dpf/core/examples/msup_transient_plate1.rst diff --git a/ansys/dpf/core/examples/rth/rth_electric.rth b/ansys/dpf/core/examples/rth/rth_electric.rth new file mode 100644 index 00000000000..a7152cdca41 Binary files /dev/null and b/ansys/dpf/core/examples/rth/rth_electric.rth differ diff --git a/ansys/dpf/core/examples/rth/rth_steady.rth b/ansys/dpf/core/examples/rth/rth_steady.rth new file mode 100644 index 00000000000..d78f36264bf Binary files /dev/null and b/ansys/dpf/core/examples/rth/rth_steady.rth differ diff --git a/ansys/dpf/core/examples/rth/rth_transient.rth b/ansys/dpf/core/examples/rth/rth_transient.rth new file mode 100644 index 00000000000..57c579618d4 Binary files /dev/null and b/ansys/dpf/core/examples/rth/rth_transient.rth differ diff --git a/ansys/dpf/core/plotter.py b/ansys/dpf/core/plotter.py index 9946ea6c050..8d00f22f6c0 100644 --- a/ansys/dpf/core/plotter.py +++ b/ansys/dpf/core/plotter.py @@ -1,6 +1,7 @@ """Dpf plotter class is contained in this module. Allows to plot a mesh and a fields container using pyvista.""" +import tempfile import pyvista as pv import matplotlib.pyplot as pyplot @@ -174,7 +175,11 @@ def plot_contour(self, field_or_fields_container, notebook=None, break # Merge field data into a single array - overall_data = np.full((len(mesh_location), component_count), np.nan) + if component_count > 1: + overall_data = np.full((len(mesh_location), component_count), np.nan) + else: + overall_data = np.full(len(mesh_location), np.nan) + for field in fields_container: ind = mesh_location.map_scoping(field.scoping) overall_data[ind] = field.data @@ -194,31 +199,38 @@ def plot_contour(self, field_or_fields_container, notebook=None, return plotter.show() def _plot_contour_using_vtk_file(self, fields_container, notebook=None): - """Plot the contour result on its mesh support. The obtained figure depends on the - support (can be a meshed_region or a time_freq_support). - If transient analysis, plot the last result. - - This method is private, publishes a vtk file and print (using pyvista) from this file.""" + """Plot the contour result on its mesh support. The obtained + figure depends on the support (can be a meshed_region or a + time_freq_support). If transient analysis, plot the last + result. + + This method is private. DPF publishes a vtk file and displays + this file using pyvista. + """ plotter = pv.Plotter(notebook=notebook) # mesh_provider = Operator("MeshProvider") # mesh_provider.inputs.data_sources.connect(self._evaluator._model.metadata.data_sources) + + # create a temporary file at the default temp directory + path = os.path.join(tempfile.gettempdir(), 'dpf_temp_hokflb2j9s.vtk') + vtk_export = dpf.core.Operator("vtk_export") - path = os.getcwd() - file_name = "dpf_temporary_hokflb2j9sjd0a3.vtk" - path += "/" + file_name vtk_export.inputs.mesh.connect(self._mesh) vtk_export.inputs.fields1.connect(fields_container) vtk_export.inputs.file_path.connect(path) vtk_export.run() grid = pv.read(path) + if os.path.exists(path): os.remove(path) + names = grid.array_names field_name = fields_container[0].name - for n in names: #get new name (for example if time_steps) + for n in names: # get new name (for example if time_steps) if field_name in n: - field_name = n #default: will plot the last time_step + field_name = n # default: will plot the last time_step val = grid.get_array(field_name) - plotter.add_mesh(grid, scalars=val, stitle = field_name, show_edges=True) + plotter.add_mesh(grid, scalars=val, stitle=field_name, show_edges=True) plotter.add_axes() plotter.show() + diff --git a/docker/run_image.sh b/docker/run_image.sh index fae98327f3b..e84ca240aea 100755 --- a/docker/run_image.sh +++ b/docker/run_image.sh @@ -17,5 +17,5 @@ # and 50054 for DPF_IP and DPF_PORT respectively. source IMAGE_NAME -docker run -it --rm -v `pwd`/../:/dpf -p 50054:50054 --name dpf $IMAGE +docker run -it --rm -v `pwd`/../:/dpf -v /tmp:/dpf/_cache -p 50054:50054 --name dpf $IMAGE diff --git a/ignore_words.txt b/ignore_words.txt index 17566ac4068..86839152b39 100644 --- a/ignore_words.txt +++ b/ignore_words.txt @@ -3,3 +3,4 @@ pres WAN filname ans +entrie diff --git a/setup.py b/setup.py index 4cec496c133..52b6e8a5314 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,6 @@ setup( name='ansys-dpf-core', packages=['ansys.dpf.core', 'ansys.dpf.core.examples'], - author='Camille Bellot, Ramdane Lagha', version=__version__, description='DPF Python gRPC client', @@ -41,7 +40,15 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', ], - package_data={'ansys.dpf.core.examples': ['ASimpleBar.rst', 'static.rst']}, + package_data={'ansys.dpf.core.examples': ['ASimpleBar.rst', + 'static.rst', + 'complex.rst', + 'model_with_ns.rst', + 'msup_transient_plate1.rst', + 'rth/rth_electric.rth', + 'rth/rth_steady.rth', + 'rth/rth_transient.rth', + ]}, python_requires='>=3.5.*', install_requires=install_requires, ) diff --git a/tests/conftest.py b/tests/conftest.py index b0101bb094a..dc45e8731a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,7 +43,7 @@ def resolve_test_file(basename, additional_path=''): @pytest.fixture() def allkindofcomplexity(): """Resolve the path of the "allKindOfComplexity.rst" result file.""" - return resolve_test_file('allKindOfComplexity.rst') + return examples.download_all_kinds_of_complexity() @pytest.fixture() @@ -85,13 +85,13 @@ def simple_rst(): @pytest.fixture() def multishells(): """Resolve the path of the "rst_operators/multishells.rst" result file.""" - return resolve_test_file('multishells.rst', 'rst_operators') + return examples.multishells_rst @pytest.fixture() def complex_model(): """Resolve the path of the "complex/fileComplex.rst" result file.""" - return resolve_test_file('fileComplex.rst', 'complex') + return examples.complex_rst @pytest.fixture() @@ -101,7 +101,7 @@ def plate_msup(): Originally: UnitTestDataFiles/DataProcessing/expansion/msup/Transient/plate1/file.rst """ - return resolve_test_file('plate1.rst', 'msup_transient') + return examples.msup_transient @pytest.fixture(scope="session", autouse=True) diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 00000000000..c08f8f549df --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,30 @@ +"""Verify all examples can be accessed or downloaded""" +import pytest + +from ansys.dpf.core import Model +from ansys.dpf.core import examples + + +def test_download_all_kinds_of_complexity_modal(): + path = examples.download_all_kinds_of_complexity_modal() + assert isinstance(Model(path), Model) + + +def test_download_all_kinds_of_complexity(): + path = examples.download_all_kinds_of_complexity() + assert isinstance(Model(path), Model) + + +@pytest.mark.parametrize("example", ['simple_bar', + 'static_rst', + 'complex_rst', + 'multishells_rst', + 'electric_therm', + 'steady_therm', + 'transient_therm', + 'msup_transient']) +def test_examples(example): + # get example by string so we can parameterize it without breaking + # collection + path = getattr(globals()['examples'], example) + assert isinstance(Model(path), Model) diff --git a/tests/test_model.py b/tests/test_model.py index 799fc34ea15..ff07f128bd5 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -51,9 +51,9 @@ def test_kinetic(static_model): def test_str_model(static_model): + assert 'Physics Type: Mecanic' in str(static_model) assert '81 nodes' in str(static_model) assert 'Unit: m' in str(static_model) - assert 'Physics Type: Mecanic' in str(static_model) # @pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting") diff --git a/tests/test_plotter.py b/tests/test_plotter.py index acad49fead8..20f74f2856f 100644 --- a/tests/test_plotter.py +++ b/tests/test_plotter.py @@ -1,4 +1,4 @@ -import unittest +import os from pyvista.plotting.renderer import CameraPosition import pytest @@ -9,6 +9,9 @@ from ansys.dpf import core from ansys.dpf.core import errors as dpf_errors +# currently running dpf on docker. Used for testing on CI +RUNNING_DOCKER = os.environ.get('DPF_DOCKER', False) + def test_chart_plotter(plate_msup): model = Model(plate_msup) @@ -209,3 +212,12 @@ def test_throw_complex_file(complex_model): mesh = model.metadata.meshed_region with pytest.raises(dpf_errors.ComplexPlottingError): mesh.plot(fc) + + +@pytest.mark.skipif(RUNNING_DOCKER, reason='Path hidden within docker container') +def test_plot_contour_using_vtk_file(complex_model): + model = core.Model(complex_model) + stress = model.results.displacement() + fc = stress.outputs.fields_container() + pl = DpfPlotter(model.metadata.meshed_region) + pl._plot_contour_using_vtk_file(fc) diff --git a/tests/testfiles/allKindOfComplexity.rst b/tests/testfiles/allKindOfComplexity.rst deleted file mode 100644 index 6be5534dc70..00000000000 Binary files a/tests/testfiles/allKindOfComplexity.rst and /dev/null differ