diff --git a/src/topupheat/pipes/system.py b/src/topupheat/pipes/system.py index 9fcd87e6c2806f5a2f3f19a4d7da144d6db00b07..e4a29756fdb864c6416c56db91071ef48b4b91ad 100644 --- a/src/topupheat/pipes/system.py +++ b/src/topupheat/pipes/system.py @@ -16,7 +16,7 @@ from ..common import flow #****************************************************************************** #****************************************************************************** -# TODO: implement effective_heat_transfer_rate +# TODO: implement effective_heat_transfer_rate (gross heat transfer minus losses) class SupplyReturnPipeSystem: @@ -618,28 +618,53 @@ class SupplyReturnPipeSystem: # 3) vector mode and size of list with u values does not match the number of options # 4) vector mode and the number of u values per list does not match the number of time intervals - if ((not self.vector_mode and - (type(specific_heat_transfer_coefficient) != type(temperature_surroundings) or - len(specific_heat_transfer_coefficient) != len(temperature_surroundings))) or - (self.vector_mode and len(specific_heat_transfer_coefficient) != 0 and - (len(specific_heat_transfer_coefficient) != self.number_options() or - (type(specific_heat_transfer_coefficient[0]) == type(temperature_surroundings) and - len(specific_heat_transfer_coefficient[0]) != len(temperature_surroundings))) - ) - ): - # all the aforementioned cases lead here - raise TypeError('Inconsistent inputs.') + if not self.vector_mode: + # non-vector mode + if type(specific_heat_transfer_coefficient) != type(temperature_surroundings): + # case 1 (mismatched input types) + raise TypeError('Inconsistent inputs.') + elif temporal_vector_mode and len(specific_heat_transfer_coefficient) != len(temperature_surroundings): + # case 2 (matching input types but wrong sizes) + raise TypeError('Inconsistent inputs.') + else: + # vector mode + if (type(specific_heat_transfer_coefficient) != tuple and + type(specific_heat_transfer_coefficient) != list): + # wrong type + raise TypeError('Inconsistent inputs.') + + # + if temporal_vector_mode: + # the inputs and the object have to match in terms of number of options + if len(specific_heat_transfer_coefficient) != self.number_options(): + # case 3 + raise TypeError('Inconsistent inputs.') + # the inputs have to match in terms of number of intervals + # multiple time intervals + _number_time_intervals = len(temperature_surroundings) + # check each list within the input + for u in specific_heat_transfer_coefficient: + if type(u) != list and type(u) != tuple and len(u) != _number_time_intervals: + # case 4 + raise TypeError('Inconsistent inputs.') + else: + # one time interval in vector mode: u has to be a list of numbers + for u in specific_heat_transfer_coefficient: + if not isinstance(u, Real): + # case 4 + raise TypeError('Inconsistent inputs.') + # all the inputs seem okay: compute if self.vector_mode: # vector mode: multiple options if temporal_vector_mode: + # multiple time steps out = [ [((st+rt)*0.5-ts)* - shtc* + shtc[i]* unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) - for ts in temperature_surroundings + (1 if self.losses_are_positive else -1) + for i, ts in enumerate(temperature_surroundings) ] for st, rt, shtc in zip( self.supply_temperature, @@ -653,8 +678,7 @@ class SupplyReturnPipeSystem: ((st+rt)*0.5-temperature_surroundings)* shtc* unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) + (1 if self.losses_are_positive else -1) for st, rt, shtc in zip( self.supply_temperature, self.return_temperature, @@ -669,8 +693,7 @@ class SupplyReturnPipeSystem: ((self.supply_temperature+self.return_temperature)*0.5-ts)* specific_heat_transfer_coefficient* unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) + (1 if self.losses_are_positive else -1) for ts in temperature_surroundings ] else: @@ -680,13 +703,12 @@ class SupplyReturnPipeSystem: temperature_surroundings)* specific_heat_transfer_coefficient* unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) + (1 if self.losses_are_positive else -1) ) return out # ************************************************************************* - # TODO: reconsider the implementation in vector mode + # TODO: implement using simplified_specific_heat_transfer_surroundings method def simplified_heat_transfer_surroundings( self, length: float or list, @@ -695,140 +717,140 @@ class SupplyReturnPipeSystem: time_interval_duration: float or list, unit_conversion_factor: float = 1.0 ): - - """ - Calculates the heat transfer with the surroundings using a constant - specific heat transfer coefficient. + raise NotImplementedError + # """ + # Calculates the heat transfer with the surroundings using a constant + # specific heat transfer coefficient. - Parameters - ---------- - length : float or list - The length of each segment. - specific_heat_transfer_coefficient : float or list - The heat transfer coefficient per unit length. - temperature_surroundings : float or list - The temperature of the surroundings. - time_interval_duration : float or list - The duration of each time step under consideration. - unit_conversion_factor : float, optional - A factor to adjust the final units. The default is 1.0. + # Parameters + # ---------- + # length : float or list + # The length of each segment. + # specific_heat_transfer_coefficient : float or list + # The heat transfer coefficient per unit length. + # temperature_surroundings : float or list + # The temperature of the surroundings. + # time_interval_duration : float or list + # The duration of each time step under consideration. + # unit_conversion_factor : float, optional + # A factor to adjust the final units. The default is 1.0. - Raises - ------ - TypeError - This error is raised if the combination of inputs is incompatible. + # Raises + # ------ + # TypeError + # This error is raised if the combination of inputs is incompatible. - Returns - ------- - out : float, list or list of lists - The heat transfer rate based on the inputs. If single inputs are - provided and a single option is under consideration, then a float - is returned. If there are multiple options and single inputs are - provided, then a list with one heat transfer rate per option is - returned. If there are multiple options and multiple inputs, then - a list of lists is returned: one list with one list per option, - each of which with one heat transfer rate per input. + # Returns + # ------- + # out : float, list or list of lists + # The heat transfer rate based on the inputs. If single inputs are + # provided and a single option is under consideration, then a float + # is returned. If there are multiple options and single inputs are + # provided, then a list with one heat transfer rate per option is + # returned. If there are multiple options and multiple inputs, then + # a list of lists is returned: one list with one list per option, + # each of which with one heat transfer rate per input. - """ + # """ - # length >> options - # specific_heat_transfer_coefficient >> options - # temperature_surroundings >> temporal - # determine if the inputs are for temporal vector/normal mode - if (isinstance(temperature_surroundings, Real) and - isinstance(time_interval_duration, Real)): - temporal_vector_mode = False - elif ((type(temperature_surroundings) == list or - type(temperature_surroundings) == tuple) and - (type(time_interval_duration) == list or - type(time_interval_duration) == tuple)): - temporal_vector_mode = True - else: - raise TypeError('Incompatible inputs.') + # # length >> options + # # specific_heat_transfer_coefficient >> options + # # temperature_surroundings >> temporal + # # determine if the inputs are for temporal vector/normal mode + # if (isinstance(temperature_surroundings, Real) and + # isinstance(time_interval_duration, Real)): + # temporal_vector_mode = False + # elif ((type(temperature_surroundings) == list or + # type(temperature_surroundings) == tuple) and + # (type(time_interval_duration) == list or + # type(time_interval_duration) == tuple)): + # temporal_vector_mode = True + # else: + # raise TypeError('Incompatible inputs.') - # confirm inputs are consistent with vector/normal mode - if ((not self.vector_mode and - (not isinstance(length, Real) or - not isinstance(specific_heat_transfer_coefficient, Real))) or - (self.vector_mode and - (type(length) != list and - type(length) != tuple or - len(length) != self.number_options() or - len(specific_heat_transfer_coefficient) != len(length)) - ) - ): - raise TypeError('Inconsistent inputs.') + # # confirm inputs are consistent with vector/normal mode + # if ((not self.vector_mode and + # (not isinstance(length, Real) or + # not isinstance(specific_heat_transfer_coefficient, Real))) or + # (self.vector_mode and + # (type(length) != list and + # type(length) != tuple or + # len(length) != self.number_options() or + # len(specific_heat_transfer_coefficient) != len(length)) + # ) + # ): + # raise TypeError('Inconsistent inputs.') - if self.vector_mode: - # vector mode: multiple options - if temporal_vector_mode: - out = [ - [((st+rt)*0.5-ts)* - shtc* - pipe.length* - dt* - unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) - for ts, dt in zip( - temperature_surroundings, - time_interval_duration - ) - ] - for st, rt, pipe, shtc in zip( - self.supply_temperature, - self.return_temperature, - self.supply_pipe, - specific_heat_transfer_coefficient - ) - ] - else: - # one time step - out = [ - ((st+rt)*0.5-temperature_surroundings)* - shtc* - pipe.length* - time_interval_duration* - unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) - for st, rt, pipe, shtc in zip( - self.supply_temperature, - self.return_temperature, - self.supply_pipe, - specific_heat_transfer_coefficient - ) - ] - else: - # normal mode: one option - if temporal_vector_mode: - # multiple time steps - out = [ - ((self.supply_temperature+self.return_temperature)*0.5-ts)* - specific_heat_transfer_coefficient* - length* - dt* - unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) - for ts, dt in zip( - temperature_surroundings, - time_interval_duration - ) - ] - else: - # one time step - out = ( - ((self.supply_temperature+self.return_temperature)*0.5- - temperature_surroundings)* - specific_heat_transfer_coefficient* - length* - time_interval_duration* - unit_conversion_factor* - (1 if self.losses_are_positive else -1)* - (1 if self.twin_pipes else 2) - ) - return out + # if self.vector_mode: + # # vector mode: multiple options + # if temporal_vector_mode: + # out = [ + # [((st+rt)*0.5-ts)* + # shtc* + # pipe.length* + # dt* + # unit_conversion_factor* + # (1 if self.losses_are_positive else -1)* + # (1 if self.twin_pipes else 2) + # for ts, dt in zip( + # temperature_surroundings, + # time_interval_duration + # ) + # ] + # for st, rt, pipe, shtc in zip( + # self.supply_temperature, + # self.return_temperature, + # self.supply_pipe, + # specific_heat_transfer_coefficient + # ) + # ] + # else: + # # one time step + # out = [ + # ((st+rt)*0.5-temperature_surroundings)* + # shtc* + # pipe.length* + # time_interval_duration* + # unit_conversion_factor* + # (1 if self.losses_are_positive else -1)* + # (1 if self.twin_pipes else 2) + # for st, rt, pipe, shtc in zip( + # self.supply_temperature, + # self.return_temperature, + # self.supply_pipe, + # specific_heat_transfer_coefficient + # ) + # ] + # else: + # # normal mode: one option + # if temporal_vector_mode: + # # multiple time steps + # out = [ + # ((self.supply_temperature+self.return_temperature)*0.5-ts)* + # specific_heat_transfer_coefficient* + # length* + # dt* + # unit_conversion_factor* + # (1 if self.losses_are_positive else -1)* + # (1 if self.twin_pipes else 2) + # for ts, dt in zip( + # temperature_surroundings, + # time_interval_duration + # ) + # ] + # else: + # # one time step + # out = ( + # ((self.supply_temperature+self.return_temperature)*0.5- + # temperature_surroundings)* + # specific_heat_transfer_coefficient* + # length* + # time_interval_duration* + # unit_conversion_factor* + # (1 if self.losses_are_positive else -1)* + # (1 if self.twin_pipes else 2) + # ) + # return out # ************************************************************************* @@ -841,7 +863,7 @@ class SupplyReturnPipeSystem: # list of lists: if there are multiple options and multiple time steps # TODO: test this more - + # TODO: comment this def heat_transfer_surroundings( self, **kwargs) -> float or list: diff --git a/tests/test_trenches.py b/tests/test_trenches.py index 930873848d08ec86e070c5b534d428015033ad84..0fe0fd309fa2c2b4ebccbaaa3d6389d3c50f5ce9 100644 --- a/tests/test_trenches.py +++ b/tests/test_trenches.py @@ -2763,7 +2763,7 @@ class TestPipeTrench: ] # single pipe trench - single_trench = trenches.TwinPipeTrench( + twin_trench = trenches.TwinPipeTrench( pipe_center_depth=[pipe_depth+pipe.d_cas/2 for pipe in list_pipes], fluid_db=fluiddb, phase=fluiddb.fluid_LIQUID, @@ -2773,21 +2773,48 @@ class TestPipeTrench: max_specific_pressure_loss=[max_specific_pressure_loss for i in range(len(list_pipes))], supply_pipe=list_pipes ) - assert single_trench.vector_mode - assert single_trench.number_options() == len(list_pipes) - # one u per option + assert twin_trench.vector_mode + assert twin_trench.number_options() == len(list_pipes) + + # vector mode, one u value per option, and one temperature u = [i+1 for i in range(len(list_pipe_tuples))] - q = single_trench.simplified_specific_heat_transfer_surroundings( + # q should be a list of numbers + q = twin_trench.simplified_specific_heat_transfer_surroundings( specific_heat_transfer_coefficient=u, temperature_surroundings=ground_temperature, ) - for i, q_i in enumerate(q): + assert type(q) == list or type(q) == tuple + assert len(q) == twin_trench.number_options() + for u_i, q_i in zip(u, q): assert math.isclose( q_i, - u[i]*(dh_flow_temperature/2+dh_return_temperature/2-ground_temperature), + u_i*(dh_flow_temperature/2+dh_return_temperature/2-ground_temperature), abs_tol=1e-3 ) - # TODO: multiple time intervals + + # vector mode, one u value per time interval and option, multiple temperatures + number_time_intervals = 3 + u = [ + [i+1+k for k in range(number_time_intervals)] + for i in range(len(list_pipe_tuples)) + ] + t_surroundings = [ground_temperature+k for k in range(number_time_intervals)] + # q should be a list of lists + q = twin_trench.simplified_specific_heat_transfer_surroundings( + specific_heat_transfer_coefficient=u, + temperature_surroundings=t_surroundings, + ) + assert type(q) == list or type(q) == tuple + assert len(q) == twin_trench.number_options() + for u_i, q_i in zip(u, q): + assert type(q_i) == list or type(q_i) == tuple + assert len(q_i) == number_time_intervals + for k in range(number_time_intervals): + assert math.isclose( + q_i[k], + u_i[k]*(dh_flow_temperature/2+dh_return_temperature/2-t_surroundings[k]), + abs_tol=1e-3 + ) # ************************************************************************* # ************************************************************************* @@ -2843,7 +2870,7 @@ class TestPipeTrench: ] # single pipe trench - single_trench = trenches.SupplyReturnPipeTrench( + two_pipe_trench = trenches.SupplyReturnPipeTrench( pipe_center_depth=[pipe_depth+pipe.d_cas/2 for pipe in list_pipes], pipe_center_distance=[minimum_assembling_distance_isoplus(pipe.d_cas)+pipe.d_cas for pipe in list_pipes], fluid_db=fluiddb, @@ -2854,19 +2881,48 @@ class TestPipeTrench: max_specific_pressure_loss=[max_specific_pressure_loss for i in range(len(list_pipes))], supply_pipe=list_pipes ) - assert single_trench.vector_mode - # one u per option + assert two_pipe_trench.vector_mode + assert two_pipe_trench.number_options() == len(list_pipes) + + # vector mode, one u value per option, and one temperature u = [i+1 for i in range(len(list_pipe_tuples))] - q = single_trench.simplified_specific_heat_transfer_surroundings( + # q should be a list of numbers + q = two_pipe_trench.simplified_specific_heat_transfer_surroundings( specific_heat_transfer_coefficient=u, temperature_surroundings=ground_temperature, ) - for i, q_i in enumerate(q): + assert type(q) == list or type(q) == tuple + assert len(q) == two_pipe_trench.number_options() + for u_i, q_i in zip(u, q): assert math.isclose( q_i, - u[i]*(dh_flow_temperature/2+dh_return_temperature/2-ground_temperature), + u_i*(dh_flow_temperature/2+dh_return_temperature/2-ground_temperature), abs_tol=1e-3 ) + + # vector mode, one u value per time interval and option, multiple temperatures + number_time_intervals = 3 + u = [ + [i+1+k for k in range(number_time_intervals)] + for i in range(len(list_pipe_tuples)) + ] + t_surroundings = [ground_temperature+k for k in range(number_time_intervals)] + # q should be a list of lists + q = two_pipe_trench.simplified_specific_heat_transfer_surroundings( + specific_heat_transfer_coefficient=u, + temperature_surroundings=t_surroundings, + ) + assert type(q) == list or type(q) == tuple + assert len(q) == two_pipe_trench.number_options() + for u_i, q_i in zip(u, q): + assert type(q_i) == list or type(q_i) == tuple + assert len(q_i) == number_time_intervals + for k in range(number_time_intervals): + assert math.isclose( + q_i[k], + u_i[k]*(dh_flow_temperature/2+dh_return_temperature/2-t_surroundings[k]), + abs_tol=1e-3 + ) # ************************************************************************* # *************************************************************************