diff --git a/doc/source/api_reference/post_processings.rst b/doc/source/api_reference/post_processings.rst index e75acd7..6098b28 100644 --- a/doc/source/api_reference/post_processings.rst +++ b/doc/source/api_reference/post_processings.rst @@ -56,6 +56,10 @@ Available postprocessings :members: +.. autoclass:: SurfaceVTPTDLocation() + :members: + + .. autoclass:: VolumeVTU() :members: @@ -68,4 +72,4 @@ Helpers ------- .. autoclass:: DownloadableResult() - :members: + :members: \ No newline at end of file diff --git a/src/ansys/simai/core/__init__.py b/src/ansys/simai/core/__init__.py index 754bc0d..d23f59d 100644 --- a/src/ansys/simai/core/__init__.py +++ b/src/ansys/simai/core/__init__.py @@ -37,5 +37,6 @@ Slice, SurfaceEvol, SurfaceVTP, + SurfaceVTPTDLocation, VolumeVTU, ) diff --git a/src/ansys/simai/core/data/post_processings.py b/src/ansys/simai/core/data/post_processings.py index 3576b06..d55a63d 100644 --- a/src/ansys/simai/core/data/post_processings.py +++ b/src/ansys/simai/core/data/post_processings.py @@ -268,12 +268,19 @@ class VolumeVTU(_PostProcessingVTKExport): class SurfaceVTP(_PostProcessingVTKExport): - """Provides for exporting the surface of the prediction in VTP format. + """Exports the surface of the prediction in VTP format associating all data with cells. This class is generated through the :meth:`~PredictionPostProcessings.surface_vtp()` method. """ +class SurfaceVTPTDLocation(_PostProcessingVTKExport): + """Exports the surface of the prediction in VTP format keeping the original data association. + + This class is generated through the :meth:`~PredictionPostProcessings.surface_vtp_td_location()` method. + """ + + class CustomVolumePointCloud(PostProcessing): """Provides a representation of a CustomVolumePointCloud post-processing. @@ -428,7 +435,10 @@ def slice( def surface_vtp(self, run: bool = True) -> Optional[SurfaceVTP]: """Compute or get the result of the prediction's surface in VTP format. - This is a non-blocking method. It returns the ``PostProcessingVTP`` + This method associates all data with cells; if a variable is originally + associated with points in the sample, it would be now associated with cells. + + It is a non-blocking method. It returns the ``PostProcessingVTP`` object without waiting. This object may not have data right away if the computation is still in progress. Data is filled asynchronously once the computation is finished. @@ -448,7 +458,7 @@ def surface_vtp(self, run: bool = True) -> Optional[SurfaceVTP]: Returns ``None`` if ``run=False`` and the postprocessing does not exist. Examples: - Run and download a surface VTP. + Run and download a surface VTP with data associated with cells. .. code-block:: python @@ -458,8 +468,7 @@ def surface_vtp(self, run: bool = True) -> Optional[SurfaceVTP]: prediction = simai.predictions.list()[0] surface_vtp = prediction.post.surface_vtp().data.download("/tmp/simai.vtp") - - Run a surface VTP and open a plot using PyVista. + Run a surface VTP with data association on cells, and open a plot using PyVista. .. code-block:: python @@ -479,6 +488,61 @@ def surface_vtp(self, run: bool = True) -> Optional[SurfaceVTP]: """ return self._get_or_run(SurfaceVTP, {}, run) + def surface_vtp_td_location(self, run: bool = True) -> Optional[SurfaceVTPTDLocation]: + """Compute or get the result of the prediction's surface in VTP format . + + This method keeps the original data association as they are in the sample. + + It is a non-blocking method. It returns the ``PostProcessingVTP`` + object without waiting. This object may not have data right away + if the computation is still in progress. Data is filled + asynchronously once the computation is finished. + The state of computation can be monitored with the ``is_ready`` flag + or waited upon with the ``wait()`` method. + + The computation is launched only on first call of this method. + Subsequent calls do not relaunch it. + + Args: + run: Boolean indicating whether to compute or get the postprocessing. + The default is ``True``. If ``False``, the postprocessing is not + computed, and ``None`` is returned if it does not exist yet. + + Returns: + :class:`SurfaceVTPTDLocation` object that allows downloading the binary data. + Returns ``None`` if ``run=False`` and the postprocessing does not exist. + + Examples: + Run and download a surface VTP with the original data association. + + .. code-block:: python + + import ansys.simai.core + + simai = ansys.simai.core.from_config() + prediction = simai.predictions.list()[0] + surface_vtp = prediction.post.surface_vtp_td_location().data.download("/tmp/simai.vtp") + + Run a surface VTP with the original data association, and open a plot using PyVista. + + .. code-block:: python + + import ansys.simai.core + import pyvista + import tempfile + + simai = ansys.simai.core.from_config() + prediction = simai.predictions.list()[0] + surface_vtp_data = prediction.post.surface_vtp_td_location().data + # I don't want to save the file locally but pyvista doesn't read file-objects + # Using temporary file as a workaround but a real path can be used instead + with tempfile.NamedTemporaryFile(suffix=".vtp") as temp_vtp_file: + surface_vtp_data.download(temp_vtp_file.name) + surface_vtp = pyvista.read(temp_vtp_file.name) + surface_vtp.plot() + """ + return self._get_or_run(SurfaceVTPTDLocation, {}, run) + def volume_vtu(self, run: bool = True) -> Optional[VolumeVTU]: """Compute or get the result of the prediction's volume in VTU format. diff --git a/src/ansys/simai/core/data/selection_post_processings.py b/src/ansys/simai/core/data/selection_post_processings.py index 9ddaba7..8ef2445 100644 --- a/src/ansys/simai/core/data/selection_post_processings.py +++ b/src/ansys/simai/core/data/selection_post_processings.py @@ -28,6 +28,7 @@ Slice, SurfaceEvol, SurfaceVTP, + SurfaceVTPTDLocation, VolumeVTU, ) @@ -164,3 +165,28 @@ def surface_vtp(self) -> PPList[SurfaceVTP]: :py:class:`~ansys.simai.core.data.post_processings.SurfaceVTP` objects. """ return PPList(selection=self._selection, post=lambda pred: pred.post.surface_vtp()) + + def surface_vtp_td_location(self) -> PPList[SurfaceVTPTDLocation]: + """Compute or get the result of each prediction's surface in the VTP format. + + This method keeps the original data association as they are in the sample. + + It is a non-blocking method. It returns a + :py:class:`~ansys.simai.core.data.lists.PPList` instance of + :py:class:`~ansys.simai.core.data.post_processings.SurfaceVTPTDLocation`, + objects without waiting. Those ``PostProcessing`` objects may not have + data right away if the computation is still in progress. Data is filled + asynchronously once the computation is finished. + The state of computation can be waited upon with the ``wait()`` method. + + + The computation is launched only on first call of this method. + Subsequent calls do not relaunch it. + + Returns: + :py:class:`~ansys.simai.core.data.lists.PPList` instance of + :py:class:`~ansys.simai.core.data.post_processings.SurfaceVTPTDLocation` + """ + return PPList( + selection=self._selection, post=lambda pred: pred.post.surface_vtp_td_location() + ) diff --git a/tests/test_post_processings_results.py b/tests/test_post_processings_results.py index 378b5f0..fd939e3 100644 --- a/tests/test_post_processings_results.py +++ b/tests/test_post_processings_results.py @@ -32,6 +32,7 @@ Slice, SurfaceEvol, SurfaceVTP, + SurfaceVTPTDLocation, VolumeVTU, ) @@ -344,6 +345,30 @@ def test_post_processing_result_surface_vtp(simai_client): assert responses.assert_call_count("https://test.test/post-processings/01010101654", 2) +@responses.activate +def test_post_processing_result_surface_vtp_td_location(simai_client): + """WHEN Running a surface_vtp post-processing on a prediction + WITH predict-as-learnt option (location is PPSurfaceLocation.AS_LEARNT) + AND calling its .data field, + THEN a GET request is made on the post-processings/SurfaceVTPTDLocation endpoint + AND the returned instance is of type SurfaceVTPTDLocation. + """ + # Mock request for PP creation + responses.add( + responses.POST, + "https://test.test/predictions/7546/post-processings/SurfaceVTPTDLocation", + json={"id": "01010101654", "state": "successful"}, + status=200, + ) + + pred = simai_client._prediction_directory._model_from({"id": "7546", "state": "successful"}) + surface_vtp = pred.post.surface_vtp_td_location() + assert responses.assert_call_count( + "https://test.test/predictions/7546/post-processings/SurfaceVTPTDLocation", 1 + ) + assert isinstance(surface_vtp, SurfaceVTPTDLocation) + + @responses.activate def test_post_processing_result_point_cloud(simai_client): responses.add( diff --git a/tests/test_selection_post_processings.py b/tests/test_selection_post_processings.py index 54c1bfe..f4f75f2 100644 --- a/tests/test_selection_post_processings.py +++ b/tests/test_selection_post_processings.py @@ -28,6 +28,7 @@ Slice, SurfaceEvol, SurfaceVTP, + SurfaceVTPTDLocation, VolumeVTU, ) from ansys.simai.core.data.selection_post_processings import ExportablePPList, PPList @@ -181,6 +182,33 @@ def test_selection_post_processing_surface_vtp(test_selection): assert isinstance(pp, SurfaceVTP) +@responses.activate +def test_selection_post_processing_surface_vtp_td_location(test_selection): + """WHEN I call post.post.surface_vtp() on a selection + THEN the /SurfaceVTPTDLocation endpoint is called for each prediction in the selection + AND I get a list of SurfaceVTPTDLocation objects in return + """ + assert len(test_selection.points) == 2 + + responses.add( + responses.POST, + "https://test.test/predictions/pred1/post-processings/SurfaceVTPTDLocation", + json={"id": "vtu01", "status": "queued"}, + status=200, + ) + responses.add( + responses.POST, + "https://test.test/predictions/pred2/post-processings/SurfaceVTPTDLocation", + json={"id": "vtu02", "status": "queued"}, + status=200, + ) + post_processings = test_selection.post.surface_vtp_td_location() + assert isinstance(post_processings, PPList) + assert len(post_processings) == 2 + for pp in post_processings: + assert isinstance(pp, SurfaceVTPTDLocation) + + @responses.activate def test_selection_post_processing_error(test_selection): """WHEN I call post.post.volume_vtu() on a selection