Skip to content

Commit 0c3f55f

Browse files
committed
add BET/AD/PM support in ForceOutput
1 parent 41dd2cc commit 0c3f55f

File tree

1 file changed

+44
-14
lines changed

1 file changed

+44
-14
lines changed

flow360/component/simulation/outputs/outputs.py

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
EntityListAllowingGhost,
1919
Wall,
2020
)
21+
from flow360.component.simulation.models.volume_models import (
22+
ActuatorDisk,
23+
BETDisk,
24+
PorousMedium,
25+
)
2126
from flow360.component.simulation.outputs.output_entities import (
2227
Isosurface,
2328
Point,
@@ -59,6 +64,11 @@
5964
check_deleted_surface_in_entity_list,
6065
)
6166

67+
ForceOutputModelType = Annotated[
68+
Union[Wall, BETDisk, ActuatorDisk, PorousMedium],
69+
pd.Field(discriminator="type"),
70+
]
71+
6272

6373
class UserDefinedField(Flow360BaseModel):
6474
"""
@@ -663,43 +673,46 @@ class ForceOutput(_OutputBase):
663673
664674
Define :class:`ForceOutput` to output total CL and CD on multiple wing surfaces.
665675
676+
>>> wall = fl.Wall(name = 'wing', surfaces=[volume_mesh['1'], volume_mesh["wing2"]])
666677
>>> fl.ForceOutput(
667678
... name="force_output_wings",
668-
... entities=[volume_mesh["wing1"], volume_mesh["wing2"]],
679+
... models=[wall],
669680
... output_fields=["CL", "CD"]
670681
... )
671682
672683
====
673684
"""
674685

675686
name: str = pd.Field("Force output", description="Name of the force output.")
676-
surface_models: List[Union[Wall, str]] = pd.Field(
677-
description="List of surface models whose force contribution will be calculated.",
678-
)
679687
output_fields: UniqueItemList[ForceOutputCoefficientNames] = pd.Field(
680688
description="List of force coefficients. Including CL, CD, CFx, CFy, CFz, CMx, CMy, CMz. "
681689
"For surface forces, their SkinFriction/Pressure is also supported, such as CLSkinFriction and CLPressure."
682690
)
691+
models: List[Union[ForceOutputModelType, str]] = pd.Field(
692+
description="List of surface/volume models whose force contribution will be calculated.",
693+
)
683694
moving_statistic: Optional[MovingStatistic] = pd.Field(
684695
None, description="The moving statistics used to monitor the output."
685696
)
686697
output_type: Literal["ForceOutput"] = pd.Field("ForceOutput", frozen=True)
687698

688-
@pd.field_serializer("surface_models")
689-
def serialize_models(self, v):
699+
@pd.field_serializer("models")
700+
def serialize_models(self, value, info: pd.FieldSerializationInfo):
690701
"""Serialize only the model's id of the related object."""
702+
if isinstance(info.context, dict) and info.context.get("columnar_data_processor"):
703+
return value
691704
model_ids = []
692-
for model in v:
693-
if isinstance(model, Wall):
705+
for model in value:
706+
if isinstance(model, get_args(get_args(ForceOutputModelType)[0])):
694707
model_ids.append(model.private_attribute_id)
695708
continue
696709
model_ids.append(model)
697710
return model_ids
698711

699-
@pd.field_validator("surface_models", mode="before")
712+
@pd.field_validator("models", mode="before")
700713
@classmethod
701-
def _preprocess_models_with_id(cls, v):
702-
"""Deserialize string-format surface models as Wall objects."""
714+
def _preprocess_models_with_id(cls, value):
715+
"""Deserialize string-format models as model objects."""
703716

704717
def preprocess_single_model(model, validation_info):
705718
if not isinstance(model, str):
@@ -710,16 +723,33 @@ def preprocess_single_model(model, validation_info):
710723
or validation_info.physics_model_dict.get(model) is None
711724
):
712725
raise ValueError("The model does not exist in the models list.")
713-
surface_model_dict = validation_info.physics_model_dict[model]
714-
model = Wall.model_validate(surface_model_dict)
726+
physics_model_dict = validation_info.physics_model_dict[model]
727+
model = pd.TypeAdapter(ForceOutputModelType).validate_python(physics_model_dict)
715728
return model
716729

717730
processed_models = []
718731
validation_info = get_validation_info()
719-
for model in v:
732+
for model in value:
720733
processed_models.append(preprocess_single_model(model, validation_info))
721734
return processed_models
722735

736+
@pd.field_validator("models", mode="after")
737+
@classmethod
738+
def _check_output_fields_with_volume_models_specified(cls, value, info: pd.ValidationInfo):
739+
"""Ensure the output field exists when volume models are specified."""
740+
if all(isinstance(model, Wall) for model in value):
741+
return value
742+
output_fields = info.data.get("output_fields", None)
743+
if all(
744+
field in ["CL", "CD", "CFx", "CFy", "CFz", "CMx", "CMy", "CMz"]
745+
for field in output_fields.items
746+
):
747+
return value
748+
raise ValueError(
749+
"When ActuatorDisk/BETDisk/PorousMedium is specified, "
750+
"only CL, CD, CFx, CFy, CFz, CMx, CMy, CMz can be set as output_fields."
751+
)
752+
723753

724754
class ProbeOutput(_OutputBase):
725755
"""

0 commit comments

Comments
 (0)