Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8a304ca
Setup simple district heating network
jnnr Jan 16, 2020
4c4eb46
Finish connections
jnnr Jan 16, 2020
f47778c
Add sketch for builder functions
jnnr Jan 16, 2020
2f98eaa
Unset temperature and pressure
jnnr Jan 16, 2020
4fca475
Silent char_warnings
jnnr Jan 16, 2020
3a4ef92
Add buses to network
jnnr Jan 16, 2020
f0b89ae
Adapt line breaks
jnnr Jan 16, 2020
d6cad0b
Remove fluid definition in all but one connection
jnnr Jan 16, 2020
13f1ae5
Import logging
jnnr Jan 16, 2020
d9286f2
Cleanup
jnnr Jan 16, 2020
618e7af
Write TODOs
jnnr Jan 16, 2020
babd3c5
Add draft tespy facades
jnnr Jan 16, 2020
c452008
Add minimal running example
jnnr Jan 20, 2020
e173439
Create producer and pipe from facades
jnnr Jan 20, 2020
bc9ca1a
Build producer from cycle_closer, heat_exchanger and pump
jnnr Jan 20, 2020
3c6d98c
No need to pass network to create_conns
jnnr Jan 20, 2020
de41651
Cleanup example
jnnr Jan 20, 2020
fab4e8e
Pass parameters through the facade's init
jnnr Jan 20, 2020
85a2d49
Pass return temperature of consumers
jnnr Jan 20, 2020
7402d61
Print some results
jnnr Jan 20, 2020
fe7f37b
Change position of cycle_closer to set p behind pump
jnnr Jan 20, 2020
af56f5b
Add single pipe benchmark
jnnr Jan 21, 2020
b6d2dab
Clean up
jnnr Jan 21, 2020
91f837e
Pass pipe properties through __init__
jnnr Jan 21, 2020
9bd335c
Setup benchmark tespy single pipe simulations
jnnr Jan 21, 2020
d45370e
Add exception for greek symbols in latex to flake8
jnnr Jan 21, 2020
6cb6de6
Correct postprocessed values
jnnr Jan 21, 2020
9d4fc2d
Merge branch 'dev' into features/tespy-simulation
Feb 19, 2020
6054b5c
Move tespy facades to module
Feb 27, 2020
6612e29
Add sketch for tespy simulation in module
Feb 27, 2020
b859a0d
Consistant naming
Feb 27, 2020
868e187
Setup tespy simulation example
Feb 27, 2020
d4a9697
Compare with tespy's lamb
Mar 10, 2020
ea28611
Correct pressure parameters
Mar 10, 2020
0b86a2a
Pass eps as ks
Mar 10, 2020
40577db
Pass area independent heat loss factor
Mar 10, 2020
2a6d805
Sort simulation model
Mar 18, 2020
2ce2b2c
Add tespy simulation example
Mar 18, 2020
ae2442f
Update .gitignore
Mar 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[flake8]
exclude = meta/migrations/
ignore = E265, W503
ignore = E265, W503, W605
max-line-length = 100
per-file-ignores =
__init__.py : F401
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
*.pyc
*.png
*.pdf
*.nc
*.egg-info
*.html
.idea
Expand Down
2 changes: 1 addition & 1 deletion dhnx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Model():
Abstract base class for different kind of models.
"""
def __init__(self, thermal_network):
self.network = thermal_network
self.thermal_network = thermal_network
self.setup()
self.results = Dict()

Expand Down
98 changes: 96 additions & 2 deletions dhnx/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
SPDX-License-Identifier: MIT
"""

from tespy.components import pipe, heat_exchanger_simple
from tespy.connections import bus
from tespy.networks import network

from dhnx.tespy_facades import (
HeatProducer,
HeatConsumer,
DistrictHeatingPipe,
)

from .model import SimulationModel


Expand All @@ -22,13 +32,97 @@ def __init__(self, thermal_network):
super().__init__(thermal_network)
self.results = {}

def _setup_tespy_network(self):
r"""
Initializes a tespy network
"""
self.tespy_network = network(
fluids=['water'], T_unit='C', p_unit='bar', h_unit='kJ / kg', m_unit='kg / s'
)

def _build_tespy_componenents(self):
temp_inlet = 90
p_inlet = 15
pump_efficiency = 0.8
pr_producer = 0.99

# producer
heat_producer = HeatProducer(
'heat_producer',
temp_inlet=temp_inlet,
p_inlet=p_inlet,
eta_s=pump_efficiency
)

# # consumer data
heat_demand = 50000
temp_return_heat_exchanger = 60
pr_heat_exchanger = 0.99
pr_valve = 1

# consumer
consumer_0 = HeatConsumer(
'consumer_0',
Q=-heat_demand,
temp_return_heat_exchanger=temp_return_heat_exchanger,
pr_heat_exchanger=pr_heat_exchanger,
pr_valve=pr_valve
)

# piping
pipes = []
for edge, data in self.thermal_network.components.edges.iterrows():
pipes.append(
DistrictHeatingPipe(
'label',
heat_producer, # TODO
consumer_0, # TODO
length=data['length'],
diameter=data['diameter'],
ks=7e-5, # TODO
kA=10,
temp_env=data['temp_env']
)
)

tespy_components = [heat_producer, consumer_0, *pipes]

return tespy_components

def _build_heat_accounting(self):
# collect lost and consumed heat
heat_losses = bus('network losses')
heat_consumer = bus('network consumer')

for comp in self.tespy_network.comps.index:
if isinstance(comp, pipe):
heat_losses.add_comps({'c': comp})

if (isinstance(comp, heat_exchanger_simple) and '_consumer' in comp.label):
heat_consumer.add_comps({'c': comp})

self.tespy_network.add_busses(heat_losses, heat_consumer)

def setup(self):
pass
self._setup_tespy_network()

self.tespy_components = self._build_tespy_componenents()

self.tespy_network.add_subsys(*self.tespy_components)

self.tespy_network.check_network()

self._build_heat_accounting()

# silence warnings
for comp in self.tespy_network.comps.index:
comp.char_warnings = False

def solve(self):
pass
self.tespy_network.solve('design')

def get_results(self):
self.tespy_network.print_results()
return self.results


Expand Down
171 changes: 171 additions & 0 deletions dhnx/tespy_facades.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import logging

from tespy.components import (
subsystem as Subsystem,
cycle_closer,
pump,
heat_exchanger_simple,
valve,
pipe,
)
from tespy.connections import connection


class Facade(Subsystem):
def __init__(self, label):
if not isinstance(label, str):
msg = 'Subsystem label must be of type str!'
logging.error(msg)
raise ValueError(msg)

elif len([x for x in [';', ', ', '.'] if x in label]) > 0:
msg = 'Can\'t use ' + str([';', ', ', '.']) + ' in label.'
logging.error(msg)
raise ValueError(msg)

else:
self.label = label

self.input = None
self.output = None
self.comps = {}
self.conns = {}
self.create_comps()
self.create_conns()


class HeatProducer(Facade):
r"""
A subsystem for a heat producer, comprising a source and a sink.
"""
def __init__(self, label, temp_inlet, p_inlet, eta_s, pr=0.99):
if not isinstance(label, str):
msg = 'Subsystem label must be of type str!'
logging.error(msg)
raise ValueError(msg)

elif len([x for x in [';', ', ', '.'] if x in label]) > 0:
msg = 'Can\'t use ' + str([';', ', ', '.']) + ' in label.'
logging.error(msg)
raise ValueError(msg)

else:
self.label = label

self.comps = {}
self.conns = {}
self.create_comps(pr, eta_s)
self.create_conns(temp_inlet, p_inlet)

def create_comps(self, pr, eta_s):
self.comps['cycle_closer'] = cycle_closer(self.label + '_cycle_closer')
self.comps['heat_exchanger'] = heat_exchanger_simple(self.label + '_heat_exchanger', pr=pr)
self.comps['pump'] = pump(self.label + '_pump', eta_s=eta_s)
self.output = self.comps['cycle_closer']
self.input = self.comps['heat_exchanger']

def create_conns(self, temp_inlet, p_inlet):
self.conns['heat_exchanger_pump'] = connection(
self.comps['heat_exchanger'], 'out1', self.comps['pump'], 'in1',
T=temp_inlet, fluid={'water': 1}
)
self.conns['pump_cycle_closer'] = connection(
self.comps['pump'], 'out1', self.comps['cycle_closer'], 'in1',
p=p_inlet
)


class HeatConsumer(Facade):
r"""
A subsystem for a heat consumer, comprising a heat exchanger and a valve.
"""
def __init__(self, label, Q, temp_return_heat_exchanger, pr_valve, pr_heat_exchanger):
if not isinstance(label, str):
msg = 'Subsystem label must be of type str!'
logging.error(msg)
raise ValueError(msg)

elif len([x for x in [';', ', ', '.'] if x in label]) > 0:
msg = 'Can\'t use ' + str([';', ', ', '.']) + ' in label.'
logging.error(msg)
raise ValueError(msg)

else:
self.label = label

self.comps = {}
self.conns = {}
self.create_comps(Q, pr_valve, pr_heat_exchanger)
self.create_conns(temp_return_heat_exchanger)

def create_comps(self, Q, pr_valve, pr_heat_exchanger):
self.comps['heat_exchanger'] = heat_exchanger_simple(
self.label + '_consumer', Q=Q, pr=pr_heat_exchanger
)
self.comps['valve'] = valve(self.label + '_valve', pr=pr_valve)
self.output = self.comps['valve']
self.input = self.comps['heat_exchanger']

def create_conns(self, temp_return_heat_exchanger):
self.conns['heat_exchanger_valve'] = connection(
self.comps['heat_exchanger'], 'out1', self.comps['valve'], 'in1',
T=temp_return_heat_exchanger,
)


class Fork(Facade):
r"""
A subsystem for a fork, comprising a split and a merge.

TODO: Allow for 2...n forks. Pass pressure losses.
"""
def __init__(self, label):
self.comps = {}
self.conns = {}
self.create_comps()
self.create_conns()

def create_comps(self):
self.comps['split'] = None
self.comps['merge'] = None

def create_conns(self):
self.conns['heat_exchanger_valve'] = None


class DistrictHeatingPipe(Facade):
r"""
A subsystem for a district heating pipe, comprising a feed and return pipe.
"""
def __init__(self, label, start, end, length, diameter, ks, kA, temp_env):
if not isinstance(label, str):
msg = 'Subsystem label must be of type str!'
logging.error(msg)
raise ValueError(msg)

elif len([x for x in [';', ', ', '.'] if x in label]) > 0:
msg = 'Can\'t use ' + str([';', ', ', '.']) + ' in label.'
logging.error(msg)
raise ValueError(msg)

else:
self.label = label

self.comps = {}
self.conns = {}
self.create_comps(temp_env, length, diameter, ks, kA)
self.create_conns(start, end)

def create_comps(self, temp_env, length, diameter, ks, kA):
self.comps['feed'] = pipe(
self.label + '_inlet', ks=ks, L=length, D=diameter, kA=kA, Tamb=temp_env
)
self.comps['return'] = pipe(
self.label + '_return', ks=ks, L=length, D=diameter, kA=kA, Tamb=temp_env
)

def create_conns(self, start, end):
self.conns['inlet_in'] = connection(start.output, 'out1', self.comps['feed'], 'in1')
self.conns['inlet_out'] = connection(self.comps['feed'], 'out1', end.input, 'in1')
self.conns['return_in'] = connection(end.output, 'out1', self.comps['return'], 'in1')
self.conns['return_out'] = connection(self.comps['return'], 'out1', start.input, 'in1')
48 changes: 48 additions & 0 deletions examples/tespy_simulation/builder_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from tespy.components import (
source,
sink,
pipe,
)
from tespy.connections import connection, bus

from sub_consumer import lin_consum_closed, fork


def add_producer():
so = source('source')
si = sink('sink')
new_producer = [so, si]
return new_producer


def add_consumer(network, label):
new_consumer = lin_consum_closed(label, 2)
new_consumer.comps['consumer_0'].set_attr(Q=-5e4, pr=0.99)
new_consumer.comps['consumer_1'].set_attr(Q=-5e4, pr=0.99)
new_consumer.comps['feed_0'].set_attr(ks=7e-5, L=150, D=0.15, offdesign=['kA'])
network.add_subsys(new_consumer)

return new_consumer


def add_split(network, label, number):
new_split = fork(label, number)

return new_split


def add_pipe(network, label, source, target):
new_pipe = pipe(label, ks=7e-5, L=50, D=0.15, offdesign=['kA'])

con_source_inlet = connection(source, 'out1', new_pipe, 'in1', T=90, p=15, fluid={'water': 1})
con_inlet_target = connection(new_pipe, 'out1', target, 'in1', T=90, p=15, fluid={'water': 1})

con_target_return = connection(target, 'out1', new_pipe, 'in1', T=90, p=15, fluid={'water': 1})
con_return_source = connection(new_pipe, 'out1', target, 'in1', T=90, p=15, fluid={'water': 1})
network.add(con_source_inlet, con_inlet_target, con_target_return, con_return_source)

return network


heat_losses = bus('network losses')
heat_consumer = bus('network consumer')
9 changes: 9 additions & 0 deletions examples/tespy_simulation/dhnx_simulation_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import dhnx

import_dir = 'single_pipe_simulation_input'

nw = dhnx.network.ThermalNetwork(import_dir)

nw.simulate()

print(nw.results)
Loading