heatrapy
Heatrapy stands for HEAt TRAnsfer in PYthon. It simulates dynamic 1D and 2D heat transfer processes in solids using the finite difference method. Heatrapy includes both the modeling of caloric effects and the incorporation of phase transitions.
author: Daniel Silva (djsilva@gmx.com)
current version: v2.0.1
Installation
To install heatrapy use the pip package manager:
$ pip install heatrapy
To import the heatrapy module type in the python shell:
>>> import heatrapy as htp
Thermal object
Thermal objects are the building block of the heatrapy module. Each thermal object is defined by a range of discrete space, where each point incorporates thermal properties, power sources and temperature. Boundary conditions are also defined and can be changed at any time. Thermal objects can use several methods that change the sate of each point, materials properties, and power sources. The state of each point defines if a field is applied (if True) or not (if False). This is of paramount importance for caloric materials. Moreover, phase change transitions can be incorporated according to latent heat data. There are two different types of thermal object classes: SingleObject and SystemObjects. While the first only computes heat transfer processes of single thermal objects, the second can compute heat transfer processes of several thermal objects that can contact each other.
Database of materials
A builtin database of materials is provided in the database folder. This database is very limited. The use of customized data can be done by defining the material data absolute path variable file_name
. The builtin database includes several standard materials and can be consulted here. Some folder names end with _mag
. This means that the state of each point defines if a magnetic field is applied.
Visualization
Although the temperature data can be saved for each point and time in a csv file, the package also allows live visualization when using the SingleObject classes. By default, when initializing the related objects, the plotting of the temperature begins. The different live plot types are defined in the draw
parameter. So far, one can plot the temperature, materials, heat power sources and state for SingleObject2D. Only the temperature can be plotted when using SingleObject1D.
Example
As an example, the code below computes 100 s of a Cu 2D solid with a water circle and a aluminum square.
>>> import heatrapy as htp
>>> example = htp.SingleObject2D(
... 293,
... material='Cu',
... boundaries=(300, 0, 0, 0),
... size=(20, 20)
... )
>>> example.change_material(
... material='Al',
... shape='square',
... initial_point=(5, 2),
... length=(4, 4)
... )
>>> example.change_material(
... material='water',
... shape='circle',
... initial_point=(5, 13),
... length=4
... )
>>> example.compute(100, 10)
The output at the end of the computation is the following:
Changelog
The changelog can be consulted here.
License
Heatrapy is licensed under the terms of the MIT License (MIT).
Contributing
Any type of contributions are welcome. A road map for future releases can be consulted in the wiki pages. Besides contributing by coding, feel free to report bugs and suggest new features. When contributing, please first discuss the change you wish to make via issue or email (djsilva@gmx.com), and then test the new code according to the contributing file.
Citing Heatrapy
Please acknowledge heatrapy if it contributes to a project that leads to a publication by citing this paper.
View Source
""" Heatrapy stands for HEAt TRAnsfer in PYthon. It simulates dynamic 1D and 2D heat transfer processes in solids using the finite difference method. Heatrapy includes both the modeling of caloric effects and the incorporation of phase transitions. author: Daniel Silva (djsilva@gmx.com) <br> current version: v2.0.1 Installation ------------ To install heatrapy use the pip package manager: ```bash $ pip install heatrapy ``` To import the heatrapy module type in the python shell: ```python >>> import heatrapy as htp ``` Thermal object -------------- Thermal objects are the building block of the heatrapy module. Each thermal object is defined by a range of discrete space, where each point incorporates thermal properties, power sources and temperature. Boundary conditions are also defined and can be changed at any time. Thermal objects can use several methods that change the sate of each point, materials properties, and power sources. The state of each point defines if a field is applied (if True) or not (if False). This is of paramount importance for caloric materials. Moreover, phase change transitions can be incorporated according to latent heat data. There are two different types of thermal object classes: SingleObject and SystemObjects. While the first only computes heat transfer processes of single thermal objects, the second can compute heat transfer processes of several thermal objects that can contact each other. Database of materials --------------------- A builtin database of materials is provided in the database folder. This database is very limited. The use of customized data can be done by defining the material data absolute path variable `file_name`. The builtin database includes several standard materials and can be consulted <a href='https://github.com/djsilva99/heatrapy/tree/master/heatrapy/database'>here</a>. Some folder names end with `_mag`. This means that the state of each point defines if a magnetic field is applied. Visualization ------------- Although the temperature data can be saved for each point and time in a csv file, the package also allows live visualization when using the SingleObject classes. By default, when initializing the related objects, the plotting of the temperature begins. The different live plot types are defined in the `draw` parameter. So far, one can plot the temperature, materials, heat power sources and state for SingleObject2D. Only the temperature can be plotted when using SingleObject1D. Example ------- As an example, the code below computes 100 s of a Cu 2D solid with a water circle and a aluminum square. ```python >>> import heatrapy as htp >>> example = htp.SingleObject2D( ... 293, ... material='Cu', ... boundaries=(300, 0, 0, 0), ... size=(20, 20) ... ) >>> example.change_material( ... material='Al', ... shape='square', ... initial_point=(5, 2), ... length=(4, 4) ... ) >>> example.change_material( ... material='water', ... shape='circle', ... initial_point=(5, 13), ... length=4 ... ) >>> example.compute(100, 10) ``` The output at the end of the computation will be the following: <img src="https://github.com/danieljosesilva/heatrapy/blob/master/img/example.png"> Changelog --------- The changelog can be consulted <a href='https://github.com/djsilva99/heatrapy/tree/master/CHANGELOG.md'>here</a>. License ------- Heatrapy is licensed under the terms of the <a href='https://github.com/djsilva99/heatrapy/tree/master/LICENSE'>MIT License (MIT)</a>. Contributing ------------ Any type of contributions are welcome. A road map for future releases can be consulted in the <a href='https://github.com/djsilva99/heatrapy/wiki'>wiki pages</a>. Besides contributing by coding, feel free to report bugs and suggest new features. When contributing, please first discuss the change you wish to make via issue or email (djsilva@gmx.com), and then test the new code according to the <a href='https://github.com/djsilva99/heatrapy/tree/master/CONTRIBUTING.md'> contributing file</a>. Citing Heatrapy ----------------- Please acknowledge heatrapy if it contributes to a project that leads to a publication by citing <a href='https://github.com/djsilva99/heatrapy/wiki'>this paper</a>. """ from .dimension_1.objects import SystemObjects as SystemObjects1D from .dimension_1.objects import SingleObject as SingleObject1D from .dimension_2.objects import SystemObjects as SystemObjects2D from .dimension_2.objects import SingleObject as SingleObject2D __all__ = ['SystemObjects1D', 'SingleObject1D', 'SystemObjects2D', 'SingleObject2D']
View Source
class SystemObjects: """System_objects class. This class creates a system of unidimensional thermal objects, establishes contact between them and computes the respective thermal processes. """ def __init__(self, number_objects=2, materials=('Cu', 'Cu'), objects_length=(10, 10), amb_temperature=293, dx=0.01, dt=0.1, file_name=None, initial_state=False, boundaries=((2, 0), (3, 0)), materials_path=False): """System object initialization. `number_objects` is the integer number of thermal objects. `materials` is the list of strings of all the used materials present in `material_path`. `amb_temperature` is the ambient temperature of the whole system. `object_length` is the list of thermal object lengths (spacial steps). `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 tuples of length two that define each boundary condition for temperature. If 0 the boundary condition is insulation. `materials_path` is absolute path of the materials database. If false, then the materials database is the standard heatrapy database. """ # check the validity of inputs materials = tuple(materials) objects_length = tuple(objects_length) boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(materials, tuple) cond03 = isinstance(number_objects, int) cond04 = isinstance(objects_length, 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) cond09 = isinstance(initial_state, bool) condition = cond01 and cond02 and cond03 and cond04 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 if not condition: raise ValueError # initial definitions self.objects = [] for i in range(number_objects): if file_name: file_name = file_name + '_' + str(i) + '.txt' self.objects.append(Object(amb_temperature, materials=(materials[i],), borders=(1, objects_length[i]+1), materials_order=(0,), dx=dx, dt=dt, file_name=file_name, boundaries=(0, 0), Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path)) self.contacts = set() self.boundaries = boundaries self.dt = dt self.q1 = 0. self.q2 = 0. for i in boundaries: if i[1] != 0: for j in range(len(self.objects[i[0]].temperature)): self.objects[i[0]].temperature[j] = [i[1], i[1]] def contact_filter(self, object): """Filter self.contacts by thermal object id. object: thermal object id """ # check the validity of inputs condition = object in range(len(self.objects)) if not condition: raise ValueError filtered = [x for x in self.contacts if (x[0][0] == object or x[1][0] == object)] return set(filtered) def contact_add(self, contact): """Add contact to self.contacts. The `contact` parameter is a tuple of length 3 (one element for thermal object A, one for thermal object B, and one for the heat transfer coefficient). Each thermal object element is a tuple of length 2 where the first element is the index of the thermal object and the second is the spatial point index. """ # check the validity of inputs if isinstance(contact, list) or isinstance(contact, tuple): if len(contact) == 3: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.contacts.add(contact) def contact_remove(self, object_one, object_two): """Contact removal. Removes all contacts between `object_one` id and `object_two` id. """ # check the validity of inputs condition = isinstance(object_one, int) condition = condition and isinstance(object_two, int) if not condition: raise ValueError contact_list = list(self.contacts) for i in range(len(contact_list)): cond_1 = contact_list[i][0][0] == object_one cond_1 = cond_1 and contact_list[i][1][0] == object_two cond_2 = contact_list[i][0][0] == object_two cond_2 = cond_2 and contact_list[i][1][0] == object_one if cond_1 or cond_2: self.contacts.remove(contact_list[i]) def change_boundaries(self, object_id, boundaries): """Change boundaries. Changes the `boundaries` of `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) condition = condition and isinstance(boundaries, tuple) if condition: if len(boundaries) == 2: condition = True else: condition = False if not condition: raise ValueError self.objects[object_id].boundaries = boundaries def compute(self, time_interval, write_interval, solver='implicit_k(x)', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, 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 is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) cond3 = isinstance(solver, str) 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.dt) # number of time steps counting from the last writing process nw = 0 # computes for j in range(nt): for obj in self.objects: obj.Q0 = copy.copy(obj.Q0_ref) for contact in self.contacts: ind1 = int(contact[1][1]) ind2 = int(contact[0][1]) td1 = self.objects[contact[1][0]].temperature[ind1][0] td2 = self.objects[contact[0][0]].temperature[ind2][0] heat_contact_1 = contact[2] * (td1 - td2) heat_contact_2 = contact[2] * (td2 - td1) self.objects[contact[0][0]].Q0[ind2] = heat_contact_1 self.objects[contact[1][0]].Q0[ind1] = heat_contact_2 object_number = -1 for obj in self.objects: object_number = object_number + 1 obj.time_passed = obj.time_passed + obj.dt cond1 = object_number not in [l[0] for l in self.boundaries] if cond1 or (object_number, 0) in self.boundaries: # defines the material properties for i in range(1, obj.num_points - 1): if obj.state[i] is True: ind = obj.materials_index[i] obj.rho[i] = obj.materials[ind].rhoa( obj.temperature[i][0]) obj.Cp[i] = obj.materials[ind].cpa( obj.temperature[i][0]) obj.k[i] = obj.materials[ind].ka( obj.temperature[i][0]) if obj.state[i] is False: ind = obj.materials_index[i] obj.rho[i] = obj.materials[ind].rho0( obj.temperature[i][0]) obj.Cp[i] = obj.materials[ind].cp0( obj.temperature[i][0]) obj.k[i] = obj.materials[ind].k0( obj.temperature[i][0]) # SOLVERS # implicit k constant if solver == 'implicit_general': value = solvers.implicit_general(obj) obj.temperature, obj.lheat = value # implicit k dependent on x if solver == 'implicit_k(x)': obj.temperature, obj.lheat = solvers.implicit_k(obj) # explicit k constant if solver == 'explicit_general': value = solvers.explicit_general(obj) obj.temperature, obj.lheat = value # explicit k dependent on x if solver == 'explicit_k(x)': obj.temperature, obj.lheat = solvers.explicit_k(obj) # writes the temperature to file_name file ... # if the number of time steps is verified if obj.file_name: if nw + 1 == write_interval or j == 0 or j == nt - 1: line = '%f' % obj.time_passed for i in obj.temperature: new_line = ',%f' % i[1] line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() else: heat = [p*self.dt*obj.dx for p in obj.Q0 if p is not None] heat = sum(heat)/(len(heat)*obj.dx) if object_number == self.boundaries[0][0]: self.q1 = self.q1 + heat q = self.q1 else: self.q2 = self.q2 + heat q = self.q2 # writes the temperature to file_name file ... # if the number of time steps is verified if obj.file_name: if nw + 1 == write_interval or j == 0 or j == nt - 1: line = '%f' % obj.time_passed for i in obj.temperature: new_line = ',%f' % i[1] line = line + new_line new_line = ',%f' % q line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() if nw == write_interval: nw = 0 if verbose: print('progress:', int(100*j/nt), '%', end='\r') else: nw = nw + 1 if verbose: print('Finished simulation')
System_objects class.
This class creates a system of unidimensional thermal objects, establishes contact between them and computes the respective thermal processes.
View Source
def __init__(self, number_objects=2, materials=('Cu', 'Cu'), objects_length=(10, 10), amb_temperature=293, dx=0.01, dt=0.1, file_name=None, initial_state=False, boundaries=((2, 0), (3, 0)), materials_path=False): """System object initialization. `number_objects` is the integer number of thermal objects. `materials` is the list of strings of all the used materials present in `material_path`. `amb_temperature` is the ambient temperature of the whole system. `object_length` is the list of thermal object lengths (spacial steps). `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 tuples of length two that define each boundary condition for temperature. If 0 the boundary condition is insulation. `materials_path` is absolute path of the materials database. If false, then the materials database is the standard heatrapy database. """ # check the validity of inputs materials = tuple(materials) objects_length = tuple(objects_length) boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(materials, tuple) cond03 = isinstance(number_objects, int) cond04 = isinstance(objects_length, 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) cond09 = isinstance(initial_state, bool) condition = cond01 and cond02 and cond03 and cond04 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 if not condition: raise ValueError # initial definitions self.objects = [] for i in range(number_objects): if file_name: file_name = file_name + '_' + str(i) + '.txt' self.objects.append(Object(amb_temperature, materials=(materials[i],), borders=(1, objects_length[i]+1), materials_order=(0,), dx=dx, dt=dt, file_name=file_name, boundaries=(0, 0), Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path)) self.contacts = set() self.boundaries = boundaries self.dt = dt self.q1 = 0. self.q2 = 0. for i in boundaries: if i[1] != 0: for j in range(len(self.objects[i[0]].temperature)): self.objects[i[0]].temperature[j] = [i[1], i[1]]
System object initialization.
number_objects
is the integer number of thermal objects. materials
is the list of strings of all the used materials present in
material_path
. amb_temperature
is the ambient temperature of the
whole system. object_length
is the list of thermal object lengths
(spacial steps). 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 tuples of length two that define each
boundary condition for temperature. If 0 the boundary condition is
insulation. materials_path
is absolute path of the materials
database. If false, then the materials database is the standard
heatrapy database.
View Source
def contact_filter(self, object): """Filter self.contacts by thermal object id. object: thermal object id """ # check the validity of inputs condition = object in range(len(self.objects)) if not condition: raise ValueError filtered = [x for x in self.contacts if (x[0][0] == object or x[1][0] == object)] return set(filtered)
Filter self.contacts by thermal object id.
object: thermal object id
View Source
def contact_add(self, contact): """Add contact to self.contacts. The `contact` parameter is a tuple of length 3 (one element for thermal object A, one for thermal object B, and one for the heat transfer coefficient). Each thermal object element is a tuple of length 2 where the first element is the index of the thermal object and the second is the spatial point index. """ # check the validity of inputs if isinstance(contact, list) or isinstance(contact, tuple): if len(contact) == 3: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.contacts.add(contact)
Add contact to self.contacts.
The contact
parameter is a tuple of length 3 (one element for thermal
object A, one for thermal object B, and one for the heat transfer
coefficient). Each thermal object element is a tuple of length 2 where
the first element is the index of the thermal object and the second is
the spatial point index.
View Source
def contact_remove(self, object_one, object_two): """Contact removal. Removes all contacts between `object_one` id and `object_two` id. """ # check the validity of inputs condition = isinstance(object_one, int) condition = condition and isinstance(object_two, int) if not condition: raise ValueError contact_list = list(self.contacts) for i in range(len(contact_list)): cond_1 = contact_list[i][0][0] == object_one cond_1 = cond_1 and contact_list[i][1][0] == object_two cond_2 = contact_list[i][0][0] == object_two cond_2 = cond_2 and contact_list[i][1][0] == object_one if cond_1 or cond_2: self.contacts.remove(contact_list[i])
Contact removal.
Removes all contacts between object_one
id and object_two
id.
View Source
def change_boundaries(self, object_id, boundaries): """Change boundaries. Changes the `boundaries` of `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) condition = condition and isinstance(boundaries, tuple) if condition: if len(boundaries) == 2: condition = True else: condition = False if not condition: raise ValueError self.objects[object_id].boundaries = boundaries
Change boundaries.
Changes the boundaries
of object_id
.
View Source
def compute(self, time_interval, write_interval, solver='implicit_k(x)', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, 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 is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) cond3 = isinstance(solver, str) 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.dt) # number of time steps counting from the last writing process nw = 0 # computes for j in range(nt): for obj in self.objects: obj.Q0 = copy.copy(obj.Q0_ref) for contact in self.contacts: ind1 = int(contact[1][1]) ind2 = int(contact[0][1]) td1 = self.objects[contact[1][0]].temperature[ind1][0] td2 = self.objects[contact[0][0]].temperature[ind2][0] heat_contact_1 = contact[2] * (td1 - td2) heat_contact_2 = contact[2] * (td2 - td1) self.objects[contact[0][0]].Q0[ind2] = heat_contact_1 self.objects[contact[1][0]].Q0[ind1] = heat_contact_2 object_number = -1 for obj in self.objects: object_number = object_number + 1 obj.time_passed = obj.time_passed + obj.dt cond1 = object_number not in [l[0] for l in self.boundaries] if cond1 or (object_number, 0) in self.boundaries: # defines the material properties for i in range(1, obj.num_points - 1): if obj.state[i] is True: ind = obj.materials_index[i] obj.rho[i] = obj.materials[ind].rhoa( obj.temperature[i][0]) obj.Cp[i] = obj.materials[ind].cpa( obj.temperature[i][0]) obj.k[i] = obj.materials[ind].ka( obj.temperature[i][0]) if obj.state[i] is False: ind = obj.materials_index[i] obj.rho[i] = obj.materials[ind].rho0( obj.temperature[i][0]) obj.Cp[i] = obj.materials[ind].cp0( obj.temperature[i][0]) obj.k[i] = obj.materials[ind].k0( obj.temperature[i][0]) # SOLVERS # implicit k constant if solver == 'implicit_general': value = solvers.implicit_general(obj) obj.temperature, obj.lheat = value # implicit k dependent on x if solver == 'implicit_k(x)': obj.temperature, obj.lheat = solvers.implicit_k(obj) # explicit k constant if solver == 'explicit_general': value = solvers.explicit_general(obj) obj.temperature, obj.lheat = value # explicit k dependent on x if solver == 'explicit_k(x)': obj.temperature, obj.lheat = solvers.explicit_k(obj) # writes the temperature to file_name file ... # if the number of time steps is verified if obj.file_name: if nw + 1 == write_interval or j == 0 or j == nt - 1: line = '%f' % obj.time_passed for i in obj.temperature: new_line = ',%f' % i[1] line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() else: heat = [p*self.dt*obj.dx for p in obj.Q0 if p is not None] heat = sum(heat)/(len(heat)*obj.dx) if object_number == self.boundaries[0][0]: self.q1 = self.q1 + heat q = self.q1 else: self.q2 = self.q2 + heat q = self.q2 # writes the temperature to file_name file ... # if the number of time steps is verified if obj.file_name: if nw + 1 == write_interval or j == 0 or j == nt - 1: line = '%f' % obj.time_passed for i in obj.temperature: new_line = ',%f' % i[1] line = line + new_line new_line = ',%f' % q line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() if nw == write_interval: nw = 0 if verbose: print('progress:', int(100*j/nt), '%', end='\r') else: nw = nw + 1 if verbose: print('Finished simulation')
Compute the thermal process.
Computes the system for time_interval
, 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 is shown.
View Source
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, materials=('Cu',), borders=(1, 11), materials_order=(0,), dx=0.01, dt=0.1, file_name=None, boundaries=(0, 0), initial_state=False, materials_path=False, draw=['temperature'], draw_scale=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) def show_figure(self, figure_type, draw_scale=None): """Plotting. Initializes a specific live plotting. `figure_type` is a string identifying the plotting. This version only allows the plotting of the 'temperature'. `draw_scale` defines the range of temperatures. If None, this range is found automatically for every frame. """ # check the validity of inputs if isinstance(draw_scale, list) or isinstance(draw_scale, tuple): condition = (len(draw_scale) == 2) elif draw_scale is None: condition = True else: condition = False condition = condition and isinstance(figure_type, str) if not condition: raise ValueError self.draw_scale = draw_scale if figure_type == 'temperature': if figure_type not in self.draw: self.draw.append(figure_type) 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) def activate(self, initial_point, final_point): """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: pass def deactivate(self, initial_point, final_point): """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: pass def change_power(self, power_type, power, initial_point, final_point): """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 def change_boundaries(self, boundaries): """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 def compute(self, time_interval, write_interval, solver='explicit_k(x)', verbose=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() except: 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('pogress:', int(100*j/nt), '%', end="\r") if verbose: print('Finished simulation')
Single_object class.
This class solves numerically the heat conduction equation for 1 dimension of a single material(s). The class has 6 methods.
View Source
def __init__(self, amb_temperature, materials=('Cu',), borders=(1, 11), materials_order=(0,), dx=0.01, dt=0.1, file_name=None, boundaries=(0, 0), initial_state=False, materials_path=False, draw=['temperature'], draw_scale=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)
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.
View Source
def show_figure(self, figure_type, draw_scale=None): """Plotting. Initializes a specific live plotting. `figure_type` is a string identifying the plotting. This version only allows the plotting of the 'temperature'. `draw_scale` defines the range of temperatures. If None, this range is found automatically for every frame. """ # check the validity of inputs if isinstance(draw_scale, list) or isinstance(draw_scale, tuple): condition = (len(draw_scale) == 2) elif draw_scale is None: condition = True else: condition = False condition = condition and isinstance(figure_type, str) if not condition: raise ValueError self.draw_scale = draw_scale if figure_type == 'temperature': if figure_type not in self.draw: self.draw.append(figure_type) 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)
Plotting.
Initializes a specific live plotting. figure_type
is a string
identifying the plotting. This version only allows the plotting of the
'temperature'. draw_scale
defines the range of temperatures. If None,
this range is found automatically for every frame.
View Source
def activate(self, initial_point, final_point): """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: pass
Activation.
Activates the thermal object between initial_point
to final_point
.
View Source
def deactivate(self, initial_point, final_point): """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: pass
Deactivation.
Deactivates the thermal object between initial_point
to
final_point
.
View Source
def change_power(self, power_type, power, initial_point, final_point): """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
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'.
View Source
def change_boundaries(self, boundaries): """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
Boundary change.
Changes the boundaries
variable.
View Source
def compute(self, time_interval, write_interval, solver='explicit_k(x)', verbose=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() except: 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('pogress:', int(100*j/nt), '%', end="\r") if verbose: print('Finished simulation')
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.
View Source
class SystemObjects: """System_objects class. This class creates a system of 2D thermal objects, establishes contact between them and computes the respective thermal processes. """ def __init__(self, number_objects=2, materials=('Cu', 'Cu'), objects_length=((10, 10), (10, 10)), amb_temperature=293, dx=0.01, dy=0.01, dt=0.1, file_name=None, initial_state=False, boundaries=((0, 0, 0, 0), (0, 0, 0, 0)), materials_path=False): """System object initialization. `number_objects` is the integer number of thermal objects. `materials` is the list of strings of all the used materials present in `material_path`. `amb_temperature` is the ambient temperature of the whole system. `object_length` is the list of thermal object lengths (tuple of spacial steps). `dx` and `dy` are the space steps along the x- and y-axis, respectively. dt is the time step. `file_name` is the file name where the temperature is saved. `boundaries` is a list of four entries that define the boundary condition for temperature (left, right, bottom, top). If 0, the boundary condition is insulation. `materials_path` is absolute path of the materials database. If false, then the materials database is the standard heatrapy database. """ # check the validity of inputs materials = tuple(materials) objects_length = tuple(objects_length) boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(materials, tuple) cond03 = isinstance(number_objects, int) cond04 = isinstance(objects_length, tuple) cond05 = isinstance(dx, int) or isinstance(dx, float) cond06 = isinstance(dy, int) or isinstance(dy, float) cond07 = isinstance(dt, int) or isinstance(dt, float) cond08 = isinstance(file_name, str) cond08 = cond08 or (file_name is None) cond09 = isinstance(boundaries, tuple) cond10 = isinstance(initial_state, bool) condition = cond01 and cond02 and cond03 and cond04 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 condition = condition and cond10 if not condition: raise ValueError # initial definitions self.objects = [] file_name_obj = None for i in range(number_objects): if file_name: file_name_obj = file_name + '_' + str(i) + '.txt' self.objects.append(Object(amb_temperature, material=materials[i], dx=dx, dy=dy, dt=dt, file_name=file_name_obj, boundaries=boundaries[i], Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path)) self.contacts = set() self.boundaries = boundaries self.dt = dt self.q1 = 0. self.q2 = 0. def contact_filter(self, object_id): """Filter thermal contacts. Filter thermal contacts by `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) if not condition: raise ValueError value = object_id filtered = [x for x in self.contacts if (x[0][0] == value or x[1][0] == value)] return set(filtered) def contact_add(self, contact): """Add contact to self.contacts. `contact` is a thermal contact tuple of length 3, where the first and second entries correspond to tuples of the thermal objects and points (object_id, (x,y)), and the third entry is the heat transfer coefficient. """ # check the validity of inputs if isinstance(contact, tuple): if len(contact) == 3: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.contacts.add(contact) def contact_remove(self, object_one, object_two): """Contact removal. Removes all contacts between `object_one` id and `object_two` id. """ # check the validity of inputs condition = isinstance(object_one, int) condition = condition and isinstance(object_two, int) if not condition: raise ValueError contact_list = list(self.contacts) for i in range(len(contact_list)): cond_1 = contact_list[i][0][0] == object_one cond_1 = cond_1 and contact_list[i][1][0] == object_two cond_2 = contact_list[i][0][0] == object_two cond_2 = cond_2 and contact_list[i][1][0] == object_one if cond_1 or cond_2: self.contacts.remove(contact_list[i]) def change_boundaries(self, object_id, boundaries): """Change boundaries. Changes the `boundaries` variable of `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) condition = condition and isinstance(boundaries, tuple) if condition: if len(boundaries) == 4: condition = True else: condition = False if not condition: raise ValueError self.objects[object_id].boundaries = boundaries def compute(self, time_interval, write_interval, solver='explicit_k(x)', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, and writes into the `file_name` file every `write_interval` time steps. Two different solvers can be used: `'explicit_general'` and `'explicit_k(x)'`. If verbose = True, then the progress of the computation is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) cond3 = isinstance(solver, str) cond4 = isinstance(verbose, bool) condition = cond1 and cond2 and cond3 and cond4 if not condition: raise ValueError # number of time steps for the given time_interval nt = int(time_interval / self.dt) # number of time steps counting from the last writing process nw = 0 # computes for k in range(nt): for obj in self.objects: obj.Q0 = copy.copy(obj.Q0_ref) for contact in self.contacts: in1_x = int(contact[1][1][0]) in1_y = int(contact[1][1][1]) in2_x = int(contact[0][1][0]) in2_y = int(contact[0][1][1]) td1 = self.objects[contact[1][0]].temperature[in1_x][in1_y][0] td2 = self.objects[contact[0][0]].temperature[in2_x][in2_y][0] heat_contact_1 = contact[2] * (td1 - td2) heat_contact_2 = contact[2] * (td2 - td1) self.objects[contact[0][0]].Q0[in2_x][in2_y] = heat_contact_1 self.objects[contact[1][0]].Q0[in1_x][in2_y] = heat_contact_2 for obj in self.objects: obj.time_passed = obj.time_passed + obj.dt # defines the material properties for i in range(obj.size[0]): for j in range(obj.size[1]): if obj.state[i][j] is True: ind = obj.materials_index[i][j] obj.rho[i][j] = obj.materials[ind].rhoa( obj.temperature[i][j][0]) obj.Cp[i][j] = obj.materials[ind].cpa( obj.temperature[i][j][0]) obj.k[i][j] = obj.materials[ind].ka( obj.temperature[i][j][0]) if obj.state[i][j] is False: ind = obj.materials_index[i][j] obj.rho[i][j] = obj.materials[ind].rho0( obj.temperature[i][j][0]) obj.Cp[i][j] = obj.materials[ind].cp0( obj.temperature[i][j][0]) obj.k[i][j] = obj.materials[ind].k0( obj.temperature[i][j][0]) # explicit k constant if solver == 'explicit_general': temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) obj.temperature, obj.lheat = solvers.explicit_general(obj) temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) # explicit k constant if solver == 'explicit_k(x)': temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) obj.temperature, obj.lheat = solvers.explicit_k(obj) temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) if obj.file_name: if nw + 1 == write_interval or k == 0 or k == nt - 1: line = '%f' % obj.time_passed for i in range(obj.size[0]): for j in range(obj.size[1]): new_line = ',%f' % obj.temperature[i][j][1] line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() if nw == write_interval: nw = 0 if verbose: print('progress:', int(100*k/nt), '%', end='\r') else: nw = nw + 1 if verbose: print('Finished simulation')
System_objects class.
This class creates a system of 2D thermal objects, establishes contact between them and computes the respective thermal processes.
View Source
def __init__(self, number_objects=2, materials=('Cu', 'Cu'), objects_length=((10, 10), (10, 10)), amb_temperature=293, dx=0.01, dy=0.01, dt=0.1, file_name=None, initial_state=False, boundaries=((0, 0, 0, 0), (0, 0, 0, 0)), materials_path=False): """System object initialization. `number_objects` is the integer number of thermal objects. `materials` is the list of strings of all the used materials present in `material_path`. `amb_temperature` is the ambient temperature of the whole system. `object_length` is the list of thermal object lengths (tuple of spacial steps). `dx` and `dy` are the space steps along the x- and y-axis, respectively. dt is the time step. `file_name` is the file name where the temperature is saved. `boundaries` is a list of four entries that define the boundary condition for temperature (left, right, bottom, top). If 0, the boundary condition is insulation. `materials_path` is absolute path of the materials database. If false, then the materials database is the standard heatrapy database. """ # check the validity of inputs materials = tuple(materials) objects_length = tuple(objects_length) boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(materials, tuple) cond03 = isinstance(number_objects, int) cond04 = isinstance(objects_length, tuple) cond05 = isinstance(dx, int) or isinstance(dx, float) cond06 = isinstance(dy, int) or isinstance(dy, float) cond07 = isinstance(dt, int) or isinstance(dt, float) cond08 = isinstance(file_name, str) cond08 = cond08 or (file_name is None) cond09 = isinstance(boundaries, tuple) cond10 = isinstance(initial_state, bool) condition = cond01 and cond02 and cond03 and cond04 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 condition = condition and cond10 if not condition: raise ValueError # initial definitions self.objects = [] file_name_obj = None for i in range(number_objects): if file_name: file_name_obj = file_name + '_' + str(i) + '.txt' self.objects.append(Object(amb_temperature, material=materials[i], dx=dx, dy=dy, dt=dt, file_name=file_name_obj, boundaries=boundaries[i], Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path)) self.contacts = set() self.boundaries = boundaries self.dt = dt self.q1 = 0. self.q2 = 0.
System object initialization.
number_objects
is the integer number of thermal objects. materials
is the list of strings of all the used materials present in
material_path
. amb_temperature
is the ambient temperature of the
whole system. object_length
is the list of thermal object lengths
(tuple of spacial steps). dx
and dy
are the space steps along the
x- and y-axis, respectively. dt is the time step. file_name
is the
file name where the temperature is saved. boundaries
is a list of
four entries that define the boundary condition for temperature (left,
right, bottom, top). If 0, the boundary condition is insulation.
materials_path
is absolute path of the materials database. If false,
then the materials database is the standard heatrapy database.
View Source
def contact_filter(self, object_id): """Filter thermal contacts. Filter thermal contacts by `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) if not condition: raise ValueError value = object_id filtered = [x for x in self.contacts if (x[0][0] == value or x[1][0] == value)] return set(filtered)
Filter thermal contacts.
Filter thermal contacts by object_id
.
View Source
def contact_add(self, contact): """Add contact to self.contacts. `contact` is a thermal contact tuple of length 3, where the first and second entries correspond to tuples of the thermal objects and points (object_id, (x,y)), and the third entry is the heat transfer coefficient. """ # check the validity of inputs if isinstance(contact, tuple): if len(contact) == 3: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.contacts.add(contact)
Add contact to self.contacts.
contact
is a thermal contact tuple of length 3, where the first and
second entries correspond to tuples of the thermal objects and points
(object_id, (x,y)), and the third entry is the heat transfer
coefficient.
View Source
def contact_remove(self, object_one, object_two): """Contact removal. Removes all contacts between `object_one` id and `object_two` id. """ # check the validity of inputs condition = isinstance(object_one, int) condition = condition and isinstance(object_two, int) if not condition: raise ValueError contact_list = list(self.contacts) for i in range(len(contact_list)): cond_1 = contact_list[i][0][0] == object_one cond_1 = cond_1 and contact_list[i][1][0] == object_two cond_2 = contact_list[i][0][0] == object_two cond_2 = cond_2 and contact_list[i][1][0] == object_one if cond_1 or cond_2: self.contacts.remove(contact_list[i])
Contact removal.
Removes all contacts between object_one
id and object_two
id.
View Source
def change_boundaries(self, object_id, boundaries): """Change boundaries. Changes the `boundaries` variable of `object_id`. """ # check the validity of inputs condition = isinstance(object_id, int) condition = condition and isinstance(boundaries, tuple) if condition: if len(boundaries) == 4: condition = True else: condition = False if not condition: raise ValueError self.objects[object_id].boundaries = boundaries
Change boundaries.
Changes the boundaries
variable of object_id
.
View Source
def compute(self, time_interval, write_interval, solver='explicit_k(x)', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, and writes into the `file_name` file every `write_interval` time steps. Two different solvers can be used: `'explicit_general'` and `'explicit_k(x)'`. If verbose = True, then the progress of the computation is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) cond3 = isinstance(solver, str) cond4 = isinstance(verbose, bool) condition = cond1 and cond2 and cond3 and cond4 if not condition: raise ValueError # number of time steps for the given time_interval nt = int(time_interval / self.dt) # number of time steps counting from the last writing process nw = 0 # computes for k in range(nt): for obj in self.objects: obj.Q0 = copy.copy(obj.Q0_ref) for contact in self.contacts: in1_x = int(contact[1][1][0]) in1_y = int(contact[1][1][1]) in2_x = int(contact[0][1][0]) in2_y = int(contact[0][1][1]) td1 = self.objects[contact[1][0]].temperature[in1_x][in1_y][0] td2 = self.objects[contact[0][0]].temperature[in2_x][in2_y][0] heat_contact_1 = contact[2] * (td1 - td2) heat_contact_2 = contact[2] * (td2 - td1) self.objects[contact[0][0]].Q0[in2_x][in2_y] = heat_contact_1 self.objects[contact[1][0]].Q0[in1_x][in2_y] = heat_contact_2 for obj in self.objects: obj.time_passed = obj.time_passed + obj.dt # defines the material properties for i in range(obj.size[0]): for j in range(obj.size[1]): if obj.state[i][j] is True: ind = obj.materials_index[i][j] obj.rho[i][j] = obj.materials[ind].rhoa( obj.temperature[i][j][0]) obj.Cp[i][j] = obj.materials[ind].cpa( obj.temperature[i][j][0]) obj.k[i][j] = obj.materials[ind].ka( obj.temperature[i][j][0]) if obj.state[i][j] is False: ind = obj.materials_index[i][j] obj.rho[i][j] = obj.materials[ind].rho0( obj.temperature[i][j][0]) obj.Cp[i][j] = obj.materials[ind].cp0( obj.temperature[i][j][0]) obj.k[i][j] = obj.materials[ind].k0( obj.temperature[i][j][0]) # explicit k constant if solver == 'explicit_general': temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) obj.temperature, obj.lheat = solvers.explicit_general(obj) temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) # explicit k constant if solver == 'explicit_k(x)': temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) obj.temperature, obj.lheat = solvers.explicit_k(obj) temp = [] for i in range(obj.size[0]): temp.append([]) for j in range(obj.size[1]): temp[-1].append(obj.temperature[i][j][0]) if obj.file_name: if nw + 1 == write_interval or k == 0 or k == nt - 1: line = '%f' % obj.time_passed for i in range(obj.size[0]): for j in range(obj.size[1]): new_line = ',%f' % obj.temperature[i][j][1] line = line + new_line f = open(obj.file_name, 'a') f.write(line+'\n') f.close() if nw == write_interval: nw = 0 if verbose: print('progress:', int(100*k/nt), '%', end='\r') else: nw = nw + 1 if verbose: print('Finished simulation')
Compute the thermal process.
Computes the system for time_interval
, and writes into the
file_name
file every write_interval
time steps. Two different
solvers can be used: 'explicit_general'
and 'explicit_k(x)'
. If
verbose = True, then the progress of the computation is shown.
View Source
class SingleObject: """Single_object class. This class solves numerically the two-dimensional heat conduction equation. """ def __init__(self, amb_temperature, material='Cu', dx=0.01, dy=0.01, dt=0.1, size=(10, 10), file_name=None, boundaries=(0, 0, 0, 0), Q=[], Q0=[], initial_state=False, materials_path=False, draw=['temperature', 'materials'], draw_scale=None): """Object initialization. `amb_temperature` is the ambient temperature of the whole system. `materials` is the background material present in `material_path`. `dx`, `dy` are the space steps along the x- and y-axis, respectively. `dt` is the time step. `file_name` is the file name where the temperature is saved. `boundaries` is a list of four entries that define the boundary condition for temperature (left, right, bottom, top). 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 live plotting can be performed for `temperature`, `materials`, `state`, `Q` and `Q0`. 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. `Q` is a list of fixed heat source coefficient and `Q0` is a list of temperature dependent heat source coefficient. """ boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(material, str) cond05 = isinstance(dx, int) or isinstance(dx, float) cond06 = isinstance(dy, int) or isinstance(dy, float) cond07 = isinstance(dt, int) or isinstance(dt, float) cond08 = isinstance(file_name, str) cond08 = cond08 or (file_name is None) cond09 = isinstance(boundaries, tuple) cond10 = isinstance(initial_state, bool) cond11 = isinstance(Q, list) cond12 = isinstance(Q0, list) cond13 = isinstance(draw, list) cond14 = isinstance(draw_scale, list) or isinstance(draw_scale, tuple) cond14 = cond14 or draw_scale is None condition = cond01 and cond02 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 condition = condition and cond10 and cond11 and cond12 and cond13 condition = condition and cond14 if not condition: raise ValueError self.materials_path = materials_path self.time_passed = 0. self.size = size self.dt = dt self.dx = dx self.dy = dy self.object = Object(amb_temperature, material=material, dx=dx, dy=dy, dt=dt, size=size, file_name=file_name, boundaries=boundaries, Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path) # Initializes plotting self.draw = draw self.draw_scale = draw_scale if self.draw: cmap = matplotlib.cm.RdBu name = 'my_cmap_r' reverse = [] k = [] for key in cmap._segmentdata: k.append(key) channel = cmap._segmentdata[key] data = [] for t in channel: data.append((1-t[0], t[2], t[1])) reverse.append(sorted(data)) linear = dict(zip(k, reverse)) my_cmap_r = matplotlib.colors.LinearSegmentedColormap(name, linear) cmap_r = my_cmap_r for drawing in self.draw: if drawing == 'temperature': self.figure = plt.figure() self.ax = self.figure.add_subplot(111) temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) temp = np.array(temp) extent = [0, size[0]*dx, 0, size[1]*dy] self.im = self.ax.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap=cmap_r, extent=extent, origin='lower', interpolation='hamming') else: temp = np.array(temp) extent = [0, size[0]*dx, 0, size[1]*dy] self.im = self.ax.imshow(np.transpose(temp), vmax=self.draw_scale[0], vmin=self.draw_scale[1], cmap=cmap_r, extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax.figure.colorbar(self.im, ax=self.ax, **cbar_kw) self.ax.set_title('Temperature (K)') self.ax.set_xlabel('x axis (m)') self.ax.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'state': self.figure_state = plt.figure() self.ax_state = self.figure_state.add_subplot(111) vmax = 1 vmin = 0 state = np.array(self.object.state) cmap = plt.get_cmap("gray", 2) extent = [0, size[0]*dx, 0, size[1]*dy] self.im_state = self.ax_state.imshow(np.transpose(state), vmax=1.5, vmin=-0.5, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" qrates = np.array(['active', 'inactive']) value = np.linspace(0, 1, 2) norm = matplotlib.colors.BoundaryNorm(value, 1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) cbar_stat = self.ax_state.figure.colorbar(self.im_state, ax=self.ax_state, **cbar_kw) cbar_stat.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_state.set_title('State') self.ax_state.set_xlabel('x axis (m)') self.ax_state.set_ylabel('y axis (m)') plt.show(block=False) value = len(self.object.materials_name) > 1 if drawing == 'materials' and value: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 material_id = np.array(self.object.materials_index) cmap = plt.get_cmap("gray", vmax) extent = [0, size[0]*dx, 0, size[1]*dy] origin = 'lower' value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin=origin) cbar_kw = {} cbarlabel = "" qrates = np.array(self.object.materials_name) value_1 = np.linspace(0, 1, len(self.object.materials)) value_2 = len(self.object.materials)-1 norm = matplotlib.colors.BoundaryNorm(value_1, value_2) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) value_1 = self.im_materials value_2 = self.ax_materials cbar_m = self.ax_materials.figure.colorbar(value_1, ax=value_2, **cbar_kw) cbar_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'Q': self.figure_Q = plt.figure() self.ax_Q = self.figure_Q.add_subplot(111) temp = np.array(self.object.Q) vmax = 1 vmin = 0 extent = [0, size[0]*dx, 0, size[1]*dy] self.im_Q = self.ax_Q.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q.figure.colorbar(self.im_Q, ax=self.ax_Q, **cbar_kw) self.ax_Q.set_title('Q (W/m²)') self.ax_Q.set_xlabel('x axis (m)') self.ax_Q.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'Q0': self.figure_Q0 = plt.figure() self.ax_Q0 = self.figure_Q0.add_subplot(111) temp = np.array(self.object.Q0) vmax = 1 vmin = 0 extent = [0, size[0]*dx, 0, size[1]*dy] self.im_Q0 = self.ax_Q0.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q0.figure.colorbar(self.im_Q0, ax=self.ax_Q0, **cbar_kw) self.ax_Q0.set_title('Q0 (W/m²)') self.ax_Q0.set_xlabel('x axis (m)') self.ax_Q0.set_ylabel('y axis (m)') plt.show(block=False) def show_figure(self, figure_type): """Plotting. Initializes a specific plotting. `figure_type` is a string identifying the plotting. In this version live plotting can be performed for `temperature`, `materials`, `state`, `Q` and `Q0`. """ # check the validity of inputs condition = isinstance(figure_type, str) if not condition: raise ValueError if figure_type not in self.draw: self.draw.append(figure_type) if figure_type == 'temperature': self.figure = plt.figure() self.ax = self.figure.add_subplot(111) temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) temp = np.array(temp) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im = self.ax.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='jet', extent=extent, origin='lower', interpolation='hamming') else: temp = np.array(temp) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im = self.ax.imshow(np.transpose(temp), vmax=self.draw_scale[0], vmin=self.draw_scale[1], cmap='jet', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbarlabel = "temperature (K)" cbar = self.ax.figure.colorbar(self.im, ax=self.ax, **cbar_kw) cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax.set_title('Temperature') self.ax.set_xlabel('x axis (m)') self.ax.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'state': self.figure_state = plt.figure() self.ax_state = self.figure_state.add_subplot(111) vmax = 1 vmin = 0 state = np.array(self.object.state) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_state = self.ax_state.imshow(np.transpose(state), vmax=1.5, vmin=-0.5, cmap=plt.get_cmap("gray", 2), extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" # "state" qrates = np.array(['active', 'inactive']) norm = matplotlib.colors.BoundaryNorm(np.linspace(0, 1, 2), 1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) cbar_state = self.ax_state.figure.colorbar(self.im_state, ax=self.ax_state, **cbar_kw) cbar_state.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_state.set_title('State') self.ax_state.set_xlabel('x axis (m)') self.ax_state.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'materials' and len(self.object.materials_name) > 1: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 material_id = np.array(self.object.materials_index) cmap = plt.get_cmap("PiYG", vmax+1) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" materials_name_list = copy.deepcopy(self.object.materials_name) materials_name_list.reverse() qrates = np.array(materials_name_list) value = np.linspace(0, len(self.object.materials) - 1, len(self.object.materials)) norm = matplotlib.colors.BoundaryNorm(value, len(self.object.materials)-1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(0, len(self.object.materials)+1), format=fmt) cbar_m = self.ax_materials.figure.colorbar(self.im_materials, ax=self.ax_materials, **cbar_kw) cbar_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False) print(self.object.materials_name) if figure_type == 'materials' and len(self.object.materials_name) == 1: print(self.object.materials_name) if figure_type == 'Q': self.figure_Q = plt.figure() self.ax_Q = self.figure_Q.add_subplot(111) temp = np.array(self.object.Q) vmax = 1 vmin = 0 extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_Q = self.ax_Q.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q.figure.colorbar(self.im_Q, ax=self.ax_Q, **cbar_kw) self.ax_Q.set_title('Q (W/m²)') self.ax_Q.set_xlabel('x axis (m)') self.ax_Q.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'Q0': self.figure_Q0 = plt.figure() self.ax_Q0 = self.figure_Q0.add_subplot(111) temp = np.array(self.object.Q0) vmax = 1 vmin = 0 extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_Q0 = self.ax_Q0.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q0.figure.colorbar(self.im_Q0, ax=self.ax_Q0, **cbar_kw) self.ax_Q0.set_title('Q0 (W/m²)') self.ax_Q0.set_xlabel('x axis (m)') self.ax_Q0.set_ylabel('y axis (m)') plt.show(block=False) def activate(self, initial_point, final_point, shape='square'): """Activation of the material. Activates a given piece of material. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): condition = len(initial_point) == 2 condition = condition and len(final_point) == 2 else: condition = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False if not condition: raise ValueError self.object.activate(initial_point=initial_point, final_point=final_point, shape=shape) if self.draw: for drawing in self.draw: if drawing == 'temperature': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() if drawing == 'state': self.im_state.set_array(np.transpose(self.object.state)) self.figure_state.canvas.draw() def deactivate(self, initial_point, final_point, shape='square'): """Deactivation of the material. Deactivates a given piece of material. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): condition = len(initial_point) == 2 condition = condition and len(final_point) == 2 else: condition = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False if not condition: raise ValueError self.object.deactivate(initial_point=initial_point, final_point=final_point, shape=shape) if self.draw: for drawing in self.draw: if drawing == 'temperature': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() if drawing == 'state': self.im_state.set_array(np.transpose(self.object.state)) self.figure_state.canvas.draw() def change_boundaries(self, boundaries): """Boundary change. Changes the `boundaries` variable. """ # check the validity of inputs if isinstance(boundaries, tuple): if len(boundaries) == 4: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.object.boundaries = boundaries def change_material(self, shape, material, initial_point, length, state=False): """Material change. Changes the material of a given piece of the background material. If `shape` is `'square'`, then the initial_point is the tuple (x,y) of the bottom left point and the `length` is the tuple (x,y) of the length. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `length` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(length, tuple): condition = len(initial_point) == 2 condition = condition and len(length) == 2 else: condition = False elif shape == 'circle': value = isinstance(length, int) value = value or isinstance(length, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False condition = condition and isinstance(state, bool) if not condition: raise ValueError if shape == 'square': self.object.square(material=material, initial_point=initial_point, length=length, state=state, materials_path=self.materials_path) if shape == 'circle': self.object.circle(material=material, initial_point=initial_point, radius=length, state=state, materials_path=self.materials_path) try: plt.close(self.figure_materials) except: pass if self.draw: if 'materials' in self.draw: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 cmap = plt.get_cmap("PiYG", vmax+1) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] material_id = np.array(self.object.materials_index) value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" materials_name_list = copy.deepcopy(self.object.materials_name) materials_name_list.reverse() qrates = np.array(materials_name_list) value_1 = np.linspace(0, len(self.object.materials)-1, len(self.object.materials)) value_2 = len(self.object.materials)-1 norm = matplotlib.colors.BoundaryNorm(value_1, value_2) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) value = np.arange(0, len(self.object.materials)+1) cbar_kw = dict(ticks=value, format=fmt) cba_m = self.ax_materials.figure.colorbar(self.im_materials, ax=self.ax_materials, **cbar_kw) cba_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False) def change_power(self, shape, power_type, initial_point, final_point, power): """Power change. Changes the power matrix of the thermal object. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the `shape` is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. `power` is the value of the power to add, and `power_type` is the type of power to be introduced, which has the value `'Q'` if it is temperature dependent and `'Q0'` if it is temperature independent. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): cond1 = len(initial_point) == 2 cond1 = cond1 and len(final_point) == 2 else: cond1 = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: cond1 = len(initial_point) == 2 else: cond1 = False else: cond1 = False 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 shape == 'square': self.object.power_add(initial_point, final_point, power, shape=shape, power_type=power_type) if shape == 'circle': self.object.power_add(initial_point, final_point, power, shape=shape, power_type=power_type) if self.draw: if 'Q' in self.draw and power_type == 'Q': self.im_Q.set_array(np.transpose(self.object.Q)) vmax = max(max(self.object.Q, key=max)) vmin = min(min(self.object.Q, key=min)) self.im_Q.set_clim(vmin=vmin) self.im_Q.set_clim(vmax=vmax) self.figure_Q.canvas.draw() if 'Q0' in self.draw and power_type == 'Q0': self.im_Q0.set_array(np.transpose(self.object.Q0)) vmax = max(max(self.object.Q0, key=max)) vmin = min(min(self.object.Q0, key=min)) self.im_Q0.set_clim(vmin=vmin) self.im_Q0.set_clim(vmax=vmax) self.figure_Q0.canvas.draw() def compute(self, time_interval, write_interval, solver='explicit_general', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, and writes into the `file_name` file every `write_interval` time steps. Two different solvers can be used: `'explicit_general'` and `'explicit_k(x)'`. If verbose = True, then the progress of the computation is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) all_solvers = ['explicit_general', 'explicit_k(x)'] cond3 = isinstance(solver, str) and solver in all_solvers 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 k in range(nt): # updates the time_passed self.time_passed = self.time_passed + self.object.dt # defines the material properties accoring to the state list for i in range(self.object.size[0]): for j in range(self.object.size[1]): if self.object.state[i][j] is True: ix = self.object.materials_index[i][j] self.object.rho[i][j] = self.object.materials[ix].rhoa( self.object.temperature[i][j][0]) self.object.Cp[i][j] = self.object.materials[ix].cpa( self.object.temperature[i][j][0]) self.object.k[i][j] = self.object.materials[ix].ka( self.object.temperature[i][j][0]) if self.object.state[i][j] is False: ix = self.object.materials_index[i][j] self.object.rho[i][j] = self.object.materials[ix].rho0( self.object.temperature[i][j][0]) self.object.Cp[i][j] = self.object.materials[ix].cp0( self.object.temperature[i][j][0]) self.object.k[i][j] = self.object.materials[ix].k0( self.object.temperature[i][j][0]) # SOLVERS # explicit k constant if solver == 'explicit_general': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) value = solvers.explicit_general(self.object) self.object.temperature, self.object.lheat = value temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) # explicit k constant if solver == 'explicit_k(x)': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) value = solvers.explicit_k(self.object) self.object.temperature, self.object.lheat = value temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) nw = nw + 1 if self.object.file_name: if nw + 1 == write_interval or k == 0 or k == nt - 1: line = '%f' % self.time_passed for i in range(self.object.size[0]): for j in range(self.object.size[1]): new_line = ',%f' % self.object.temperature[i][j][1] line = line + new_line f = open(self.object.file_name, 'a') f.write(line+'\n') f.close() if self.draw: for drawing in self.draw: if drawing == 'temperature': if nw + 1 == write_interval or k == 0 or k == nt - 1: temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): value = self.object.temperature[i][j][0] temp[-1].append(value) try: self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() except: pass if nw == write_interval: nw = 0 if verbose: print('pogress:', int(100*k/nt), '%', end="\r") if verbose: print('Finished simulation')
Single_object class.
This class solves numerically the two-dimensional heat conduction equation.
View Source
def __init__(self, amb_temperature, material='Cu', dx=0.01, dy=0.01, dt=0.1, size=(10, 10), file_name=None, boundaries=(0, 0, 0, 0), Q=[], Q0=[], initial_state=False, materials_path=False, draw=['temperature', 'materials'], draw_scale=None): """Object initialization. `amb_temperature` is the ambient temperature of the whole system. `materials` is the background material present in `material_path`. `dx`, `dy` are the space steps along the x- and y-axis, respectively. `dt` is the time step. `file_name` is the file name where the temperature is saved. `boundaries` is a list of four entries that define the boundary condition for temperature (left, right, bottom, top). 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 live plotting can be performed for `temperature`, `materials`, `state`, `Q` and `Q0`. 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. `Q` is a list of fixed heat source coefficient and `Q0` is a list of temperature dependent heat source coefficient. """ boundaries = tuple(boundaries) cond01 = isinstance(amb_temperature, float) cond01 = cond01 or isinstance(amb_temperature, int) cond02 = isinstance(material, str) cond05 = isinstance(dx, int) or isinstance(dx, float) cond06 = isinstance(dy, int) or isinstance(dy, float) cond07 = isinstance(dt, int) or isinstance(dt, float) cond08 = isinstance(file_name, str) cond08 = cond08 or (file_name is None) cond09 = isinstance(boundaries, tuple) cond10 = isinstance(initial_state, bool) cond11 = isinstance(Q, list) cond12 = isinstance(Q0, list) cond13 = isinstance(draw, list) cond14 = isinstance(draw_scale, list) or isinstance(draw_scale, tuple) cond14 = cond14 or draw_scale is None condition = cond01 and cond02 and cond05 condition = condition and cond06 and cond07 and cond08 and cond09 condition = condition and cond10 and cond11 and cond12 and cond13 condition = condition and cond14 if not condition: raise ValueError self.materials_path = materials_path self.time_passed = 0. self.size = size self.dt = dt self.dx = dx self.dy = dy self.object = Object(amb_temperature, material=material, dx=dx, dy=dy, dt=dt, size=size, file_name=file_name, boundaries=boundaries, Q=[], Q0=[], initial_state=initial_state, materials_path=materials_path) # Initializes plotting self.draw = draw self.draw_scale = draw_scale if self.draw: cmap = matplotlib.cm.RdBu name = 'my_cmap_r' reverse = [] k = [] for key in cmap._segmentdata: k.append(key) channel = cmap._segmentdata[key] data = [] for t in channel: data.append((1-t[0], t[2], t[1])) reverse.append(sorted(data)) linear = dict(zip(k, reverse)) my_cmap_r = matplotlib.colors.LinearSegmentedColormap(name, linear) cmap_r = my_cmap_r for drawing in self.draw: if drawing == 'temperature': self.figure = plt.figure() self.ax = self.figure.add_subplot(111) temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) temp = np.array(temp) extent = [0, size[0]*dx, 0, size[1]*dy] self.im = self.ax.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap=cmap_r, extent=extent, origin='lower', interpolation='hamming') else: temp = np.array(temp) extent = [0, size[0]*dx, 0, size[1]*dy] self.im = self.ax.imshow(np.transpose(temp), vmax=self.draw_scale[0], vmin=self.draw_scale[1], cmap=cmap_r, extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax.figure.colorbar(self.im, ax=self.ax, **cbar_kw) self.ax.set_title('Temperature (K)') self.ax.set_xlabel('x axis (m)') self.ax.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'state': self.figure_state = plt.figure() self.ax_state = self.figure_state.add_subplot(111) vmax = 1 vmin = 0 state = np.array(self.object.state) cmap = plt.get_cmap("gray", 2) extent = [0, size[0]*dx, 0, size[1]*dy] self.im_state = self.ax_state.imshow(np.transpose(state), vmax=1.5, vmin=-0.5, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" qrates = np.array(['active', 'inactive']) value = np.linspace(0, 1, 2) norm = matplotlib.colors.BoundaryNorm(value, 1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) cbar_stat = self.ax_state.figure.colorbar(self.im_state, ax=self.ax_state, **cbar_kw) cbar_stat.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_state.set_title('State') self.ax_state.set_xlabel('x axis (m)') self.ax_state.set_ylabel('y axis (m)') plt.show(block=False) value = len(self.object.materials_name) > 1 if drawing == 'materials' and value: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 material_id = np.array(self.object.materials_index) cmap = plt.get_cmap("gray", vmax) extent = [0, size[0]*dx, 0, size[1]*dy] origin = 'lower' value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin=origin) cbar_kw = {} cbarlabel = "" qrates = np.array(self.object.materials_name) value_1 = np.linspace(0, 1, len(self.object.materials)) value_2 = len(self.object.materials)-1 norm = matplotlib.colors.BoundaryNorm(value_1, value_2) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) value_1 = self.im_materials value_2 = self.ax_materials cbar_m = self.ax_materials.figure.colorbar(value_1, ax=value_2, **cbar_kw) cbar_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'Q': self.figure_Q = plt.figure() self.ax_Q = self.figure_Q.add_subplot(111) temp = np.array(self.object.Q) vmax = 1 vmin = 0 extent = [0, size[0]*dx, 0, size[1]*dy] self.im_Q = self.ax_Q.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q.figure.colorbar(self.im_Q, ax=self.ax_Q, **cbar_kw) self.ax_Q.set_title('Q (W/m²)') self.ax_Q.set_xlabel('x axis (m)') self.ax_Q.set_ylabel('y axis (m)') plt.show(block=False) if drawing == 'Q0': self.figure_Q0 = plt.figure() self.ax_Q0 = self.figure_Q0.add_subplot(111) temp = np.array(self.object.Q0) vmax = 1 vmin = 0 extent = [0, size[0]*dx, 0, size[1]*dy] self.im_Q0 = self.ax_Q0.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q0.figure.colorbar(self.im_Q0, ax=self.ax_Q0, **cbar_kw) self.ax_Q0.set_title('Q0 (W/m²)') self.ax_Q0.set_xlabel('x axis (m)') self.ax_Q0.set_ylabel('y axis (m)') plt.show(block=False)
Object initialization.
amb_temperature
is the ambient temperature of the whole system.
materials
is the background material present in material_path
.
dx
, dy
are the space steps along the x- and y-axis, respectively.
dt
is the time step. file_name
is the file name where the
temperature is saved. boundaries
is a list of four entries that
define the boundary condition for temperature (left, right, bottom,
top). 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 live plotting can be performed for
temperature
, materials
, state
, Q
and Q0
. 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. Q
is a list of fixed heat source
coefficient and Q0
is a list of temperature dependent heat source
coefficient.
View Source
def show_figure(self, figure_type): """Plotting. Initializes a specific plotting. `figure_type` is a string identifying the plotting. In this version live plotting can be performed for `temperature`, `materials`, `state`, `Q` and `Q0`. """ # check the validity of inputs condition = isinstance(figure_type, str) if not condition: raise ValueError if figure_type not in self.draw: self.draw.append(figure_type) if figure_type == 'temperature': self.figure = plt.figure() self.ax = self.figure.add_subplot(111) temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) temp = np.array(temp) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im = self.ax.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='jet', extent=extent, origin='lower', interpolation='hamming') else: temp = np.array(temp) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im = self.ax.imshow(np.transpose(temp), vmax=self.draw_scale[0], vmin=self.draw_scale[1], cmap='jet', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbarlabel = "temperature (K)" cbar = self.ax.figure.colorbar(self.im, ax=self.ax, **cbar_kw) cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax.set_title('Temperature') self.ax.set_xlabel('x axis (m)') self.ax.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'state': self.figure_state = plt.figure() self.ax_state = self.figure_state.add_subplot(111) vmax = 1 vmin = 0 state = np.array(self.object.state) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_state = self.ax_state.imshow(np.transpose(state), vmax=1.5, vmin=-0.5, cmap=plt.get_cmap("gray", 2), extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" # "state" qrates = np.array(['active', 'inactive']) norm = matplotlib.colors.BoundaryNorm(np.linspace(0, 1, 2), 1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(-3, 4), format=fmt) cbar_state = self.ax_state.figure.colorbar(self.im_state, ax=self.ax_state, **cbar_kw) cbar_state.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_state.set_title('State') self.ax_state.set_xlabel('x axis (m)') self.ax_state.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'materials' and len(self.object.materials_name) > 1: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 material_id = np.array(self.object.materials_index) cmap = plt.get_cmap("PiYG", vmax+1) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" materials_name_list = copy.deepcopy(self.object.materials_name) materials_name_list.reverse() qrates = np.array(materials_name_list) value = np.linspace(0, len(self.object.materials) - 1, len(self.object.materials)) norm = matplotlib.colors.BoundaryNorm(value, len(self.object.materials)-1) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) cbar_kw = dict(ticks=np.arange(0, len(self.object.materials)+1), format=fmt) cbar_m = self.ax_materials.figure.colorbar(self.im_materials, ax=self.ax_materials, **cbar_kw) cbar_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False) print(self.object.materials_name) if figure_type == 'materials' and len(self.object.materials_name) == 1: print(self.object.materials_name) if figure_type == 'Q': self.figure_Q = plt.figure() self.ax_Q = self.figure_Q.add_subplot(111) temp = np.array(self.object.Q) vmax = 1 vmin = 0 extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_Q = self.ax_Q.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q.figure.colorbar(self.im_Q, ax=self.ax_Q, **cbar_kw) self.ax_Q.set_title('Q (W/m²)') self.ax_Q.set_xlabel('x axis (m)') self.ax_Q.set_ylabel('y axis (m)') plt.show(block=False) if figure_type == 'Q0': self.figure_Q0 = plt.figure() self.ax_Q0 = self.figure_Q0.add_subplot(111) temp = np.array(self.object.Q0) vmax = 1 vmin = 0 extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] self.im_Q0 = self.ax_Q0.imshow(np.transpose(temp), vmax=vmax, vmin=vmin, cmap='inferno', extent=extent, origin='lower', interpolation='hamming') cbar_kw = {} cbar = self.ax_Q0.figure.colorbar(self.im_Q0, ax=self.ax_Q0, **cbar_kw) self.ax_Q0.set_title('Q0 (W/m²)') self.ax_Q0.set_xlabel('x axis (m)') self.ax_Q0.set_ylabel('y axis (m)') plt.show(block=False)
Plotting.
Initializes a specific plotting. figure_type
is a string identifying
the plotting. In this version live plotting can be performed for
temperature
, materials
, state
, Q
and Q0
.
View Source
def activate(self, initial_point, final_point, shape='square'): """Activation of the material. Activates a given piece of material. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): condition = len(initial_point) == 2 condition = condition and len(final_point) == 2 else: condition = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False if not condition: raise ValueError self.object.activate(initial_point=initial_point, final_point=final_point, shape=shape) if self.draw: for drawing in self.draw: if drawing == 'temperature': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() if drawing == 'state': self.im_state.set_array(np.transpose(self.object.state)) self.figure_state.canvas.draw()
Activation of the material.
Activates a given piece of material. If shape
is 'square'
, then the
initial_point
is the tuple (x,y) of the bottom left point and the
final_point
is the tuple (x,y) of the top right point. If the shape
is 'circle'
, the initial_point
is the tuple (x,y) of the center of
the circle and final_point
is its radius.
View Source
def deactivate(self, initial_point, final_point, shape='square'): """Deactivation of the material. Deactivates a given piece of material. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): condition = len(initial_point) == 2 condition = condition and len(final_point) == 2 else: condition = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False if not condition: raise ValueError self.object.deactivate(initial_point=initial_point, final_point=final_point, shape=shape) if self.draw: for drawing in self.draw: if drawing == 'temperature': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() if drawing == 'state': self.im_state.set_array(np.transpose(self.object.state)) self.figure_state.canvas.draw()
Deactivation of the material.
Deactivates a given piece of material. If shape
is 'square'
, then
the initial_point
is the tuple (x,y) of the bottom left point and the
final_point
is the tuple (x,y) of the top right point. If the shape
is 'circle'
, the initial_point
is the tuple (x,y) of the center of
the circle and final_point
is its radius.
View Source
def change_boundaries(self, boundaries): """Boundary change. Changes the `boundaries` variable. """ # check the validity of inputs if isinstance(boundaries, tuple): if len(boundaries) == 4: condition = True else: condition = False else: condition = False if not condition: raise ValueError self.object.boundaries = boundaries
Boundary change.
Changes the boundaries
variable.
View Source
def change_material(self, shape, material, initial_point, length, state=False): """Material change. Changes the material of a given piece of the background material. If `shape` is `'square'`, then the initial_point is the tuple (x,y) of the bottom left point and the `length` is the tuple (x,y) of the length. If the shape is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `length` is its radius. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(length, tuple): condition = len(initial_point) == 2 condition = condition and len(length) == 2 else: condition = False elif shape == 'circle': value = isinstance(length, int) value = value or isinstance(length, float) value = value and isinstance(initial_point, tuple) if value: condition = len(initial_point) == 2 else: condition = False else: condition = False else: condition = False condition = condition and isinstance(state, bool) if not condition: raise ValueError if shape == 'square': self.object.square(material=material, initial_point=initial_point, length=length, state=state, materials_path=self.materials_path) if shape == 'circle': self.object.circle(material=material, initial_point=initial_point, radius=length, state=state, materials_path=self.materials_path) try: plt.close(self.figure_materials) except: pass if self.draw: if 'materials' in self.draw: self.figure_materials = plt.figure() self.ax_materials = self.figure_materials.add_subplot(111) vmax = len(self.object.materials)-1 vmin = 0 cmap = plt.get_cmap("PiYG", vmax+1) extent = [0, self.size[0]*self.dx, 0, self.size[1]*self.dy] material_id = np.array(self.object.materials_index) value = np.transpose(material_id) self.im_materials = self.ax_materials.imshow(value, vmax=vmax, vmin=vmin, cmap=cmap, extent=extent, origin='lower') cbar_kw = {} cbarlabel = "" materials_name_list = copy.deepcopy(self.object.materials_name) materials_name_list.reverse() qrates = np.array(materials_name_list) value_1 = np.linspace(0, len(self.object.materials)-1, len(self.object.materials)) value_2 = len(self.object.materials)-1 norm = matplotlib.colors.BoundaryNorm(value_1, value_2) func = lambda x, pos: qrates[::-1][norm(x)] fmt = matplotlib.ticker.FuncFormatter(func) value = np.arange(0, len(self.object.materials)+1) cbar_kw = dict(ticks=value, format=fmt) cba_m = self.ax_materials.figure.colorbar(self.im_materials, ax=self.ax_materials, **cbar_kw) cba_m.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom") self.ax_materials.set_title('Materials') self.ax_materials.set_xlabel('x axis (m)') self.ax_materials.set_ylabel('y axis (m)') plt.show(block=False)
Material change.
Changes the material of a given piece of the background material. If
shape
is 'square'
, then the initial_point is the tuple (x,y) of the
bottom left point and the length
is the tuple (x,y) of the length. If
the shape is 'circle'
, the initial_point
is the tuple (x,y) of the
center of the circle and length
is its radius.
View Source
def change_power(self, shape, power_type, initial_point, final_point, power): """Power change. Changes the power matrix of the thermal object. If `shape` is `'square'`, then the `initial_point` is the tuple (x,y) of the bottom left point and the `final_point` is the tuple (x,y) of the top right point. If the `shape` is `'circle'`, the `initial_point` is the tuple (x,y) of the center of the circle and `final_point` is its radius. `power` is the value of the power to add, and `power_type` is the type of power to be introduced, which has the value `'Q'` if it is temperature dependent and `'Q0'` if it is temperature independent. """ # check the validity of inputs if isinstance(shape, str): if shape == 'square': value = isinstance(initial_point, tuple) if value and isinstance(final_point, tuple): cond1 = len(initial_point) == 2 cond1 = cond1 and len(final_point) == 2 else: cond1 = False elif shape == 'circle': value = isinstance(final_point, int) value = value or isinstance(final_point, float) value = value and isinstance(initial_point, tuple) if value: cond1 = len(initial_point) == 2 else: cond1 = False else: cond1 = False 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 shape == 'square': self.object.power_add(initial_point, final_point, power, shape=shape, power_type=power_type) if shape == 'circle': self.object.power_add(initial_point, final_point, power, shape=shape, power_type=power_type) if self.draw: if 'Q' in self.draw and power_type == 'Q': self.im_Q.set_array(np.transpose(self.object.Q)) vmax = max(max(self.object.Q, key=max)) vmin = min(min(self.object.Q, key=min)) self.im_Q.set_clim(vmin=vmin) self.im_Q.set_clim(vmax=vmax) self.figure_Q.canvas.draw() if 'Q0' in self.draw and power_type == 'Q0': self.im_Q0.set_array(np.transpose(self.object.Q0)) vmax = max(max(self.object.Q0, key=max)) vmin = min(min(self.object.Q0, key=min)) self.im_Q0.set_clim(vmin=vmin) self.im_Q0.set_clim(vmax=vmax) self.figure_Q0.canvas.draw()
Power change.
Changes the power matrix of the thermal object. If shape
is
'square'
, then the initial_point
is the tuple (x,y) of the bottom
left point and the final_point
is the tuple (x,y) of the top right
point. If the shape
is 'circle'
, the initial_point
is the tuple
(x,y) of the center of the circle and final_point
is its radius.
power
is the value of the power to add, and power_type
is the type
of power to be introduced, which has the value 'Q'
if it is
temperature dependent and 'Q0'
if it is temperature independent.
View Source
def compute(self, time_interval, write_interval, solver='explicit_general', verbose=True): """Compute the thermal process. Computes the system for `time_interval`, and writes into the `file_name` file every `write_interval` time steps. Two different solvers can be used: `'explicit_general'` and `'explicit_k(x)'`. If verbose = True, then the progress of the computation is shown. """ # check the validity of inputs cond1 = isinstance(time_interval, float) cond1 = cond1 or isinstance(time_interval, int) cond2 = isinstance(write_interval, int) all_solvers = ['explicit_general', 'explicit_k(x)'] cond3 = isinstance(solver, str) and solver in all_solvers 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 k in range(nt): # updates the time_passed self.time_passed = self.time_passed + self.object.dt # defines the material properties accoring to the state list for i in range(self.object.size[0]): for j in range(self.object.size[1]): if self.object.state[i][j] is True: ix = self.object.materials_index[i][j] self.object.rho[i][j] = self.object.materials[ix].rhoa( self.object.temperature[i][j][0]) self.object.Cp[i][j] = self.object.materials[ix].cpa( self.object.temperature[i][j][0]) self.object.k[i][j] = self.object.materials[ix].ka( self.object.temperature[i][j][0]) if self.object.state[i][j] is False: ix = self.object.materials_index[i][j] self.object.rho[i][j] = self.object.materials[ix].rho0( self.object.temperature[i][j][0]) self.object.Cp[i][j] = self.object.materials[ix].cp0( self.object.temperature[i][j][0]) self.object.k[i][j] = self.object.materials[ix].k0( self.object.temperature[i][j][0]) # SOLVERS # explicit k constant if solver == 'explicit_general': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) value = solvers.explicit_general(self.object) self.object.temperature, self.object.lheat = value temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) # explicit k constant if solver == 'explicit_k(x)': temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) value = solvers.explicit_k(self.object) self.object.temperature, self.object.lheat = value temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): temp[-1].append(self.object.temperature[i][j][0]) nw = nw + 1 if self.object.file_name: if nw + 1 == write_interval or k == 0 or k == nt - 1: line = '%f' % self.time_passed for i in range(self.object.size[0]): for j in range(self.object.size[1]): new_line = ',%f' % self.object.temperature[i][j][1] line = line + new_line f = open(self.object.file_name, 'a') f.write(line+'\n') f.close() if self.draw: for drawing in self.draw: if drawing == 'temperature': if nw + 1 == write_interval or k == 0 or k == nt - 1: temp = [] for i in range(self.object.size[0]): temp.append([]) for j in range(self.object.size[1]): value = self.object.temperature[i][j][0] temp[-1].append(value) try: self.im.set_array(np.transpose(temp)) if not self.draw_scale: vmax = max(max(temp, key=max)) vmin = min(min(temp, key=min)) self.im.set_clim(vmin=vmin) self.im.set_clim(vmax=vmax) self.figure.canvas.draw() except: pass if nw == write_interval: nw = 0 if verbose: print('pogress:', int(100*k/nt), '%', end="\r") if verbose: print('Finished simulation')
Compute the thermal process.
Computes the system for time_interval
, and writes into the
file_name
file every write_interval
time steps. Two different
solvers can be used: 'explicit_general'
and 'explicit_k(x)'
. If
verbose = True, then the progress of the computation is shown.