Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion flow360/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
TimeAverageIsosurfaceOutput,
TimeAverageProbeOutput,
TimeAverageSliceOutput,
TimeAverageStreamlineOutput,
TimeAverageSurfaceOutput,
TimeAverageSurfaceProbeOutput,
TimeAverageVolumeOutput,
Expand Down Expand Up @@ -241,6 +242,8 @@
"ImportedSurfaceOutput",
"TimeAverageImportedSurfaceOutput",
"ImportedSurfaceIntegralOutput",
"StreamlineOutput",
"TimeAverageStreamlineOutput",
"Observer",
"HeatEquationSolver",
"NavierStokesSolver",
Expand Down Expand Up @@ -295,7 +298,6 @@
"migration",
"Water",
"PointArray2D",
"StreamlineOutput",
"Transformation",
"WallRotation",
"UserVariable",
Expand Down
73 changes: 71 additions & 2 deletions flow360/component/simulation/outputs/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ def ensure_surface_existence(cls, value):
return check_deleted_surface_in_entity_list(value)


class StreamlineOutput(Flow360BaseModel):
class StreamlineOutput(_OutputBase):
"""
:class:`StreamlineOutput` class for calculating streamlines.
Stramtraces are computed upwind and downwind, and may originate from a single point,
Expand Down Expand Up @@ -1080,7 +1080,8 @@ class StreamlineOutput(Flow360BaseModel):
... u_number_of_points=11,
... v_number_of_points=20
... )
... ]
... ],
... output_fields = [fl.solution.pressure, fl.solution.velocity],
... )

====
Expand All @@ -1099,9 +1100,75 @@ class StreamlineOutput(Flow360BaseModel):
+ ":class:`~flow360.PointArray2D` "
+ "is used to define streamline originating from a parallelogram.",
)
output_fields: Optional[UniqueItemList[UserVariable]] = pd.Field(
[],
description="List of output variables. Vector-valued fields will be colored by their magnitude.",
)
output_type: Literal["StreamlineOutput"] = pd.Field("StreamlineOutput", frozen=True)


class TimeAverageStreamlineOutput(StreamlineOutput):
"""
:class:`StreamlineOutput` class for calculating time-averaged streamlines.
Stramtraces are computed upwind and downwind, and may originate from a single point,
from a line, or from points evenly distributed across a parallelogram.

Example
-------

Define a :class:`TimeAverageStreamlineOutput` with streaptraces originating from points,
lines (:class:`~flow360.PointArray`), and parallelograms (:class:`~flow360.PointArray2D`).

- :code:`Point_1` and :code:`Point_2` are two specific points we want to track the streamlines.
- :code:`Line_streamline` is from (1,0,0) * fl.u.m to (1,0,-10) * fl.u.m and has 11 points,
including both starting and end points.
- :code:`Parallelogram_streamline` is a parallelogram in 3D space with an origin at (1.0, 0.0, 0.0), a u-axis
orientation of (0, 2.0, 2.0) with 11 points in the u direction, and a v-axis orientation of (0, 1.0, 0)
with 20 points along the v direction.

>>> fl.TimeAverageStreamlineOutput(
... entities=[
... fl.Point(
... name="Point_1",
... location=(0.0, 1.5, 0.0) * fl.u.m,
... ),
... fl.Point(
... name="Point_2",
... location=(0.0, -1.5, 0.0) * fl.u.m,
... ),
... fl.PointArray(
... name="Line_streamline",
... start=(1.0, 0.0, 0.0) * fl.u.m,
... end=(1.0, 0.0, -10.0) * fl.u.m,
... number_of_points=11,
... ),
... fl.PointArray2D(
... name="Parallelogram_streamline",
... origin=(1.0, 0.0, 0.0) * fl.u.m,
... u_axis_vector=(0, 2.0, 2.0) * fl.u.m,
... v_axis_vector=(0, 1.0, 0) * fl.u.m,
... u_number_of_points=11,
... v_number_of_points=20
... )
... ]
... )

====
"""

name: Optional[str] = pd.Field(
"Time-average Streamline output", description="Name of the `TimeAverageStreamlineOutput`."
)

start_step: Union[pd.NonNegativeInt, Literal[-1]] = pd.Field(
default=-1, description="Physical time step to start calculating averaging."
)

output_type: Literal["TimeAverageStreamlineOutput"] = pd.Field(
"TimeAverageStreamlineOutput", frozen=True
)


class ImportedSurfaceOutput(_AnimationAndFileFormatSettings):
"""
:class:`ImportedSurfaceOutput` class for generating interpolated output on imported surfaces.
Expand Down Expand Up @@ -1231,6 +1298,7 @@ class ImportedSurfaceIntegralOutput(_OutputBase):
ImportedSurfaceOutput,
TimeAverageImportedSurfaceOutput,
ImportedSurfaceIntegralOutput,
TimeAverageStreamlineOutput,
],
pd.Field(discriminator="output_type"),
]
Expand All @@ -1243,4 +1311,5 @@ class ImportedSurfaceIntegralOutput(_OutputBase):
TimeAverageProbeOutput,
TimeAverageSurfaceProbeOutput,
TimeAverageImportedSurfaceOutput,
TimeAverageStreamlineOutput,
)
30 changes: 26 additions & 4 deletions flow360/component/simulation/translator/solver_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
TimeAverageIsosurfaceOutput,
TimeAverageProbeOutput,
TimeAverageSliceOutput,
TimeAverageStreamlineOutput,
TimeAverageSurfaceOutput,
TimeAverageSurfaceProbeOutput,
TimeAverageVolumeOutput,
Expand Down Expand Up @@ -263,6 +264,8 @@ def translate_output_fields(
StreamlineOutput,
ImportedSurfaceOutput,
TimeAverageImportedSurfaceOutput,
StreamlineOutput,
TimeAverageStreamlineOutput,
],
):
"""Get output fields"""
Expand Down Expand Up @@ -765,11 +768,25 @@ def process_output_fields_for_udf(input_params: SimulationParams):
return generated_udfs, list(user_variable_udfs.values())


def translate_streamline_output(output_params: list):
def translate_streamline_output(output_params: list, streamline_class):
"""Translate streamline output settings."""
streamline_output = {"Points": [], "PointArrays": [], "PointArrays2D": []}
streamline_output = {
"Points": [],
"PointArrays": [],
"PointArrays2D": [],
"outputFields": [],
"animationFrequency": -1,
"animationFrequencyOffset": 0,
}
for output in output_params:
if isinstance(output, StreamlineOutput):
if isinstance(output, streamline_class):
streamline_output["outputFields"] = translate_output_fields(output)["outputFields"]
# streamline_output["outputFields"].extend(output.output_fields.items)
if isinstance(output, TimeAverageStreamlineOutput):
streamline_output["startAverageIntegrationStep"] = output.start_step
streamline_output["animationFrequencyTimeAverage"] = -1
streamline_output["animationFrequencyTimeAverageOffset"] = 0

for entity in output.entities.stored_entities:
if isinstance(entity, Point):
point = {"name": entity.name, "location": entity.location.value.tolist()}
Expand Down Expand Up @@ -945,7 +962,12 @@ def translate_output(input_params: SimulationParams, translated: dict):

##:: Step8: Get translated["streamlineOutput"]
if has_instance_in_list(outputs, StreamlineOutput):
translated["streamlineOutput"] = translate_streamline_output(outputs)
translated["streamlineOutput"] = translate_streamline_output(outputs, StreamlineOutput)

if has_instance_in_list(outputs, TimeAverageStreamlineOutput):
translated["timeAverageStreamlineOutput"] = translate_streamline_output(
outputs, TimeAverageStreamlineOutput
)

##:: Step9: Get translated["importedSurfaceIntegralOutput"]
if has_instance_in_list(outputs, ImportedSurfaceIntegralOutput):
Expand Down
Loading
Loading