from typing import TYPE_CHECKING
import numpy as np
from TidalPy.logger import get_logger
from TidalPy.exceptions import (IncorrectMethodToSetStateProperty, MissingAttributeError, OuterscopePropertySetError)
from TidalPy.utilities.classes.model import LayerModelHolder
from . import known_model_const_args, known_model_live_args, known_models
if TYPE_CHECKING:
from TidalPy.utilities.types import FloatArray
from TidalPy.structures.layers import PhysicalLayerType
from .cooling_models import CoolingOutputType
log = get_logger("TidalPy")
[docs]
class CoolingModel(LayerModelHolder):
""" CoolingModel
Cooling model provides the functionality to calculate a layer's cooling efficiency based on user provided
parameters related to convection and conduction.
See Also
--------
TidalPy.utilities.methods.model.LayerModelHolder
"""
known_models = known_models
known_model_const_args = known_model_const_args
known_model_live_args = known_model_live_args
model_config_key = 'cooling'
def __init__(self,
layer: 'PhysicalLayerType', model_name: str = None, store_config_in_layer: bool = True,
initialize: bool = True
):
""" Constructor for CoolingModel class
Parameters
----------
layer : PhysicalLayerType
The layer instance which the model should perform calculations on.
model_name : str = None
The user-provided model name.
store_config_in_layer: bool = True
Flag that determines if the final model's configuration dictionary should be copied into the
`layer.config` dictionary.
initialize : bool = True
Determines if initial reinit should be performed on the model (loading in data from its `self.config`).
"""
super().__init__(layer, model_name, store_config_in_layer, initialize=False)
log.debug(f'Loading cooling model ({self.model}) into {self.layer}.')
# State properties
self._cooling = None
self._cooling_flux = None
self._boundary_layer_thickness = None
self._rayleigh = None
self._nusselt = None
if initialize:
self.reinit(initial_init=True)
[docs]
def clear_state(self):
super().clear_state()
self._cooling = None
self._cooling_flux = None
self._boundary_layer_thickness = None
self._rayleigh = None
self._nusselt = None
def _calculate(self) -> 'CoolingOutputType':
""" Calculate layer cooling based on the layer's state properties.
Returns
-------
cooling_flux : np.ndarray
Heat flux leaving the layer [W m-2]
boundary_layer_thickness : np.ndarray
Thickness of boundary layer (if any) [m]
rayleigh : np.ndarray
Rayleigh number (only non-zero for convection)
nusselt : np.ndarray
Nusselt number (only != 1 for convection)
"""
if self.temperature is None:
raise MissingAttributeError(
f"Can not calculate cooling until layer's temperature ({self.layer.name}) has been set"
)
if self.temperature_surf is None:
raise MissingAttributeError(
f"Can not calculate cooling until layer's surface temperature ({self.layer.name}) has been set. "
f"Set the layer above its dynamic temperature, or (if top layer) set the world's surface temperature."
)
delta_temp = self.temperature - self.temperature_surf
# Check if we need to use the float or array version of the complex compliance calculator
# OPT: Put this check in the setter for the live args / freqs?
use_float = True
for input_ in self.live_inputs:
if type(input_) is np.ndarray:
use_float = False
break
if use_float:
if type(delta_temp) is np.ndarray:
use_float = False
if use_float:
cooling_func = self.func
else:
cooling_func = self.func_array
cooling_flux, boundary_layer_thickness, rayleigh, nusselt = \
cooling_func(delta_temp, *self.live_inputs, *self.inputs)
self._cooling_flux = cooling_flux
self._boundary_layer_thickness = boundary_layer_thickness
self._rayleigh = rayleigh
self._nusselt = nusselt
self._cooling = self._cooling_flux * self.surface_area
return self.cooling_flux, self.boundary_layer_thickness, self.rayleigh, self.nusselt
def _calculate_debug(self) -> 'CoolingOutputType':
if self.layer.surface_temperature is None:
raise MissingAttributeError(f"Layer {self.layer.name}'s surface temperature has not been set yet.")
if self.layer.temperature is None:
raise MissingAttributeError(f"Layer {self.layer.name}'s average/central temperature has not been set yet.")
return self._calculate()
# # State properties
@property
def cooling(self) -> 'FloatArray':
""" This layer's cooling [W] """
return self._cooling
@cooling.setter
def cooling(self, value):
raise IncorrectMethodToSetStateProperty
@property
def cooling_flux(self) -> 'FloatArray':
""" This layer's cooling flux [W m-2] """
return self._cooling_flux
@cooling_flux.setter
def cooling_flux(self, value):
raise IncorrectMethodToSetStateProperty
@property
def boundary_layer_thickness(self) -> 'FloatArray':
""" This layer's thermal boundary layer thickness [m] """
return self._boundary_layer_thickness
@boundary_layer_thickness.setter
def boundary_layer_thickness(self, value):
raise IncorrectMethodToSetStateProperty
@property
def rayleigh(self) -> 'FloatArray':
""" This layer's rayleigh number """
return self._rayleigh
@rayleigh.setter
def rayleigh(self, value):
raise IncorrectMethodToSetStateProperty
@property
def nusselt(self) -> 'FloatArray':
""" This layer's nusselt number """
return self._nusselt
@nusselt.setter
def nusselt(self, value):
raise IncorrectMethodToSetStateProperty
# # Outer-scope properties
@property
def temperature(self):
""" Outer-scope wrapper for layer.temperature """
return self.layer.temperature
@temperature.setter
def temperature(self, value):
raise OuterscopePropertySetError
@property
def temperature_surf(self):
""" Outer-scope wrapper for layer.surface_temperature """
return self.layer.surface_temperature
@temperature_surf.setter
def temperature_surf(self, value):
raise OuterscopePropertySetError
@property
def surface_area(self):
""" Outer-scope wrapper for layer.surface_area_outer """
return self.layer.surface_area_outer
@surface_area.setter
def surface_area(self, value):
raise OuterscopePropertySetError
@property
def viscosity(self):
""" Outer-scope wrapper for layer.viscosity """
return self.layer.viscosity
@viscosity.setter
def viscosity(self, value):
raise OuterscopePropertySetError
@property
def thermal_conductivity(self):
""" Outer-scope wrapper for layer.thermal_conductivity """
return self.layer.thermal_conductivity
@thermal_conductivity.setter
def thermal_conductivity(self, value):
raise OuterscopePropertySetError
@property
def thermal_diffusivity(self):
""" Outer-scope wrapper for layer.thermal_diffusivity """
return self.layer.thermal_diffusivity
@thermal_diffusivity.setter
def thermal_diffusivity(self, value):
raise OuterscopePropertySetError
@property
def thermal_expansion(self):
""" Outer-scope wrapper for layer.thermal_expansion """
return self.layer.thermal_expansion
@thermal_expansion.setter
def thermal_expansion(self, value):
raise OuterscopePropertySetError
@property
def thickness(self):
""" Outer-scope wrapper for layer.thickness """
return self.layer.thickness
@thickness.setter
def thickness(self, value):
raise OuterscopePropertySetError
@property
def gravity(self):
""" Outer-scope wrapper for layer.gravity """
return self.layer.gravity
@gravity.setter
def gravity(self, value):
raise OuterscopePropertySetError
@property
def density_bulk(self):
""" Outer-scope wrapper for layer.density_bulk """
return self.layer.density_bulk
@density_bulk.setter
def density_bulk(self, value):
raise OuterscopePropertySetError
# # Aliased properties
@property
def blt(self):
""" Alias for self.boundary_layer_thickness [m] """
return self.boundary_layer_thickness
@blt.setter
def blt(self, value):
self.boundary_layer_thickness = value