Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2f164ae
Initial forceOutput interface
angranl-flex Oct 9, 2025
8c60547
Update description of coefficients
angranl-flex Oct 13, 2025
5574720
include forceOutput to init
angranl-flex Oct 15, 2025
dcec0ec
Update the translator logic to support computation of BET/AD/PM coeff…
angranl-flex Oct 17, 2025
b59c5ff
update remote file name for porous medium coefficients
angranl-flex Oct 20, 2025
e30f71a
Use surface models to define ForceOutput and add corresponding valida…
angranl-flex Oct 20, 2025
e62153c
unify remote file names for coefficients csv
angranl-flex Oct 21, 2025
dc9f683
Fix the bug when preprocess surface models with id
angranl-flex Oct 21, 2025
9987f39
keep the same pseudo_step and physical_step column order in the outpu…
angranl-flex Oct 22, 2025
75d5c84
translate stop criterion setting to solver json and perform checking …
angranl-flex Oct 23, 2025
e06bde9
Fix solver translator
angranl-flex Oct 26, 2025
aa60d5a
update solver translator for stopping criteria and fix unit test
angranl-flex Oct 27, 2025
3111332
add BET/AD/PM support in ForceOutput
angranl-flex Oct 28, 2025
2a1a31b
Fix unit test
angranl-flex Oct 28, 2025
71dad76
dump a separate json for columnar data postprocessing
angranl-flex Oct 28, 2025
bb590c2
Fix that stopping criterion without moving statistic in MonitorOutput…
angranl-flex Oct 28, 2025
f53c17b
update solver translator
angranl-flex Oct 29, 2025
3bfb357
Update hash calculation logic
angranl-flex Oct 29, 2025
467b04b
Add validation and unit test for ForceOutput
angranl-flex Oct 30, 2025
5521585
Address comments
angranl-flex Nov 1, 2025
97e7d4c
Enable pylint at the correct location
angranl-flex Nov 1, 2025
3dedc25
Remove unnecessary str input for ForceOutput and StoppingCriterion
angranl-flex Nov 3, 2025
4e85961
Move parse_model_dict forward to ensure the multiconstructor models a…
angranl-flex Nov 4, 2025
0c6d938
update solver translator for more flexible dataset selection of stopp…
angranl-flex Nov 7, 2025
c3ff30f
Address comments
angranl-flex Nov 12, 2025
53c48cc
Fix unit test after rebase
angranl-flex Nov 14, 2025
1423b4b
Moving statistic improvement based on QA testing
angranl-flex Nov 14, 2025
d05b38b
Add note for standard deviation computation
angranl-flex Nov 17, 2025
681ed25
Add description to explain the use of tolerance window size in stoppi…
angranl-flex Nov 18, 2025
6aec51c
Add stopping criterion support for imported surface integral
angranl-flex Nov 19, 2025
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
2 changes: 2 additions & 0 deletions flow360/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
from flow360.component.simulation.outputs.outputs import (
AeroAcousticOutput,
ForceDistributionOutput,
ForceOutput,
IsosurfaceOutput,
MovingStatistic,
Observer,
Expand Down Expand Up @@ -337,6 +338,7 @@
"ImportedSurface",
"OctreeSpacing",
"RunControl",
"ForceOutput",
]

_warn_prerelease()
70 changes: 12 additions & 58 deletions flow360/component/results/case_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import re
from enum import Enum
from pathlib import Path
from typing import Callable, Dict, List, Optional
from typing import Callable, Dict, List, Optional, get_args

import numpy as np
import pydantic as pd
Expand All @@ -23,43 +23,22 @@
ResultTarGZModel,
)
from flow360.component.results.results_utils import (
_CD,
BETDiskCSVHeaderOperation,
DiskCoefficientsComputation,
PorousMediumCoefficientsComputation,
)
from flow360.component.simulation.conversion import unit_converter as unit_converter_v2
from flow360.component.simulation.outputs.output_fields import (
_CD_PER_STRIP,
_CD_PRESSURE,
_CD_SKIN_FRICTION,
_CL,
_CL_PRESSURE,
_CL_SKIN_FRICTION,
_CUMULATIVE_CD_CURVE,
_HEAT_FLUX,
_X,
_Y,
BETDiskCSVHeaderOperation,
DiskCoefficientsComputation,
PorousMediumCoefficientsComputation,
_CFx,
ForceOutputCoefficientNames,
_CFx_PER_SPAN,
_CFx_PRESSURE,
_CFx_SKIN_FRICTION,
_CFy,
_CFy_PRESSURE,
_CFy_SKIN_FRICTION,
_CFz,
_CFz_PER_SPAN,
_CFz_PRESSURE,
_CFz_SKIN_FRICTION,
_CMx,
_CMx_PRESSURE,
_CMx_SKIN_FRICTION,
_CMy,
_CMy_PER_SPAN,
_CMy_PRESSURE,
_CMy_SKIN_FRICTION,
_CMz,
_CMz_PRESSURE,
_CMz_SKIN_FRICTION,
)
from flow360.component.simulation.conversion import unit_converter as unit_converter_v2
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.simulation.unit_system import (
Flow360UnitSystem,
Expand Down Expand Up @@ -185,32 +164,7 @@ class SurfaceForcesResultCSVModel(PerEntityResultCSVModel, TimeSeriesResultCSVMo

remote_file_name: str = pd.Field(CaseDownloadable.SURFACE_FORCES.value, frozen=True)

_variables: List[str] = [
_CL,
_CD,
_CFx,
_CFy,
_CFz,
_CMx,
_CMy,
_CMz,
_CL_PRESSURE,
_CD_PRESSURE,
_CFx_PRESSURE,
_CFy_PRESSURE,
_CFz_PRESSURE,
_CMx_PRESSURE,
_CMy_PRESSURE,
_CMz_PRESSURE,
_CL_SKIN_FRICTION,
_CD_SKIN_FRICTION,
_CFx_SKIN_FRICTION,
_CFy_SKIN_FRICTION,
_CFz_SKIN_FRICTION,
_CMx_SKIN_FRICTION,
_CMy_SKIN_FRICTION,
_CMz_SKIN_FRICTION,
]
_variables: List[str] = list(get_args(ForceOutputCoefficientNames))

def _preprocess(self, filter_physical_steps_only: bool = True, include_time: bool = True):
"""
Expand Down Expand Up @@ -705,7 +659,7 @@ def _iterate_step_values(disk_name, disk_ctx, env, values):
class ActuatorDiskCoefficientsCSVModel(ResultCSVModel):
"""CSV model for actuator disk coefficients output."""

remote_file_name: str = pd.Field("actuator_disk_coefficients_v2.csv", frozen=True)
remote_file_name: str = pd.Field("actuatorDisk_force_coefficients_v2.csv", frozen=True)


class _BETDiskResults(_DimensionedCSVResultModel):
Expand Down Expand Up @@ -900,7 +854,7 @@ def _iterate_step_values(disk_name, disk_ctx, env, values):
class BETDiskCoefficientsCSVModel(ResultCSVModel):
"""CSV model for BET disk coefficients output."""

remote_file_name: str = pd.Field("bet_disk_coefficients_v2.csv", frozen=True)
remote_file_name: str = pd.Field("bet_force_coefficients_v2.csv", frozen=True)

def format_headers(
self, params: SimulationParams, pattern: str = "$BETName_$CylinderName"
Expand Down Expand Up @@ -984,7 +938,7 @@ def _iterate_step_values(zone_name, _, env, values):
class PorousMediumCoefficientsCSVModel(ResultCSVModel):
"""CSV model for porous medium coefficients output."""

remote_file_name: str = pd.Field("porous_medium_coefficients_v2.csv", frozen=True)
remote_file_name: str = pd.Field("porous_media_force_coefficients_v2.csv", frozen=True)


class BETForcesRadialDistributionResultCSVModel(OptionallyDownloadableResultCSVModel):
Expand Down
84 changes: 23 additions & 61 deletions flow360/component/results/results_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,21 @@
ResultCSVModel,
)
from flow360.component.simulation.models.volume_models import BETDisk
from flow360.component.simulation.outputs.output_fields import (
_CD,
_CL,
_CFx,
_CFy,
_CFz,
_CMx,
_CMy,
_CMz,
)
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.simulation.user_code.core.types import Expression
from flow360.exceptions import Flow360ValueError
from flow360.log import log

# pylint:disable=invalid-name
_CL = "CL"
_CD = "CD"
_CFx = "CFx"
_CFy = "CFy"
_CFz = "CFz"
_CMx = "CMx"
_CMy = "CMy"
_CMz = "CMz"
_CL_PRESSURE = "CLPressure"
_CD_PRESSURE = "CDPressure"
_CFx_PRESSURE = "CFxPressure"
_CFy_PRESSURE = "CFyPressure"
_CFz_PRESSURE = "CFzPressure"
_CMx_PRESSURE = "CMxPressure"
_CMy_PRESSURE = "CMyPressure"
_CMz_PRESSURE = "CMzPressure"
_CL_SKIN_FRICTION = "CLSkinFriction"
_CD_SKIN_FRICTION = "CDSkinFriction"
_CFx_SKIN_FRICTION = "CFxSkinFriction"
_CFy_SKIN_FRICTION = "CFySkinFriction"
_CFz_SKIN_FRICTION = "CFzSkinFriction"
_CMx_SKIN_FRICTION = "CMxSkinFriction"
_CMy_SKIN_FRICTION = "CMySkinFriction"
_CMz_SKIN_FRICTION = "CMzSkinFriction"
_CL_VISCOUS = "CLViscous"
_CD_VISCOUS = "CDViscous"
_CFx_VISCOUS = "CFxViscous"
_CFy_VISCOUS = "CFyViscous"
_CFz_VISCOUS = "CFzViscous"
_CMx_VISCOUS = "CMxViscous"
_CMy_VISCOUS = "CMyViscous"
_CMz_VISCOUS = "CMzViscous"
_HEAT_TRANSFER = "HeatTransfer"
_HEAT_FLUX = "HeatFlux"
_X = "X"
_Y = "Y"
_CUMULATIVE_CD_CURVE = "Cumulative_CD_Curve"
_CD_PER_STRIP = "CD_per_strip"
_CFx_PER_SPAN = "CFx_per_span"
_CFz_PER_SPAN = "CFz_per_span"
_CMy_PER_SPAN = "CMy_per_span"


# Static utilities for aerodynamic coefficient computations.

# Provides helper methods for computing aerodynamic coefficients using
Expand Down Expand Up @@ -161,7 +127,7 @@ def _get_lift_drag_direction(params: SimulationParams):


def _get_dynamic_pressure_in_flow360_unit(params: SimulationParams):
# pylint:disable=protected-access
# pylint:disable=protected-access,invalid-name

v_ref = params.reference_velocity

Expand Down Expand Up @@ -189,8 +155,10 @@ def _build_coeff_env(params) -> Dict[str, Any]:

def _copy_time_columns(src: Dict[str, list]) -> Dict[str, list]:
out: Dict[str, list] = {}
out[_PSEUDO_STEP] = src[_PSEUDO_STEP]
out[_PHYSICAL_STEP] = src[_PHYSICAL_STEP]
for key in src.keys():
if key in [_PSEUDO_STEP, _PHYSICAL_STEP]:
out[key] = src[key]
continue
return out


Expand Down Expand Up @@ -338,13 +306,6 @@ class PorousMediumCoefficientsComputation:
# pylint:disable=too-few-public-methods
"""Static utilities for porous medium coefficient computations."""

@staticmethod
def _copy_time_columns(src: Dict[str, list]) -> Dict[str, list]:
out: Dict[str, list] = {}
out[_PSEUDO_STEP] = src[_PSEUDO_STEP]
out[_PHYSICAL_STEP] = src[_PHYSICAL_STEP]
return out

@staticmethod
def _iter_zones(values: Dict[str, list]):
# Support both default "zone_<idx>_<...>" and arbitrary GenericVolume-style names
Expand Down Expand Up @@ -413,6 +374,7 @@ def compute_coefficients_static(

for zone_name in PorousMediumCoefficientsComputation._iter_zones(values):
PorousMediumCoefficientsComputation._init_zone_output(out, zone_name)
# pylint: disable=invalid-name
for CF, CM, CL_val, CD_val in iterate_step_values_func(zone_name, {}, env, values):
out[f"{zone_name}_{_CFx}"].append(CF[0])
out[f"{zone_name}_{_CFy}"].append(CF[1])
Expand All @@ -437,7 +399,7 @@ class BETDiskCSVHeaderOperation:

@staticmethod
def format_headers(
BETCSVModel: ResultCSVModel,
BETCSVModel: ResultCSVModel, # pylint: disable=invalid-name
params: SimulationParams,
pattern: str = "$BETName_$CylinderName",
) -> LocalResultCSVModel:
Expand Down Expand Up @@ -473,15 +435,15 @@ def format_headers(

disk_rename_map = {}

diskCount = 0
disk_count = 0
for disk in bet_disks:
for disk_local_index, cylinder in enumerate(disk.entities.stored_entities):
new_name = pattern.replace("$BETName", disk.name)
new_name = new_name.replace("$CylinderName", cylinder.name)
new_name = new_name.replace("$DiskLocalIndex", str(disk_local_index))
new_name = new_name.replace("$DiskGlobalIndex", str(diskCount))
disk_rename_map[f"Disk{diskCount}"] = new_name
diskCount = diskCount + 1
new_name = new_name.replace("$DiskGlobalIndex", str(disk_count))
disk_rename_map[f"Disk{disk_count}"] = new_name
disk_count = disk_count + 1

for header, values in csv_data.items():
matched = False
Expand All @@ -492,5 +454,5 @@ def format_headers(
break
if not matched:
new_csv[header] = values
newModel = LocalResultCSVModel().from_dict(new_csv)
return newModel
new_model = LocalResultCSVModel().from_dict(new_csv)
return new_model
7 changes: 7 additions & 0 deletions flow360/component/simulation/framework/param_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,10 @@ def _set_boundary_full_name_with_zone_name(
continue
with model_attribute_unlock(surface, "private_attribute_full_name"):
surface.private_attribute_full_name = f"{give_zone_name}/{surface.name}"


def serialize_model_obj_to_id(model_obj: Flow360BaseModel) -> str:
"""Serialize a model object to its id."""
if hasattr(model_obj, "private_attribute_id"):
return model_obj.private_attribute_id
raise ValueError(f"The model object {model_obj} cannot be serialized to id.")
71 changes: 71 additions & 0 deletions flow360/component/simulation/outputs/output_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,50 @@
)
from flow360.component.simulation.unit_system import u

# pylint:disable=invalid-name
_CD = "CD"
_CL = "CL"
_CFx = "CFx"
_CFy = "CFy"
_CFz = "CFz"
_CMx = "CMx"
_CMy = "CMy"
_CMz = "CMz"
_CD_PRESSURE = "CDPressure"
_CL_PRESSURE = "CLPressure"
_CFx_PRESSURE = "CFxPressure"
_CFy_PRESSURE = "CFyPressure"
_CFz_PRESSURE = "CFzPressure"
_CMx_PRESSURE = "CMxPressure"
_CMy_PRESSURE = "CMyPressure"
_CMz_PRESSURE = "CMzPressure"
_CD_SKIN_FRICTION = "CDSkinFriction"
_CL_SKIN_FRICTION = "CLSkinFriction"
_CFx_SKIN_FRICTION = "CFxSkinFriction"
_CFy_SKIN_FRICTION = "CFySkinFriction"
_CFz_SKIN_FRICTION = "CFzSkinFriction"
_CMx_SKIN_FRICTION = "CMxSkinFriction"
_CMy_SKIN_FRICTION = "CMySkinFriction"
_CMz_SKIN_FRICTION = "CMzSkinFriction"
_CL_VISCOUS = "CLViscous"
_CD_VISCOUS = "CDViscous"
_CFx_VISCOUS = "CFxViscous"
_CFy_VISCOUS = "CFyViscous"
_CFz_VISCOUS = "CFzViscous"
_CMx_VISCOUS = "CMxViscous"
_CMy_VISCOUS = "CMyViscous"
_CMz_VISCOUS = "CMzViscous"
_HEAT_TRANSFER = "HeatTransfer"
_HEAT_FLUX = "HeatFlux"
_X = "X"
_Y = "Y"
_CUMULATIVE_CD_CURVE = "Cumulative_CD_Curve"
_CD_PER_STRIP = "CD_per_strip"
_CFx_PER_SPAN = "CFx_per_span"
_CFz_PER_SPAN = "CFz_per_span"
_CMy_PER_SPAN = "CMy_per_span"
# pylint:enable=invalid-name

# Coefficient of pressure
# Coefficient of total pressure
# Gradient of primitive solution
Expand Down Expand Up @@ -209,6 +253,33 @@
"heatTransferCoefficientStaticTemperature",
"heatTransferCoefficientTotalTemperature",
]

ForceOutputCoefficientNames = Literal[
_CL,
_CD,
_CFx,
_CFy,
_CFz,
_CMx,
_CMy,
_CMz,
_CL_PRESSURE,
_CD_PRESSURE,
_CFx_PRESSURE,
_CFy_PRESSURE,
_CFz_PRESSURE,
_CMx_PRESSURE,
_CMy_PRESSURE,
_CMz_PRESSURE,
_CL_SKIN_FRICTION,
_CD_SKIN_FRICTION,
_CFx_SKIN_FRICTION,
_CFy_SKIN_FRICTION,
_CFz_SKIN_FRICTION,
_CMx_SKIN_FRICTION,
_CMy_SKIN_FRICTION,
_CMz_SKIN_FRICTION,
]
# pylint: disable=no-member
_FIELD_UNIT_MAPPING = {
# Standard non-dimensioned fields - (unit, unit_system)
Expand Down
Loading
Loading