Skip to content

Add translator for rotation #327

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 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
2 changes: 1 addition & 1 deletion flow360/component/simulation/exposed_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"pressure": [],
"density": [],
"viscosity": [],
"angular_velocity": [],
"angular_velocity": [u.rpm],
"heat_flux": [],
"heat_source": [],
"specific_heat_capacity": [],
Expand Down
1 change: 1 addition & 0 deletions flow360/component/simulation/meshing_param/edge_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class SurfaceEdgeRefinement(_BaseEdgeRefinement):
(equivalent to `ProjectAniso` in old params).
"""

name: Optional[str] = pd.Field(None)
refinement_type: Literal["SurfaceEdgeRefinement"] = pd.Field(
"SurfaceEdgeRefinement", frozen=True
)
Expand Down
2 changes: 2 additions & 0 deletions flow360/component/simulation/meshing_param/face_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class SurfaceRefinement(Flow360BaseModel):
these defaults so that the when face name is not present, what config we ues. Depending on how we go down the road.
"""

name: Optional[str] = pd.Field(None)
refinement_type: Literal["SurfaceRefinement"] = pd.Field("SurfaceRefinement", frozen=True)
entities: Optional[EntityList[Surface]] = pd.Field(None, alias="faces")
# pylint: disable=no-member
Expand Down Expand Up @@ -51,6 +52,7 @@ class BoundaryLayer(Flow360BaseModel):
need to have dedicated field for global settings. This is also consistent with the `FluidDynamics` class' design.
"""

name: Optional[str] = pd.Field(None)
refinement_type: Literal["BoundaryLayer"] = pd.Field("BoundaryLayer", frozen=True)
type: Literal["aniso", "projectAnisoSpacing", "none"] = pd.Field()
entities: Optional[EntityList[Surface]] = pd.Field(None, alias="faces")
Expand Down
2 changes: 2 additions & 0 deletions flow360/component/simulation/meshing_param/volume_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AxisymmetricRefinement(Flow360BaseModel):
the mesh data. But this currently is out of scope due to the estimated efforts.
"""

name: Optional[str] = pd.Field(None)
refinement_type: Literal["AxisymmetricRefinement"] = pd.Field(
"AxisymmetricRefinement", frozen=True
)
Expand All @@ -52,6 +53,7 @@ class RotationCylinder(AxisymmetricRefinement):
78d442233fa944e6af8eed4de9541bb1?pvs=4#c2de0b822b844a12aa2c00349d1f68a3
"""

name: Optional[str] = pd.Field(None)
enclosed_objects: Optional[EntityList[Cylinder, Surface]] = pd.Field(
None,
description="""Entities enclosed by this sliding interface. Can be faces, boxes and/or other cylinders etc.
Expand Down
42 changes: 27 additions & 15 deletions flow360/component/simulation/models/surface_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ class Mach(SingleAttributeModel):
value: pd.NonNegativeFloat = pd.Field()


class Translational(Flow360BaseModel):
"""Translational periodicity"""

type: Literal["Translational"] = pd.Field("Translational", frozen=True)


class Rotational(Flow360BaseModel):
"""Rotational periodicity"""

type: Literal["Rotational"] = pd.Field("Rotational", frozen=True)
# pylint: disable=fixme
# TODO: Maybe we need more precision when serializeing this one?
axis_of_rotation: Optional[Axis] = pd.Field(None)


##########################################
############# Surface models #############
##########################################


class Wall(BoundaryBase):
"""Replace Flow360Param:
- NoSlipWall
Expand All @@ -93,6 +113,7 @@ class Wall(BoundaryBase):
- SolidAdiabaticWall
"""

name: Optional[str] = pd.Field(None)
type: Literal["Wall"] = pd.Field("Wall", frozen=True)
use_wall_function: bool = pd.Field(False)
velocity: Optional[VelocityVectorType] = pd.Field(None)
Expand All @@ -103,6 +124,7 @@ class Wall(BoundaryBase):
class Freestream(BoundaryBaseWithTurbulenceQuantities):
"""Freestream"""

name: Optional[str] = pd.Field(None)
type: Literal["Freestream"] = pd.Field("Freestream", frozen=True)
velocity: Optional[VelocityVectorType] = pd.Field(None)
velocity_type: Literal["absolute", "relative"] = pd.Field("relative")
Expand All @@ -115,6 +137,7 @@ class Outflow(BoundaryBase):
- MassOutflow
"""

name: Optional[str] = pd.Field(None)
type: Literal["Outflow"] = pd.Field("Outflow", frozen=True)
spec: Union[Pressure, MassFlowRate, Mach] = pd.Field()

Expand All @@ -125,6 +148,7 @@ class Inflow(BoundaryBaseWithTurbulenceQuantities):
- MassInflow
"""

name: Optional[str] = pd.Field(None)
type: Literal["Inflow"] = pd.Field("Inflow", frozen=True)
# pylint: disable=no-member
total_temperature: TemperatureType.Positive = pd.Field()
Expand All @@ -135,36 +159,24 @@ class Inflow(BoundaryBaseWithTurbulenceQuantities):
class SlipWall(BoundaryBase):
"""Slip wall"""

name: Optional[str] = pd.Field(None)
type: Literal["SlipWall"] = pd.Field("SlipWall", frozen=True)


class SymmetryPlane(BoundaryBase):
"""Symmetry plane"""

name: Optional[str] = pd.Field(None)
type: Literal["SymmetryPlane"] = pd.Field("SymmetryPlane", frozen=True)


class Translational(Flow360BaseModel):
"""Translational"""

type: Literal["Translational"] = pd.Field("Translational", frozen=True)


class Rotational(Flow360BaseModel):
"""Rotational"""

type: Literal["Rotational"] = pd.Field("Rotational", frozen=True)
# pylint: disable=fixme
# TODO: Maybe we need more precision when serializeing this one?
axis_of_rotation: Optional[Axis] = pd.Field(None)


class Periodic(Flow360BaseModel):
"""Replace Flow360Param:
- TranslationallyPeriodic
- RotationallyPeriodic
"""

name: Optional[str] = pd.Field(None)
type: Literal["Periodic"] = pd.Field("Periodic", frozen=True)
entity_pairs: UniqueItemList[SurfacePair] = pd.Field(alias="surface_pairs")
spec: Union[Translational, Rotational] = pd.Field(discriminator="type")
Expand Down
33 changes: 22 additions & 11 deletions flow360/component/simulation/models/volume_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

from flow360.component.simulation.framework.base_model import Flow360BaseModel
from flow360.component.simulation.framework.entity_base import EntityList
from flow360.component.simulation.framework.single_attribute_base import (
SingleAttributeModel,
)
from flow360.component.simulation.framework.expressions import StringExpression
from flow360.component.simulation.models.material import (
Air,
FluidMaterialTypes,
Expand All @@ -24,7 +22,6 @@
)
from flow360.component.simulation.primitives import Box, Cylinder, GenericVolume
from flow360.component.simulation.unit_system import (
AngleType,
AngularVelocityType,
HeatSourceType,
InverseAreaType,
Expand All @@ -37,9 +34,10 @@
from flow360.component.types import Axis


# pylint: disable=missing-class-docstring
class AngularVelocity(SingleAttributeModel):
value: AngularVelocityType = pd.Field()
class FromUserDefinedDynamics(Flow360BaseModel):
"""Rotation is controlled by user defined dynamics"""

type: Literal["FromUserDefinedDynamics"] = pd.Field("FromUserDefinedDynamics", frozen=True)


class ExpressionInitialConditionBase(Flow360BaseModel):
Expand All @@ -49,6 +47,7 @@ class ExpressionInitialConditionBase(Flow360BaseModel):
constants: Optional[Dict[str, str]] = pd.Field()


# pylint: disable=missing-class-docstring
class NavierStokesInitialCondition(ExpressionInitialConditionBase):
rho: str = pd.Field()
u: str = pd.Field()
Expand Down Expand Up @@ -80,6 +79,7 @@ class Fluid(PDEModelBase):
General FluidDynamics volume model that contains all the common fields every fluid dynamics zone should have.
"""

type: Literal["Fluid"] = pd.Field("Fluid", frozen=True)
navier_stokes_solver: NavierStokesSolver = pd.Field(NavierStokesSolver())
turbulence_model_solver: TurbulenceModelSolverType = pd.Field(SpalartAllmaras())
transition_model_solver: Optional[TransitionModelSolver] = pd.Field(None)
Expand All @@ -99,6 +99,8 @@ class Solid(PDEModelBase):
General HeatTransfer volume model that contains all the common fields every heat transfer zone should have.
"""

name: Optional[str] = pd.Field(None)
type: Literal["Solid"] = pd.Field("Solid", frozen=True)
entities: EntityList[GenericVolume, str] = pd.Field(alias="volumes")

material: SolidMaterialTypes = pd.Field()
Expand Down Expand Up @@ -168,6 +170,8 @@ class ActuatorDisk(Flow360BaseModel):
Note that `center`, `axis_thrust`, `thickness` can be acquired from `entity` so they are not required anymore.
"""

name: Optional[str] = pd.Field(None)
type: Literal["ActuatorDisk"] = pd.Field("ActuatorDisk", frozen=True)
entities: Optional[EntityList[Cylinder]] = pd.Field(None, alias="volumes")

force_per_area: ForcePerArea = pd.Field()
Expand Down Expand Up @@ -203,6 +207,8 @@ class BETDisk(Flow360BaseModel):
so they are not required anymore.
"""

name: Optional[str] = pd.Field(None)
type: Literal["BETDisk"] = pd.Field("BETDisk", frozen=True)
entities: Optional[EntityList[Cylinder]] = pd.Field(None, alias="volumes")

rotation_direction_rule: Literal["leftHand", "rightHand"] = pd.Field("rightHand")
Expand All @@ -222,21 +228,26 @@ class BETDisk(Flow360BaseModel):
sectional_radiuses: List[float] = pd.Field()


class RotatingReferenceFrame(Flow360BaseModel):
class Rotation(Flow360BaseModel):
"""Similar to Flow360Param ReferenceFrame.
Note that `center`, `axis` can be acquired from `entity` so they are not required anymore.
Note: Should use the unit system to convert degree or degree per second to radian and radian per second
"""

name: Optional[str] = pd.Field(None)
type: Literal["Rotation"] = pd.Field("Rotation", frozen=True)
entities: EntityList[GenericVolume, Cylinder, str] = pd.Field(alias="volumes")

rotation: Union[AngularVelocity, AngleType] = pd.Field()
parent_volume_name: Optional[Union[GenericVolume, str]] = pd.Field(None)
# TODO: Add test for each of the spec specification.
spec: Union[StringExpression, FromUserDefinedDynamics, AngularVelocityType] = pd.Field()
parent_volume: Optional[Union[GenericVolume, Cylinder, str]] = pd.Field(None)


class PorousMedium(Flow360BaseModel):
"""Constains Flow360Param PorousMediumBox and PorousMediumVolumeZone"""

name: Optional[str] = pd.Field(None)
type: Literal["PorousMedium"] = pd.Field("PorousMedium", frozen=True)
entities: Optional[EntityList[GenericVolume, Box, str]] = pd.Field(None, alias="volumes")

darcy_coefficient: InverseAreaType.Point = pd.Field()
Expand All @@ -251,6 +262,6 @@ class PorousMedium(Flow360BaseModel):
Solid,
ActuatorDisk,
BETDisk,
RotatingReferenceFrame,
Rotation,
PorousMedium,
]
8 changes: 6 additions & 2 deletions flow360/component/simulation/simulation_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from __future__ import annotations

from typing import List, Optional, Union
from typing import Annotated, List, Optional, Union

import pydantic as pd

Expand Down Expand Up @@ -33,6 +33,10 @@
from flow360.exceptions import Flow360ConfigurationError, Flow360RuntimeError
from flow360.version import __version__

AllowedModelTypes = Annotated[
Union[VolumeModelTypes, SurfaceModelTypes], pd.Field(discriminator="type")
]


class AssetCache(Flow360BaseModel):
"""
Expand Down Expand Up @@ -210,7 +214,7 @@ class SimulationParams(_ParamModelBase):
3. by_name(pattern:str) to use regexpr/glob to select all zones/surfaces with matched name
3. by_type(pattern:str) to use regexpr/glob to select all zones/surfaces with matched type
"""
models: Optional[List[Union[VolumeModelTypes, SurfaceModelTypes]]] = pd.Field(None)
models: Optional[List[AllowedModelTypes]] = pd.Field(None)
"""
Below can be mostly reused with existing models
"""
Expand Down
59 changes: 48 additions & 11 deletions flow360/component/simulation/translator/solver_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
SymmetryPlane,
Wall,
)
from flow360.component.simulation.models.volume_models import BETDisk, Fluid
from flow360.component.simulation.models.volume_models import BETDisk, Fluid, Rotation
from flow360.component.simulation.outputs.outputs import (
SliceOutput,
SurfaceOutput,
Expand All @@ -26,6 +26,7 @@
remove_units_in_dict,
replace_dict_key,
replace_dict_value,
translate_setting_and_apply_to_all_entities,
)
from flow360.component.simulation.unit_system import LengthType

Expand All @@ -35,13 +36,6 @@ def dump_dict(input_params):
return input_params.model_dump(by_alias=True, exclude_none=True)


def remove_empty_keys(input_dict):
"""I do not know what this is for --- Ben"""
# pylint: disable=fixme
# TODO: implement
return input_dict


def init_output_attr_dict(obj_list, class_type):
"""Initialize the common output attribute."""
return {
Expand All @@ -53,6 +47,32 @@ def init_output_attr_dict(obj_list, class_type):
}


def rotation_entity_info_serializer(volume):
"""Rotation entity serializer"""
return {
"referenceFrame": {
"axisOfRotation": list(volume.axis),
"centerOfRotation": list(volume.center),
},
}


def rotation_translator(model: Rotation):
"""Rotation translator"""
volume_zone = {
"modelType": "FluidDynamics",
"referenceFrame": {},
}
if model.parent_volume:
volume_zone["referenceFrame"]["parentVolumeName"] = model.parent_volume.name
spec = dump_dict(model)["spec"]
if isinstance(spec, str):
volume_zone["referenceFrame"]["thetaRadians"] = spec
elif spec.get("units", "") == "flow360_angular_velocity_unit":
volume_zone["referenceFrame"]["omegaRadians"] = spec["value"]
return volume_zone


# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
Expand Down Expand Up @@ -211,6 +231,9 @@ def get_solver_json(
replace_dict_key(disk_param, "machNumbers", "MachNumbers")
replace_dict_key(disk_param, "reynoldsNumbers", "ReynoldsNumbers")
volumes = disk_param.pop("volumes")
for extra_attr in ["name", "type"]:
if extra_attr in disk_param:
disk_param.pop(extra_attr)
for v in volumes["storedEntities"]:
disk_i = deepcopy(disk_param)
disk_i["axisOfRotation"] = v["axis"]
Expand All @@ -221,11 +244,25 @@ def get_solver_json(
bet_disks.append(disk_i)
translated["BETDisks"] = bet_disks

##:: Step 8: Get porous media
##:: Step 8: Get rotation
if has_instance_in_list(input_params.models, Rotation):
volume_zones = translated.get("volumeZones", {})
volume_zones.update(
translate_setting_and_apply_to_all_entities(
input_params.models,
Rotation,
rotation_translator,
to_list=False,
entity_injection_func=rotation_entity_info_serializer,
)
)
translated["volumeZones"] = volume_zones

##:: Step 9: Get porous media

##:: Step 9: Get heat transfer zones
##:: Step 10: Get heat transfer zones

##:: Step 10: Get user defined dynamics
##:: Step 11: Get user defined dynamics
if input_params.user_defined_dynamics is not None:
translated["userDefinedDynamics"] = []
for udd in input_params.user_defined_dynamics:
Expand Down
Loading
Loading