-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Thermal Storage as Component #423
Comments
Dear @jubra97, thank you for your idea and for reaching out! I have been on holidays, and did not yet have a lot of time to look into your suggestion, I will hopefully be able to do that on the next weekend. Quick general comments after scimming through your comment:
I'll come back to you later Best |
Hello fwitte, from tespy.components import Merge, Splitter, Subsystem, HeatExchangerSimple
from tespy.connections import Connection
import numpy as np
from CoolProp.CoolProp import PropsSI
class TESSystem(Subsystem):
"""
Class that represents the TES.
"""
def __init__(self, label: str, step_time: float=5, tank_diameter: float = 3, tank_height: float = 10, tank_max_change_vel: float = 0.001, hot_water_volume: float = None):
super().__init__(label)
self.step_time = step_time # time in minues
self.tank_diameter = tank_diameter
self.tank_height = tank_height
self.tank_volume = np.pi * (self.tank_diameter / 2) ** 2 * self.tank_height
self.tank_max_change_vel = tank_max_change_vel
self.max_flow = (np.pi * (self.tank_diameter / 2) ** 2 * self.tank_max_change_vel) * 1000 # approx for density of water
self.hot_water_volume = hot_water_volume if hot_water_volume else self.tank_volume / 2
self.hot_water_level = self.hot_water_volume / self.tank_volume
self.set_default_component_constraints()
def create_comps(self):
self.comps["hx_charge"] = HeatExchangerSimple("TES Charge")
self.comps["hx_discharge"] = HeatExchangerSimple("TES Discharge")
def check_constrains(self):
if abs(self.comps["hx_charge"].inl[0].get_attr("m").val_SI) > 1e-3 and abs(self.comps["hx_discharge"].inl[0].get_attr("m").val_SI) > 1e-3:
raise ValueError("Only massflow in one direction allowed.")
if self.comps["hx_charge"].inl[0].get_attr("m").val_SI > self.max_flow or self.comps["hx_discharge"].inl[0].get_attr("m").val_SI > self.max_flow:
raise ValueError("Charge velocity is too high.")
if self.hot_water_level > 1:
raise ValueError("TES is already full!")
if self.hot_water_level < 0:
raise ValueError("TES is already empty!")
def update_storage(self):
self.hot_water_volume = self.hot_water_volume + (
((self.comps["hx_charge"].inl[0].m.val_SI - self.comps["hx_discharge"].inl[0].m.val_SI) * (self.step_time * 60)) / PropsSI("D", "T", 160 + 273.15, "P", self.comps["hx_charge"].inl[0].p.val_SI, "Water"))
self.hot_water_level = self.hot_water_volume / self.tank_volume
def postprocess(self):
self.update_storage()
self.check_constrains()
def set_default_component_constraints(self):
self.comps["hx_charge"].get_attr("pr").max_val = 2
self.comps["hx_charge"].get_attr("zeta").min_val = -self.comps["hx_charge"].get_attr("zeta").max_val
if __name__ == "__main__":
from tespy.networks import Network
from tespy.components import Sink, Source, HeatExchangerSimple
from tespy.connections import Ref
fluid_list = ["Water"]
network = Network(fluids=fluid_list)
network.set_attr(p_unit="bar", T_unit="C", h_unit="kJ / kg", m_unit="kg / s", m_range=[-0.1, 20])
he = HeatExchangerSimple("HE")
tes = TESSystem("TES")
merge_j = Merge("Merge J")
splitter_j = Splitter("Splitter J")
merge_g = Merge("Merge G")
splitter_e = Splitter("Splitter E")
sink = Sink("Sink")
source = Source("source")
c_27 = Connection(merge_g, "out1", splitter_e, "in1")
c_16 = Connection(merge_j, "out1", sink, "in1")
c_4b = Connection(source, "out1", merge_g, "in2")
c_15_charge = Connection(splitter_j, "out1", tes.comps["hx_charge"], "in1")
c_15_intern = Connection(splitter_j, "out2", merge_j, "in1")
c_15_discharge = Connection(tes.comps["hx_discharge"], "out1", merge_j, "in2")
c_14_charge = Connection(tes.comps["hx_charge"], "out1", merge_g, "in1")
c_14_discharge = Connection(splitter_e, "out2", tes.comps["hx_discharge"], "in1")
c_10 = Connection(splitter_e, "out1", he, "in1")
c_6 = Connection(he, "out1", splitter_j, "in1")
network.add_subsys(tes)
network.add_conns(c_27, c_16, c_4b, c_15_charge, c_15_intern, c_15_discharge, c_14_charge, c_14_discharge, c_10, c_6)
he.set_attr(Q=3.5e6)
c_27.set_attr(fluid={"Water": 1})
c_15_discharge.set_attr(T=160)
c_14_charge.set_attr(T=60)
c_14_discharge.set_attr(p0=9.3)
c_14_charge.set_attr(m=0, p=Ref(c_15_discharge, 1, tes.tank_height/10))
c_15_discharge.set_attr(m=1, p=8)
c_4b.set_attr(T=58)
c_10.set_attr(m=8)
network.solve("design")
network.print_results()
tes.update_storage() Come back to me if you have any questions to the code. It would by nice to get this subsystem working as a component. Dou you have any idea how it would be possible to transfer this logic in a component? Best |
thank you for updating on this. I am struggling to find the time to go deep into this. Generally, I would think, that this is possible, but in some way it is not the core of what TESPy does. Nevertheless, coming up with some kind of template to reuse your structure/logic for similar components has some value for other people I think. Maybe we can meet some time in the future and discuss this? There is the TESPy user meeting, every 3rd Monday of the month at 17:00 CEST, if you want you can join for the August meeting. Best |
Sorry for the late answer. Would that be today? |
That is today, yes! |
I was searching for some representation of thermal storage in TESpy and came across this - many thanks |
Hi @jgunstone, thank you for reaching out. There was actually an approach recently implemented by someone in the discussions (#505, title does not show that immediately...). I would like to put this on the list for more examples/tutorials in the docs (see #506). Maybe we can discuss the concept for a simple to replicate and understand integration of storage into a tespy simulation model in on of the next online user meeting, would you like to join? Best Francesco |
Hello,
thank you for your helpful package. I’m currently working on a model of a network to distribute heat between an ORC power plant and a district heating network. In this network a thermocline thermal storage is included as well. But I have a hard time to model this storage with your framework.
My first goal is to model the storage in a way that the outgoing mass flows have a fixed temperature if the storage in not full or empty. The ingoing temperatures can be ignored. There must be no flow in both incoming mass flow streams at the same time. One of the incoming flows must be zero. The pressure at the bottom of the storage tank must be 1.8 bar higher than the pressure at the top. To compute the current hot water level in the tank I use multiple simulations and integrate the inflows and outflows. This configuration is shown in the figure below. The green color at the connections indicate that these values are the model constrains. The black values should be calculated.
And here is the corresponding code:
I tried to use the HeatExchanger module as a guide. My two main problems are with the thermal_deriv function, where I’m not sure if I’ve placed the derivatives in the right place in the Jacobian matrix and with calculation the output pressure. If I try to run my code, I get a singularity in the Jacobian matrix. If I use the pseudo inverse instead of the normal inverse my code runs but sometimes produces some strange bugs. Could you help me with the required equations?
Thank you in advance!
The text was updated successfully, but these errors were encountered: