@@ -77,6 +77,8 @@ def __init__(
77
77
self ._last_display_width = display_width
78
78
self ._last_display_max_colwidth = display_max_colwidth
79
79
80
+ self ._last_minmax : dict = {"axis" : None , "min" : None , "max" : None }
81
+
80
82
@property
81
83
def columns (self ) -> MultiIndex :
82
84
"""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):
513
515
else empty
514
516
for i in range (len (combination ))
515
517
]
516
- to_append .append (empty )
518
+ to_append .append (empty ) # row where row index headers are
517
519
# Get data in the FieldsContainer for those positions
518
520
# Create label_space from combination
519
521
label_space = {}
@@ -833,3 +835,201 @@ def animate(
833
835
return fc .animate (
834
836
save_as = save_as , deform_by = deform_by , scale_factor = scale_factor , ** kwargs
835
837
)
838
+
839
+ def min (self , axis : Union [int , str , None ] = 0 ) -> Union [DataFrame , float ]:
840
+ """Return the minimum value over the requested axis.
841
+
842
+ Parameters
843
+ ----------
844
+ axis:
845
+ Axis to perform minimum across.
846
+ Defaults to the MeshIndex (0), the row index containing mesh entity IDs.
847
+ This computes the minimum across the mesh for each set.
848
+ Can also be the SetIndex (1), the column index containing set (time/frequency) IDs.
849
+ This computes the minimum across sets (time/frequency) for each mesh entity.
850
+
851
+ Returns
852
+ -------
853
+ A scalar if the result of the query is a single number,
854
+ or a DataFrame if several numbers along one or several axes.
855
+
856
+ Examples
857
+ --------
858
+ >>> from ansys.dpf import post
859
+ >>> from ansys.dpf.post import examples
860
+ >>> simulation = post.StaticMechanicalSimulation(examples.download_crankshaft())
861
+ >>> displacement = simulation.displacement(all_sets=True)
862
+ >>> # Compute the maximum displacement value for each component at each time-step
863
+ >>> minimum_over_mesh = displacement.min(axis="node_ids")
864
+ >>> print(minimum_over_mesh) # doctest: +NORMALIZE_WHITESPACE
865
+ results U (m)
866
+ set_ids 1 2 3
867
+ components
868
+ X -7.4732e-04 -1.5081e-03 -2.2755e-03
869
+ Y -4.0138e-04 -8.0316e-04 -1.2014e-03
870
+ Z -2.1555e-04 -4.3299e-04 -6.5101e-04
871
+ >>> # Compute the maximum displacement for each node and component across time
872
+ >>> minimum_over_time = displacement.min(axis="set_ids")
873
+ >>> print(minimum_over_time) # doctest: +NORMALIZE_WHITESPACE
874
+ results U (m)
875
+ node_ids components
876
+ 4872 X -3.4137e-05
877
+ Y 5.1667e-04
878
+ Z -4.1346e-06
879
+ 9005 X -5.5625e-05
880
+ Y 4.8445e-04
881
+ Z -4.9795e-07
882
+ ...
883
+ >>> # Compute the maximum displacement overall
884
+ >>> minimum_overall = minimum_over_time.min()
885
+ >>> print(minimum_overall) # doctest: +NORMALIZE_WHITESPACE
886
+ results U (m)
887
+ components
888
+ X -2.2755e-03
889
+ Y -1.2014e-03
890
+ Z -6.5101e-04
891
+ """
892
+ self ._query_min_max (axis )
893
+ return self ._last_minmax ["min" ]
894
+
895
+ def max (self , axis : Union [int , str , None ] = 0 ) -> Union [DataFrame , float ]:
896
+ """Return the maximum value over the requested axis.
897
+
898
+ Parameters
899
+ ----------
900
+ axis:
901
+ Axis to perform maximum across.
902
+ Defaults to the MeshIndex (0), the row index containing mesh entity IDs.
903
+ This computes the maximum across the mesh for each set.
904
+ Can also be the SetIndex (1), the column index containing set (time/frequency) IDs.
905
+ This computes the maximum across sets (time/frequency) for each mesh entity.
906
+
907
+ Returns
908
+ -------
909
+ A scalar if the result of the query is a single number,
910
+ or a DataFrame if several numbers along one or several axes.
911
+
912
+ Examples
913
+ --------
914
+ >>> from ansys.dpf import post
915
+ >>> from ansys.dpf.post import examples
916
+ >>> simulation = post.StaticMechanicalSimulation(examples.download_crankshaft())
917
+ >>> displacement = simulation.displacement(all_sets=True)
918
+ >>> # Compute the maximum displacement value for each component at each time-step
919
+ >>> maximum_over_mesh = displacement.max(axis="node_ids")
920
+ >>> print(maximum_over_mesh) # doctest: +NORMALIZE_WHITESPACE
921
+ results U (m)
922
+ set_ids 1 2 3
923
+ components
924
+ X 7.3303e-04 1.4495e-03 2.1441e-03
925
+ Y 1.3962e-03 2.7884e-03 4.1656e-03
926
+ Z 2.1567e-04 4.3321e-04 6.5135e-04
927
+ >>> # Compute the maximum displacement for each node and component across time
928
+ >>> maximum_over_time = displacement.max(axis="set_ids")
929
+ >>> print(maximum_over_time) # doctest: +NORMALIZE_WHITESPACE
930
+ results U (m)
931
+ node_ids components
932
+ 4872 X 5.6781e-06
933
+ Y 1.5417e-03
934
+ Z -2.6398e-06
935
+ 9005 X -2.6323e-06
936
+ Y 1.4448e-03
937
+ Z 5.3134e-06
938
+ ...
939
+ >>> # Compute the maximum displacement overall
940
+ >>> maximum_overall = maximum_over_time.max()
941
+ >>> print(maximum_overall) # doctest: +NORMALIZE_WHITESPACE
942
+ results U (m)
943
+ components
944
+ X 2.1441e-03
945
+ Y 4.1656e-03
946
+ Z 6.5135e-04
947
+ """
948
+ self ._query_min_max (axis )
949
+ return self ._last_minmax ["max" ]
950
+
951
+ def _query_min_max (self , axis : Union [int , str , None ]) -> None :
952
+ """Create a DPF workflow based on the query arguments for min/max."""
953
+ # Translate None query to empty dict
954
+ if axis in [None , 0 , self .index .mesh_index .name ]:
955
+ axis = 0
956
+ elif axis in [1 , ref_labels .set_ids ]:
957
+ axis = 1
958
+ else :
959
+ raise ValueError (f"'{ axis } ' is not an available axis value." )
960
+
961
+ # print(f"{axis=}")
962
+ # If same query as last and last is not None, do not change
963
+ if self ._last_minmax ["axis" ] == axis and not self ._last_minmax ["axis" ] is None :
964
+ return
965
+ # If in need of an update, create the appropriate workflow
966
+ wf = dpf .Workflow (server = self ._fc ._server )
967
+ wf .progress_bar = False
968
+
969
+ # If over mesh
970
+ if axis == 0 :
971
+ min_max_op = dpf .operators .min_max .min_max_over_label_fc (
972
+ fields_container = self ._fc ,
973
+ label = "time" ,
974
+ server = self ._fc ._server ,
975
+ )
976
+ # Here the fields are located on the label ("time"), so we have to "transpose" it.
977
+ # Extract the data for each time (entity) from the field and create a fields_container
978
+
979
+ min_fc = dpf .FieldsContainer (server = self ._fc ._server )
980
+ min_fc .add_label (label = "time" )
981
+ min_field = min_max_op .outputs .field_min ()
982
+ for i , time in enumerate (min_field .scoping .ids ):
983
+ min_fc .add_field (
984
+ label_space = {"time" : time },
985
+ field = dpf .fields_factory .field_from_array (
986
+ arr = min_field .get_entity_data (i ),
987
+ server = self ._fc ._server ,
988
+ ),
989
+ )
990
+
991
+ max_fc = dpf .FieldsContainer (server = self ._fc ._server )
992
+ max_fc .add_label (label = "time" )
993
+ max_field = min_max_op .outputs .field_max ()
994
+ for i , time in enumerate (max_field .scoping .ids ):
995
+ max_fc .add_field (
996
+ label_space = {"time" : time },
997
+ field = dpf .fields_factory .field_from_array (
998
+ arr = max_field .get_entity_data (i ),
999
+ server = self ._fc ._server ,
1000
+ ),
1001
+ )
1002
+
1003
+ index = MultiIndex (
1004
+ indexes = [i for i in self .index if i != self .index .mesh_index ]
1005
+ )
1006
+ columns = self .columns
1007
+
1008
+ # If over time
1009
+ else :
1010
+ min_max_op = dpf .operators .min_max .min_max_over_time_by_entity (
1011
+ fields_container = self ._fc ,
1012
+ server = self ._fc ._server ,
1013
+ )
1014
+ wf .set_output_name ("min" , min_max_op .outputs .min )
1015
+ wf .set_output_name ("max" , min_max_op .outputs .max )
1016
+
1017
+ index = self .index
1018
+ columns = MultiIndex (
1019
+ indexes = [c for c in self .columns if c != self .columns .set_ids ]
1020
+ )
1021
+
1022
+ min_fc = wf .get_output ("min" , dpf .types .fields_container )
1023
+ max_fc = wf .get_output ("max" , dpf .types .fields_container )
1024
+
1025
+ self ._last_minmax ["min" ] = DataFrame (
1026
+ data = min_fc ,
1027
+ index = index ,
1028
+ columns = columns ,
1029
+ )
1030
+ self ._last_minmax ["max" ] = DataFrame (
1031
+ data = max_fc ,
1032
+ index = index ,
1033
+ columns = columns ,
1034
+ )
1035
+ self ._last_minmax ["axis" ] = axis
0 commit comments