# -*- coding: utf-8 -*-
"""
This file is part of FElupe.
FElupe is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FElupe is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FElupe. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from scipy.constants import sigma
from scipy.sparse import csr_matrix
from ..assembly import IntegralForm
from ..mechanics import Assemble, Results, UpdateItem
[ドキュメント]
class SolidBodySurfaceRadiation:
r"""Radiative heat transfer on the surface of a thermal solid body.
Parameters
----------
field : felupe.FieldContainer
Field container with the temperature as first field.
emissivity : float
Emissivity :math:`\varepsilon` of the surface (dimensionless,
:math:`0 \le \varepsilon \le 1`).
temperature : float
The ambient temperature :math:`T_\infty` in °C.
Notes
-----
This class represents a boundary condition for a thermal solid body, which
is used to model radiative heat transfer at the boundary of a solid material. The
emissivity is used to calculate the heat flux at the boundary based on the
difference between the temperature at the boundary and the ambient temperature.
.. note:
A thermal analysis must use temperatures in °C, if
:class:`~felupe.thermal.SolidBodySurfaceRadiation` is included.
Examples
--------
.. pyvista-plot::
>>> import felupe as fem
>>> import numpy as np
>>>
>>> mesh = fem.Rectangle(n=11)
>>> region = fem.RegionQuad(mesh)
>>> temperature = fem.Field(region, dim=1, values=20.0)
>>> field = fem.FieldContainer([temperature])
>>>
>>> region_radiation = fem.RegionQuadBoundary(mesh, mask=mesh.x == 1.0)
>>> temperature_radiation = fem.Field(region_radiation, dim=1)
>>> field_radiation = fem.FieldContainer([temperature_radiation])
>>>
>>> boundaries = fem.BoundaryDict(
... left=fem.Boundary(temperature, fx=0, value=20.0),
... )
>>>
>>> solid = fem.thermal.SolidBodyThermal(
... field=field,
... mass_density=1400.0, # kg / m^3
... specific_heat_capacity=1000.0, # J / (kg K)
... time_step=720.0, # s
... thermal_conductivity=1.0, # W / (m K)
... )
>>> radiation = fem.thermal.SolidBodySurfaceRadiation(
... field=field_radiation,
... emissivity=0.8,
... temperature=20.0, # °C
... )
>>> time = fem.thermal.TimeStep([solid])
>>> table = fem.math.linsteps([0, 1], num=15)
>>> air_temperature = fem.math.linsteps([20, 40], num=15) # air temperature
>>> emissivity = fem.math.linsteps([0.6, 0.8], num=15) # a value between 0 ... 1
>>> ramp = {
... time: 18000 * table, # five hours
... radiation["temperature"]: air_temperature,
... radiation["emissivity"]: emissivity,
... }
>>> step = fem.Step(
... items=[time, solid, radiation], ramp=ramp, boundaries=boundaries
... )
>>> job = fem.Job(steps=[step]).evaluate()
>>>
>>> mesh.view(
... point_data={"Temperature in °C": temperature.values}
... ).plot("Temperature in °C").show()
See Also
--------
felupe.thermal.TimeStep : A time step item.
felupe.thermal.SolidBodyThermal : A thermal solid body for heat conduction.
"""
def __init__(self, field, emissivity, temperature):
self.field = field
self.time_step = None
self.results = Results()
self.results.temperature = temperature # ambient temperature in °C
self.results.emissivity = emissivity
self._sigma = sigma # Stefan-Boltzmann constant
self.assemble = Assemble(
vector=self._vector, matrix=self._matrix, multiplier=-1.0
)
def __getitem__(self, key):
return UpdateItem(self, key)
[ドキュメント]
def update(self, temperature):
self._update_temperature(temperature)
def _update_temperature(self, temperature):
self.results.temperature = temperature
def _update_emissivity(self, emissivity):
self.results.emissivity = emissivity
def _vector(self, field=None, **kwargs):
if field is not None:
self.field = field
if self.time_step is not None and self.time_step == 0: # inactive time step
return csr_matrix(([0.0], ([0], [0])), shape=(1, 1))
temperature = self.field.extract(grad=False)[0]
fun = [
-self.results.emissivity
* self._sigma
* ((temperature + 273.15) ** 4 - (self.results.temperature + 273.15) ** 4)
]
self.results.force = IntegralForm(
fun=fun, v=self.field, dV=self.field.region.dV, grad_v=[False]
).assemble(**kwargs)
return self.results.force
def _matrix(self, field=None, **kwargs):
if field is not None:
self.field = field
if self.time_step is not None and self.time_step == 0: # inactive time step
return csr_matrix(([0.0], ([0], [0])), shape=(1, 1))
dim = self.field[0].dim
temperature = self.field.extract(grad=False)[0]
fun = [
-self.results.emissivity
* self._sigma
* 4
* (temperature + 273.15) ** 3
* np.eye(dim).reshape(dim, dim, 1, 1)
]
self.results.stiffness = IntegralForm(
fun=fun,
v=self.field,
u=self.field,
dV=self.field.region.dV,
grad_v=[False],
grad_u=[False],
).assemble(**kwargs)
return self.results.stiffness