Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix iselect, update DataFrame.axes, Naming convention #304

Merged
merged 8 commits into from
Mar 3, 2023
12 changes: 7 additions & 5 deletions examples/00-Different-analysis-types/01-static-simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@
# ---------------------------------

# To get X displacements
x_displacement = displacement.select(comp="X")
x_displacement = displacement.select(components="X")
print(x_displacement)


# equivalent to
# equivalent to
x_displacement = simulation.displacement(components=["X"])
print(x_displacement)

# plot
x_displacement.plot()

# extract displacement on specific nodes
nodes_displacement = displacement.select(node=[1, 10, 100])
nodes_displacement = displacement.select(node_ids=[1, 10, 100])
nodes_displacement.plot()

# equivalent to:
Expand All @@ -66,7 +66,9 @@
# ---------------------------------
# Compute the norm of displacement on a selection of nodes

nodes_displacement = simulation.displacement(node_ids=simulation.mesh.node_ids[10:], norm=True)
nodes_displacement = simulation.displacement(
node_ids=simulation.mesh.node_ids[10:], norm=True
)
print(nodes_displacement)
nodes_displacement.plot()

Expand All @@ -87,7 +89,7 @@
print(elemental_stress)

# extract elemental stresses on specific elements
elemental_stress = elemental_stress.select(element=[5, 6, 7])
elemental_stress = elemental_stress.select(element_ids=[5, 6, 7])
elemental_stress.plot()

# Compute nodal eqv stresses from the result file
Expand Down
4 changes: 2 additions & 2 deletions examples/00-Different-analysis-types/02-modal-simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
print(displacement_norm)

for set_id in simulation.set_ids:
displacement_norm.plot(set_id=set_id)
displacement_norm.plot(set_ids=set_id)


###############################################################################
Expand All @@ -61,4 +61,4 @@
print(displacement_norm)

for set_id in modes:
displacement_norm.plot(set_id=set_id)
displacement_norm.plot(set_ids=set_id)
20 changes: 14 additions & 6 deletions examples/00-Different-analysis-types/03-transient-simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,31 @@
# Extract displacement at all times or on a selection
# ---------------------------------------------------

# query the displacement vectorial field for all times
displacement = simulation.displacement(all_sets=True)
print(displacement)
displacement.animate(deform=True)
# animation shows the norm of vectorial fields with several components
displacement.animate(deform=True, title="U")


# equivalent to
# get specific components with "components"
x_displacement = simulation.displacement(all_sets=True, components=["X"])
print(x_displacement)
displacement.animate(deform=True)
x_displacement.animate(deform=True, title="UX")


# get the norm of a vectorial result with "norm=True"
displacement_norm = simulation.displacement(all_sets=True, norm=True)
print(displacement_norm)
displacement_norm.animate(deform=True, title="U norm")

# get the available time set ids in the simulation
print(simulation.set_ids)

# extract displacement on given time steps or select the times steps from teh already evaluated
# extract displacement on given time steps or select the times steps from the already evaluated
# displacements
displacement = simulation.displacement(set_ids=simulation.set_ids[5:])
displacement = displacement.select(set_id=simulation.set_ids[5:])
displacement = displacement.select(set_ids=simulation.set_ids[5:])
print(displacement)

###############################################################################
Expand All @@ -73,4 +81,4 @@
# ---------------------------------

strain_eqv = simulation.elastic_strain_eqv_von_mises_nodal(all_sets=True)
strain_eqv.animate()
strain_eqv.animate(deform=True, title="E_eqv")
30 changes: 18 additions & 12 deletions examples/00-Different-analysis-types/04-harmonic-complex-results.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,17 @@
displacement = simulation.displacement(set_ids=[1, 2])
print(displacement)

subdisp = displacement.select(complex=0, set_id=1)
subdisp.plot()
subdisp = displacement.select(complex=0, set_ids=1)
print(subdisp)
subdisp.plot(title="U tot real")

subdisp = displacement.select(complex=1, set_id=1)
subdisp.plot()
subdisp = displacement.select(complex=1, set_ids=1)
print(subdisp)
subdisp.plot(title="U tot imaginary")

subdisp = displacement.select(complex=0, set_id=2)
subdisp.plot()
subdisp = displacement.select(complex=0, set_ids=2)
print(subdisp)
subdisp.plot(title="U tot real")

###############################################################################
# Extract stress eqv over a list of frequencies sets
Expand All @@ -60,11 +63,14 @@
stress_eqv = simulation.stress_eqv_von_mises_nodal(set_ids=[1, 2])
print(stress_eqv)

sub_eqv = stress_eqv.select(complex=0, set_id=1)
sub_eqv.plot()
sub_eqv = stress_eqv.select(complex=0, set_ids=1)
print(sub_eqv)
sub_eqv.plot(title="S_eqv real")

sub_eqv = stress_eqv.select(complex=1, set_id=1)
sub_eqv.plot()
sub_eqv = stress_eqv.select(complex=1, set_ids=1)
print(sub_eqv)
sub_eqv.plot(title="S_eqv imaginary")

sub_eqv = stress_eqv.select(complex=0, set_id=2)
sub_eqv.plot()
sub_eqv = stress_eqv.select(complex=0, set_ids=2)
print(sub_eqv)
sub_eqv.plot(title="S_eqv real")
131 changes: 70 additions & 61 deletions src/ansys/dpf/post/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
MultiIndex,
ResultsIndex,
SetIndex,
ref_labels,
)

display_width = 80
display_max_colwidth = 10
display_width = 100
display_max_colwidth = 12
display_max_lines = 6


Expand Down Expand Up @@ -91,11 +92,9 @@ def index(self) -> Union[MultiIndex, Index]:
return self._index

@property
def axes(self) -> List[str]:
"""Returns a list of the axes of the DataFrame with the row Index and the column Index."""
names = self.index.names
names.extend(self.columns.names)
return names
def axes(self) -> List[MultiIndex]:
"""Returns a list with the row MultiIndex first and the columns MultiIndex second."""
return [self.index, self.columns]

@property
def results_index(self) -> Union[ResultsIndex, None]:
Expand Down Expand Up @@ -127,6 +126,22 @@ def _core_object(self):
"""Returns the underlying PyDPF-Core class:`ansys.dpf.core.FieldsContainer` object."""
return self._fc

def _filter_arguments(self, arguments):
"""Filter arguments based on available Index names."""
rows, columns = self.axes
axes_names = rows.names
axes_names.extend(columns.names)
axis_arguments = {}
keys = list(arguments.keys())
for argument in keys:
if argument in axes_names:
axis_arguments[argument] = arguments.pop(argument)
# raise ValueError(
# f"The DataFrame has no axis {argument}, cannot select it. "
# f"Available axes are: {axes_names}."
# )
return axis_arguments, arguments

def select(self, **kwargs) -> DataFrame:
"""Returns a new DataFrame based on selection criteria (value-based).

Expand All @@ -146,16 +161,9 @@ def select(self, **kwargs) -> DataFrame:
A DataFrame of the selected values.

"""
# Check for invalid arguments
axes = self.axes
for argument in kwargs.keys():
if argument not in axes:
raise ValueError(
f"The DataFrame has no axis {argument}, cannot select it. "
f"Available axes are: {axes}."
)
if "set_id" in kwargs.keys():
kwargs["time"] = kwargs["set_id"]
axis_kwargs, _ = self._filter_arguments(arguments=kwargs)
if ref_labels.set_ids in axis_kwargs.keys():
axis_kwargs[ref_labels.time] = axis_kwargs[ref_labels.set_ids]
# Initiate a workflow
server = self._fc._server
wf = dpf.Workflow(server=server)
Expand All @@ -176,30 +184,37 @@ def select(self, **kwargs) -> DataFrame:
results_index = self.results_index

# Treat selection on a label
if any([label in kwargs.keys() for label in self._fc.labels]):
if any([label in axis_kwargs.keys() for label in self._fc.labels]):
fc = dpf.FieldsContainer()
fc.labels = self._fc.labels
for i, field in enumerate(self._fc):
to_add = True
label_space = self._fc.get_label_space(i)
for fc_key in label_space.keys():
if fc_key in kwargs.keys() or (
fc_key == "time" and "set_id" in kwargs.keys()
if fc_key in axis_kwargs.keys() or (
fc_key == ref_labels.time
and ref_labels.set_ids in axis_kwargs.keys()
):
key = fc_key
if fc_key == "time" and "set_id" in kwargs.keys():
key = "set_id"
if not isinstance(kwargs[key], list):
kwargs[key] = [kwargs[key]]
if label_space[fc_key] not in kwargs[key]:
continue
if (
fc_key == ref_labels.time
and ref_labels.set_ids in axis_kwargs.keys()
):
key = ref_labels.set_ids
if not isinstance(axis_kwargs[key], list):
axis_kwargs[key] = [axis_kwargs[key]]
if label_space[fc_key] not in axis_kwargs[key]:
to_add = False
break
if to_add:
fc.add_field(label_space=label_space, field=field)
input_fc = fc

# # Treat selection on components
if "comp" in kwargs.keys():
if ref_labels.components in axis_kwargs.keys():
from ansys.dpf.post.simulation import component_label_to_index

comp_to_extract = kwargs["comp"]
comp_to_extract = axis_kwargs[ref_labels.components]
if not isinstance(comp_to_extract, list):
comp_to_extract = [comp_to_extract]
component_indexes = [component_label_to_index[c] for c in comp_to_extract]
Expand All @@ -217,19 +232,19 @@ def select(self, **kwargs) -> DataFrame:
else:
mesh_index_name = self.index.mesh_index.name
if (
mesh_index_name in kwargs.keys()
mesh_index_name in axis_kwargs.keys()
and mesh_index.location != locations.elemental_nodal
):
if "node" in mesh_index_name:
node_ids = kwargs[mesh_index_name]
if ref_labels.node_ids in mesh_index_name:
node_ids = axis_kwargs[mesh_index_name]
if not isinstance(node_ids, (DPFArray, list)):
node_ids = [node_ids]
mesh_scoping = dpf.mesh_scoping_factory.nodal_scoping(
node_ids=node_ids,
server=server,
)
elif "element" in mesh_index_name:
element_ids = kwargs[mesh_index_name]
elif ref_labels.element_ids in mesh_index_name:
element_ids = axis_kwargs[mesh_index_name]
if not isinstance(element_ids, list):
element_ids = [element_ids]
mesh_scoping = dpf.mesh_scoping_factory.elemental_scoping(
Expand All @@ -249,7 +264,7 @@ def select(self, **kwargs) -> DataFrame:
out = rescope_fc.outputs.fields_container
mesh_index = MeshIndex(location=location, values=mesh_scoping.ids)
elif (
mesh_index_name in kwargs.keys()
mesh_index_name in axis_kwargs.keys()
and mesh_index.location == locations.elemental_nodal
):
raise NotImplementedError(
Expand Down Expand Up @@ -306,24 +321,21 @@ def iselect(self, **kwargs) -> DataFrame:
A DataFrame of the selected values.

"""
# Check for invalid arguments
axes = self.axes
for argument in kwargs.keys():
if argument not in axes:
raise ValueError(
f"The DataFrame has no axis {argument}, cannot select it. "
f"Available axes are: {axes}."
)
for label in kwargs.keys():
indices = kwargs[label]
axis_kwargs, _ = self._filter_arguments(arguments=kwargs)
for label in axis_kwargs.keys():
if label in self.index.names:
ids = getattr(self.index, label).values[indices]
values = getattr(self.index, label).values
else:
values = getattr(self.columns, label).values
indices = axis_kwargs[label]
if isinstance(indices, list):
ids = [values[i] for i in indices]
else:
ids = getattr(self.columns, label).values[indices]
ids = values[indices]
if isinstance(ids, DPFArray):
ids = ids.tolist()
kwargs[label] = ids
return self.select(**kwargs)
axis_kwargs[label] = ids
return self.select(**axis_kwargs)

def __len__(self):
"""Return the length of the DataFrame."""
Expand Down Expand Up @@ -477,8 +489,8 @@ def treat_elemental_nodal(treat_lines, pos, n_comp, n_ent, n_lines):
label_space = {}
for label_name in self.labels:
value = combination[label_positions_in_combinations[label_name]]
if label_name == "set_id":
label_name = "time"
if label_name == ref_labels.set_ids:
label_name = ref_labels.time
label_space[label_name] = value
fields = self._fc.get_fields(label_space=label_space)
values = []
Expand Down Expand Up @@ -616,6 +628,8 @@ def plot(self, shell_layer=shell_layers.top, **kwargs):
`set_id` 1 by using `df.plot(set_ids=1)`.
One can get the list of available axes using
:func:`DataFrame.axes <ansys.dpf.post.DataFrame.axes>`.
Also supports additional keyword arguments for the plotter. For additional keyword
arguments, see ``help(pyvista.plot)``.

Returns
-------
Expand All @@ -625,16 +639,9 @@ def plot(self, shell_layer=shell_layers.top, **kwargs):
from ansys.dpf.core.plotter import DpfPlotter as Plotter

if kwargs != {}:
# Check for invalid arguments
axes = self.axes
for argument in kwargs.keys():
if argument not in axes:
raise ValueError(
f"The DataFrame has no axis {argument}, cannot plot it. "
f"Available axes are: {axes}."
)
axis_kwargs, kwargs = self._filter_arguments(arguments=kwargs)
# Construct the associated label_space
fc = self.select(**kwargs)._fc
fc = self.select(**axis_kwargs)._fc
else:
# If no kwarg was given, construct a default label_space
fc = self._fc
Expand Down Expand Up @@ -683,7 +690,7 @@ def plot(self, shell_layer=shell_layers.top, **kwargs):

def animate(
self,
save_as: Union[PathLike, None] = None,
save_as: Union[PathLike, str, None] = None,
deform: bool = False,
scale_factor: Union[List[float], float] = 1.0,
**kwargs,
Expand Down Expand Up @@ -718,6 +725,8 @@ def animate(
f"unable to animate on the deformed mesh:\n{e}"
)
)
else:
deform_by = False
return self._fc.animate(
save_as=save_as, deform_by=deform_by, scale_factor=scale_factor, **kwargs
)
)
Loading