From 1ec0c91a11cecc9b25d9641c89df7b485ab500e4 Mon Sep 17 00:00:00 2001 From: Yifan Bai Date: Fri, 20 Jun 2025 17:58:41 +0000 Subject: [PATCH 1/2] Add TimeAverageIsosurfaceOutput --- flow360/__init__.py | 2 + .../component/simulation/outputs/outputs.py | 47 +++++++++++++++++++ .../translator/solver_translator.py | 26 ++++++++++ .../validation_simulation_params.py | 15 ++++++ 4 files changed, 90 insertions(+) diff --git a/flow360/__init__.py b/flow360/__init__.py index 3ade4723e..e752ed069 100644 --- a/flow360/__init__.py +++ b/flow360/__init__.py @@ -119,6 +119,7 @@ SurfaceOutput, SurfaceProbeOutput, SurfaceSliceOutput, + TimeAverageIsosurfaceOutput, TimeAverageProbeOutput, TimeAverageSliceOutput, TimeAverageSurfaceOutput, @@ -215,6 +216,7 @@ "SliceOutput", "TimeAverageSliceOutput", "IsosurfaceOutput", + "TimeAverageIsosurfaceOutput", "SurfaceIntegralOutput", "ProbeOutput", "SurfaceProbeOutput", diff --git a/flow360/component/simulation/outputs/outputs.py b/flow360/component/simulation/outputs/outputs.py index 1fb58828d..5cddfe4a7 100644 --- a/flow360/component/simulation/outputs/outputs.py +++ b/flow360/component/simulation/outputs/outputs.py @@ -421,6 +421,51 @@ class IsosurfaceOutput(_AnimationAndFileFormatSettings): output_type: Literal["IsosurfaceOutput"] = pd.Field("IsosurfaceOutput", frozen=True) +class TimeAverageIsosurfaceOutput(IsosurfaceOutput): + """ + + :class:`TimeAverageIsosurfaceOutput` class for isosurface output settings. + + Example + ------- + + Define the :class:`TimeAverageIsosurfaceOutput` of :code:`qcriterion` on two isosurfaces: + + - :code:`TimeAverageIsosurface_T_0.1` is the :class:`Isosurface` with its temperature equals + to 1.5 non-dimensional temperature; + - :code:`TimeAverageIsosurface_p_0.5` is the :class:`Isosurface` with its pressure equals + to 0.5 non-dimensional pressure. + + >>> fl.TimeAverageIsosurfaceOutput( + ... isosurfaces=[ + ... fl.Isosurface( + ... name="TimeAverageIsosurface_T_0.1", + ... iso_value=0.1, + ... field="T", + ... ), + ... fl.Isosurface( + ... name="TimeAverageIsosurface_p_0.5", + ... iso_value=0.5, + ... field="p", + ... ), + ... ], + ... output_fields=["qcriterion"], + ... ) + + ==== + """ + + name: Optional[str] = pd.Field( + "Time Average Isosurface output", description="Name of `TimeAverageIsosurfaceOutput`." + ) + start_step: Union[pd.NonNegativeInt, Literal[-1]] = pd.Field( + default=-1, description="Physical time step to start calculating averaging." + ) + output_type: Literal["TimeAverageIsosurfaceOutput"] = pd.Field( + "TimeAverageIsosurfaceOutput", frozen=True + ) + + class SurfaceIntegralOutput(_OutputBase): """ @@ -991,6 +1036,7 @@ class StreamlineOutput(Flow360BaseModel): SliceOutput, TimeAverageSliceOutput, IsosurfaceOutput, + TimeAverageIsosurfaceOutput, SurfaceIntegralOutput, ProbeOutput, SurfaceProbeOutput, @@ -1007,6 +1053,7 @@ class StreamlineOutput(Flow360BaseModel): TimeAverageSurfaceOutput, TimeAverageVolumeOutput, TimeAverageSliceOutput, + TimeAverageIsosurfaceOutput, TimeAverageProbeOutput, TimeAverageSurfaceProbeOutput, ) diff --git a/flow360/component/simulation/translator/solver_translator.py b/flow360/component/simulation/translator/solver_translator.py index 53f71e5be..cc69d6eeb 100644 --- a/flow360/component/simulation/translator/solver_translator.py +++ b/flow360/component/simulation/translator/solver_translator.py @@ -62,6 +62,7 @@ SurfaceOutput, SurfaceProbeOutput, SurfaceSliceOutput, + TimeAverageIsosurfaceOutput, TimeAverageProbeOutput, TimeAverageSliceOutput, TimeAverageSurfaceOutput, @@ -438,6 +439,27 @@ def translate_isosurface_output( return translated_output +def translate_time_average_isosurface_output( + output_params: list, + injection_function, +): + """Translate time average isosurface output settings.""" + translated_output = init_output_base( + output_params, + TimeAverageIsosurfaceOutput, + has_average_capability=True, + is_average=True, + ) + translated_output["isoSurfaces"] = translate_setting_and_apply_to_all_entities( + output_params, + TimeAverageIsosurfaceOutput, + translation_func=translate_output_fields, + to_list=False, + entity_injection_func=injection_function, + ) + return translated_output + + def translate_surface_slice_output( output_params: list, output_class: Union[SurfaceSliceOutput], @@ -635,6 +657,10 @@ def translate_output(input_params: SimulationParams, translated: dict): translated["isoSurfaceOutput"] = translate_isosurface_output( outputs, inject_isosurface_info ) + if has_instance_in_list(outputs, TimeAverageIsosurfaceOutput): + translated["timeAverageIsoSurfaceOutput"] = translate_time_average_isosurface_output( + outputs, inject_isosurface_info + ) ##:: Step5: Get translated["monitorOutput"] probe_output = {} diff --git a/flow360/component/simulation/validation/validation_simulation_params.py b/flow360/component/simulation/validation/validation_simulation_params.py index 6f9b5179d..ec16005a3 100644 --- a/flow360/component/simulation/validation/validation_simulation_params.py +++ b/flow360/component/simulation/validation/validation_simulation_params.py @@ -17,10 +17,14 @@ ProbeOutput, SliceOutput, SurfaceOutput, + TimeAverageIsosurfaceOutput, TimeAverageOutputTypes, VolumeOutput, ) from flow360.component.simulation.time_stepping.time_stepping import Steady, Unsteady +from flow360.component.simulation.utils import ( + is_exact_instance, +) from flow360.component.simulation.validation.validation_context import ( ALL, CASE, @@ -477,6 +481,7 @@ def _check_duplicate_isosurface_names(outputs): if outputs is None: return outputs isosurface_names = [] + isosurface_time_avg_names = [] for output in outputs: if isinstance(output, IsosurfaceOutput): for entity in output.entities.items: @@ -485,9 +490,19 @@ def _check_duplicate_isosurface_names(outputs): "The name `qcriterion` is reserved for the autovis isosurface from solver, " "please rename the isosurface." ) + if is_exact_instance(output, IsosurfaceOutput): + for entity in output.entities.items: if entity.name in isosurface_names: raise ValueError( f"Another isosurface with name: `{entity.name}` already exists, please rename the isosurface." ) isosurface_names.append(entity.name) + if is_exact_instance(output, TimeAverageIsosurfaceOutput): + for entity in output.entities.items: + if entity.name in isosurface_names: + raise ValueError( + f"Another time average isosurface with name: " + "`{entity.name}` already exists, please rename the isosurface." + ) + isosurface_time_avg_names.append(entity.name) return outputs From c266612cf5b122b1bdd50d3597a57e9be2d9fd23 Mon Sep 17 00:00:00 2001 From: Yifan Bai Date: Fri, 20 Jun 2025 18:06:05 +0000 Subject: [PATCH 2/2] lint --- .../simulation/validation/validation_simulation_params.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/flow360/component/simulation/validation/validation_simulation_params.py b/flow360/component/simulation/validation/validation_simulation_params.py index ec16005a3..3989af927 100644 --- a/flow360/component/simulation/validation/validation_simulation_params.py +++ b/flow360/component/simulation/validation/validation_simulation_params.py @@ -22,9 +22,7 @@ VolumeOutput, ) from flow360.component.simulation.time_stepping.time_stepping import Steady, Unsteady -from flow360.component.simulation.utils import ( - is_exact_instance, -) +from flow360.component.simulation.utils import is_exact_instance from flow360.component.simulation.validation.validation_context import ( ALL, CASE, @@ -501,8 +499,8 @@ def _check_duplicate_isosurface_names(outputs): for entity in output.entities.items: if entity.name in isosurface_names: raise ValueError( - f"Another time average isosurface with name: " - "`{entity.name}` already exists, please rename the isosurface." + "Another time average isosurface with name: " + f"`{entity.name}` already exists, please rename the isosurface." ) isosurface_time_avg_names.append(entity.name) return outputs