Skip to content

Support volume zones and aeroacoustic output #60

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 5 commits into from
Oct 3, 2023
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
5 changes: 5 additions & 0 deletions flow360/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
from .component.case import CaseList as MyCases
from .component.flow360_params import solvers
from .component.flow360_params.flow360_params import (
AeroacousticOutput,
Boundaries,
Flow360MeshParams,
Flow360Params,
FluidDynamicsVolumeZone,
Freestream,
FreestreamBoundary,
Geometry,
HeatTransferVolumeZone,
IsothermalWall,
MassInflow,
MassOutflow,
MeshBoundary,
NavierStokesSolver,
NoSlipWall,
ReferenceFrame,
SlidingInterface,
SlidingInterfaceBoundary,
SlipWall,
Expand All @@ -33,6 +37,7 @@
TimeSteppingCFL,
TurbulenceModelSolver,
UnvalidatedFlow360Params,
VolumeZones,
WallFunction,
)
from .component.meshing.params import SurfaceMeshingParams, VolumeMeshingParams
Expand Down
169 changes: 169 additions & 0 deletions flow360/component/flow360_params/flow360_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Dict, List, Optional, Union

import pydantic as pd
from pydantic import StrictStr
from typing_extensions import Literal

from ...exceptions import ConfigError, Flow360NotImplementedError, ValidationError
Expand Down Expand Up @@ -580,6 +581,172 @@ def validate_boundary(cls, values):
)


class VolumeZoneType(ABC, Flow360BaseModel):
"""Basic Boundary class"""

model_type: str


class InitialConditionHeatTransfer(Flow360BaseModel):
"""InitialConditionHeatTransfer"""

T_solid: Union[PositiveFloat, StrictStr]


class HeatTransferVolumeZone(VolumeZoneType):
"""HeatTransferVolumeZone type"""

model_type = pd.Field("HeatTransfer", const=True)
thermal_conductivity: PositiveFloat = pd.Field(alias="thermalConductivity")
volumetric_heat_source: Optional[Union[NonNegativeFloat, StrictStr]] = pd.Field(
alias="volumetricHeatSource"
)
heat_capacity: Optional[PositiveFloat] = pd.Field(alias="heatCapacity")
initial_condition: Optional[InitialConditionHeatTransfer] = pd.Field(alias="initialCondition")


class ReferenceFrame(Flow360BaseModel):
""":class:`ReferenceFrame` class for setting up reference frame

Parameters
----------
center : Coordinate
Coordinate representing the origin of rotation, eg. (0, 0, 0)

axis : Axis
Axis of rotation, eg. (0, 0, 1)

parent_volume_name : str, optional
Name of the volume zone that the rotating reference frame is contained in, used to compute the acceleration in
the nested rotating reference frame

theta_radians : str, optional
Expression for rotation angle (in radians) as a function of time

theta_degrees : str, optional
Expression for rotation angle (in degrees) as a function of time

omega_radians
Nondimensional rotating speed, radians/nondim-unit-time

omega_degrees
Nondimensional rotating speed, degrees/nondim-unit-time

is_dynamic
Whether rotation of this interface is dictated by userDefinedDynamics


Returns
-------
:class:`ReferenceFrame`
An instance of the component class ReferenceFrame.

Example
-------
>>> rf = ReferenceFrame(
center=(0, 0, 0),
axis=(0, 0, 1),
omega_radians=1
)
"""

theta_radians: Optional[str] = pd.Field(alias="thetaRadians")
theta_degrees: Optional[str] = pd.Field(alias="thetaDegrees")
omega_radians: Optional[float] = pd.Field(alias="omegaRadians")
omega_degrees: Optional[float] = pd.Field(alias="omegaDegrees")
center: Coordinate = pd.Field(alias="centerOfRotation")
axis: Axis = pd.Field(alias="axisOfRotation")
parent_volume_name: Optional[str] = pd.Field(alias="parentVolumeName")
is_dynamic: Optional[bool] = pd.Field(alias="isDynamic")

# pylint: disable=missing-class-docstring,too-few-public-methods
class Config(Flow360BaseModel.Config):
require_one_of = [
"omega_radians",
"omega_degrees",
"theta_radians",
"theta_degrees",
"is_dynamic",
]


class FluidDynamicsVolumeZone(VolumeZoneType):
"""FluidDynamicsVolumeZone type"""

model_type = pd.Field("FluidDynamics", const=True)
reference_frame: Optional[ReferenceFrame] = pd.Field(alias="ReferenceFrame")


class _GenericVolumeZonesWrapper(Flow360BaseModel):
v: Union[FluidDynamicsVolumeZone, HeatTransferVolumeZone]


class VolumeZones(Flow360SortableBaseModel):
""":class:`VolumeZones` class for setting up volume zones

Parameters
----------
<zone_name> : Union[FluidDynamicsVolumeZone, HeatTransferVolumeZone]

Returns
-------
:class:`VolumeZones`
An instance of the component class VolumeZones.

Example
-------
>>> zones = VolumeZones(
zone1=FluidDynamicsVolumeZone(),
zone2=HeatTransferVolumeZone(thermal_conductivity=1)
)
"""

@pd.root_validator(pre=True)
def validate_zone(cls, values):
"""Validator for zone list section

Raises
------
ValidationError
When zone is incorrect
"""
return _self_named_property_validator(
values, _GenericVolumeZonesWrapper, msg="is not any of supported volume zone types."
)


class AeroacousticOutput(Flow360BaseModel):
""":class:`AeroacousticOutput` class for configuring output data about acoustic pressure signals

Parameters
----------
observers : List[Coordinate]
List of observer locations at which time history of acoustic pressure signal is stored in aeroacoustic output
file. The observer locations can be outside the simulation domain, but cannot be inside the solid surfaces of
the simulation domain.
animation_frequency: Union[PositiveInt, Literal[-1]], optional
Frame frequency in the animation
animation_frequency_offset: int, optional
Animation frequency offset

Returns
-------
:class:`AeroacousticOutput`
An instance of the component class AeroacousticOutput.

Example
-------
>>> aeroacoustics = AeroacousticOutput(observers=[(0, 0, 0), (1, 1, 1)], animation_frequency=1)
"""

animation_frequency: Optional[Union[PositiveInt, Literal[-1]]] = pd.Field(
alias="animationFrequency"
)
animation_frequency_offset: Optional[int] = pd.Field(alias="animationFrequencyOffset")
patch_type: Optional[str] = pd.Field("solid", const=True, alias="patchType")
observers: List[Coordinate] = pd.Field()


class Geometry(Flow360BaseModel):
"""
Geometry component
Expand Down Expand Up @@ -757,6 +924,8 @@ class Flow360Params(Flow360BaseModel):
slice_output: Optional[Dict] = pd.Field(alias="sliceOutput")
iso_surface_output: Optional[Dict] = pd.Field(alias="isoSurfaceOutput")
monitor_output: Optional[Dict] = pd.Field(alias="monitorOutput")
volume_zones: Optional[VolumeZones] = pd.Field(alias="volumeZones")
aeroacoustic_output: Optional[AeroacousticOutput] = pd.Field(alias="aeroacousticOutput")

# pylint: disable=invalid-name
def _get_non_dimensionalisation(self):
Expand Down
6 changes: 5 additions & 1 deletion flow360/log.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Logging for Flow360."""
import os
import platform
import time
from datetime import datetime
from typing import Union
Expand Down Expand Up @@ -331,4 +332,7 @@ def set_logging_file(
log_dir = flow360_dir + "logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
set_logging_file(os.path.join(log_dir, "flow360_python.log"), level="DEBUG")

# Writing log files on Windows is currently very slow, toggled off until a fix is implemented
if platform.system() != "Windows":
set_logging_file(os.path.join(log_dir, "flow360_python.log"), level="DEBUG")
Loading