Commit 36e65922 authored by christian.foerster's avatar christian.foerster

Initial commit

parents
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'tanks'
copyright = '2019, Christian Foerster'
author = 'Christian Foerster'
# The full version, including alpha/beta/rc tags
release = '1.0'
import os
import sys
sys.path.append(os.path.abspath('.'))
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.napoleon'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
.. tanks documentation master file, created by
sphinx-quickstart on Thu Nov 7 15:49:27 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to tanks's documentation!
=================================
.. toctree::
:maxdepth: 2
:caption: Contents:
./source/modules
./source/tanks
Description
===========
Add a basic description to the title page. Maye links to other pages and so on.
Examples
--------
Leave some example code like ...
**Basic Example**
.. code-block:: python
import matplotlib.pyplot as plt
from tanks.tanks import Tank, initiate_tanks, get_levels
## define network and attributes
network_structure = {"tank_6":["tank_4","tank_5"],"tank_4":["tank_1","tank_2"],"tank_5":["tank_3"]}
attributes = {"tank_1":(10, 0.0),
"tank_2":(20, 0.06),
"tank_3":(15, 0.9),
"tank_4":(8, 0.3),
"tank_5":(44, 0.5),
"tank_6":(2.5, 0.0)}
## generate the network
all_tanks = initiate_tanks(network_structure,attributes)
final_tank = all_tanks["tank_6"] # only the last tank is necessary to run the entire simulation
## simulate
levels = {"tank_{}".format(k):[] for k in range(7)[1:]} # init dict for our level values
for dt in range(100):
tank_levels = get_levels(all_tanks) # retrieve levels for every iteration
for tank_id, level in tank_levels.items(): # save the levels!
levels[tank_id].append(level) #
final_tank.fill_tank() # open valves and let the water through to our last tank!
# checking results
fig, axis = plt.subplots(6,1,figsize = (12,8))
for idx, ax in enumerate(axis):
ax.plot(levels["tank_{}".format(idx+1)])
ax.set_title("tank_{}".format(idx+1))
plt.tight_layout()
plt.show()
Issues
------
**Any trouble with the package?**
Leave me an issue on GitLab_
.. _GitLab: https://gitlab.switch.ch/sww/examplepypackage
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
tanks
=====
.. toctree::
:maxdepth: 4
tanks
tanks package
=============
Submodules
----------
tanks.tanks module
------------------
.. automodule:: tanks.tanks
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: tanks
:members:
:undoc-members:
:show-inheritance:
Copyright <2019> <Christian Foerster>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from setuptools import setup, find_packages
setup(name='tanks',
version='1.0',
description='A tool for simulating water tanks.',
long_description='Our very long explanation on what our package does',
classifiers=[
'Development Status :: early stage',
'Programming Language :: Python :: 3',
],
install_requires=[], # this will normally be containg depencies from packages
packages=find_packages(exclude=("doc",".git",".idea","venv", "test")),
keywords='Water Management',
author='Christian Foerster',
author_email='Christian.Foerster@eawag.ch',
license='MIT')
# A tank is a object (-> class) we can touch,
# it has attributes like level and id and so on,
# discharge is property of a tank, furthermore it does have the capability to fill up (a function or method)
class Tank:
"""Representation of a water tank with a footprint of 1 m²."""
def __init__(self, tank_id, level, rate, upstream_tanks=None):
"""
Parameters
----------
tank_id : str
Uniquely identifies tanks in your system.
level : float
Water level of a tank in meters [m].
rate : float
Specifies the changes in water level over time [1/s].
Positive values denote a decrease in water level, negative values an increase.
Used to calculate the discharge (level*rate).
upstream_tanks : list
Defines connections between tanks.
The provided list must contain entries of type Tank!
Example
-------
# create a Tanke instance
>>> Tank("id_1",1,0.1)
<type Tank>
tank_id: id_1
level: 1
rate: 0.1
"""
self.tank_id = tank_id
self.level = level
self.rate = rate
self.upstream_tanks = upstream_tanks
# the method Q calculates the flow of a given tank every time it's called
@property # allows use of the method without brackets
def Q(self):
"""
Discharge of a tank. Product of level and rate.
"""
return self.level * self.rate
# filling a tank from all its upstream tanks
def fill_tank(self):
"""
Propagates the water through the system from the downstream to upstream.
"""
self.level -= self.Q
if self.upstream_tanks is not None:
for uptank in self.upstream_tanks:
# recursion (sort of)
self.level += uptank.Q
uptank.fill_tank()
# our joice of representation for a tank!
# so that the user knows whats happening
def __repr__(self):
return "<type Tank>\ntank_id: {}\nlevel: {}\nrate: {}".format(self.tank_id,self.level,self.rate)
# in order to automatically init our tanks we will have to
# provide the network structure and attributes of all tanks.
# a graph would be a good way to go. But we want to keep thinks nice and simple!
# lets go with some dictionaries, we could write adapters for graphs later...
# network_structure = {tank_down_i:[tank_up_i,tank_up_(i+1),...],} eg.: {6:[4,5],4:[1,2],5:[3]}
# attributes = {tank_id:(tank_level, tank_rate),...}
def initiate_tanks(network_structure, attributes):
"""Creates all tanks in a network.
Parameters
----------
network_structure : dict
Each entry describes the connection from one or more upstream tanks to one downstream tank.
Entries must be in this shape:
{..., tank_id_downstream: [..., tank_id_upstream_n, tank_id_upstream_np1, ...], ...}
attributes : dict
Each tank mentioned in the network_structure must have an entry here, specifying its level and rate.
Entries must be in this shape:
{..., tank_id: (level, rate), ...}
Returns
-------
dict
Containing all tanks. Dictionary keys are equal to tank_ids.
Example
-------
# define network structure
# tank4 & tank5 flow into tank6, tank1 & tank2 flow into tank4, ...
>>> network_structure = {"tank_6":["tank_4","tank_5"],
"tank_4":["tank_1","tank_2"],
"tank_5":["tank_3"]}
# define attributes
# tank_3 has a level on 15m and a rate of 0.9/s.
>>> attributes = {"tank_1":(10, 0.0),
"tank_2":(20, 0.06),
"tank_3":(15, 0.9),
"tank_4":(8, 0.3),
"tank_5":(44, 0.5),
"tank_6":(2.5, 0.0)}
# generate the network
>>> all_tanks = initiate_tanks(network_structure,attributes)
"""
# init all tanks without upstream tanks
tanks = {}
for tank_id in attributes:
level = attributes[tank_id][0]
rate = attributes[tank_id][1]
tanks[tank_id] = Tank(tank_id, level, rate, upstream_tanks=None)
# add upstream tanks
for tank_id in network_structure:
tanks[tank_id].upstream_tanks = [tanks[tank_id_up] for tank_id_up in network_structure[tank_id]]
return tanks
# iterate over all tanks and retrieve these levels with id
def get_levels(tanks):
"""Extracts levels of provided tanks.
Parameters
----------
tanks : dict
Containing tanks you want to extract levels of. Shape {..., tank_id: <tank instance>, ...}
Returns
-------
dict
Containing levels of all the tanks.
Example
-------
# define tanks or generate networt with the 'initiate_tanks' function
>>> tanks = {"id_0": Tank("id_0",10,0.1), "id_1": Tank("id_1",1,0.1)}
# extract levels
>>> levels = get_levels(tanks)
"""
fill_dict={}
for tank_id, tank in tanks.items():
fill_dict[tank_id] = tank.level
return fill_dict
### --- Tests ---- (those are normally in a sperate file, basic test example!)
import os
import sys
sys.path.append(os.path.abspath("."))
from tanks.tanks import Tank
# checking the calcualtion of discharge
tank = Tank(tank_id=1, level=10, rate=0.1)
assert tank.Q == 10*0.1, "The tank's discharge should be 0.3."
# checking the "leakage of a tank"
tank.fill_tank()
assert tank.level == 9, "The tank's level should be 9."
# checking when leakage 0
tank = Tank(tank_id=1, level=10, rate=0.00)
tank.fill_tank()
assert tank.level == 10, "The tank's level should be 10."
# checking mass balance with upstream tanks
tank_1 = Tank(tank_id=1, level=10, rate=0.1)
tank_2 = Tank(tank_id=2, level=50, rate=0.08)
tank_3 = Tank(tank_id=3, level=0, rate=0, upstream_tanks=[tank_1, tank_2])
tank_3.fill_tank()
assert tank_3.level == 10*0.1+50*0.08, "The tank's level should be 5."
assert tank_1.level == 10-1, "The tank's level should be 9."
assert tank_2.level == 50-4, "The tank's level should be 46."
# ...
# .
# .
# .
print("All tests passed!")
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment