Skip to content

Add BET solver JSON translator #319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 11 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: 2 additions & 2 deletions flow360/component/simulation/framework/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,14 @@ def _convert_dimensions_to_solver(
assert mesh_unit is not None

for property_name, value in self_dict.items():
if property_name in [COMMENTS, TYPE_TAG_STR] + exclude:
if property_name in [COMMENTS, TYPE_TAG_STR]:
continue
loc_name = property_name
field = self.model_fields.get(property_name)
if field is not None and field.alias is not None:
loc_name = field.alias

if need_conversion(value):
if need_conversion(value) and property_name not in exclude:
log.debug(f" -> need conversion for: {property_name} = {value}")
flow360_conv_system = unit_converter(
value.units.dimensions,
Expand Down
4 changes: 2 additions & 2 deletions flow360/component/simulation/framework/entity_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ def _format_input_to_list(cls, input: Union[dict, list]):
return dict(stored_entities=None)
else:
cls._valid_individual_input(input)
if isinstance(item, tuple(valid_types)):
formated_input.append(item)
if isinstance(input, tuple(valid_types)):
formated_input.append(input)
return dict(stored_entities=formated_input)

@pd.field_validator("stored_entities", mode="after")
Expand Down
34 changes: 31 additions & 3 deletions flow360/component/simulation/models/material.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Literal, Optional, Union

import pydantic as pd
from numpy import sqrt

import flow360.component.simulation.units as u
from flow360.component.simulation.framework.base_model import Flow360BaseModel
Expand All @@ -9,6 +10,7 @@
SpecificHeatCapacityType,
TemperatureType,
ThermalConductivityType,
VelocityType,
ViscosityType,
)

Expand All @@ -26,21 +28,33 @@ class Sutherland(Flow360BaseModel):
reference_temperature: TemperatureType.Positive = pd.Field()
effective_temperature: TemperatureType.Positive = pd.Field()

@pd.validate_call
def dynamic_viscosity_from_temperature(
self, temperature: TemperatureType.Positive
) -> ViscosityType.Positive:
return (
self.reference_viscosity
* pow(temperature / self.reference_temperature, 1.5)
* (self.reference_temperature + self.effective_temperature)
/ (temperature + self.effective_temperature)
)


class Air(MaterialBase):
type: Literal["air"] = pd.Field("air", frozen=True)
name: str = pd.Field("air")
dynamic_viscosity: Union[Sutherland, ViscosityType.Positive] = pd.Field(
Sutherland(
reference_viscosity=1.716e-5 * u.Pa * u.s,
reference_temperature=273 * u.K,
effective_temperature=111 * u.K,
reference_temperature=273.15 * u.K,
# pylint: disable=fixme
# TODO: validation error for effective_temperature not equal 110.4 K
effective_temperature=110.4 * u.K,
)
)

@property
def specific_heat_ratio(self) -> pd.PositiveFloat:
# TODO: serialize
return 1.4

@property
Expand All @@ -51,6 +65,20 @@ def gas_constant(self) -> SpecificHeatCapacityType.Positive:
def prandtl_number(self) -> pd.PositiveFloat:
return 0.72

@pd.validate_call
def speed_of_sound_from_temperature(
self, temperature: TemperatureType.Positive
) -> VelocityType.Positive:
return sqrt(self.specific_heat_ratio * self.gas_constant * temperature)

@pd.validate_call
def dynamic_viscosity_from_temperature(
self, temperature: TemperatureType.Positive
) -> ViscosityType.Positive:
if isinstance(self.dynamic_viscosity, Sutherland):
return self.dynamic_viscosity.dynamic_viscosity_from_temperature(temperature)
return self.dynamic_viscosity


class SolidMaterial(MaterialBase):
type: Literal["solid"] = pd.Field("solid", frozen=True)
Expand Down
2 changes: 1 addition & 1 deletion flow360/component/simulation/models/volume_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class BETDisk(Flow360BaseModel):
n_loading_nodes: pd.StrictInt = pd.Field(gt=0, le=1000)
blade_line_chord: LengthType.NonNegative = pd.Field(0)
initial_blade_direction: Optional[Axis] = pd.Field(None)
tip_gap: Union[LengthType.NonNegative, Literal["inf"]] = pd.Field("inf")
tip_gap: Union[Literal["inf"], LengthType.NonNegative] = pd.Field("inf")
mach_numbers: List[pd.NonNegativeFloat] = pd.Field()
reynolds_numbers: List[pd.PositiveFloat] = pd.Field()
alphas: List[float] = pd.Field()
Expand Down
51 changes: 32 additions & 19 deletions flow360/component/simulation/operating_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Optional, Tuple, Union

import pydantic as pd
from typing_extensions import Self

import flow360.component.simulation.units as u
from flow360.component.simulation.framework.base_model import Flow360BaseModel
Expand Down Expand Up @@ -98,8 +99,8 @@ def speed_of_sound(self) -> VelocityType.Positive:
"""Computes speed of sound."""
# pylint: disable=fixme
# TODO: implement
# return self.material.speed_of_sound(self.temperature)
return 343 * u.m / u.s
return self.material.speed_of_sound_from_temperature(self.temperature)
# return 343 * u.m / u.s

@property
def pressure(self) -> PressureType.Positive:
Expand All @@ -113,8 +114,8 @@ def dynamic_viscosity(self) -> ViscosityType.Positive:
"""Computes dynamic viscosity."""
# pylint: disable=fixme
# TODO: implement
# return self.material.dynamic_viscosity(self.temperature)
return 1.825e-5 * u.Pa * u.s
return self.material.dynamic_viscosity_from_temperature(self.temperature)
# return 1.825e-5 * u.Pa * u.s

@pd.validate_call
def mu_ref(self, mesh_unit: LengthType.Positive) -> pd.PositiveFloat:
Expand Down Expand Up @@ -189,7 +190,7 @@ class AerospaceCondition(CachedModelBase):
@pd.validate_call
def from_mach(
cls,
mach: pd.PositiveFloat,
mach: pd.NonNegativeFloat,
alpha: AngleType = 0 * u.deg,
beta: AngleType = 0 * u.deg,
thermal_state: ThermalState = ThermalState(),
Expand All @@ -202,6 +203,7 @@ def from_mach(
reference_velocity_magnitude = (
reference_mach * thermal_state.speed_of_sound if reference_mach else None
)

return cls(
velocity_magnitude=velocity_magnitude,
alpha=alpha,
Expand All @@ -210,20 +212,31 @@ def from_mach(
reference_velocity_magnitude=reference_velocity_magnitude,
)

# pylint: disable=no-self-argument, not-callable
@CachedModelBase.model_constructor
@pd.validate_call
def from_stationary(
cls,
reference_velocity_magnitude: VelocityType.Positive,
thermal_state: ThermalState = ThermalState(),
):
"""Constructs a `AerospaceCondition` for stationary conditions."""
return cls(
velocity_magnitude=0 * u.m / u.s,
thermal_state=thermal_state,
reference_velocity_magnitude=reference_velocity_magnitude,
)
@pd.model_validator(mode="after")
def check_valid_reference_velocity(self) -> Self:
"""Ensure reference velocity is provided when freestream velocity is 0."""
if self.velocity_magnitude.value == 0 and self.reference_velocity_magnitude is None:
raise ValueError(
"Reference velocity magnitude/Mach must be provided when freestream velocity magnitude/Mach is 0."
)
return self

# Note: Decided to move `velocity==0 ref_velocity is not None` check to dedicated validator because user can
# Note: still construct by just calling AerospaceCondition()
# # pylint: disable=no-self-argument, not-callable
# @CachedModelBase.model_constructor
# @pd.validate_call
# def from_stationary(
# cls,
# reference_velocity_magnitude: VelocityType.Positive,
# thermal_state: ThermalState = ThermalState(),
# ):
# """Constructs a `AerospaceCondition` for stationary conditions."""
# return cls(
# velocity_magnitude=0 * u.m / u.s,
# thermal_state=thermal_state,
# reference_velocity_magnitude=reference_velocity_magnitude,
# )

@property
def mach(self) -> pd.PositiveFloat:
Expand Down
5 changes: 3 additions & 2 deletions flow360/component/simulation/outputs/outputs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Annotated, List, Literal, Union
from typing import Annotated, List, Literal, Optional, Union

import pydantic as pd

Expand Down Expand Up @@ -56,7 +56,8 @@ class _AnimationAndFileFormatSettings(_AnimationSettings):


class SurfaceOutput(_AnimationAndFileFormatSettings):
entities: EntityList[Surface] = pd.Field(alias="surfaces")
# TODO: entities is None --> use all surfaces. This is not implemented yet.
entities: Optional[EntityList[Surface]] = pd.Field(None, alias="surfaces")
write_single_file: bool = pd.Field(
default=False,
description="Enable writing all surface outputs into a single file instead of one file per surface. This option currently only supports Tecplot output format. Will choose the value of the last instance of this option of the same output type (SurfaceOutput or TimeAverageSurfaceOutput) in the `output` list.",
Expand Down
Loading
Loading