From 2d97cb4945efcae17b4b53bfaada2943c55d8b63 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 13 Mar 2023 16:56:48 +0100 Subject: [PATCH 01/14] DataFrame.min and DataFrame.max over time-Index and MeshIndex --- .../06-compute-statistics.py | 57 +++++++++ src/ansys/dpf/post/dataframe.py | 118 +++++++++++++++++- tests/test_dataframe.py | 36 ++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 examples/01-Detailed-Examples/06-compute-statistics.py diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py new file mode 100644 index 000000000..2d51f6e81 --- /dev/null +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -0,0 +1,57 @@ +""" +.. _ref_compute_statistics_example: + +Compute statistics of a DataFrame +================================= +In this example, transient mechanical displacement data is used +to show how to compute statistical values such as the min, max or mean. +""" + +############################################################################### +# Perform required imports +# ------------------------ +# Perform required imports. +# This example uses a supplied file that you can +# get using the ``examples`` module. + +from ansys.dpf import post +from ansys.dpf.post import examples + +############################################################################### +# Get ``Simulation`` object +# ------------------------- +# Get the ``Simulation`` object that allows access to the result. The ``Simulation`` +# object must be instantiated with the path for the result file. For example, +# ``"C:/Users/user/my_result.rst"`` on Windows or ``"/home/user/my_result.rst"`` +# on Linux. + +example_path = examples.download_crankshaft() +simulation = post.StaticMechanicalSimulation(example_path) + +# print the simulation to get an overview of what's available +print(simulation) + +############################################################################### +# Extract displacement data +# ------------------------- + +displacement = simulation.displacement(all_sets=True) +print(displacement) + +############################################################################### +# Compute the maximum displacement value for each component at each time-step +# --------------------------------------------------------------------------- + +# The default axis is the MeshIndex +maximum = displacement.max() +print(maximum) +# is equivalent to +maximum = displacement.max(axis="node_ids") +print(maximum) + +############################################################################### +# Compute the minimum displacement for each node and component across time +# ------------------------------------------------------------------------ + +minimum = displacement.min(axis="set_ids") +print(minimum) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index 085884cf2..6e3185c5b 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -77,6 +77,8 @@ def __init__( self._last_display_width = display_width self._last_display_max_colwidth = display_max_colwidth + self._last_minmax: dict = {"axis": None, "min": None, "max": None} + @property def columns(self) -> MultiIndex: """Returns the MultiIndex for the columns of the DataFrame.""" @@ -513,7 +515,7 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines): else empty for i in range(len(combination)) ] - to_append.append(empty) + to_append.append(empty) # row where row index headers are # Get data in the FieldsContainer for those positions # Create label_space from combination label_space = {} @@ -833,3 +835,117 @@ def animate( return fc.animate( save_as=save_as, deform_by=deform_by, scale_factor=scale_factor, **kwargs ) + + def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: + """Return the minimum value over the requested axis. + + Parameters + ---------- + axis: + Axis to perform minimum across. + Defaults to the MeshIndex (0). + Can also be the SetIndex (1). + + """ + self._query_min_max(axis) + return self._last_minmax["min"] + + def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: + """Return the maximum value over the requested axis. + + Parameters + ---------- + axis: + Axis to perform maximums across. + Defaults to the MeshIndex (0). + Can also be the SetIndex (1). + + """ + self._query_min_max(axis) + return self._last_minmax["max"] + + def _query_min_max(self, axis: Union[int, str, None]) -> None: + """Create a DPF workflow based on the query arguments for min/max.""" + # Translate None query to empty dict + if axis in [None, 0, self.index.mesh_index.name]: + axis = 0 + elif axis in [1, ref_labels.set_ids]: + axis = 1 + else: + raise ValueError(f"'{axis}' is not an available axis value.") + + # print(f"{axis=}") + # If same query as last and last is not None, do not change + if self._last_minmax["axis"] == axis and not self._last_minmax["axis"] is None: + return + # If in need of an update, create the appropriate workflow + wf = dpf.Workflow(server=self._fc._server) + wf.progress_bar = False + + # If over mesh + if axis == 0: + min_max_op = dpf.operators.min_max.min_max_over_label_fc( + fields_container=self._fc, + label="time", + server=self._fc._server, + ) + # Here the fields are located on the label ("time"), so we have to "transpose" it. + # Extract the data for each time (entity) from the field and create a fields_container + + min_fc = dpf.FieldsContainer(server=self._fc._server) + min_fc.add_label(label="time") + min_field = min_max_op.outputs.field_min() + for i, time in enumerate(min_field.scoping.ids): + min_fc.add_field( + label_space={"time": time}, + field=dpf.fields_factory.field_from_array( + arr=min_field.get_entity_data(i), + server=self._fc._server, + ), + ) + + max_fc = dpf.FieldsContainer(server=self._fc._server) + max_fc.add_label(label="time") + max_field = min_max_op.outputs.field_max() + for i, time in enumerate(max_field.scoping.ids): + max_fc.add_field( + label_space={"time": time}, + field=dpf.fields_factory.field_from_array( + arr=max_field.get_entity_data(i), + server=self._fc._server, + ), + ) + + index = MultiIndex( + indexes=[i for i in self.index if i != self.index.mesh_index] + ) + columns = self.columns + + # If over time + else: + min_max_op = dpf.operators.min_max.min_max_over_time_by_entity( + fields_container=self._fc, + server=self._fc._server, + ) + wf.set_output_name("min", min_max_op.outputs.min) + wf.set_output_name("max", min_max_op.outputs.max) + + index = self.index + columns = MultiIndex( + indexes=[c for c in self.columns if c != self.columns.set_ids] + ) + + min_fc = wf.get_output("min", dpf.types.fields_container) + max_fc = wf.get_output("max", dpf.types.fields_container) + + self._last_minmax["min"] = DataFrame( + data=min_fc, + index=index, + columns=columns, + ) + self._last_minmax["max"] = DataFrame( + data=max_fc, + index=index, + columns=columns, + ) + self._last_minmax["axis"] = axis diff --git a/tests/test_dataframe.py b/tests/test_dataframe.py index d1697e5a8..8fc9df2ff 100644 --- a/tests/test_dataframe.py +++ b/tests/test_dataframe.py @@ -4,6 +4,7 @@ from pytest import fixture from ansys.dpf import post +from ansys.dpf.post import examples from ansys.dpf.post.index import ( CompIndex, LabelIndex, @@ -258,3 +259,38 @@ def test_dataframe_array_raise(transient_rst): ValueError, match="Can only export to array if the DataFrame contains a single" ): _ = df.array + + +def test_dataframe_min_max(): + simulation = post.TransientMechanicalSimulation(examples.download_crankshaft()) + df = simulation.displacement(all_sets=True) + # Over the mesh entities + min_over_mesh = [[-0.00074732, -0.00040138, -0.00021555]] + assert np.all(np.isclose(df.min()._fc[0].data.tolist(), min_over_mesh)) + assert np.all(np.isclose(df.min(axis=0)._fc[0].data.tolist(), min_over_mesh)) + assert np.all( + np.isclose(df.min(axis="node_ids")._fc[0].data.tolist(), min_over_mesh) + ) + + max_over_mesh = [[0.00073303, 0.00139618, 0.00021567]] + assert np.all(np.isclose(df.max()._fc[0].data.tolist(), max_over_mesh)) + assert np.all(np.isclose(df.max(axis=0)._fc[0].data.tolist(), max_over_mesh)) + assert np.all( + np.isclose(df.max(axis="node_ids")._fc[0].data.tolist(), max_over_mesh) + ) + + # Over the SetIndex + min_over_time = [-3.41368775e-05, 5.16665595e-04, -4.13456506e-06] + assert np.all(np.isclose(df.min(axis=1)._fc[0].data[0].tolist(), min_over_time)) + assert np.all( + np.isclose(df.min(axis="set_ids")._fc[0].data[0].tolist(), min_over_time) + ) + max_over_time = [5.67807472e-06, 1.54174694e-03, -2.63976203e-06] + assert np.all(np.isclose(df.max(axis=1)._fc[0].data[0].tolist(), max_over_time)) + assert np.all( + np.isclose(df.max(axis="set_ids")._fc[0].data[0].tolist(), max_over_time) + ) + + # Raise unrecognized axis + with pytest.raises(ValueError, match="is not an available axis value"): + df.max(axis="raises") From e9af80fed65226f9420a7756189a9444265cd445 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 13 Mar 2023 16:59:02 +0100 Subject: [PATCH 02/14] DataFrame.min and DataFrame.max over time-Index and MeshIndex --- src/ansys/dpf/post/dataframe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index 6e3185c5b..c1356117d 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -856,7 +856,7 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Parameters ---------- axis: - Axis to perform maximums across. + Axis to perform maximum across. Defaults to the MeshIndex (0). Can also be the SetIndex (1). From 5bc0d0cf6948f80915ac3a774e6b8ec43ba1e252 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 13 Mar 2023 18:12:13 +0100 Subject: [PATCH 03/14] WIP mean --- .../06-compute-statistics.py | 16 ++++ src/ansys/dpf/post/dataframe.py | 80 ++++++++++++++++++- 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py index 2d51f6e81..996cbe7ad 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -55,3 +55,19 @@ minimum = displacement.min(axis="set_ids") print(minimum) + +############################################################################### +# Compute the average displacement value for each component at each time-step +# --------------------------------------------------------------------------- + +mean = displacement.mean() +print("Mean over mesh") +print(mean) + +############################################################################### +# Compute the average displacement for each node and component across time +# ------------------------------------------------------------------------ + +mean = displacement.mean(axis=1) +print("Mean over time") +print(mean) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index c1356117d..babe857b4 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -78,6 +78,7 @@ def __init__( self._last_display_max_colwidth = display_max_colwidth self._last_minmax: dict = {"axis": None, "min": None, "max": None} + self._last_mean: dict = {"axis": None, "mean": None} @property def columns(self) -> MultiIndex: @@ -836,7 +837,7 @@ def animate( save_as=save_as, deform_by=deform_by, scale_factor=scale_factor, **kwargs ) - def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: + def min(self, axis: Union[int, str, None] = 0) -> DataFrame: """Return the minimum value over the requested axis. Parameters @@ -850,7 +851,7 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: self._query_min_max(axis) return self._last_minmax["min"] - def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: + def max(self, axis: Union[int, str, None] = 0) -> DataFrame: """Return the maximum value over the requested axis. Parameters @@ -864,6 +865,20 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: self._query_min_max(axis) return self._last_minmax["max"] + def mean(self, axis: Union[int, str, None] = 0) -> DataFrame: + """Return the mean value over the requested axis. + + Parameters + ---------- + axis: + Axis to perform average across. + Defaults to the MeshIndex (0). + Can also be the SetIndex (1). + + """ + self._query_mean(axis) + return self._last_mean["mean"] + def _query_min_max(self, axis: Union[int, str, None]) -> None: """Create a DPF workflow based on the query arguments for min/max.""" # Translate None query to empty dict @@ -932,7 +947,11 @@ def _query_min_max(self, axis: Union[int, str, None]) -> None: index = self.index columns = MultiIndex( - indexes=[c for c in self.columns if c != self.columns.set_ids] + indexes=[ + c + for c in self.columns + if c != getattr(self.columns, ref_labels.set_ids) + ] ) min_fc = wf.get_output("min", dpf.types.fields_container) @@ -949,3 +968,58 @@ def _query_min_max(self, axis: Union[int, str, None]) -> None: columns=columns, ) self._last_minmax["axis"] = axis + + def _query_mean(self, axis: Union[int, str, None]) -> None: + """Create a DPF workflow based on the query arguments for mean.""" + # Translate None query to empty dict + if axis in [None, 0, self.index.mesh_index.name]: + axis = 0 + elif axis in [1, ref_labels.set_ids]: + axis = 1 + else: + raise ValueError(f"'{axis}' is not an available axis value.") + # If same query as last and last is not None, do not change + if self._last_mean["axis"] == axis and not self._last_mean["axis"] is None: + return + # If in need of an update, create the appropriate workflow + wf = dpf.Workflow(server=self._fc._server) + wf.progress_bar = False + + # If over mesh + if axis == 0: + sum_op = dpf.operators.math.accumulate_fc( + fields_container=self._fc, + server=self._fc._server, + ) + + mean_fc = sum_op.outputs.fields_container() + + index = MultiIndex( + indexes=[i for i in self.index if i != self.index.mesh_index] + ) + columns = self.columns + + # If over time + else: + average_over_label_op = dpf.operators.math.average_over_label_fc( + fields_container=self._fc, + server=self._fc._server, + ) + + mean_fc = average_over_label_op.outputs.field() + + index = self.index + columns = MultiIndex( + indexes=[ + c + for c in self.columns + if c != getattr(self.columns, ref_labels.set_ids) + ] + ) + + self._last_mean["mean"] = DataFrame( + data=mean_fc, + index=index, + columns=columns, + ) + self._last_mean["axis"] = axis From 63722681a0820063ee5a628b1d6c49278cbcba0d Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 20 Mar 2023 13:54:04 +0100 Subject: [PATCH 04/14] Revert "WIP mean" This reverts commit 5bc0d0cf6948f80915ac3a774e6b8ec43ba1e252. --- .../06-compute-statistics.py | 16 ---- src/ansys/dpf/post/dataframe.py | 80 +------------------ 2 files changed, 3 insertions(+), 93 deletions(-) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py index 996cbe7ad..2d51f6e81 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -55,19 +55,3 @@ minimum = displacement.min(axis="set_ids") print(minimum) - -############################################################################### -# Compute the average displacement value for each component at each time-step -# --------------------------------------------------------------------------- - -mean = displacement.mean() -print("Mean over mesh") -print(mean) - -############################################################################### -# Compute the average displacement for each node and component across time -# ------------------------------------------------------------------------ - -mean = displacement.mean(axis=1) -print("Mean over time") -print(mean) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index babe857b4..c1356117d 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -78,7 +78,6 @@ def __init__( self._last_display_max_colwidth = display_max_colwidth self._last_minmax: dict = {"axis": None, "min": None, "max": None} - self._last_mean: dict = {"axis": None, "mean": None} @property def columns(self) -> MultiIndex: @@ -837,7 +836,7 @@ def animate( save_as=save_as, deform_by=deform_by, scale_factor=scale_factor, **kwargs ) - def min(self, axis: Union[int, str, None] = 0) -> DataFrame: + def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: """Return the minimum value over the requested axis. Parameters @@ -851,7 +850,7 @@ def min(self, axis: Union[int, str, None] = 0) -> DataFrame: self._query_min_max(axis) return self._last_minmax["min"] - def max(self, axis: Union[int, str, None] = 0) -> DataFrame: + def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: """Return the maximum value over the requested axis. Parameters @@ -865,20 +864,6 @@ def max(self, axis: Union[int, str, None] = 0) -> DataFrame: self._query_min_max(axis) return self._last_minmax["max"] - def mean(self, axis: Union[int, str, None] = 0) -> DataFrame: - """Return the mean value over the requested axis. - - Parameters - ---------- - axis: - Axis to perform average across. - Defaults to the MeshIndex (0). - Can also be the SetIndex (1). - - """ - self._query_mean(axis) - return self._last_mean["mean"] - def _query_min_max(self, axis: Union[int, str, None]) -> None: """Create a DPF workflow based on the query arguments for min/max.""" # Translate None query to empty dict @@ -947,11 +932,7 @@ def _query_min_max(self, axis: Union[int, str, None]) -> None: index = self.index columns = MultiIndex( - indexes=[ - c - for c in self.columns - if c != getattr(self.columns, ref_labels.set_ids) - ] + indexes=[c for c in self.columns if c != self.columns.set_ids] ) min_fc = wf.get_output("min", dpf.types.fields_container) @@ -968,58 +949,3 @@ def _query_min_max(self, axis: Union[int, str, None]) -> None: columns=columns, ) self._last_minmax["axis"] = axis - - def _query_mean(self, axis: Union[int, str, None]) -> None: - """Create a DPF workflow based on the query arguments for mean.""" - # Translate None query to empty dict - if axis in [None, 0, self.index.mesh_index.name]: - axis = 0 - elif axis in [1, ref_labels.set_ids]: - axis = 1 - else: - raise ValueError(f"'{axis}' is not an available axis value.") - # If same query as last and last is not None, do not change - if self._last_mean["axis"] == axis and not self._last_mean["axis"] is None: - return - # If in need of an update, create the appropriate workflow - wf = dpf.Workflow(server=self._fc._server) - wf.progress_bar = False - - # If over mesh - if axis == 0: - sum_op = dpf.operators.math.accumulate_fc( - fields_container=self._fc, - server=self._fc._server, - ) - - mean_fc = sum_op.outputs.fields_container() - - index = MultiIndex( - indexes=[i for i in self.index if i != self.index.mesh_index] - ) - columns = self.columns - - # If over time - else: - average_over_label_op = dpf.operators.math.average_over_label_fc( - fields_container=self._fc, - server=self._fc._server, - ) - - mean_fc = average_over_label_op.outputs.field() - - index = self.index - columns = MultiIndex( - indexes=[ - c - for c in self.columns - if c != getattr(self.columns, ref_labels.set_ids) - ] - ) - - self._last_mean["mean"] = DataFrame( - data=mean_fc, - index=index, - columns=columns, - ) - self._last_mean["axis"] = axis From 450450c3002a922465fd22e021922033982c97e6 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 20 Mar 2023 13:58:18 +0100 Subject: [PATCH 05/14] Improve docstring --- src/ansys/dpf/post/dataframe.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index c1356117d..5c79c4e1b 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -843,8 +843,10 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: ---------- axis: Axis to perform minimum across. - Defaults to the MeshIndex (0). - Can also be the SetIndex (1). + Defaults to the MeshIndex (0), the row index containing mesh entity IDs. + This computes the minimum across the mesh for each set. + Can also be the SetIndex (1), the column index containing set (time/freq) IDs. + This computes the minimum across sets (time/freq) for each mesh entity. """ self._query_min_max(axis) @@ -857,8 +859,10 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: ---------- axis: Axis to perform maximum across. - Defaults to the MeshIndex (0). - Can also be the SetIndex (1). + Defaults to the MeshIndex (0), the row index containing mesh entity IDs. + This computes the maximum across the mesh for each set. + Can also be the SetIndex (1), the column index containing set (time/freq) IDs. + This computes the maximum across sets (time/freq) for each mesh entity. """ self._query_min_max(axis) From 4bcaec362e4d35dd27f9fa48b0eefb4a3a30d6af Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 20 Mar 2023 16:30:51 +0100 Subject: [PATCH 06/14] Fix step "Set licensing if necessary" in CI and CI_release for retro tests --- .github/workflows/ci.yml | 2 +- .github/workflows/ci_release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9ccdc9fb..f17641530 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,7 +135,7 @@ jobs: - uses: actions/checkout@v3 - name: "Set licensing if necessary" - if: env.ANSYS_VERSION > 231 + if: matrix.ANSYS_VERSION > 231 shell: bash run: | echo "ANSYS_DPF_ACCEPT_LA=Y" >> $GITHUB_ENV diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index 31025d3dc..8e2a270c2 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -131,7 +131,7 @@ jobs: - uses: actions/checkout@v3 - name: "Set licensing if necessary" - if: env.ANSYS_VERSION > 231 + if: matrix.ANSYS_VERSION > 231 shell: bash run: | echo "ANSYS_DPF_ACCEPT_LA=Y" >> $GITHUB_ENV From 976459922faa876aa1388c4887c2f069dac666ad Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 20 Mar 2023 16:33:22 +0100 Subject: [PATCH 07/14] Fix ANSYS_VERSION for "upload test results" step of retro in ci.yml and ci_release.yml --- .github/workflows/ci.yml | 2 +- .github/workflows/ci_release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f17641530..988a6790f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,7 +183,7 @@ jobs: - name: "Upload Test Results" uses: actions/upload-artifact@v3 with: - name: ${{ env.PACKAGE_NAME }}_${{ matrix.python-version }}_${{ matrix.os }}_pytest_${{ env.ANSYS_VERSION }} + name: ${{ env.PACKAGE_NAME }}_${{ matrix.python-version }}_${{ matrix.os }}_pytest_${{ matrix.ANSYS_VERSION }} path: tests/junit/test-results.xml if: always() diff --git a/.github/workflows/ci_release.yml b/.github/workflows/ci_release.yml index 8e2a270c2..5f16cb7be 100644 --- a/.github/workflows/ci_release.yml +++ b/.github/workflows/ci_release.yml @@ -179,7 +179,7 @@ jobs: - name: "Upload Test Results" uses: actions/upload-artifact@v3 with: - name: ${{ env.PACKAGE_NAME }}_${{ matrix.python-version }}_${{ matrix.os }}_pytest_${{ env.ANSYS_VERSION }} + name: ${{ env.PACKAGE_NAME }}_${{ matrix.python-version }}_${{ matrix.os }}_pytest_${{ matrix.ANSYS_VERSION }} path: tests/junit/test-results.xml if: always() From 6a553cbf2831e4a3f1bfd81dbecaa8b47a2c37ea Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 14:26:17 +0200 Subject: [PATCH 08/14] Update example to remove mention of mean --- examples/01-Detailed-Examples/06-compute-statistics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py index 2d51f6e81..c99321009 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -4,7 +4,8 @@ Compute statistics of a DataFrame ================================= In this example, transient mechanical displacement data is used -to show how to compute statistical values such as the min, max or mean. +to show how to compute statistical values such as the min or max +for a given DataFrame. """ ############################################################################### From 2478cf7be073480257b2d3c7335e3094aa6d9101 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 14:45:36 +0200 Subject: [PATCH 09/14] Update example --- .../06-compute-statistics.py | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py index c99321009..18349c238 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -40,19 +40,43 @@ print(displacement) ############################################################################### -# Compute the maximum displacement value for each component at each time-step -# --------------------------------------------------------------------------- +# Compute the maximum displacement for each component at each time-step +# --------------------------------------------------------------------- # The default axis is the MeshIndex -maximum = displacement.max() -print(maximum) +maximum_over_mesh = displacement.max() +print(maximum_over_mesh) # is equivalent to -maximum = displacement.max(axis="node_ids") -print(maximum) +maximum_over_mesh = displacement.max(axis="node_ids") +print(maximum_over_mesh) + +# Compute the maximum displacement for each node and component across time +# ------------------------------------------------------------------------ +maximum_over_time = displacement.max(axis="set_ids") +print(maximum_over_time) + +# Compute the maximum displacement overall +# ---------------------------------------- +maximum_overall = maximum_over_time.max() +print(maximum_overall) ############################################################################### +# Compute the minimum displacement for each component at each time-step +# --------------------------------------------------------------------- + +# The default axis is the MeshIndex +minimum_over_mesh = displacement.min() +print(minimum_over_mesh) +# is equivalent to +minimum_over_mesh = displacement.min(axis="node_ids") +print(minimum_over_mesh) + # Compute the minimum displacement for each node and component across time # ------------------------------------------------------------------------ +minimum_over_time = displacement.min(axis="set_ids") +print(minimum_over_time) -minimum = displacement.min(axis="set_ids") -print(minimum) +# Compute the minimum displacement overall +# ---------------------------------------- +minimum_overall = minimum_over_time.min() +print(minimum_overall) From 7ecde11cf00b4d84ee42e1e22fd14233aee6a2e9 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 14:49:36 +0200 Subject: [PATCH 10/14] Add examples to docstrings --- src/ansys/dpf/post/dataframe.py | 100 ++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index 5c79c4e1b..b13cf9338 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -842,12 +842,52 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Parameters ---------- axis: - Axis to perform minimum across. - Defaults to the MeshIndex (0), the row index containing mesh entity IDs. - This computes the minimum across the mesh for each set. - Can also be the SetIndex (1), the column index containing set (time/freq) IDs. - This computes the minimum across sets (time/freq) for each mesh entity. + Axis to perform minimum across. + Defaults to the MeshIndex (0), the row index containing mesh entity IDs. + This computes the minimum across the mesh for each set. + Can also be the SetIndex (1), the column index containing set (time/freq) IDs. + This computes the minimum across sets (time/freq) for each mesh entity. + Returns + ------- + A scalar if the result of the query is a single number, + or a DataFrame if several numbers along one or several axes. + + Examples + -------- + >>> from ansys.dpf import post + >>> from ansys.dpf.post import examples + >>> simulation = post.StaticMechanicalSimulation(examples.download_crankshaft()) + >>> displacement = simulation.displacement(all_sets=True) + >>> # Compute the maximum displacement value for each component at each time-step + >>> minimum_over_mesh = displacement.min(axis="node_ids") + >>> print(minimum_over_mesh) # doctest: +NORMALIZE_WHITESPACE + results U (m) + set_ids 1 2 3 + components + X -7.4732e-04 -1.5081e-03 -2.2755e-03 + Y -4.0138e-04 -8.0316e-04 -1.2014e-03 + Z -2.1555e-04 -4.3299e-04 -6.5101e-04 + >>> # Compute the maximum displacement for each node and component across time + >>> minimum_over_time = displacement.min(axis="set_ids") + >>> print(minimum_over_time) # doctest: +NORMALIZE_WHITESPACE + results U (m) + node_ids components + 4872 X -3.4137e-05 + Y 5.1667e-04 + Z -4.1346e-06 + 9005 X -5.5625e-05 + Y 4.8445e-04 + Z -4.9795e-07 + ... + >>> # Compute the maximum displacement overall + >>> minimum_overall = minimum_over_time.max() + >>> print(minimum_overall) # doctest: +NORMALIZE_WHITESPACE + results U (m) + components + X -2.2755e-03 + Y -1.2014e-03 + Z -6.5101e-04 """ self._query_min_max(axis) return self._last_minmax["min"] @@ -858,12 +898,52 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Parameters ---------- axis: - Axis to perform maximum across. - Defaults to the MeshIndex (0), the row index containing mesh entity IDs. - This computes the maximum across the mesh for each set. - Can also be the SetIndex (1), the column index containing set (time/freq) IDs. - This computes the maximum across sets (time/freq) for each mesh entity. + Axis to perform maximum across. + Defaults to the MeshIndex (0), the row index containing mesh entity IDs. + This computes the maximum across the mesh for each set. + Can also be the SetIndex (1), the column index containing set (time/freq) IDs. + This computes the maximum across sets (time/freq) for each mesh entity. + Returns + ------- + A scalar if the result of the query is a single number, + or a DataFrame if several numbers along one or several axes. + + Examples + -------- + >>> from ansys.dpf import post + >>> from ansys.dpf.post import examples + >>> simulation = post.StaticMechanicalSimulation(examples.download_crankshaft()) + >>> displacement = simulation.displacement(all_sets=True) + >>> # Compute the maximum displacement value for each component at each time-step + >>> maximum_over_mesh = displacement.max(axis="node_ids") + >>> print(maximum_over_mesh) # doctest: +NORMALIZE_WHITESPACE + results U (m) + set_ids 1 2 3 + components + X 7.3303e-04 1.4495e-03 2.1441e-03 + Y 1.3962e-03 2.7884e-03 4.1656e-03 + Z 2.1567e-04 4.3321e-04 6.5135e-04 + >>> # Compute the maximum displacement for each node and component across time + >>> maximum_over_time = displacement.max(axis="set_ids") + >>> print(maximum_over_time) # doctest: +NORMALIZE_WHITESPACE + results U (m) + node_ids components + 4872 X 5.6781e-06 + Y 1.5417e-03 + Z -2.6398e-06 + 9005 X -2.6323e-06 + Y 1.4448e-03 + Z 5.3134e-06 + ... + >>> # Compute the maximum displacement overall + >>> maximum_overall = maximum_over_time.max() + >>> print(maximum_overall) # doctest: +NORMALIZE_WHITESPACE + results U (m) + components + X 2.1441e-03 + Y 4.1656e-03 + Z 6.5135e-04 """ self._query_min_max(axis) return self._last_minmax["max"] From 4d9615e39561bd858e01eb333bb187d64464f9df Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 15:01:21 +0200 Subject: [PATCH 11/14] Fix docstring examples --- src/ansys/dpf/post/dataframe.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index b13cf9338..d7a271ae5 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -862,15 +862,18 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: >>> # Compute the maximum displacement value for each component at each time-step >>> minimum_over_mesh = displacement.min(axis="node_ids") >>> print(minimum_over_mesh) # doctest: +NORMALIZE_WHITESPACE + results U (m) set_ids 1 2 3 components X -7.4732e-04 -1.5081e-03 -2.2755e-03 Y -4.0138e-04 -8.0316e-04 -1.2014e-03 Z -2.1555e-04 -4.3299e-04 -6.5101e-04 + >>> # Compute the maximum displacement for each node and component across time >>> minimum_over_time = displacement.min(axis="set_ids") >>> print(minimum_over_time) # doctest: +NORMALIZE_WHITESPACE + results U (m) node_ids components 4872 X -3.4137e-05 @@ -880,14 +883,17 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Y 4.8445e-04 Z -4.9795e-07 ... + >>> # Compute the maximum displacement overall >>> minimum_overall = minimum_over_time.max() >>> print(minimum_overall) # doctest: +NORMALIZE_WHITESPACE + results U (m) components X -2.2755e-03 Y -1.2014e-03 Z -6.5101e-04 + """ self._query_min_max(axis) return self._last_minmax["min"] @@ -918,15 +924,18 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: >>> # Compute the maximum displacement value for each component at each time-step >>> maximum_over_mesh = displacement.max(axis="node_ids") >>> print(maximum_over_mesh) # doctest: +NORMALIZE_WHITESPACE + results U (m) set_ids 1 2 3 components X 7.3303e-04 1.4495e-03 2.1441e-03 Y 1.3962e-03 2.7884e-03 4.1656e-03 Z 2.1567e-04 4.3321e-04 6.5135e-04 + >>> # Compute the maximum displacement for each node and component across time >>> maximum_over_time = displacement.max(axis="set_ids") >>> print(maximum_over_time) # doctest: +NORMALIZE_WHITESPACE + results U (m) node_ids components 4872 X 5.6781e-06 @@ -936,14 +945,17 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Y 1.4448e-03 Z 5.3134e-06 ... + >>> # Compute the maximum displacement overall >>> maximum_overall = maximum_over_time.max() >>> print(maximum_overall) # doctest: +NORMALIZE_WHITESPACE + results U (m) components X 2.1441e-03 Y 4.1656e-03 Z 6.5135e-04 + """ self._query_min_max(axis) return self._last_minmax["max"] From 702cc2f67df2472710815af443589cba2dc8d764 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 15:58:33 +0200 Subject: [PATCH 12/14] Take comments into account --- examples/01-Detailed-Examples/06-compute-statistics.py | 3 +-- src/ansys/dpf/post/dataframe.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-statistics.py index 18349c238..2cdc1c7c8 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-statistics.py @@ -5,13 +5,12 @@ ================================= In this example, transient mechanical displacement data is used to show how to compute statistical values such as the min or max -for a given DataFrame. +of a given DataFrame. """ ############################################################################### # Perform required imports # ------------------------ -# Perform required imports. # This example uses a supplied file that you can # get using the ``examples`` module. diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index d7a271ae5..0592f306d 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -845,8 +845,8 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Axis to perform minimum across. Defaults to the MeshIndex (0), the row index containing mesh entity IDs. This computes the minimum across the mesh for each set. - Can also be the SetIndex (1), the column index containing set (time/freq) IDs. - This computes the minimum across sets (time/freq) for each mesh entity. + Can also be the SetIndex (1), the column index containing set (time/frequency) IDs. + This computes the minimum across sets (time/frequency) for each mesh entity. Returns ------- @@ -907,8 +907,8 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Axis to perform maximum across. Defaults to the MeshIndex (0), the row index containing mesh entity IDs. This computes the maximum across the mesh for each set. - Can also be the SetIndex (1), the column index containing set (time/freq) IDs. - This computes the maximum across sets (time/freq) for each mesh entity. + Can also be the SetIndex (1), the column index containing set (time/frequency) IDs. + This computes the maximum across sets (time/frequency) for each mesh entity. Returns ------- From d5fe0c7966d6f9b567b9d11ee21b7777238a1b6c Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 16:01:27 +0200 Subject: [PATCH 13/14] Fix docstring example for min --- src/ansys/dpf/post/dataframe.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/ansys/dpf/post/dataframe.py b/src/ansys/dpf/post/dataframe.py index 0592f306d..d71ca905b 100644 --- a/src/ansys/dpf/post/dataframe.py +++ b/src/ansys/dpf/post/dataframe.py @@ -862,18 +862,15 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: >>> # Compute the maximum displacement value for each component at each time-step >>> minimum_over_mesh = displacement.min(axis="node_ids") >>> print(minimum_over_mesh) # doctest: +NORMALIZE_WHITESPACE - results U (m) set_ids 1 2 3 components X -7.4732e-04 -1.5081e-03 -2.2755e-03 Y -4.0138e-04 -8.0316e-04 -1.2014e-03 Z -2.1555e-04 -4.3299e-04 -6.5101e-04 - >>> # Compute the maximum displacement for each node and component across time >>> minimum_over_time = displacement.min(axis="set_ids") >>> print(minimum_over_time) # doctest: +NORMALIZE_WHITESPACE - results U (m) node_ids components 4872 X -3.4137e-05 @@ -883,17 +880,14 @@ def min(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Y 4.8445e-04 Z -4.9795e-07 ... - >>> # Compute the maximum displacement overall - >>> minimum_overall = minimum_over_time.max() + >>> minimum_overall = minimum_over_time.min() >>> print(minimum_overall) # doctest: +NORMALIZE_WHITESPACE - results U (m) components X -2.2755e-03 Y -1.2014e-03 Z -6.5101e-04 - """ self._query_min_max(axis) return self._last_minmax["min"] @@ -924,18 +918,15 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: >>> # Compute the maximum displacement value for each component at each time-step >>> maximum_over_mesh = displacement.max(axis="node_ids") >>> print(maximum_over_mesh) # doctest: +NORMALIZE_WHITESPACE - results U (m) set_ids 1 2 3 components X 7.3303e-04 1.4495e-03 2.1441e-03 Y 1.3962e-03 2.7884e-03 4.1656e-03 Z 2.1567e-04 4.3321e-04 6.5135e-04 - >>> # Compute the maximum displacement for each node and component across time >>> maximum_over_time = displacement.max(axis="set_ids") >>> print(maximum_over_time) # doctest: +NORMALIZE_WHITESPACE - results U (m) node_ids components 4872 X 5.6781e-06 @@ -945,17 +936,14 @@ def max(self, axis: Union[int, str, None] = 0) -> Union[DataFrame, float]: Y 1.4448e-03 Z 5.3134e-06 ... - >>> # Compute the maximum displacement overall >>> maximum_overall = maximum_over_time.max() >>> print(maximum_overall) # doctest: +NORMALIZE_WHITESPACE - results U (m) components X 2.1441e-03 Y 4.1656e-03 Z 6.5135e-04 - """ self._query_min_max(axis) return self._last_minmax["max"] From 5f8c145d4b69951bd48467aa1a176cd9b55ca176 Mon Sep 17 00:00:00 2001 From: "paul.profizi" Date: Mon, 3 Apr 2023 17:47:16 +0200 Subject: [PATCH 14/14] Rename example 06-compute-min-max.py --- .../{06-compute-statistics.py => 06-compute-min-max.py} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename examples/01-Detailed-Examples/{06-compute-statistics.py => 06-compute-min-max.py} (94%) diff --git a/examples/01-Detailed-Examples/06-compute-statistics.py b/examples/01-Detailed-Examples/06-compute-min-max.py similarity index 94% rename from examples/01-Detailed-Examples/06-compute-statistics.py rename to examples/01-Detailed-Examples/06-compute-min-max.py index 2cdc1c7c8..21653d2b8 100644 --- a/examples/01-Detailed-Examples/06-compute-statistics.py +++ b/examples/01-Detailed-Examples/06-compute-min-max.py @@ -1,11 +1,10 @@ """ .. _ref_compute_statistics_example: -Compute statistics of a DataFrame -================================= +Compute minimum and maximum of a DataFrame +========================================== In this example, transient mechanical displacement data is used -to show how to compute statistical values such as the min or max -of a given DataFrame. +to show how to compute the min or max of a given DataFrame. """ ###############################################################################