From 97d810fc98bf99f8f6c8a4b3321a5428411c2820 Mon Sep 17 00:00:00 2001 From: Angran Li Date: Thu, 31 Oct 2024 18:25:04 +0000 Subject: [PATCH 1/5] Add sphinx params plugins --- flow360/__init__.py | 2 - .../simulation/models/volume_models.py | 52 ++++++++++--------- poetry.lock | 4 +- pyproject.toml | 1 + 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/flow360/__init__.py b/flow360/__init__.py index 85a7d9138..ea7f2ec25 100644 --- a/flow360/__init__.py +++ b/flow360/__init__.py @@ -5,7 +5,6 @@ from flow360.component.simulation import services from flow360.component.simulation import units as u from flow360.component.simulation.entity_info import GeometryEntityInfo -from flow360.component.simulation.framework.param_utils import AssetCache from flow360.component.simulation.meshing_param.edge_params import ( HeightBasedRefinement, SurfaceEdgeRefinement, @@ -134,7 +133,6 @@ "Edge", "ReferenceGeometry", "Cylinder", - "AssetCache", "GeometryEntityInfo", "AerospaceCondition", "ThermalState", diff --git a/flow360/component/simulation/models/volume_models.py b/flow360/component/simulation/models/volume_models.py index b720860b4..3ee28054c 100644 --- a/flow360/component/simulation/models/volume_models.py +++ b/flow360/component/simulation/models/volume_models.py @@ -141,12 +141,12 @@ class Fluid(PDEModelBase): + ":class:`~flow360.component.simulation.models.solver_numerics.TransitionModelSolver` documentation.", ) - material: FluidMaterialTypes = pd.Field(Air(), description="The material propetry of fluid") + material: FluidMaterialTypes = pd.Field(Air(), description="The material propetry of fluid.") initial_condition: Optional[ Union[NavierStokesModifiedRestartSolution, NavierStokesInitialCondition] ] = pd.Field( - None, discriminator="type", description="The initial condition of the fluid solver" + None, discriminator="type", description="The initial condition of the fluid solver." ) # pylint: disable=fixme @@ -163,29 +163,29 @@ class Solid(PDEModelBase): type: Literal["Solid"] = pd.Field("Solid", frozen=True) entities: EntityList[GenericVolume] = pd.Field( alias="volumes", - description="The list of solid entities on which the heat transfer equation is solved.", + description="The list of :class:`~flow360.component.simulation.primitives.GenericVolume` "+ + "entities on which the heat transfer equation is solved.", ) - material: SolidMaterialTypes = pd.Field(description="The material property of solid") + material: SolidMaterialTypes = pd.Field(description="The material property of solid.") heat_equation_solver: HeatEquationSolver = pd.Field( HeatEquationSolver(), description="Heat equation solver settings, see " - + ":class:`~flow360.component.simulation.models.solver_numerics.HeatEquationSolver` documentation. " - + ":class:`~flow360.component.simulation.outputs.outputs.SurfaceIntegralOutput`", + + ":class:`~flow360.component.simulation.models.solver_numerics.HeatEquationSolver` documentation." ) volumetric_heat_source: Union[HeatSourceType, pd.StrictStr] = pd.Field( - 0, description="The volumetric heat source" + 0, description="The volumetric heat source." ) initial_condition: Optional[HeatEquationInitialCondition] = pd.Field( - None, description="The initial condition of the heat equation solver" + None, description="The initial condition of the heat equation solver." ) # pylint: disable=duplicate-code class ForcePerArea(Flow360BaseModel): - """:class:`ForcePerArea` class for setting up force per area for Actuator Disk + """:class:`ForcePerArea` class for setting up force per area for Actuator Disk. Example ------- @@ -200,13 +200,13 @@ class ForcePerArea(Flow360BaseModel): thrust: PressureType.Array = pd.Field( description="Force per area in the axial direction, positive means the axial force follows the same " + "direction as the thrust axis. " - + "It is non-dimensional: :math:`\\frac{\\text{thrustPerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`" + + "It is non-dimensional: :math:`\\frac{\\text{thrustPerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`." ) # pylint: disable=no-member circumferential: PressureType.Array = pd.Field( description="Force per area in the circumferential direction, positive means the circumferential force " + "follows the same direction as the thrust axis with the right hand rule. It is non-dimensional: " - + ":math:`\\frac{\\text{circumferentialForcePerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`" + + ":math:`\\frac{\\text{circumferentialForcePerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`." ) # pylint: disable=no-self-argument, missing-function-docstring @@ -235,10 +235,11 @@ class ActuatorDisk(Flow360BaseModel): entities: EntityList[Cylinder] = pd.Field( alias="volumes", - description="The list of `Cylinder` entities for the ActuatorDisk model", + description="The list of :class:`~flow360.component.simulation.primitives.Cylinder` entities "+ + "for the `ActuatorDisk` model", ) force_per_area: ForcePerArea = pd.Field( - description="The force per area input for the ActuatorDisk model. " + description="The force per area input for the `ActuatorDisk` model. " + "See :class:`ForcePerArea` documentation." ) name: Optional[str] = pd.Field(None, description="Name of the `ActuatorDisk` model.") @@ -246,7 +247,7 @@ class ActuatorDisk(Flow360BaseModel): class BETDiskTwist(Flow360BaseModel): - """:class:`BETDiskTwist` class for setting up twist for BETDisk""" + """:class:`BETDiskTwist` class for setting up twist for BETDisk.""" # TODO: Use dimensioned values, why optional? radius: Optional[float] = pd.Field(None, description="A list of radial locations.") @@ -258,7 +259,7 @@ class BETDiskTwist(Flow360BaseModel): class BETDiskChord(Flow360BaseModel): - """:class:`BETDiskChord` class for setting up chord for BETDisk""" + """:class:`BETDiskChord` class for setting up chord for BETDisk.""" # TODO: Use dimensioned values, why optional? radius: Optional[float] = pd.Field(None, description="A list of radial locations.") @@ -273,11 +274,14 @@ class BETDiskSectionalPolar(Flow360BaseModel): """:class:`BETDiskSectionalPolar` class for setting up sectional polars for BETDisk. There are two variables, “lift_coeffs” and “drag_coeffs”, need to be set up as 3D arrays (implemented as nested lists). - The first index of the array corresponds to the :code:`MachNumbers` of the specified polar - data. - The second index of the array corresponds to the :code:`ReynoldsNumbers` of the polar - data. - The third index corresponds to the :code:`alphas`. + The first index of the array corresponds to the + :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.mach_numbers` + of the specified polar data. + The second index of the array corresponds to the + :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.reynolds_numbers` + of the polar data. + The third index corresponds to the + :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.alphas`. The value specifies the lift or drag coefficient, respectively. """ @@ -343,15 +347,15 @@ class BETDisk(Flow360BaseModel): ) mach_numbers: List[pd.NonNegativeFloat] = pd.Field( description="Mach numbers associated with airfoil polars provided " - + "in :code:`sectionalPolars`" + + "in :class:`BETDiskSectionalPolar`." ) reynolds_numbers: List[pd.PositiveFloat] = pd.Field( description="Reynolds numbers associated with the airfoil polars " - + "provided in :code:`sectionalPolars`" + + "provided in :class:`BETDiskSectionalPolar`." ) alphas: List[float] = pd.Field( description="Alphas associated with airfoil polars provided in " - + ":code:`sectionalPolars` in degrees" + + ":class:`BETDiskSectionalPolar` in degrees" ) twists: List[BETDiskTwist] = pd.Field( description="A list of :class:`BETDiskTwist` objects specifying the twist in degrees as a " @@ -367,7 +371,7 @@ class BETDisk(Flow360BaseModel): ) sectional_radiuses: List[float] = pd.Field( description="A list of the radial locations in grid units at which :math:`C_l` " - + "and :math:`C_d` are specified in :code:`sectional_polars`" + + "and :math:`C_d` are specified in :class:`BETDiskSectionalPolar`" ) @pd.model_validator(mode="after") diff --git a/poetry.lock b/poetry.lock index 31b7584e3..ec16b36d4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -5129,4 +5129,4 @@ docs = ["autodoc_pydantic", "cairosvg", "ipython", "jinja2", "jupyter", "myst-pa [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "68e2b0a819ba27869350047433031ea97ab479abe517f1bbbb2d60498b71e6aa" +content-hash = "035965f33d10db440f29afa7421b474ae228c0374a9f4f572163d4f085a11533" diff --git a/pyproject.toml b/pyproject.toml index 6d1ed1e63..04db8578d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,6 +99,7 @@ docs = [ "sphinx-prompt", "sphinx_design", "sphinx_toolbox", + "sphinx-paramlinks", ] [tool.isort] From d1922b5040e2b71c83da64f347058c57d2c795d8 Mon Sep 17 00:00:00 2001 From: Angran Li Date: Fri, 1 Nov 2024 16:04:59 +0000 Subject: [PATCH 2/5] Add more documentations --- flow360/__init__.py | 18 +- .../simulation/models/solver_numerics.py | 190 +++++++++++------ .../simulation/models/surface_models.py | 198 +++++++++++++----- .../models/turbulence_quantities.py | 50 ++++- .../simulation/models/volume_models.py | 108 +++++----- .../simulation/outputs/output_entities.py | 26 ++- .../component/simulation/outputs/outputs.py | 73 ++++--- flow360/component/simulation/primitives.py | 33 +-- .../simulation/time_stepping/time_stepping.py | 45 ++-- .../user_defined_dynamics.py | 53 ++++- 10 files changed, 540 insertions(+), 254 deletions(-) diff --git a/flow360/__init__.py b/flow360/__init__.py index ea7f2ec25..bb57675cd 100644 --- a/flow360/__init__.py +++ b/flow360/__init__.py @@ -31,13 +31,13 @@ SpalartAllmaras, SpalartAllmarasModelConstants, TransitionModelSolver, - TransitionModelSolverType, - TurbulenceModelSolverType, ) from flow360.component.simulation.models.surface_models import ( Freestream, HeatFlux, Inflow, + Mach, + MassFlowRate, Outflow, Periodic, Pressure, @@ -75,6 +75,7 @@ ThermalState, ) from flow360.component.simulation.outputs.output_entities import ( + Isosurface, Point, PointArray, Slice, @@ -87,6 +88,7 @@ SurfaceIntegralOutput, SurfaceOutput, SurfaceProbeOutput, + TimeAverageSliceOutput, TimeAverageSurfaceOutput, TimeAverageVolumeOutput, VolumeOutput, @@ -94,10 +96,7 @@ from flow360.component.simulation.primitives import ( Box, Cylinder, - Edge, - GenericVolume, ReferenceGeometry, - Surface, ) from flow360.component.simulation.simulation_params import SimulationParams from flow360.component.simulation.time_stepping.time_stepping import ( @@ -129,8 +128,6 @@ "UniformRefinement", "SurfaceEdgeRefinement", "HeightBasedRefinement", - "Surface", - "Edge", "ReferenceGeometry", "Cylinder", "GeometryEntityInfo", @@ -162,6 +159,7 @@ "VolumeOutput", "TimeAverageVolumeOutput", "SliceOutput", + "TimeAverageSliceOutput", "IsosurfaceOutput", "SurfaceIntegralOutput", "ProbeOutput", @@ -174,21 +172,19 @@ "KOmegaSST", "SpalartAllmarasModelConstants", "KOmegaSSTModelConstants", - "TransitionModelSolverType", - "TurbulenceModelSolverType", "LinearSolver", "ForcePerArea", "Air", "Sutherland", "SolidMaterial", "Slice", + "Isosurface", "TurbulenceQuantities", "UserDefinedDynamic", "Translational", "NavierStokesInitialCondition", "FromUserDefinedDynamics", "HeatEquationInitialCondition", - "GenericVolume", "Temperature", "HeatFlux", "Point", @@ -200,4 +196,6 @@ "Pressure", "TotalPressure", "Rotational", + "Mach", + "MassFlowRate", ] diff --git a/flow360/component/simulation/models/solver_numerics.py b/flow360/component/simulation/models/solver_numerics.py index 6ebdf44cd..a886d0f89 100644 --- a/flow360/component/simulation/models/solver_numerics.py +++ b/flow360/component/simulation/models/solver_numerics.py @@ -31,23 +31,23 @@ class LinearSolver(Flow360BaseModel): - """:class:`LinearSolver` class for setting up linear solver for heat equation + """:class:`LinearSolver` class for setting up the linear solver. Example ------- >>> ls = LinearSolver( - max_iterations=50, - absoluteTolerance=1e-10 - ) + ... max_iterations=50, + ... absoluteTolerance=1e-10 + ... ) """ max_iterations: PositiveInt = pd.Field( - 30, description="Maximum number of linear solver iterations" + 30, description="Maximum number of linear solver iterations." ) absolute_tolerance: Optional[PositiveFloat] = pd.Field( None, description="The linear solver converges when the final residual of the pseudo steps below this value." - + "Either absolute tolerance or relative tolerance can be used to determine convergence", + + "Either absolute tolerance or relative tolerance can be used to determine convergence.", ) relative_tolerance: Optional[PositiveFloat] = pd.Field( None, @@ -70,54 +70,60 @@ class GenericSolverSettings(Flow360BaseModel, metaclass=ABCMeta): + "Relative residual is defined as the ratio of the current pseudoStep's residual to the maximum " + "residual present in the first 10 pseudoSteps within the current physicalStep. " + "NOTE: relativeTolerance is ignored in steady simulations and only absoluteTolerance is " - + "used as the convergence criterion", + + "used as the convergence criterion.", ) - order_of_accuracy: Literal[1, 2] = pd.Field(2, description="Order of accuracy in space") + order_of_accuracy: Literal[1, 2] = pd.Field(2, description="Order of accuracy in space.") equation_evaluation_frequency: PositiveInt = pd.Field( - 1, description="Frequency at which to solve the equation" + 1, description="Frequency at which to solve the equation." ) linear_solver: LinearSolver = pd.Field(LinearSolver()) class NavierStokesSolver(GenericSolverSettings): - """:class:`NavierStokesSolver` class for setting up compressible Navier-Stokes solver. + """:class:`NavierStokesSolver` class for setting up the compressible Navier-Stokes solver. For more information on setting up the numerical parameters for the Navier-Stokes solver, refer to :ref:`Navier-Stokes solver knowledge base `. Example ------- - >>> ns = NavierStokesSolver(absolute_tolerance=1e-10) + >>> ns = navier_stokes_solver=NavierStokesSolver(absolute_tolerance=1e-10, + ... numerical_dissipation_factor=0.01, + ... linear_solver=LinearSolver(max_iterations=50), + ... low_mach_preconditioner=True, + ... ) """ absolute_tolerance: PositiveFloat = pd.Field( 1.0e-10, - description="Tolerance for the NS residual, below which the solver goes to the next physical step", + description="Tolerance for the NS residual, below which the solver goes to the next physical step.", ) CFL_multiplier: PositiveFloat = pd.Field( - 1.0, description="Factor to the CFL definitions defined in :code:`time_stepping` section" + 1.0, + description="Factor to the CFL definitions defined in the " + + ":ref:`Time Stepping ` section.", ) kappa_MUSCL: pd.confloat(ge=-1, le=1) = pd.Field( -1, description="Kappa for the MUSCL scheme, range from [-1, 1], with 1 being unstable. " + "The default value of -1 leads to a 2nd order upwind scheme and is the most stable. " + "A value of 0.33 leads to a blended upwind/central scheme and is recommended for low " - + "subsonic flows leading to reduced dissipation", + + "subsonic flows leading to reduced dissipation.", ) numerical_dissipation_factor: pd.confloat(ge=0.01, le=1) = pd.Field( 1, description="A factor in the range [0.01, 1.0] which exponentially reduces the " + "dissipation of the numerical flux. The recommended starting value for most " - + "low-dissipation runs is 0.2", + + "low-dissipation runs is 0.2.", ) limit_velocity: bool = pd.Field(False, description="Limiter for velocity") - limit_pressure_density: bool = pd.Field(False, description="Limiter for pressure and density") + limit_pressure_density: bool = pd.Field(False, description="Limiter for pressure and density.") type_name: Literal["Compressible"] = pd.Field("Compressible", frozen=True) low_mach_preconditioner: bool = pd.Field( - False, description="Use preconditioning for accelerating low Mach number flows" + False, description="Use preconditioning for accelerating low Mach number flows." ) low_mach_preconditioner_threshold: Optional[NonNegativeFloat] = pd.Field( None, @@ -128,17 +134,25 @@ class NavierStokesSolver(GenericSolverSettings): ) update_jacobian_frequency: PositiveInt = pd.Field( - 4, description="Frequency at which the jacobian is updated" + 4, description="Frequency at which the jacobian is updated." ) max_force_jac_update_physical_steps: NonNegativeInt = pd.Field( 0, description="When physical step is less than this value, the jacobian matrix is " - + "updated every pseudo step", + + "updated every pseudo step.", ) class SpalartAllmarasModelConstants(Flow360BaseModel): - """:class:`SpalartAllmarasModelConstants` class""" + """ + :class:`SpalartAllmarasModelConstants` class specifies the constants of the Spalart-Allmaras model. + + Example + ------- + >>> ts = SpalartAllmaras( + ... modeling_constants = SpalartAllmarasModelConstants(C_w2=2.718) + ... ) + """ type_name: Literal["SpalartAllmarasConsts"] = pd.Field("SpalartAllmarasConsts", frozen=True) C_DES: NonNegativeFloat = pd.Field(0.72) @@ -155,7 +169,15 @@ class SpalartAllmarasModelConstants(Flow360BaseModel): class KOmegaSSTModelConstants(Flow360BaseModel): - """:class:`KOmegaSSTModelConstants` class""" + """ + :class:`KOmegaSSTModelConstants` class specifies the constants of the SST k-omega model. + + Example + ------- + >>> ts = SpalartAllmaras( + ... modeling_constants = KOmegaSSTModelConstants(C_sigma_omega1=2.718) + ... ) + """ type_name: Literal["kOmegaSSTConsts"] = pd.Field("kOmegaSSTConsts", frozen=True) C_DES1: NonNegativeFloat = pd.Field(0.78) @@ -189,26 +211,31 @@ class TurbulenceModelSolver(GenericSolverSettings, metaclass=ABCMeta): """ CFL_multiplier: PositiveFloat = pd.Field( - 2.0, description="Factor to the CFL definitions defined in :code:`time_stepping` section" + 2.0, + description="Factor to the CFL definitions defined in the " + + ":ref:`Time Stepping ` section.", + ) + type_name: str = pd.Field( + description=":code:`SpalartAllmaras`, :code:`kOmegaSST`, or :code:`None`." ) - type_name: str = pd.Field() absolute_tolerance: PositiveFloat = pd.Field( 1e-8, - description="Tolerance for the NS residual, below which the solver goes to the next physical step", + description="Tolerance for the turbulence model residual, below which the solver progresses to the " + + "next physical step (unsteady) or completes the simulation (steady).", ) equation_evaluation_frequency: PositiveInt = pd.Field( - 4, description="Frequency at which to update the NS equation in loosely-coupled simulations" + 4, description="Frequency at which to update the turbulence equation." ) DDES: bool = pd.Field( False, - description="Enables Delayed Detached Eddy Simulation. " - + " Supported for both SpalartAllmaras and kOmegaSST turbulence models, " + description=":code:`True` enables Delayed Detached Eddy Simulation. " + + "Supported for both SpalartAllmaras and kOmegaSST turbulence models, " + "with and without AmplificationFactorTransport transition model enabled.", ) grid_size_for_LES: Literal["maxEdgeLength", "meanEdgeLength"] = pd.Field( "maxEdgeLength", description="Specifes the length used for the computation of LES length scale. " - + 'The allowed inputs are "maxEdgeLength" and "meanEdgeLength"', + + "The allowed inputs are :code:`maxEdgeLength` and :code:`meanEdgeLength`.", ) reconstruction_gradient_limiter: pd.confloat(ge=0, le=2) = pd.Field( 1.0, @@ -219,22 +246,23 @@ class TurbulenceModelSolver(GenericSolverSettings, metaclass=ABCMeta): quadratic_constitutive_relation: bool = pd.Field( False, description="Use quadratic constitutive relation for turbulence shear stress tensor " - + "instead of Boussinesq Approximation", + + "instead of Boussinesq Approximation.", ) modeling_constants: Optional[TurbulenceModelConstants] = pd.Field( discriminator="type_name", - description=" A dictionary containing the DDES coefficients in the solver: **SpalartAllmaras**: " - + ':code:`"C_DES"` (= 0.72), :code:`"C_d"` (= 8.0), **kOmegaSST**: :code:`"C_DES1"` (= 0.78), ' - + ':code:`"C_DES2"` (= 0.61), :code:`"C_d1"` (= 20.0), :code:`"C_d2"` (= 3.0), ' - + "*(values shown in the parentheses are the default values used in Flow360)*", + description=" A :class:`TurbulenceModelConstants` object containing the DDES coefficients " + + "in the solver: **SpalartAllmaras**: :code:`C_DES` (= 0.72), :code:`C_d` (= 8.0)," + + '**kOmegaSST**: :code:`"C_DES1"` (= 0.78), ' + + ":code:`C_DES2` (= 0.61), :code:`C_d1` (= 20.0), :code:`C_d2` (= 3.0), " + + "*(values shown in the parentheses are the default values used in Flow360).*", ) update_jacobian_frequency: PositiveInt = pd.Field( - 4, description="Frequency at which the jacobian is updated" + 4, description="Frequency at which the jacobian is updated." ) max_force_jac_update_physical_steps: NonNegativeInt = pd.Field( 0, description="For physical steps less than the input value, the jacobian matrix is " - + "updated every pseudo-step overriding the :code:`updateJacobianFrequency` value", + + "updated every pseudo-step overriding the :paramref:`update_jacobian_frequency` value.", ) linear_solver: LinearSolver = pd.Field( @@ -244,26 +272,61 @@ class TurbulenceModelSolver(GenericSolverSettings, metaclass=ABCMeta): class KOmegaSST(TurbulenceModelSolver): - """:class:`KOmegaSST` class for setting up the turbulence solver based on the SST k-omega model.""" + """ + :class:`KOmegaSST` class for setting up the turbulence solver based on the SST k-omega model. + + Example + ------- + >>> ts = KOmegaSST( + ... absolute_tolerance=1e-10, + ... linear_solver=LinearSolver(max_iterations=25), + ... update_jacobian_frequency=2, + ... equation_evaluation_frequency=1, + ... ) + """ type_name: Literal["kOmegaSST"] = pd.Field("kOmegaSST", frozen=True) - modeling_constants: KOmegaSSTModelConstants = pd.Field(KOmegaSSTModelConstants()) + modeling_constants: KOmegaSSTModelConstants = pd.Field( + KOmegaSSTModelConstants(), + description="A :class:`TurbulenceModelConstants` object containing the coefficients " + + "used in the SST k-omega model. For the default values used in Flow360, " + + "please refer to :class:`KOmegaSSTModelConstants`.", + ) class SpalartAllmaras(TurbulenceModelSolver): - """:class:`SpalartAllmaras` class for setting up the turbulence solver based on the Spalart–Allmaras model""" + """ + :class:`SpalartAllmaras` class for setting up the turbulence solver based on the Spalart-Allmaras model. + + Example + ------- + >>> ts = SpalartAllmaras( + ... absolute_tolerance=1e-10, + ... linear_solver=LinearSolver(max_iterations=25), + ... update_jacobian_frequency=2, + ... equation_evaluation_frequency=1, + ... ) + """ type_name: Literal["SpalartAllmaras"] = pd.Field("SpalartAllmaras", frozen=True) rotation_correction: bool = pd.Field(False) modeling_constants: Optional[SpalartAllmarasModelConstants] = pd.Field( - SpalartAllmarasModelConstants() + SpalartAllmarasModelConstants(), + description="A :class:`TurbulenceModelConstants` object containing the coefficients " + + "used in the Spalart-Allmaras model. For the default values used in Flow360, " + + "please refer to :class:`SpalartAllmarasModelConstants`.", + ) + reconstruction_gradient_limiter: Optional[pd.confloat(ge=0, le=2)] = pd.Field( + 0.5, + description="The strength of gradient limiter used in reconstruction of solution " + + "variables at the faces (specified in the range [0.0, 2.0]). 0.0 corresponds to " + + "setting the gradient equal to zero, and 2.0 means no limiting.", ) - reconstruction_gradient_limiter: Optional[pd.confloat(ge=0, le=2)] = pd.Field(0.5) class NoneSolver(Flow360BaseModel): - """:class:`NoneSolver` class""" + """:class:`NoneSolver` class for disabling the turbulence solver.""" type_name: Literal["None"] = pd.Field("None", frozen=True) @@ -274,17 +337,17 @@ class NoneSolver(Flow360BaseModel): class HeatEquationSolver(GenericSolverSettings): - """`HeatEquationSolver` class for setting up heat equation solver. + """:class:`HeatEquationSolver` class for setting up heat equation solver. Example ------- >>> he = HeatEquationSolver( - equation_evaluation_frequency=10, - linear_solver_config=LinearSolver( - max_iterations=50, - absoluteTolerance=1e-10 - ) - ) + ... equation_evaluation_frequency=10, + ... linear_solver_config=LinearSolver( + ... max_iterations=50, + ... absoluteTolerance=1e-10 + ... ) + ... ) """ type_name: Literal["HeatEquation"] = pd.Field("HeatEquation", frozen=True) @@ -296,9 +359,9 @@ class HeatEquationSolver(GenericSolverSettings): ) equation_evaluation_frequency: PositiveInt = pd.Field( 10, - description="Frequency at which to solve the heat equation in conjugate heat transfer simulations", + description="Frequency at which to solve the heat equation in conjugate heat transfer simulations.", ) - order_of_accuracy: Literal[2] = pd.Field(2, description="Order of accuracy in space") + order_of_accuracy: Literal[2] = pd.Field(2, description="Order of accuracy in space.") linear_solver: LinearSolver = pd.Field( LinearSolver(max_iterations=50, absolute_tolerance=1e-10), @@ -321,39 +384,48 @@ class TransitionModelSolver(GenericSolverSettings): "AmplificationFactorTransport", frozen=True ) CFL_multiplier: PositiveFloat = pd.Field( - 2.0, description="Factor to the CFL definitions defined in :code:`time_stepping` section" + 2.0, + description="Factor to the CFL definitions defined in the " + + ":ref:`Time Stepping ` section.", ) absolute_tolerance: PositiveFloat = pd.Field( 1e-7, description="Tolerance for the transition model residual, below which the solver progresses to " - + "the next physical step (unsteady) or completes the simulation (steady)", + + "the next physical step (unsteady) or completes the simulation (steady).", ) equation_evaluation_frequency: PositiveInt = pd.Field( - 4, description="Frequency at which to update the transition equation" + 4, description="Frequency at which to update the transition equation." ) turbulence_intensity_percent: Optional[pd.confloat(ge=0.03, le=2.5)] = pd.Field( 1.0, description=":ref:`Turbulence Intensity `, Range from [0.03-2.5]. " - + "Only valid when :code:`Ncrit` is not specified.", + + "Only valid when :paramref:`N_crit` is not specified.", ) # pylint: disable=invalid-name N_crit: Optional[pd.confloat(ge=1.0, le=11.0)] = pd.Field( None, description=":ref:`Critical Amplification Factor `, Range from [1-11]. " - + "Only valid when :code:`turbulenceIntensityPercent` is not specified.", + + "Only valid when :paramref:`turbulence_intensity_percent` is not specified.", ) update_jacobian_frequency: PositiveInt = pd.Field( - 4, description="Frequency at which the jacobian is updated" + 4, description="Frequency at which the jacobian is updated." ) max_force_jac_update_physical_steps: NonNegativeInt = pd.Field( 0, description="For physical steps less than the input value, the jacobian matrix " - + "is updated every pseudo-step overriding the :code:`updateJacobianFrequency` value", + + "is updated every pseudo-step overriding the :paramref:`update_jacobian_frequency` value.", ) - reconstruction_gradient_limiter: Optional[pd.confloat(ge=0.0, le=2.0)] = pd.Field(1.0) + reconstruction_gradient_limiter: Optional[pd.confloat(ge=0.0, le=2.0)] = pd.Field( + 1.0, + description="The strength of gradient limiter used in reconstruction of solution " + + "variables at the faces (specified in the range [0.0, 2.0]). 0.0 corresponds to " + + "setting the gradient equal to zero, and 2.0 means no limiting.", + ) - trip_regions: Optional[EntityList[Box]] = pd.Field(None) + trip_regions: Optional[EntityList[Box]] = pd.Field( + None, description="A list of :class:`~flow360.Box` entities defining the trip zones." + ) linear_solver: LinearSolver = pd.Field( LinearSolver(max_iterations=20), diff --git a/flow360/component/simulation/models/surface_models.py b/flow360/component/simulation/models/surface_models.py index 0ca9d5afa..ba35b0c67 100644 --- a/flow360/component/simulation/models/surface_models.py +++ b/flow360/component/simulation/models/surface_models.py @@ -42,63 +42,94 @@ class BoundaryBase(Flow360BaseModel, metaclass=ABCMeta): class BoundaryBaseWithTurbulenceQuantities(BoundaryBase, metaclass=ABCMeta): """Boundary base with turbulence quantities""" - turbulence_quantities: Optional[TurbulenceQuantitiesType] = pd.Field(None) + turbulence_quantities: Optional[TurbulenceQuantitiesType] = pd.Field( + None, + description="The turbulence related quantities definition." + + "See :func:`TurbulenceQuantities` documentation.", + ) class HeatFlux(SingleAttributeModel): - """Heat flux""" + """ + :class:`HeatFlux` class to specify the heat flux for `Wall` boundary condition + via :paramref:`Wall.heat_spec`. + """ type_name: Literal["HeatFlux"] = pd.Field("HeatFlux", frozen=True) - value: Union[HeatFluxType, pd.StrictStr] = pd.Field() + value: Union[HeatFluxType, pd.StrictStr] = pd.Field(description="The heat flux value.") class Temperature(SingleAttributeModel): - """Temperature""" + """ + :class:`Temperature` class to specify the temperature for `Wall` or `Inflow` + boundary condition via :paramref:`Wall.heat_spec`/ + :paramref:`Inflow.spec`. + """ type_name: Literal["Temperature"] = pd.Field("Temperature", frozen=True) # pylint: disable=no-member - value: Union[TemperatureType.Positive, pd.StrictStr] = pd.Field() + value: Union[TemperatureType.Positive, pd.StrictStr] = pd.Field( + description="The temperature value." + ) class TotalPressure(SingleAttributeModel): - """Total pressure""" + """ + :class:`TotalPressure` class to specify the total pressure for `Inflow` + boundary condition via :paramref:`Inflow.spec`. + """ type_name: Literal["TotalPressure"] = pd.Field("TotalPressure", frozen=True) # pylint: disable=no-member - value: PressureType.Positive = pd.Field() + value: PressureType.Positive = pd.Field(description="The total pressure value.") class Pressure(SingleAttributeModel): - """Pressure""" + """ + :class:`Pressure` class to specify the pressure for `Outflow` + boundary condition via :paramref:`Outflow.spec`. + """ type_name: Literal["Pressure"] = pd.Field("Pressure", frozen=True) # pylint: disable=no-member - value: PressureType.Positive = pd.Field() + value: PressureType.Positive = pd.Field(description="The pressure value.") class MassFlowRate(SingleAttributeModel): - """Mass flow rate""" + """ + :class:`MassFlowRate` class to specify the mass flow rate for `Inflow` or `Outflow` + boundary condition via :paramref:`Inflow.spec`/:paramref:`Outflow.spec`. + """ type_name: Literal["MassFlowRate"] = pd.Field("MassFlowRate", frozen=True) # pylint: disable=no-member - value: MassFlowRateType.NonNegative = pd.Field() + value: MassFlowRateType.NonNegative = pd.Field(description="The mass flow rate.") class Mach(SingleAttributeModel): - """Mach""" + """ + :class:`Mach` class to specify Mach number for the `Inflow` + boundary condition via :paramref:`Inflow.spec`. + """ type_name: Literal["Mach"] = pd.Field("Mach", frozen=True) - value: pd.NonNegativeFloat = pd.Field() + value: pd.NonNegativeFloat = pd.Field(description="The Mach number.") class Translational(Flow360BaseModel): - """Translational periodicity""" + """ + :class:`Translational` class to specify translational periodic + boundary condition via :paramref:`Periodic.spec`. + """ type_name: Literal["Translational"] = pd.Field("Translational", frozen=True) class Rotational(Flow360BaseModel): - """Rotational periodicity""" + """ + :class:`Rotational` class to specify rotational periodic + boundary condition via :paramref:`Periodic.spec`. + """ type_name: Literal["Rotational"] = pd.Field("Rotational", frozen=True) # pylint: disable=fixme @@ -112,83 +143,140 @@ class Rotational(Flow360BaseModel): class Wall(BoundaryBase): - """Replace Flow360Param: - - NoSlipWall - - IsothermalWall - - HeatFluxWall - - WallFunction - - SolidIsothermalWall - - SolidAdiabaticWall + """ + :class:`Wall` class defines the Wall boundary conditions below based on the input: + - NoSlipWall + - IsothermalWall + - HeatFluxWall + - WallFunction + - SolidIsothermalWall + - SolidAdiabaticWall """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `Wall` boundary condition.") type: Literal["Wall"] = pd.Field("Wall", frozen=True) - use_wall_function: bool = pd.Field(False) - velocity: Optional[VelocityVectorType] = pd.Field(None) - heat_spec: Optional[Union[HeatFlux, Temperature]] = pd.Field(None, discriminator="type_name") + use_wall_function: bool = pd.Field( + False, + description="Specify if use wall functions to estimate the velocity field " + + "close to the solid boundaries.", + ) + velocity: Optional[VelocityVectorType] = pd.Field( + None, description="Prescribe a tangential velocity on the wall." + ) + heat_spec: Optional[Union[HeatFlux, Temperature]] = pd.Field( + None, + discriminator="type_name", + description="Specify the heat flux or temperature at the `Wall` boundary.", + ) class Freestream(BoundaryBaseWithTurbulenceQuantities): - """Freestream""" + """ + :class:`Freestream` defines the Freestream condition. + """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `Freestream` boundary condition.") type: Literal["Freestream"] = pd.Field("Freestream", frozen=True) - velocity: Optional[VelocityVectorType] = pd.Field(None) - entities: EntityList[Surface, GhostSurface] = pd.Field(alias="surfaces") + velocity: Optional[VelocityVectorType] = pd.Field( + None, + description="The default values are set according to the " + + ":paramref:`AerospaceCondition.alpha` and :paramref:`AerospaceCondition.beta` angles. " + + "Optionally, an expression for each of the velocity components can be specified.", + ) + entities: EntityList[Surface, GhostSurface] = pd.Field( + alias="surfaces", + description="A list of :class:`Surface` entities with " + + "the `Freestream` boundary condition imposed.", + ) class Outflow(BoundaryBase): - """Replace Flow360Param: - - SubsonicOutflowPressure - - SubsonicOutflowMach - - MassOutflow + """ + :class:`Outflow` defines the Outflow boundary conditions below based on the input: + - SubsonicOutflowPressure + - SubsonicOutflowMach + - MassOutflow """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `Outflow` boundary condition.") type: Literal["Outflow"] = pd.Field("Outflow", frozen=True) - spec: Union[Pressure, MassFlowRate, Mach] = pd.Field(discriminator="type_name") + spec: Union[Pressure, MassFlowRate, Mach] = pd.Field( + discriminator="type_name", + description="Specify the static pressure, mass flow rate or Mach number at the `Outflow` boundary.", + ) class Inflow(BoundaryBaseWithTurbulenceQuantities): - """Replace Flow360Param: - - SubsonicInflow - - MassInflow + """ + :class:`Inflow` defines the Inflow boundary condition below based on the input: + - SubsonicInflow + - MassInflow """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `Inflow` boundary condition.") type: Literal["Inflow"] = pd.Field("Inflow", frozen=True) # pylint: disable=no-member - total_temperature: TemperatureType.Positive = pd.Field() - velocity_direction: Optional[Axis] = pd.Field(None) - spec: Union[TotalPressure, MassFlowRate] = pd.Field(discriminator="type_name") + total_temperature: TemperatureType.Positive = pd.Field( + description="Specify the total temperature at the `Inflow` boundary." + ) + velocity_direction: Optional[Axis] = pd.Field( + None, + description=" Direction of the incoming flow. Must be a unit vector pointing " + + "into the volume. If unspecified, the direction will be normal to the surface.", + ) + spec: Union[TotalPressure, MassFlowRate] = pd.Field( + discriminator="type_name", + description="Specify the total pressure or the mass flow rate at the `Inflow` boundary.", + ) class SlipWall(BoundaryBase): - """Slip wall""" + """:class:`SlipWall` class defines the SlipWall boundary condition.""" - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `SlipWall` boundary condition.") type: Literal["SlipWall"] = pd.Field("SlipWall", frozen=True) - entities: EntityList[Surface, GhostSurface] = pd.Field(alias="surfaces") + entities: EntityList[Surface, GhostSurface] = pd.Field( + alias="surfaces", + description="A list of :class:`Surface` entities with " + + "the `SlipWall` boundary condition imposed.", + ) class SymmetryPlane(BoundaryBase): - """Symmetry plane""" + """ + :class:`SymmetryPlane` defines the `SymmetryPlane` boundary condition. + It is similar to :class:`SlipWall`, but the normal gradient of scalar quantities + are forced to be zero on the symmetry plane. Only planar surfaces are supported. + """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field( + None, description="Name of the `SymmetryPlane` boundary condition." + ) type: Literal["SymmetryPlane"] = pd.Field("SymmetryPlane", frozen=True) - entities: EntityList[Surface, GhostSurface] = pd.Field(alias="surfaces") + entities: EntityList[Surface, GhostSurface] = pd.Field( + alias="surfaces", + description="A list of :class:`Surface` entities with " + + "the `SymmetryPlane` boundary condition imposed.", + ) class Periodic(Flow360BaseModel): - """Replace Flow360Param: - - TranslationallyPeriodic - - RotationallyPeriodic + """ + :class:`Periodic` defines the translational or rotational periodic boundary condition. """ - name: Optional[str] = pd.Field(None) + name: Optional[str] = pd.Field(None, description="Name of the `Periodic` boundary condition.") 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_name") + entity_pairs: UniqueItemList[SurfacePair] = pd.Field( + alias="surface_pairs", + description=" Patch name of the matching pair of a :class:`~flow360.Surface` patch. " + + "Needs to be specified for one side of each of the periodic boundary pairs.", + ) + spec: Union[Translational, Rotational] = pd.Field( + discriminator="type_name", + description="Define the type of periodic boundary condition (translational/rotational) " + + "via :class:`Translational`/:class:`Rotational`.", + ) SurfaceModelTypes = Union[ diff --git a/flow360/component/simulation/models/turbulence_quantities.py b/flow360/component/simulation/models/turbulence_quantities.py index e68abc3fd..2a15d808f 100644 --- a/flow360/component/simulation/models/turbulence_quantities.py +++ b/flow360/component/simulation/models/turbulence_quantities.py @@ -202,7 +202,55 @@ def TurbulenceQuantities( turbulent_length_scale=None, turbulent_intensity=None, ) -> TurbulenceQuantitiesType: - """Return a matching tubulence specification object""" + """ + + :func:`TurbulenceQuantities` function specifies turbulence conditions + for the :class:`~flow360.Inflow` or :class:`~flow360.Freestream` + at boundaries. The turbulence properties that can be + specified are listed below. All values are dimensional. + For valid specifications as well as the default values, + please refer to :ref:`knowledge base`. + + Parameters + ---------- + viscosity_ratio : >= 0 + The ratio between the turbulent viscosity and freestream laminar + viscosity. Applicable to both :class:`~flow360.KOmegaSST` and + `~flow360.SpalartAllmaras`. Its value will be converted to + :paramref:`modifiedTurbulentViscosityRatio` when using + SpalartAllmaras model. + modified_viscosity_ratio : >= 0 + The ratio between the modified turbulent viscosity (in SA model) and + freestream laminar viscosity. + Applicable to :class:`~flow360.SpalartAllmaras`. + modified_viscosity : >=0 + The modified turbulent viscosity, aka nuHat. + Applicable to :class:`~flow360.SpalartAllmaras`. + specific_dissipation_rate : >= 0 + The turbulent specific dissipation rate. Applicable to :class:`~flow360.KOmegaSST`. + turbulent_kinetic_energy : >=0 + The turbulent kinetic energy. Applicable to :class:`~flow360.KOmegaSST`. + turbulent_length_scale : > 0 + The turbulent length scale is an estimation of the size of + the eddies that are modeled/not resolved. + Applicable to :class:`~flow360.KOmegaSST`. + turbulent_intensity : >= 0 + The turbulent intensity is related to the turbulent kinetic energy by + :math:`k = 1.5(U_{ref} * I)^2` where :math:`k` is the dimensional + turbulent kinetic energy, :math:`U_{ref}` is the reference velocity + and :math:`I` is the turbulent intensity. The value represents the + actual magnitude of intensity instead of percentage. Applicable to + :class:`~flow360.KOmegaSST`. + + Returns + ------- + A matching tubulence specification object. + + Raises + ------- + ValueError + If the TurbulenceQuantities inputs do not represent a valid specification. + """ non_none_arg_count = sum(arg is not None for arg in locals().values()) if non_none_arg_count == 0: return None diff --git a/flow360/component/simulation/models/volume_models.py b/flow360/component/simulation/models/volume_models.py index 3ee28054c..f677576f6 100644 --- a/flow360/component/simulation/models/volume_models.py +++ b/flow360/component/simulation/models/volume_models.py @@ -51,30 +51,39 @@ class AngleExpression(SingleAttributeModel): - """Angle expression for Rotation""" + """ + :class:`AngleExpression` class for define the angle expression for :paramref:`Rotation.spec`. + """ type_name: Literal["AngleExpression"] = pd.Field("AngleExpression", frozen=True) value: StringExpression = pd.Field( - description="The expression defining the rotation angle " + "as a function of time." + description="The expression defining the rotation angle as a function of time." ) class AngularVelocity(SingleAttributeModel): - """Angular velocity for Rotation""" + """ + :class:`AngularVelocity` class to define the angular velocity for :paramref:`Rotation.spec`. + """ type_name: Literal["AngularVelocity"] = pd.Field("AngularVelocity", frozen=True) value: AngularVelocityType = pd.Field(description="The value of the angular velocity.") class FromUserDefinedDynamics(Flow360BaseModel): - """Rotation is controlled by user defined dynamics""" + """ + :class:`FromUserDefinedDynamics` class to define the rotation + controlled by user defined dynamics for :paramref:`Rotation.spec`. + """ type_name: Literal["FromUserDefinedDynamics"] = pd.Field("FromUserDefinedDynamics", frozen=True) class ExpressionInitialConditionBase(Flow360BaseModel): - """:class:`ExpressionInitialCondition` class for specifying the intial conditions of - :class:`~flow360.component.simulation.models.solver_numerics.NavierStokesSolver`.""" + """ + :class:`ExpressionInitialCondition` class for specifying the intial conditions of + :paramref:`Fluid.initial_condition`. + """ type: Literal["expression"] = pd.Field("expression", frozen=True) constants: Optional[Dict[str, str]] = pd.Field( @@ -85,8 +94,9 @@ class ExpressionInitialConditionBase(Flow360BaseModel): # pylint: disable=missing-class-docstring class NavierStokesInitialCondition(ExpressionInitialConditionBase): """ - :class:`NavierStokesInitialCondition` class for specifying the intial conditions of - :class:`~flow360.component.simulation.models.solver_numerics.NavierStokesSolver`. + :class:`NavierStokesInitialCondition` class for specifying the + :paramref:`Fluid.initial_condition`. + """ rho: str = pd.Field(description="Density") @@ -102,7 +112,8 @@ class NavierStokesModifiedRestartSolution(NavierStokesInitialCondition): class HeatEquationInitialCondition(ExpressionInitialConditionBase): """ - :class:`HeatEquationInitialCondition` class for specifying the intial conditions of heat equation in :class:`Solid`. + :class:`HeatEquationInitialCondition` class for specifying the + :paramref:`Solid.initial_condition` of heat equation. """ temperature: str = pd.Field(description="The initial temperature value") @@ -128,17 +139,17 @@ class Fluid(PDEModelBase): navier_stokes_solver: NavierStokesSolver = pd.Field( NavierStokesSolver(), description="Navier-Stokes solver settings, see " - + ":class:`~flow360.component.simulation.models.solver_numerics.NavierStokesSolver` documentation.", + + ":class:`NavierStokesSolver` documentation.", ) turbulence_model_solver: TurbulenceModelSolverType = pd.Field( SpalartAllmaras(), description="Turbulence model solver settings, see " - + ":class:`~flow360.component.simulation.models.solver_numerics.TurbulenceModelSolver` documentation.", + + ":class:`~flow360.TurbulenceModelSolver` documentation.", ) transition_model_solver: TransitionModelSolverType = pd.Field( NoneSolver(), description="Transition solver settings, see " - + ":class:`~flow360.component.simulation.models.solver_numerics.TransitionModelSolver` documentation.", + + ":class:`~flow360.TransitionModelSolver` documentation.", ) material: FluidMaterialTypes = pd.Field(Air(), description="The material propetry of fluid.") @@ -163,8 +174,8 @@ class Solid(PDEModelBase): type: Literal["Solid"] = pd.Field("Solid", frozen=True) entities: EntityList[GenericVolume] = pd.Field( alias="volumes", - description="The list of :class:`~flow360.component.simulation.primitives.GenericVolume` "+ - "entities on which the heat transfer equation is solved.", + description="The list of :class:`GenericVolume` " + + "entities on which the heat transfer equation is solved.", ) material: SolidMaterialTypes = pd.Field(description="The material property of solid.") @@ -172,7 +183,7 @@ class Solid(PDEModelBase): heat_equation_solver: HeatEquationSolver = pd.Field( HeatEquationSolver(), description="Heat equation solver settings, see " - + ":class:`~flow360.component.simulation.models.solver_numerics.HeatEquationSolver` documentation." + + ":class:`HeatEquationSolver` documentation.", ) volumetric_heat_source: Union[HeatSourceType, pd.StrictStr] = pd.Field( 0, description="The volumetric heat source." @@ -198,15 +209,13 @@ class ForcePerArea(Flow360BaseModel): radius: LengthType.Array = pd.Field(description="Radius of the sampled locations in grid unit.") # pylint: disable=no-member thrust: PressureType.Array = pd.Field( - description="Force per area in the axial direction, positive means the axial force follows the same " - + "direction as the thrust axis. " - + "It is non-dimensional: :math:`\\frac{\\text{thrustPerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`." + description="Dimensional force per area in the axial direction, positive means the axial " + + "force follows the same direction as the thrust axis. " ) # pylint: disable=no-member circumferential: PressureType.Array = pd.Field( - description="Force per area in the circumferential direction, positive means the circumferential force " - + "follows the same direction as the thrust axis with the right hand rule. It is non-dimensional: " - + ":math:`\\frac{\\text{circumferentialForcePerArea}(SI=N/m^2)}{\\rho_\\infty C^2_\\infty}`." + description="Dimensional force per area in the circumferential direction, positive means the " + + "circumferential force follows the same direction as the thrust axis with the right hand rule. " ) # pylint: disable=no-self-argument, missing-function-docstring @@ -235,8 +244,7 @@ class ActuatorDisk(Flow360BaseModel): entities: EntityList[Cylinder] = pd.Field( alias="volumes", - description="The list of :class:`~flow360.component.simulation.primitives.Cylinder` entities "+ - "for the `ActuatorDisk` model", + description="The list of :class:`Cylinder` entities for the `ActuatorDisk` model", ) force_per_area: ForcePerArea = pd.Field( description="The force per area input for the `ActuatorDisk` model. " @@ -247,7 +255,7 @@ class ActuatorDisk(Flow360BaseModel): class BETDiskTwist(Flow360BaseModel): - """:class:`BETDiskTwist` class for setting up twist for BETDisk.""" + """:class:`BETDiskTwist` class for setting up the :paramref:`BETDisk.twists`.""" # TODO: Use dimensioned values, why optional? radius: Optional[float] = pd.Field(None, description="A list of radial locations.") @@ -259,7 +267,7 @@ class BETDiskTwist(Flow360BaseModel): class BETDiskChord(Flow360BaseModel): - """:class:`BETDiskChord` class for setting up chord for BETDisk.""" + """:class:`BETDiskChord` class for setting up the :paramref:`BETDisk.chords`.""" # TODO: Use dimensioned values, why optional? radius: Optional[float] = pd.Field(None, description="A list of radial locations.") @@ -271,17 +279,14 @@ class BETDiskChord(Flow360BaseModel): class BETDiskSectionalPolar(Flow360BaseModel): - """:class:`BETDiskSectionalPolar` class for setting up sectional polars for BETDisk. - There are two variables, “lift_coeffs” and “drag_coeffs”, + """:class:`BETDiskSectionalPolar` class for setting up :paramref:`BETDisk.sectional_polars` + for :class:`BETDisk`. There are two variables, “lift_coeffs” and “drag_coeffs”, need to be set up as 3D arrays (implemented as nested lists). - The first index of the array corresponds to the - :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.mach_numbers` + The first index of the array corresponds to the :paramref:`BETDisk.mach_numbers` of the specified polar data. - The second index of the array corresponds to the - :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.reynolds_numbers` + The second index of the array corresponds to the :paramref:`BETDisk.reynolds_numbers` of the polar data. - The third index corresponds to the - :paramref:`~flow360.component.simulation.models.volume_models.BETDisk.alphas`. + The third index corresponds to the :paramref:`BETDisk.alphas`. The value specifies the lift or drag coefficient, respectively. """ @@ -316,23 +321,20 @@ class BETDisk(Flow360BaseModel): "rightHand", description='The rule for rotation direction and thrust direction, "rightHand" or "leftHand".', ) - number_of_blades: pd.StrictInt = pd.Field(gt=0, le=10, description="Number of blades to model") - omega: AngularVelocityType.NonNegative = pd.Field( - description="Nondimensional rotating speed, radians/nondim-unit-time, " - + "= :math:`\\frac{\\Omega*L_{gridUnit}}{C_\\infty}`" - ) + number_of_blades: pd.StrictInt = pd.Field(gt=0, le=10, description="Number of blades to model.") + omega: AngularVelocityType.NonNegative = pd.Field(description="Rotating speed.") chord_ref: LengthType.Positive = pd.Field( - description="Nondimensional reference chord used to compute sectional blade loadings" + description="Dimensional reference chord used to compute sectional blade loadings." ) n_loading_nodes: pd.StrictInt = pd.Field( gt=0, le=1000, description="Number of nodes used to compute the sectional thrust and " - + "torque coefficients :math:`C_t` and :math:`C_q`, defined in :ref:`betDiskLoadingNote`", + + "torque coefficients :math:`C_t` and :math:`C_q`, defined in :ref:`betDiskLoadingNote`.", ) blade_line_chord: LengthType.NonNegative = pd.Field( 0, - description="Nondimensional chord to use if performing an unsteady BET Line simulation. " + description="Dimensional chord to use if performing an unsteady BET Line simulation. " + "Default of 0.0 is an indication to run a steady BET Disk simulation.", ) initial_blade_direction: Optional[Axis] = pd.Field( @@ -342,7 +344,7 @@ class BETDisk(Flow360BaseModel): ) tip_gap: Union[Literal["inf"], LengthType.NonNegative] = pd.Field( "inf", - description="Nondimensional distance between blade tip and solid bodies to " + description="Dimensional distance between blade tip and solid bodies to " + "define a :ref:`tip loss factor `.", ) mach_numbers: List[pd.NonNegativeFloat] = pd.Field( @@ -355,7 +357,7 @@ class BETDisk(Flow360BaseModel): ) alphas: List[float] = pd.Field( description="Alphas associated with airfoil polars provided in " - + ":class:`BETDiskSectionalPolar` in degrees" + + ":class:`BETDiskSectionalPolar` in degrees." ) twists: List[BETDiskTwist] = pd.Field( description="A list of :class:`BETDiskTwist` objects specifying the twist in degrees as a " @@ -367,11 +369,11 @@ class BETDisk(Flow360BaseModel): ) sectional_polars: List[BETDiskSectionalPolar] = pd.Field( description="A list of :class:`BETDiskSectionalPolar` objects for every radial location specified in " - + ":attr:`sectional_radiuses`." + + ":paramref:`sectional_radiuses`." ) sectional_radiuses: List[float] = pd.Field( description="A list of the radial locations in grid units at which :math:`C_l` " - + "and :math:`C_d` are specified in :class:`BETDiskSectionalPolar`" + + "and :math:`C_d` are specified in :class:`BETDiskSectionalPolar`." ) @pd.model_validator(mode="after") @@ -423,7 +425,7 @@ class Rotation(Flow360BaseModel): type: Literal["Rotation"] = pd.Field("Rotation", frozen=True) entities: EntityList[GenericVolume, Cylinder] = pd.Field( alias="volumes", - description="The entity list for the Rotation model. " + description="The entity list for the `Rotation` model. " + "The entity should be :class:`Cylinder` or :class:`GenericVolume` type.", ) @@ -433,7 +435,9 @@ class Rotation(Flow360BaseModel): description="The angular velocity or rotation angle as a function of time.", ) parent_volume: Optional[Union[GenericVolume, Cylinder]] = pd.Field( - None, description="The parent rotating entity in a nested rotation case." + None, + description="The parent rotating entity in a nested rotation case." + + "The entity should be :class:`Cylinder` or :class:`GenericVolume` type.", ) @pd.field_validator("entities", mode="after") @@ -461,7 +465,11 @@ class PorousMedium(Flow360BaseModel): name: Optional[str] = pd.Field(None, description="Name of the `PorousMedium` model.") type: Literal["PorousMedium"] = pd.Field("PorousMedium", frozen=True) - entities: EntityList[GenericVolume, Box] = pd.Field(alias="volumes") + entities: EntityList[GenericVolume, Box] = pd.Field( + alias="volumes", + description="The entity list for the `PorousMedium` model. " + + "The entity should be :class:`Box` type.", + ) darcy_coefficient: InverseAreaType.Point = pd.Field( description="Darcy coefficient of the porous media model which determines the scaling of the " @@ -470,10 +478,10 @@ class PorousMedium(Flow360BaseModel): ) forchheimer_coefficient: InverseLengthType.Point = pd.Field( description="Forchheimer coefficient of the porous media model which determines " - + "the scaling of the inertial loss term" + + "the scaling of the inertial loss term." ) volumetric_heat_source: Optional[Union[HeatSourceType, pd.StrictStr]] = pd.Field( - None, description="The volumetric heat source" + None, description="The volumetric heat source." ) # Note: Axes will always come from the entity diff --git a/flow360/component/simulation/outputs/output_entities.py b/flow360/component/simulation/outputs/output_entities.py index 7056fe1d3..86e9d69fb 100644 --- a/flow360/component/simulation/outputs/output_entities.py +++ b/flow360/component/simulation/outputs/output_entities.py @@ -51,16 +51,16 @@ class _PointEntityBase(EntityBase, metaclass=ABCMeta): class Slice(_SliceEntityBase): - """Slice output item.""" + """:class:`Slice` class for defining a slice for :class:`~flow360.SliceOutput`.""" private_attribute_entity_type_name: Literal["Slice"] = pd.Field("Slice", frozen=True) - normal: Axis = pd.Field() + normal: Axis = pd.Field(description="Normal direction of the slice.") # pylint: disable=no-member - origin: LengthType.Point = pd.Field() + origin: LengthType.Point = pd.Field(description="A single point on the slice.") class Isosurface(_OutputItemBase): - """Isosurface output item.""" + """:class:`Isosurface` class for defining an isosurface for :class:`~flow360.IsosurfaceOutput`.""" field: Literal[IsoSurfaceFieldNames] = pd.Field() # pylint: disable=fixme @@ -69,18 +69,24 @@ class Isosurface(_OutputItemBase): class Point(_PointEntityBase): - """A single point for probe output""" + """ + :class:`Point` class for defining a single point for + :class:`~flow360.ProbeOutput`/:class:`~flow360.SurfaceProbeOutput`. + """ private_attribute_entity_type_name: Literal["Point"] = pd.Field("Point", frozen=True) # pylint: disable=no-member - location: LengthType.Point = pd.Field() + location: LengthType.Point = pd.Field(description="The coordinate of the point.") class PointArray(_PointEntityBase): - """A single point for probe output""" + """ + :class:`PointArray` class for defining a line for + :class:`~flow360.ProbeOutput`/:class:`~flow360.SurfaceProbeOutput`. + """ private_attribute_entity_type_name: Literal["PointArray"] = pd.Field("PointArray", frozen=True) # pylint: disable=no-member - start: LengthType.Point = pd.Field() - end: LengthType.Point = pd.Field() - number_of_points: int = pd.Field(gt=2) + start: LengthType.Point = pd.Field(description="The starting point of the line.") + end: LengthType.Point = pd.Field(description="The end point of the line.") + number_of_points: int = pd.Field(gt=2, description="Number of points along the line.") diff --git a/flow360/component/simulation/outputs/outputs.py b/flow360/component/simulation/outputs/outputs.py index 5fdaa27f2..7e045287c 100644 --- a/flow360/component/simulation/outputs/outputs.py +++ b/flow360/component/simulation/outputs/outputs.py @@ -56,7 +56,7 @@ class _AnimationAndFileFormatSettings(_AnimationSettings): """ output_format: Literal["paraview", "tecplot", "both"] = pd.Field( - default="paraview", description='"paraview", "tecplot" or "both".' + default="paraview", description=":code:`paraview`, :code:`tecplot` or :code:`both`." ) @@ -69,20 +69,19 @@ class SurfaceOutput(_AnimationAndFileFormatSettings): name: Optional[str] = pd.Field(None, description="Name of the `SurfaceOutput`.") entities: EntityList[Surface, GhostSurface] = pd.Field( alias="surfaces", - description="List of output :class:`~flow360.component.simulation.primitives.Surface`/" - + ":class:`~flow360.component.simulation.primitives.GhostSurface` entities. " - + "These surface names have to be the patch name in the grid file or the alias name specified in case JSON.", + description="List of output :class:`~flow360.Surface`/" + + ":class:`~flow360.GhostSurface` entities. ", ) 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.", + + "Will choose the value of the last instance of this option of the same output type " + + "(:class:`SurfaceOutput` or :class:`TimeAverageSurfaceOutput`) in the output list.", ) output_fields: UniqueItemList[SurfaceFieldNames] = pd.Field( - description="List of output variables. Including :ref:`universal output variables` " - + "and :ref:`variables specific to surfaceOutput`. " + description="List of output variables. Including :ref:`universal output variables` " + + "and :ref:`variables specific to SurfaceOutput`. " ) output_type: Literal["SurfaceOutput"] = pd.Field("SurfaceOutput", frozen=True) @@ -101,7 +100,7 @@ class TimeAverageSurfaceOutput(SurfaceOutput): """ start_step: Union[pd.NonNegativeInt, Literal[-1]] = pd.Field( - default=-1, description="Physical time step to start calculating averaging" + default=-1, description="Physical time step to start calculating averaging." ) output_type: Literal["TimeAverageSurfaceOutput"] = pd.Field( "TimeAverageSurfaceOutput", frozen=True @@ -113,8 +112,8 @@ class VolumeOutput(_AnimationAndFileFormatSettings): name: Optional[str] = pd.Field(None, description="Name of the `VolumeOutput`.") output_fields: UniqueItemList[VolumeFieldNames] = pd.Field( - description="List of output variables. Including :ref:`universal output variables`, " - + "and :ref:`variables specific to volumeOutput`." + description="List of output variables. Including :ref:`universal output variables`, " + + "and :ref:`variables specific to VolumeOutput`." ) output_type: Literal["VolumeOutput"] = pd.Field("VolumeOutput", frozen=True) @@ -123,8 +122,8 @@ class TimeAverageVolumeOutput(VolumeOutput): """ :class:`TimeAverageVolumeOutput` class for time average volume output settings. Caveats: - Solver side only accept exactly the same set of output_fields (is shared) - between VolumeOutput and TimeAverageVolumeOutput. + The solver only accepts exactly the same set of :paramref:`output_fields` (is shared) + between :class:`VolumeOutput` and :class:`TimeAverageVolumeOutput`. Also let's not worry about allowing entities here as it is not supported by solver anyway. Notes @@ -147,11 +146,11 @@ class SliceOutput(_AnimationAndFileFormatSettings): name: Optional[str] = pd.Field(None, description="Name of the `SliceOutput`.") entities: EntityList[Slice] = pd.Field( alias="slices", - description="List of output :class:`~flow360.component.simulation.outputs.output_entities.Slice` entities.", + description="List of output :class:`~flow360.Slice` entities.", ) output_fields: UniqueItemList[SliceFieldNames] = pd.Field( - description="List of output variables. Including :ref:`universal output variables` " - + "and :ref:`variables specific to sliceOutput`. " + description="List of output variables. Including :ref:`universal output variables` " + + "and :ref:`variables specific to SliceOutput`. " ) output_type: Literal["SliceOutput"] = pd.Field("SliceOutput", frozen=True) @@ -160,7 +159,7 @@ class TimeAverageSliceOutput(SliceOutput): """:class:`TimeAverageSliceOutput` class for time average slice output settings.""" start_step: Union[pd.NonNegativeInt, Literal[-1]] = pd.Field( - default=-1, description="Physical time step to start calculating averaging" + default=-1, description="Physical time step to start calculating averaging." ) output_type: Literal["TimeAverageSliceOutput"] = pd.Field("TimeAverageSliceOutput", frozen=True) @@ -171,7 +170,7 @@ class IsosurfaceOutput(_AnimationAndFileFormatSettings): name: Optional[str] = pd.Field(None, description="Name of the `IsosurfaceOutput`.") entities: UniqueItemList[Isosurface] = pd.Field( alias="isosurfaces", - description="List of :class:`~flow360.component.simulation.outputs.output_entities.Isosurface` entities.", + description="List of :class:`~flow360.Isosurface` entities.", ) output_fields: UniqueItemList[CommonFieldNames] = pd.Field( description=" Isosurface field variable to be written. One of :code:`p`, :code:`rho`, " @@ -192,8 +191,8 @@ class SurfaceIntegralOutput(Flow360BaseModel): + "the surface integral will be calculated.", ) output_fields: UniqueItemList[CommonFieldNames] = pd.Field( - description="List of output fields which will be added to all monitors within the monitor group," - + " see universal output variables." + description="List of output variables. Including " + + ":ref:`universal output variables`." ) output_type: Literal["SurfaceIntegralOutput"] = pd.Field("SurfaceIntegralOutput", frozen=True) @@ -204,14 +203,14 @@ class ProbeOutput(Flow360BaseModel): name: str = pd.Field(description="Name of the monitor group.") entities: EntityList[Point, PointArray] = pd.Field( alias="probe_points", - description="List of monitored :class:`~flow360.component.simulation.outputs.output_entities.Point`/" - + ":class:`~flow360.component.simulation.outputs.output_entities.PointArray` entities belonging to this " - + "monitor group. :class:`~flow360.component.simulation.outputs.output_entities.PointArray` is used to " + description="List of monitored :class:`~flow360.Point`/" + + ":class:`~flow360.PointArray` entities belonging to this " + + "monitor group. :class:`~flow360.PointArray` is used to " + "define monitored points along a line.", ) output_fields: UniqueItemList[CommonFieldNames] = pd.Field( description="List of output fields which will be added to all monitors within the monitor group," - + " see :ref:`universal output variables`" + + " see :ref:`universal output variables`." ) output_type: Literal["ProbeOutput"] = pd.Field("ProbeOutput", frozen=True) @@ -235,9 +234,9 @@ class SurfaceProbeOutput(Flow360BaseModel): name: str = pd.Field(description="Name of the surface monitor group.") entities: EntityList[Point, PointArray] = pd.Field( alias="probe_points", - description="List of monitored :class:`~flow360.component.simulation.outputs.output_entities.Point`/" - + ":class:`~flow360.component.simulation.outputs.output_entities.PointArray` entities belonging to this " - + "surface monitor group. :class:`~flow360.component.simulation.outputs.output_entities.PointArray` " + description="List of monitored :class:`~flow360.Point`/" + + ":class:`~flow360.PointArray` entities belonging to this " + + "surface monitor group. :class:`~flow360.PointArray` " + "is used to define monitored points along a line.", ) # Maybe add preprocess for this and by default add all Surfaces? @@ -246,7 +245,10 @@ class SurfaceProbeOutput(Flow360BaseModel): + "entities belonging to this monitor group." ) - output_fields: UniqueItemList[SurfaceFieldNames] = pd.Field() + output_fields: UniqueItemList[SurfaceFieldNames] = pd.Field( + description="List of output variables. Including :ref:`universal output variables` " + + "and :ref:`variables specific to SurfaceOutput`. " + ) output_type: Literal["SurfaceProbeOutput"] = pd.Field("SurfaceProbeOutput", frozen=True) @pd.field_validator("entities", mode="after") @@ -261,14 +263,21 @@ class SurfaceSliceOutput(_AnimationAndFileFormatSettings): Surface slice settings. """ - name: str = pd.Field() - entities: EntityList[Slice] = pd.Field(alias="slices") + name: str = pd.Field(description="Name of the `SurfaceSliceOutput`.") + entities: EntityList[Slice] = pd.Field( + alias="slices", description="List of :class:`Slice` entities." + ) # Maybe add preprocess for this and by default add all Surfaces? - target_surfaces: EntityList[Surface] = pd.Field() + target_surfaces: EntityList[Surface] = pd.Field( + description="List of :class:`Surface` entities on which the slice will cut through." + ) output_format: Literal["paraview"] = pd.Field(default="paraview") - output_fields: UniqueItemList[SurfaceFieldNames] = pd.Field() + output_fields: UniqueItemList[SurfaceFieldNames] = pd.Field( + description="List of output variables. Including :ref:`universal output variables` " + + "and :ref:`variables specific to SurfaceOutput`. " + ) output_type: Literal["SurfaceSliceOutput"] = pd.Field("SurfaceSliceOutput", frozen=True) @pd.field_validator("entities", mode="after") diff --git a/flow360/component/simulation/primitives.py b/flow360/component/simulation/primitives.py index 411ef734a..cdac37c4c 100644 --- a/flow360/component/simulation/primitives.py +++ b/flow360/component/simulation/primitives.py @@ -60,18 +60,19 @@ def _check_axis_is_orthogonal(axis_pair: Tuple[Axis, Axis]) -> Tuple[Axis, Axis] class ReferenceGeometry(Flow360BaseModel): """ - Contains all geometrical related refrence values - Note: - - mesh_unit is removed from here and will be a property - TODO: - - Support expression for time-dependent axis etc? - - What about force axis? + :class:`ReferenceGeometry` class contains all geometrical related refrence values. """ # pylint: disable=no-member - moment_center: Optional[LengthType.Point] = pd.Field(None) - moment_length: Optional[Union[LengthType.Positive, LengthType.PositiveVector]] = pd.Field(None) - area: Optional[AreaType.Positive] = pd.Field(None) + moment_center: Optional[LengthType.Point] = pd.Field( + None, description="The x, y, z moment center of the geometry in grid units." + ) + moment_length: Optional[Union[LengthType.Positive, LengthType.PositiveVector]] = pd.Field( + None, description="The x, y, z component-wise moment reference lengths." + ) + area: Optional[AreaType.Positive] = pd.Field( + None, description="The reference area of the geometry." + ) class Transformation(Flow360BaseModel): @@ -184,10 +185,10 @@ class GenericVolume(_VolumeEntityBase): private_attribute_entity_type_name: Literal["GenericVolume"] = pd.Field( "GenericVolume", frozen=True ) - axes: Optional[OrthogonalAxes] = pd.Field(None) # Porous media support + axes: Optional[OrthogonalAxes] = pd.Field(None, description="") # Porous media support axis: Optional[Axis] = pd.Field(None) # Rotation support # pylint: disable=no-member - center: Optional[LengthType.Point] = pd.Field(None) # Rotation support + center: Optional[LengthType.Point] = pd.Field(None, description="") # Rotation support def rotation_matrix_from_axis_and_angle(axis, angle): @@ -222,7 +223,7 @@ class BoxCache(Flow360BaseModel): @final class Box(MultiConstructorBaseModel, _VolumeEntityBase): """ - Represents a box in three-dimensional space. + :class:`Box` class represents a box in three-dimensional space. """ type_name: Literal["Box"] = pd.Field("Box", frozen=True) @@ -232,8 +233,8 @@ class Box(MultiConstructorBaseModel, _VolumeEntityBase): size: LengthType.PositiveVector = pd.Field( description="The dimensions of the box (length, width, height)." ) - axis_of_rotation: Axis = pd.Field(default=(0, 0, 1)) - angle_of_rotation: AngleType = pd.Field(default=0 * u.degree) + axis_of_rotation: Axis = pd.Field(default=(0, 0, 1), description="The rotation axis.") + angle_of_rotation: AngleType = pd.Field(default=0 * u.degree, description="The rotation angle.") private_attribute_input_cache: BoxCache = pd.Field(BoxCache(), frozen=True) # pylint: disable=no-self-argument @@ -307,7 +308,7 @@ def axes(self): @final class Cylinder(_VolumeEntityBase): """ - Represents a cylinder in three-dimensional space. + :class:`Cylinder` class represents a cylinder in three-dimensional space. """ private_attribute_entity_type_name: Literal["Cylinder"] = pd.Field("Cylinder", frozen=True) @@ -332,7 +333,7 @@ def _check_inner_radius_is_less_than_outer_radius(self) -> Self: @final class Surface(_SurfaceEntityBase): """ - Represents a boudary surface in three-dimensional space. + :class:`Surface` represents a boudary surface in three-dimensional space. """ private_attribute_entity_type_name: Literal["Surface"] = pd.Field("Surface", frozen=True) diff --git a/flow360/component/simulation/time_stepping/time_stepping.py b/flow360/component/simulation/time_stepping/time_stepping.py index e01fffd2d..da06153c2 100644 --- a/flow360/component/simulation/time_stepping/time_stepping.py +++ b/flow360/component/simulation/time_stepping/time_stepping.py @@ -18,13 +18,20 @@ def _apply_default_to_none(original, default): class RampCFL(Flow360BaseModel): """ - Ramp CFL for time stepping component + :class:`RampCFL` class for the Ramp CFL setting of time stepping. """ type: Literal["ramp"] = pd.Field("ramp", frozen=True) - initial: Optional[pd.PositiveFloat] = pd.Field(None) - final: Optional[pd.PositiveFloat] = pd.Field(None) - ramp_steps: Optional[pd.PositiveInt] = pd.Field(None) + initial: Optional[pd.PositiveFloat] = pd.Field( + None, description="Initial CFL for solving pseudo time step." + ) + final: Optional[pd.PositiveFloat] = pd.Field( + None, description="Final CFL for solving pseudo time step." + ) + ramp_steps: Optional[pd.PositiveInt] = pd.Field( + None, + description="Number of pseudo steps before reaching :paramref:`RampCFL.final` within 1 physical step.", + ) @classmethod def default_unsteady(cls): @@ -43,14 +50,27 @@ def default_steady(cls): class AdaptiveCFL(Flow360BaseModel): """ - Adaptive CFL for time stepping component + :class:`AdaptiveCFL` class for Adaptive CFL setting of time stepping. """ type: Literal["adaptive"] = pd.Field("adaptive", frozen=True) - min: pd.PositiveFloat = pd.Field(default=0.1) - max: Optional[pd.PositiveFloat] = pd.Field(None) - max_relative_change: Optional[pd.PositiveFloat] = pd.Field(None) - convergence_limiting_factor: Optional[pd.PositiveFloat] = pd.Field(None) + min: pd.PositiveFloat = pd.Field( + default=0.1, description="The minimum allowable value for Adaptive CFL." + ) + max: Optional[pd.PositiveFloat] = pd.Field( + None, description="The maximum allowable value for Adaptive CFL." + ) + max_relative_change: Optional[pd.PositiveFloat] = pd.Field( + None, + description="The maximum allowable relative change of CFL (%) at each pseudo step. " + + "In unsteady simulations, the value of :paramref:`AdaptiveCFL.max_relative_change` " + + "is updated automatically depending on how well the solver converges in each physical step.", + ) + convergence_limiting_factor: Optional[pd.PositiveFloat] = pd.Field( + None, + description="This factor specifies the level of conservativeness when using Adaptive CFL. " + + "Smaller values correspond to a more conservative limitation on the value of CFL.", + ) @classmethod def default_unsteady(cls): @@ -77,14 +97,14 @@ class BaseTimeStepping(Flow360BaseModel, metaclass=ABCMeta): class Steady(BaseTimeStepping): """ - Steady time stepping component + :class:`Steady` class for specifying steady simulation. """ type_name: Literal["Steady"] = pd.Field("Steady", frozen=True) max_steps: int = pd.Field(2000, gt=0, le=100000, description="Maximum number of pseudo steps.") # pylint: disable=duplicate-code CFL: Union[RampCFL, AdaptiveCFL] = pd.Field( - default=AdaptiveCFL.default_steady(), + default=AdaptiveCFL.default_steady(), description="CFL settings." ) @pd.model_validator(mode="before") @@ -105,7 +125,7 @@ def set_default_cfl(cls, values): class Unsteady(BaseTimeStepping): """ - Unsteady time stepping component + :class:`Unsteady` class for specifying unsteady simulation. """ type_name: Literal["Unsteady"] = pd.Field("Unsteady", frozen=True) @@ -118,6 +138,7 @@ class Unsteady(BaseTimeStepping): # pylint: disable=duplicate-code CFL: Union[RampCFL, AdaptiveCFL] = pd.Field( default=AdaptiveCFL.default_unsteady(), + description="CFL settings within each physical step.", ) @pd.model_validator(mode="before") diff --git a/flow360/component/simulation/user_defined_dynamics/user_defined_dynamics.py b/flow360/component/simulation/user_defined_dynamics/user_defined_dynamics.py index 7d4187d27..fb030717f 100644 --- a/flow360/component/simulation/user_defined_dynamics/user_defined_dynamics.py +++ b/flow360/component/simulation/user_defined_dynamics/user_defined_dynamics.py @@ -11,15 +11,50 @@ class UserDefinedDynamic(Flow360BaseModel): - """:class:`UserDefinedDynamic` class""" + """:class:`UserDefinedDynamic` class for defining the user defined dynamics inputs.""" - name: str = pd.Field() - input_vars: List[str] = pd.Field() - constants: Optional[Dict[str, float]] = pd.Field(None) - output_vars: Optional[Dict[str, StringExpression]] = pd.Field(None) - state_vars_initial_value: List[StringExpression] = pd.Field() - update_law: List[StringExpression] = pd.Field() - input_boundary_patches: Optional[EntityList[Surface]] = pd.Field(None) + name: str = pd.Field(description="Name of the dynamics defined by the user.") + input_vars: List[str] = pd.Field( + description="List of the inputs to define the user defined dynamics. For example :code:`CL`, :code:`CD`, " + + ":code:`bet_NUM_torque`, :code:`bet_NUM_thrust`, (NUM is the index of the BET disk starting from 0), " + + ":code:`momentX`, :code:`momentY`, :code:`momentZ` (X/Y/Z moments with respect to :ref:`momentCenter " + + "`), :code:`forceX`, :code:`forceY`, :code:`forceZ`. For a full list of supported " + + "variable, see :ref:`here `." + ) + constants: Optional[Dict[str, float]] = pd.Field( + None, description="A list of constants that can be used in the expressions." + ) + output_vars: Optional[Dict[str, StringExpression]] = pd.Field( + None, + description="Name of the output variables and the expression for the output variables using state " + + "variables. For example :code:`alphaAngle`, :code:`betaAngle`, :code:`bet_NUM_omega` (NUM is the index " + + "of the BET disk starting from 0), :code:`theta`, :code:`omega` and :code:`omegaDot` (rotation angle/" + + "velocity/acceleration in radians for sliding interfaces). For a full list of supported variable, see " + + ":ref:`here `. Please exercise caution when choosing output " + + "variables, as any modifications to their values will be directly mirrored in the solver. Expressions " + + "follows similar guidelines as :ref:`user Defined Expressions`.", + ) + state_vars_initial_value: List[StringExpression] = pd.Field( + description="The initial value of state variables are specified here. The entries could be either values " + + "(in the form of strings, e.g., :code:`0.0`) or expression with constants defined earlier or any input " + + "and output variable. (e.g., :code:`2.0 * alphaAngle + someConstant`). The list entries correspond to " + + "the initial values for :code:`state[0]`, :code:`state[1]`, ..., respectively." + ) + update_law: List[StringExpression] = pd.Field( + "List of expressions for updating state variables. The list entries correspond to the update laws for " + + ":code:`state[0]`, :code:`state[1]`, ..., respectively. These expressions follows similar guidelines as " + + ":ref:`user Defined Expressions`." + ) + input_boundary_patches: Optional[EntityList[Surface]] = pd.Field( + None, + description="The list of :class:`~flow360.Surface` entities to which the input variables belongs. " + + "If multiple boundaries are specified then the summation over the boundaries are used as the input. " + + "For input variables that already specified the source in the name (like bet_NUM_torque) " + + "this entry does not have any effect.", + ) output_target: Optional[Cylinder] = pd.Field( - None + None, + description="The target to which the output variables belong to. For example this can be the rotating " + + "volume zone name. Only one output target is supported per user defined dynamics instance. Only " + + ":class:`~flow360.Cylinder` entity is supported as target for now.", ) # Limited to `Cylinder` for now as we have only tested using UDD to control rotation. From 89b58b6338ef89d88cd6cd8f9ea8d61ccba11677 Mon Sep 17 00:00:00 2001 From: Angran Li Date: Fri, 1 Nov 2024 16:34:41 +0000 Subject: [PATCH 3/5] Update Flow360 --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index ec16b36d4..1f35613ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5129,4 +5129,4 @@ docs = ["autodoc_pydantic", "cairosvg", "ipython", "jinja2", "jupyter", "myst-pa [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "035965f33d10db440f29afa7421b474ae228c0374a9f4f572163d4f085a11533" +content-hash = "06a25622fff3a3c4612d1326ada848e08eb1e83c2ddf19c348a1164963090061" diff --git a/pyproject.toml b/pyproject.toml index 04db8578d..c772725ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ docs = [ "sphinx-prompt", "sphinx_design", "sphinx_toolbox", - "sphinx-paramlinks", + "sphinx_paramlinks", ] [tool.isort] From 0f295e2c9fd069758e1949b763c2f19a1d5c76a1 Mon Sep 17 00:00:00 2001 From: Angran Li Date: Fri, 1 Nov 2024 17:26:29 +0000 Subject: [PATCH 4/5] Update solver_numerics --- flow360/component/simulation/models/solver_numerics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow360/component/simulation/models/solver_numerics.py b/flow360/component/simulation/models/solver_numerics.py index a886d0f89..cc55b066b 100644 --- a/flow360/component/simulation/models/solver_numerics.py +++ b/flow360/component/simulation/models/solver_numerics.py @@ -288,7 +288,7 @@ class KOmegaSST(TurbulenceModelSolver): type_name: Literal["kOmegaSST"] = pd.Field("kOmegaSST", frozen=True) modeling_constants: KOmegaSSTModelConstants = pd.Field( KOmegaSSTModelConstants(), - description="A :class:`TurbulenceModelConstants` object containing the coefficients " + description="A :class:`KOmegaSSTModelConstants` object containing the coefficients " + "used in the SST k-omega model. For the default values used in Flow360, " + "please refer to :class:`KOmegaSSTModelConstants`.", ) @@ -313,7 +313,7 @@ class SpalartAllmaras(TurbulenceModelSolver): modeling_constants: Optional[SpalartAllmarasModelConstants] = pd.Field( SpalartAllmarasModelConstants(), - description="A :class:`TurbulenceModelConstants` object containing the coefficients " + description="A :class:`SpalartAllmarasModelConstants` object containing the coefficients " + "used in the Spalart-Allmaras model. For the default values used in Flow360, " + "please refer to :class:`SpalartAllmarasModelConstants`.", ) From d31b73dc87fa359dc4a05d253478b08aeaa2f2da Mon Sep 17 00:00:00 2001 From: Angran Li Date: Fri, 1 Nov 2024 17:27:49 +0000 Subject: [PATCH 5/5] Fix isort --- flow360/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flow360/__init__.py b/flow360/__init__.py index bb57675cd..b5f3686ae 100644 --- a/flow360/__init__.py +++ b/flow360/__init__.py @@ -93,11 +93,7 @@ TimeAverageVolumeOutput, VolumeOutput, ) -from flow360.component.simulation.primitives import ( - Box, - Cylinder, - ReferenceGeometry, -) +from flow360.component.simulation.primitives import Box, Cylinder, ReferenceGeometry from flow360.component.simulation.simulation_params import SimulationParams from flow360.component.simulation.time_stepping.time_stepping import ( AdaptiveCFL,