"""Contains the class single_object.
Used to compute single thermal objects.
"""
from .. import solvers
from . import Object
import matplotlib.pyplot as plt
import numpy as np
[docs]class SingleObject:
"""Single_object class.
This class solves numerically the heat conduction equation for 1 dimension
of a single material(s). The class has 6 methods.
"""
def __init__(
self,
amb_temperature: int | float,
materials: tuple[str] = ('Cu',),
borders: tuple[int] = (1, 11),
materials_order: tuple[int] = (0,),
dx: float = 0.01,
dt: float = 0.1,
file_name: str | None = None,
boundaries: tuple[int] = (0, 0),
initial_state: bool = False,
materials_path: str | bool = False,
draw: list[str] = ['temperature'],
draw_scale: str | None = None
):
"""Thermal object initialization.
`amb_temperature` is the ambient temperature of the whole system.
`materials` is the list of strings of all the used materials present in
`material_path`. `borders` is a list of the points where there is a
change of material. `materials_order` is a list of the materials list
indexes that defines the material properties given by borders. `dx` and
`dt` are the space and time steps, respectively. `file_name` is the
file name where the temperature is saved. `boundaries` is a list of two
entries that define the boundary condition for temperature. If 0 the
boundary condition is insulation. `initial_state` is the initial state
of the materials. True if there are an applied field and False if them
field is absent. `materials_path` is absolute path of the materials
database. If false, then the materials database is the standard
heatrapy database. `draw` is a list of strings representing the online
plots. In this version only `'temperature'` can be potted. If the list
is empty, then no drawing is performed. `draw_scale` is a list of two
values, representing the minimum and maximum temperature to be drawn.
If None, there are no limits.
"""
# check the validity of inputs
materials = tuple(materials)
borders = tuple(borders)
materials_order = tuple(materials_order)
boundaries = tuple(boundaries)
cond01 = isinstance(amb_temperature, float)
cond01 = cond01 or isinstance(amb_temperature, int)
cond02 = isinstance(materials, tuple)
cond03 = isinstance(borders, tuple)
cond04 = isinstance(materials_order, tuple)
cond05 = isinstance(dx, int) or isinstance(dx, float)
cond06 = isinstance(dt, int) or isinstance(dt, float)
cond07 = isinstance(file_name, str)
cond07 = cond07 or (file_name is None)
cond08 = isinstance(boundaries, tuple)
cond10 = isinstance(initial_state, bool)
if isinstance(draw, list):
cond15 = True
elif draw is None:
cond15 = True
else:
cond15 = False
if isinstance(draw_scale, list) or isinstance(draw_scale, tuple):
cond16 = (len(draw_scale) == 2)
elif draw_scale is None:
cond16 = True
else:
cond16 = False
condition = cond01 and cond02 and cond03 and cond04 and cond05
condition = condition and cond06 and cond07 and cond08
condition = condition and cond10
condition = condition and cond15 and cond16
if not condition:
raise ValueError
self.object = Object(
amb_temperature,
materials=materials,
borders=borders,
materials_order=materials_order,
dx=dx,
dt=dt,
file_name=file_name,
boundaries=boundaries,
initial_state=initial_state,
materials_path=materials_path
)
# initializes the plotting
self.draw = draw
self.draw_scale = draw_scale
for drawing in self.draw:
if drawing == 'temperature':
self.figure = plt.figure()
self.ax = self.figure.add_subplot(111)
temp = []
for i in range(len(self.object.temperature)):
temp.append(self.object.temperature[i][0])
if not self.draw_scale:
vmax = max(temp)
vmin = min(temp)
if vmax == vmin:
vmin = vmin - 0.1
vmax = vmax + 0.1
temp = np.array(temp)
x_plot = [self.object.dx*j for j in range(len(temp))]
self.online, = self.ax.plot(x_plot, temp)
self.ax.set_ylim([vmin, vmax])
else:
temp = np.array(temp)
x_plot = [self.object.dx*j for j in range(len(temp))]
self.online, = self.ax.plot(x_plot, temp)
self.ax.set_ylim(self.draw_scale)
self.ax.set_title('Temperature (K)')
self.ax.set_xlabel('x axis (m)')
self.ax.set_ylabel('temperature (K)')
plt.show(block=False)
[docs] def activate(
self,
initial_point: int,
final_point: int
):
"""Activation.
Activates the thermal object between `initial_point` to `final_point`.
"""
# check the validity of inputs
condition = isinstance(initial_point, int)
condition = condition and isinstance(final_point, int)
if not condition:
raise ValueError
self.object.activate(initial_point, final_point)
if self.draw:
for drawing in self.draw:
if drawing == 'temperature':
try:
temp = []
for i in range(len(self.object.temperature)):
temp.append(self.object.temperature[i][0])
if not self.draw_scale:
vmax = max(temp)
vmin = min(temp)
if vmax == vmin:
vmin = vmin - 0.1
vmax = vmax + 0.1
temp = np.array(temp)
self.online.set_ydata(temp)
self.ax.set_ylim([vmin, vmax])
else:
temp = np.array(temp)
self.online.set_ydata(temp)
self.figure.canvas.draw()
except Exception:
pass
[docs] def deactivate(
self,
initial_point: int,
final_point: int
):
"""Deactivation.
Deactivates the thermal object between `initial_point` to
`final_point`.
"""
# check the validity of inputs
condition = isinstance(initial_point, int)
condition = condition and isinstance(final_point, int)
if not condition:
raise ValueError
self.object.deactivate(initial_point, final_point)
if self.draw:
for drawing in self.draw:
if drawing == 'temperature':
try:
temp = []
for i in range(len(self.object.temperature)):
temp.append(self.object.temperature[i][0])
if not self.draw_scale:
vmax = max(temp)
vmin = min(temp)
if vmax == vmin:
vmin = vmin - 0.1
vmax = vmax + 0.1
temp = np.array(temp)
self.online.set_ydata(temp)
self.ax.set_ylim([vmin, vmax])
else:
temp = np.array(temp)
self.online.set_ydata(temp)
self.figure.canvas.draw()
except Exception:
pass
[docs] def change_power(
self,
power_type: str,
power: int | float,
initial_point: int,
final_point: int
):
"""Heat power source change.
Changes the coeficients for the heat power sources by a value of power
from `initial_point` to `final_point`. `power_type` is a string that
represents the type of coefficient, i.e. 'Q' or 'Q0'.
"""
# check the validity of inputs
value = isinstance(initial_point, int)
if value and isinstance(final_point, int):
cond1 = True
else:
cond1 = False
cond2 = isinstance(power, int) or isinstance(power, float)
if isinstance(power_type, str):
if power_type == 'Q' or power_type == 'Q0':
cond3 = True
else:
cond3 = False
else:
cond3 = False
if not (cond1 and cond2 and cond3):
raise ValueError
if power_type == 'Q':
for j in range(initial_point, final_point):
self.object.Q[j] = power
if power_type == 'Q0':
for j in range(initial_point, final_point):
self.object.Q0[j] = power
[docs] def change_boundaries(
self,
boundaries: tuple[int | float]
):
"""Boundary change.
Changes the `boundaries` variable.
"""
# check the validity of inputs
if isinstance(boundaries, tuple):
if len(boundaries) == 2:
condition = True
else:
condition = False
else:
condition = False
if not condition:
raise ValueError
self.object.boundaries = boundaries
[docs] def compute(
self,
time_interval: int | float,
write_interval: int,
solver: str = 'explicit_k(x)',
verbose: bool = True
):
"""Compute the thermal process.
Computes the system for time_interval seconds, and writes into the
`file_name` file every `write_interval` time steps. Four different
solvers can be used: `'explicit_general'`, `'explicit_k(x)'`,
`'implicit_general'`, and `'implicit_k(x)'`. If `verbose = True`, then
the progress of the computation progress is shown.
"""
# check the validity of inputs
cond1 = isinstance(time_interval, float)
cond1 = cond1 or isinstance(time_interval, int)
cond2 = isinstance(write_interval, int)
if isinstance(solver, str):
all_solvers = ['implicit_general', 'implicit_k(x)',
'explicit_k(x)', 'explicit_general']
if solver in all_solvers:
cond3 = True
else:
cond3 = False
else:
cond3 = False
cond4 = isinstance(verbose, bool)
condition = cond1 and cond2 and cond3 and cond4
if not condition:
raise ValueError
# number of time steps for the given timeInterval
nt = int(time_interval / self.object.dt)
# number of time steps counting from the last writing process
nw = 0
# computes
for j in range(nt):
# updates the time_passed
self.object.time_passed = self.object.time_passed + self.object.dt
# defines the material properties accoring to the state list
for i in range(1, self.object.num_points - 1):
if self.object.state[i] is True:
value = self.object.materials_index[i]
self.object.rho[i] = self.object.materials[value].rhoa(
self.object.temperature[i][0])
self.object.Cp[i] = self.object.materials[value].cpa(
self.object.temperature[i][0])
self.object.k[i] = self.object.materials[value].ka(
self.object.temperature[i][0])
if self.object.state[i] is False:
value = self.object.materials_index[i]
self.object.rho[i] = self.object.materials[value].rho0(
self.object.temperature[i][0])
self.object.Cp[i] = self.object.materials[value].cp0(
self.object.temperature[i][0])
self.object.k[i] = self.object.materials[value].k0(
self.object.temperature[i][0])
# SOLVERS
# implicit k constant
if solver == 'implicit_general':
value = solvers.implicit_general(self.object)
self.object.temperature, self.object.lheat = value
# implicit k dependent on x
if solver == 'implicit_k(x)':
value = solvers.implicit_k(self.object)
self.object.temperature, self.object.lheat = value
# explicit k constant
if solver == 'explicit_general':
value = solvers.explicit_general(self.object)
self.object.temperature, self.object.lheat = value
# explicit k dependent on x
if solver == 'explicit_k(x)':
value = solvers.explicit_k(self.object)
self.object.temperature, self.object.lheat = value
nw = nw + 1
if self.draw:
for drawing in self.draw:
if drawing == 'temperature':
try:
value = nw + 1 == write_interval
if value or j == 0 or j == nt - 1:
temp = []
for i in range(len(self.object.temperature)):
temp.append(self.object.temperature[i][0])
if not self.draw_scale:
vmax = max(temp)
vmin = min(temp)
if vmax == vmin:
vmin = vmin - 0.1
vmax = vmax + 0.1
temp = np.array(temp)
self.online.set_ydata(temp)
self.ax.set_ylim([vmin, vmax])
else:
temp = np.array(temp)
self.online.set_ydata(temp)
self.figure.canvas.draw()
self.figure.canvas.flush_events()
except Exception:
pass
# writes the temperature to file_name file ...
# if the number of time steps is verified
if self.object.file_name:
if nw == write_interval or j == 0 or j == nt - 1:
line = '%f,' % self.object.time_passed
for i in self.object.temperature:
new_line = '%f,' % i[1]
line = line + new_line
line = line[:-1] + '\n'
f = open(self.object.file_name, 'a')
f.write(line)
f.close()
if nw == write_interval:
nw = 0
if verbose:
print('progress:', int(100*j/nt), '%', end="\r")
if verbose:
print('Finished simulation')