# pylint: disable=invalid-name
# pylint: disable=too-many-arguments
# pylint: disable=consider-using-f-string
# pylint: disable=no-member
"""
Heat transfer Green's function solutions due to an x-line in a semi-infinite medium.
The `Line` class represents a linear heat source that extends along all x-values passing
through coordinates (yp, zp) in the medium. The medium's surface is defined by z=0. The
class provides methods to calculate the temperature rise at any position (y, z) at a
specified time `t`, due to different types of heat source behaviors.
Three types of line sources are supported:
- **Instantaneous**:
Represents a single, instantaneous release of heat along the x-line at time `tp`.
- **Continuous**:
Represents a continuous release of heat along the x-line source starting at t=0.
- **Pulsed**:
Represents a pulsed release of heat along the line source from t=0 to t=`t_pulse`.
Each of these line sources can be analyzed under different boundary conditions at z=0:
- `'infinite'`: No boundary (infinite medium).
- `'adiabatic'`: No heat flow across the boundary.
- `'zero'`: Boundary is fixed at T=0.
The solutions are based on the mathematical formulations provided in Carslaw and
Jaeger's work.
More documentation at <https://grheat.readthedocs.io>
"""
import scipy.special
import numpy as np
# Constants for water properties
water_heat_capacity = 4.184 * 1e6 # J/degree / m**3
water_thermal_diffusivity = 0.14558 * 1e-6 # m**2/s
[docs]
class Line:
"""Provides Green's function heat transfer solutions for a line source.
The Line class encapsulates the behavior of a line source situated in a
semi-infinite medium with a surface defined at z=0. The line source extends
horizontally along all x-values passing through coordinates (yp, zp). At time tp,
the line source delivers a heat impulse of 1 Joule per meter along its length.
Boundary conditions at z=0 can be:
- 'infinite': No boundary (infinite medium).
- 'adiabatic': No heat flow across the boundary.
- 'zero': Boundary is fixed at T=0.
Attributes:
yp (scalar): y-coordinate of the line source. [meters]
zp (scalar): z-coordinate of the line source. [meters]
diffusivity (scalar): Thermal diffusivity of the medium. [m^2/s]
capacity (scalar): Volumetric heat capacity of the medium. [J/degree/m^3]
boundary (str): Boundary condition at z=0. ['infinite', 'adiabatic', 'zero']
"""
def __init__(self,
yp, zp,
tp=0,
diffusivity=water_thermal_diffusivity,
capacity=water_heat_capacity,
boundary='infinite'):
"""
Initialize a Line object representing a line source in a medium.
The line source extends infinitely parallel to the x-axis and passes through
the coordinates (yp, zp) in the medium. At time tp, the line source delivers a
heat impulse of 1 Joule per meter along the line.
The surface of the medium is defined by z=0 and the boundary conditions may be:
- 'infinite': No boundary.
- 'adiabatic': No heat flow across the boundary.
- 'zero': Boundary temperature is fixed at T=0.
Args:
yp (scalar): The y-coordinate through which the x-line source passes. [meters]
zp (scalar): The z-coordinate through which the x-line source passes,
defining its depth below the surface z=0. [meters]
tp (scalar, optional): The time at which the line source impulse occurs. [seconds]
diffusivity (scalar, optional): The thermal diffusivity of the medium.
Defaults to water_thermal_diffusivity. [m^2/s]
capacity (scalar, optional): The volumetric heat capacity of the medium.
Defaults to water_heat_capacity. [J/degree/m^3]
boundary (str, optional): string describing boundary conditions at z=0
Raises:
ValueError: If the specified boundary condition is not one of 'infinite',
'adiabatic', or 'zero'.
"""
self.yp = yp
self.zp = zp
self.tp = tp
self.diffusivity = diffusivity # m^2/s
self.capacity = capacity # J/degree/m^3
self.boundary = boundary.lower() # infinite, adiabatic, zero
if self.boundary not in ['infinite', 'adiabatic', 'zero']:
raise ValueError("boundary must be 'infinite', 'adiabatic', or 'zero'")
def _instantaneous(self, y, z, t, tp):
"""
Calculate temperature rise due to a 1 J/m instantaneous x-line source at time t.
This method computes the temperature rise at a specified location and time due to
an instantaneous x-line source with a heat impulse of 1 J/m at time `tp`. The line
source is parallel to the x-axis and passes through the coordinates (yp, zp).
The parameters `t` and `tp` should be scalars. If `t` is less than or equal to `tp`,
the method returns 0 or an array of zeros depending on y and z.
Reference: Carslaw and Jaeger (1959), page 258, Equation 10.3(1).
Args:
y (scalar or array): The y-coordinate(s) for the temperature location. [meters]
z (scalar or array): The z-coordinate(s) for the temperature location. [meters]
t (scalar): The time of the temperature. [seconds]
tp (scalar): The time of the line source impulse. [seconds]
Returns:
scalar: Normalized temperature rise.
"""
r2 = (y - self.yp)**2 + (z - self.zp)**2
if t <= tp:
return 0 * r2
factor = self.capacity * 4 * np.pi * self.diffusivity * (t - tp)
T = 1 / factor * np.exp(-r2 / (4 * self.diffusivity * (t - tp)))
if self.boundary != 'infinite':
r2 = (y - self.yp)**2 + (z + self.zp)**2
T1 = 1 / factor * np.exp(-r2 / (4 * self.diffusivity * (t - tp)))
if self.boundary == 'adiabatic':
T += T1
if self.boundary == 'zero':
T -= T1
return T
[docs]
def instantaneous(self, y, z, t):
"""
Calculate the temperature rise due to a 1 J/m instant x-line source.
This method computes the temperature rise at a specified location and time
due to an instant x-line source. The line source is parallel to the x-axis
and passes through the coordinates (yp, zp).
Args:
y (scalar or array): The y-coordinate(s) for the temperature location. [meters]
z (scalar or array): The z-coordinate(s) for the temperature location. [meters]
t (scalar or array): The time(s) of the temperature. [seconds]
Returns:
scalar: Temperature increase in degrees Celsius [°C].
Example:
The following example demonstrates how to use this method to calculate and
plot the temperature rise over time due to an instant line source:
.. code-block:: python
import grheat
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 500, 100) / 1000 # seconds
y, z = 0, 0 # meters
yp, zp = 0, 0.001 # meters
line = grheat.Line(yp, zp)
T = line.instantaneous(y, z, t)
plt.plot(t * 1000, T, color='blue')
plt.xlabel("Time (ms)")
plt.ylabel("Surface Temperature Increase (°C)")
plt.title("Instantaneous Line Source at 1mm depth")
plt.show()
"""
if np.isscalar(t):
T = 0 # return a scalar
if np.isscalar(self.tp):
T += self._instantaneous(y, z, t, self.tp)
else:
for i, tp in enumerate(self.tp):
T += self._instantaneous(y, z, t, tp)
else:
T = np.zeros_like(t) # return an array
for i, tt in enumerate(t):
if np.isscalar(self.tp):
T[i] += self._instantaneous(y, z, tt, self.tp)
else:
for tp in self.tp:
T[i] += self._instantaneous(y, z, tt, tp)
return T
def _continuous(self, y, z, t):
"""
Calculate temperature rise due to a 1W/m x-line source at single time point.
Carslaw and Jaeger page 261, 10.4(5)
Args:
y (scalar or array): The y-coordinate(s) for the temperature location. [meters]
z (scalar or array): The z-coordinate(s) for the temperature location. [meters]
t (scalar): The time of the temperature. [seconds]
Returns:
Temperature Increase [°C]
"""
r2 = (y - self.yp)**2 + (z - self.zp)**2
if t <= 0:
return 0 * r2
factor = -self.capacity * 4 * np.pi * self.diffusivity
T = 1 / factor * scipy.special.expi(-r2 / (4 * self.diffusivity * t))
if self.boundary != 'infinite':
r2 = (y - self.yp)**2 + (z + self.zp)**2
T1 = 1 / factor * scipy.special.expi(-r2 / (4 * self.diffusivity * t))
if self.boundary == 'adiabatic':
T += T1
if self.boundary == 'zero':
T -= T1
return T
[docs]
def continuous(self, y, z, t):
"""
Calculate temperature rise due to a 1W/m continuous x-line source.
The x-line source turns on at t=0 and passes through the coordinates (yp, zp).
Reference: Carslaw and Jaeger (1959), page 261, Equation 10.4(5).
Parameters:
y (scalar): The y-coordinate for the desired temperature location. [meters]
z (scalar): The z-coordinate for the desired temperature location. [meters]
t (scalar or array): Time(s) of desired temperature. [seconds]
Returns:
scalar or array: Temperature increase in degrees Celsius [°C].
Example:
The following example demonstrates how to use this method to calculate and
plot the temperature rise over time due to a continuous line source 1mm
deep that turned on at t=0:
.. code-block:: python
import grheat
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 500, 100) / 1000 # seconds
y, z = 0, 0 # meters
yp, zp = 0, 0.001 # meters
line = grheat.Line(yp, zp)
T = line.continuous(y, z, t)
plt.plot(t * 1000, T, color='blue')
plt.xlabel("Time (ms)")
plt.ylabel("Surface Temperature Increase (°C)")
plt.title("Continuous Line Source at 1mm depth")
plt.show()
"""
if np.isscalar(t):
T = 0 # return a scalar
if np.isscalar(self.tp):
T += self._continuous(y, z, t - self.tp)
else:
for i, tp in enumerate(self.tp):
T += self._continuous(y, z, t - tp)
else:
T = np.zeros_like(t) # return an array
for i, tt in enumerate(t):
if np.isscalar(self.tp):
T[i] += self._continuous(y, z, tt - self.tp)
else:
for tp in self.tp:
T[i] += self._continuous(y, z, tt - tp)
return T
[docs]
def pulsed(self, y, z, t, t_pulse):
"""
Calculate temperature rise due to a 1 J/m pulsed x-line source.
This method computes the temperature rise at a specified location and time due
to a pulsed x-line source. 1 J/m of heat is deposited along the x-line passing
through the coordinates (yp, zp) from t=0 to t=t_pulse.
Parameters:
y (scalar or array): The y-coordinate for the desired temperature location. [meters]
z (scalar or array): The z-coordinate for the desired temperature location. [meters]
t (scalar or array): Time(s) of desired temperature. [seconds]
t_pulse (scalar): Duration of the pulse. [seconds]
Returns:
scalar or array: Temperature increase in degrees Celsius [°C].
Example:
The following example demonstrates how to use this method to calculate and
plot the temperature rise over time due to a pulsed line source 1mm deep:
.. code-block:: python
import grheat
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 500, 100) / 1000 # seconds
y, z = 0, 0 # meters
yp, zp = 0, 0.001 # meters
t_pulse = 0.3
line = grheat.Line(yp, zp)
T = line.pulsed(y, z, t, t_pulse)
plt.plot(t * 1000, T, color='blue')
plt.xlabel("Time (ms)")
plt.ylabel("Surface Temperature Increase (°C)")
plt.title("Pulsed Line Source at 1mm depth")
plt.show()
"""
if t_pulse < 0:
raise ValueError("Pulse duration (%f) must be positive" % t_pulse)
T = self.continuous(y, z, t)
T -= self.continuous(y, z, t - t_pulse)
return T / t_pulse