PyTuflow: Difference between revisions

Content deleted Content added
No edit summary
 
(118 intermediate revisions by 2 users not shown)
Line 1:
=Introduction=
PyTUFLOW is a package of Python tools for extracting TUFLOW Classic and HPC time series results. It can be used to automate output tasks such as checking model health, goodness-of-fit for model calibration, viewing on-the-fly model output (used in conjunction with <tt><font color="blue">Write PO Online</font> <font color="red">==</font> ON</tt>), and high-volume output plotting from production runs.<br><Br>
For worked examples demonstrating the use of PyTUFLOW, please register for the free <u>[https://www.tuflow.com/training/training-catalogue/tt004e-introduction-to-python-for-tuflow-elearning/ Introduction to Python for TUFLOW]</u> eLearning Course.
 
=Installation=
The PyTuflow library can be installed via Python's packaging manager (pip).<br>
Open up the command prompt and enter the following:<br>
<pre>
python -m pip install pytuflow
</pre>
 
If python is not on your system path, you can navigate to the folder containing the <tt>python.exe</tt> before trying to install PyTuflow<br>
e.g.<br>
<pre>
cd c:\Python39
python -m pip install pytuflow
</pre>
 
=Class: ResData=
 
Line 6 ⟶ 24:
 
<pre>
import pytuflow as tu
 
 
res = tupytuflow.ResData() # initialising as an empty object
 
# or
res = pytuflow.ResData('M01_5m_001.tpc') # initialising with results
 
res = tu.ResData('M01_5m_001.tpc') # initialising with results
</pre>
 
'''==Methods'''==
{|class="wikitable" style="text-align: left;"
|! style="background-color:#005581; font-weight:bold; color:white" width=2030%| Method
|! style="background-color:#005581; font-weight:bold; color:white;" width=3020%|Description
|! style="background-color:#005581; font-weight:bold; color:white;" width=25%|Returns
|-
|rowspan='1' style='text-align: left'|<b>[[#channelConnectionCount|channelConnectionCount]] (</b> ''str'' Node ID <b>)</b>
|rowspan='1'|Returns the number of channels connected to a given node
|''int'' Count
|-
|rowspan='1'|<b>[[#channelConnections|channelConnections]] (</b> ''str'' Node ID <b>)</b>
|rowspan='1'|Returns a list of the channels connected to a given node
|''list'' Channel IDs
|-
|rowspan='1'|<b>[[#channelCount|channelCount]] (</b> ''str'' Node ID <b>)</b>
|rowspan='1'|Returns the total number of channels in results
|''int'' Count
|-
|rowspan='1'|<b>[[#channelResultTypes|channelResultTypes]] ()</b>
|rowspan='1'|Returns a list of all the available result types for channels
|list of Result Types
|-
|rowspan='1'|<b>[[#channels|channels]] ()</b>
|rowspan='1'|Returns a list of all the channels IDs in the results
|''list'' Channel IDs
|-
|rowspan='1'|<b>[[#channelsDownstream|channelsDownstream]] (</b> ''str'' Node ID <b>)</b>
|rowspan='1'|Returns a list of all the channels downstream of a given node
|''list'' Channel IDs
|-
|rowspan='1'|<b>[[#channelsUpstream|channelsUpstream]] (</b> ''str'' Node ID <b>)</b>
|rowspan='1'|Returns a list of all the channels upstream of a given node
|''list'' Channel IDs
|-
|rowspan='1'|<b>[[#format|format]] ()</b>
|rowspan='1'|Returns the result format i.e. '2013' or '2016'
|''str'' Format
|-
|rowspan='2'|<b>[[#getAdverseGradients|getAdverseGradients]] ()</b>
|rowspan='2'|Returns locations of any adverse water level or energy level (if result type available) gradients in long profile. Note getLongProfileData method must be called prior to calling this method
|''tuple'' Adverse Water Level Gradient Locations ( ''list'' x data, ''list'' y data )
Line 60 ⟶ 76:
|''tuple'' Adverse Energy Level Gradient Locations ( ''list'' x data, ''list'' y data )
|-
|rowspan='3'|<b>[[#getLongProfileData|getLongProfileData]] (</b> ''float'' Timestep, ''str'' Result Type, ''str'' Channel ID, '[optional] ''str'' 2nd Channel ID <b>)</b>
|rowspan='3'|Returns long profile data from a given channel to another channel. If no second channel is specified will continue until no further channels downstream
|''bool'' Error
Line 68 ⟶ 84:
|''tuple'' Profile Data ( ''list'' x values, ''list'' y values )
|-
|rowspan='2'|<b>getLongProfileTimeOfMax[[#getLongProfileTimeOfMaximum|getLongProfileTimeOfMaximum]] ()</b>
|rowspan='2'|Returns the time of maximum water level along the current profile. Note getLongProfileData method must be called prior to calling this method
|''list'' x values
Line 74 ⟶ 90:
|''list'' y values
|-
|rowspan='1'|<b>[[#getPipes|getPipes]] ()</b>
|rowspan='1'|Returns any pipe or culvert data in the current profile. Note getLongProfileData method must be called prior to calling this method
|''list'' pipes [ ''list'' pipe vertexes [ ''tuple'' vertex ( x point, y point ) ] ]
|-
|rowspan='3'|<b>[[#getTimeSeriesData|getTimeSeriesData]] (</b> ''str'' Element, ''str'' Result Type, [optional] ''str'' domain <b>)</b>
|rowspan='3'|Returns time series data for the given element (1D node, 1D channel, 2D PO, RL) for a the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')
|''bool'' Error
Line 86 ⟶ 102:
|''tuple'' time series data ( ''list'' x data, ''list'' y data )
|-
|rowspan='2'|<b>[[#load|load]] (</b> ''str'' filepathFilepath <b>)</b>
|rowspan='2'|Loads result file. File formats are *.tpc for TUFLOW release 2016 and later, or *.info for TUFLOW release 2013
|''bool'' Error
|-
|''str'' Message
|-
|rowspan='1'|<b>[[#longProfileResultTypes|longProfileResultTypes]] ()</b>
|rowspan='1'|
|''list'' Result Types
|-
|rowspan='3'|<b>[[#maximum|maximum]] (</b> ''str'' Element, ''str'' Result Type, [optional] ''str'' Domain <b>)</b>
|rowspan='3'|Returns the maximum result value for the given element (1D node, 1D channel, 2D PO, RL) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')
|''bool'' Error
|-
|''str'' Message
|-
|''float'' Maximum Value
|-
|rowspan='3'|<b>[[#maximumTimestepChange|maximumTimestepChange]] (</b> ''str'' Element, ''str'' Result Type, [optional] ''str'' Domain <b>)</b>
|rowspan='3'|Returns the maximum change in result value in one timestep for the given element (RL elements supported only) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')
|''bool'' Error
|-
|''str'' Message
|-
|''float'' Maximum Value
|-
|rowspan='1'|<b>[[#name|name]] ()</b>
|rowspan='1'|Returns the name of the result
|''str'' Name
|-
|rowspan='1'|<b>[[#nodeCount|nodeCount]] ()</b>
|rowspan='1'|Returns the total number of nodes in the results
|''int'' Count
|-
|rowspan='1'|<b>[[#nodeDownstream|nodeDownstream]] (</b> ''str'' Channel ID <b>)</b>
|rowspan='1'|Returns the name of the downstream node of a given channel
|''str'' Node ID
|-
|rowspan='1'|<b>[[#nodeResultTypes|nodeResultTypes]] ()</b>
|rowspan='1'|Returns a list of all the available result types for nodes
|''list'' Result Types
|-
|rowspan='1'|<b>[[#nodes|nodes]] ()</b>
|rowspan='1'|Returns a list of all the node IDs in the results
|''list'' Node IDs
|-
|rowspan='1'|<b>[[#nodeUpstream|nodeUpstream]] (</b> ''str'' Channel ID <b>)</b>
|rowspan='1'|Returns the name of the upstream node of a given channel
|''str'' Node ID
|-
|rowspan='1'|<b>[[#poNames|poNames]] ()</b>
|rowspan='1'|Returns a list of all the available Plot Output names
|''list'' PO ID
|-
|rowspan='1'|<b>[[#poResultTypes|poResultTypes]] ()</b>
|rowspan='1'|Returns a list of all the available Plot Output result types
|''list'' Result Types
|-
|rowspan='1'|<b>[[#rlCount|rlCount]] ()</b>
|rowspan='1'|Returns the total number of Reporting Locations (of all geometry type) in the results
|''int'' Count
|-
|rowspan='1'|<b>[[#rlLineCount|rlLineCount]] ()</b>
|rowspan='1'|Returns the total number of line Reporting Locations in the results
|''int'' Count
|-
|rowspan='1'|<b>[[#rlNames|rlNames]] ()</b>
|rowspan='1'|Returns a list of all the Reporting Location names
|''list'' RL ID
|-
|rowspan='1'|<b>[[#rlPointCount|rlPointCount]] ()</b>
|rowspan='1'|Returns the total number of point Reporting Locations in the results
|''int'' Count
|-
|rowspan='1'|<b>[[#rlRegionCount|rlRegionCount]] ()</b>
|rowspan='1'|Returns the total number of region Reporting Locations in the results
|''int'' Count
|-
|rowspan='1'|<b>[[#rlResultTypes|rlResultTypes]] ()</b>
|rowspan='1'|Returns a list of all the available Reporting Location result types
|''list'' Result Types
|-
|rowspan='1'|<b>[[#source|source]] ()</b>
|rowspan='1'|Returns the full file path to the source result file (*.tpc or *.info)
|''str'' Path
|-
|rowspan='3'|<b>[[#timeOfMaximum|timeOfMaximum]] (</b> ''str'' Element, ''str'' Result Type, [optional] ''str'' Domain <b>)</b>
|rowspan='3'|Returns the time of maximum for the given element (1D node, 1D channel, 2D PO, RL) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')
|''bool'' Error
|-
|''str'' Message
|-
|''float'' Time (hrs)
|-
|rowspan='3'|<b>[[#timeOfMaximumTimestepChange|timeOfMaximumTimestepChange]] (</b> ''str'' Element, ''str'' Result Type, [optional] ''str'' Domain <b>)</b>
|rowspan='3'|Returns the time of maximum for change in result value in one timestep for the given element (RL elements supported only) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')
|''bool'' Error
|-
|''str'' Message
|-
|''float'' Time (hrs)
|-
|}
 
==channelConnectionCount==
 
<b>channelConnectionCount (</b> Node ID <b>)</b><br>
Returns the number of channels connected to a given node<br>
;Parameters:
: '''Node ID:''' ''str''
:: Name or ID of node e.g. 'Chan_A.1'
;Returns:
: '''Count:''' ''int''
:: Number of connected channels
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
conn_count = res.channelConnectionCount(node)
# print to console
print('{0} channels connected to {1}'.format(conn_count, node))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channelConnections==
 
<b>channelConnections (</b> Node ID <b>)</b><br>
Returns a list of the channels connected to a given node<br>
;Parameters:
: '''Node ID:''' ''str''
:: Name or ID of node e.g. 'Chan_A.1'
;Returns:
: '''Out:''' ''list''
:: List of connected channels e.g. ['Chan_A', 'Chan_B']
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
conn_chans = res.channelConnections(node)
# print to console
print('Channels connected to {0}:'.format(node))
for chan in conn_chans:
print(chan)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channelCount==
 
<b>channelCount ()</b><br>
Returns the total number of channels in results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of channels
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
chan_count = res.channelCount()
# print to console
print('{0} channels in results'.format(chan_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channelResultTypes==
 
<b>channelResultTypes ()</b><br>
Returns a list of all the available result types for channels<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of result types e.g. ['Q', 'V']
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
chan_rt = res.channelResultTypes()
# print to console
print('Available channel result types:')
for rt in chan_rt:
print(rt)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channels==
<b>channels ()</b><br>
Returns a list of all the channels IDs in the results<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of channels e.g. ['Chan_A', 'Chan_B', 'Chan_C']
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
chans = res.channels()
# print to console
print('Channels:')
for chan in chans:
print(chan)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channelsDownstream==
<b>channelsDownstream (</b> Node ID <b>)</b><br>
Returns a list of all the channels downstream of a given node<br>
;Parameters:
: '''Node ID:''' ''str''
:: Name or ID of node e.g. 'Chan_A.1'
;Returns:
: '''Out:''' ''list''
:: List of channels e.g. ['Chan_B']
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
dns_chans = res.channelsDownstream(node)
# print to console
print('Channels downstream of {0}:'.format(node))
for chan in dns_chans:
print(chan)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==channelsUpstream==
<b>channelsUpstream (</b> Node ID <b>)</b><br>
Returns a list of all the channels upstream of a given node<br>
;Parameters:
: '''Node ID:''' ''str''
:: Name or ID of node e.g. 'Chan_A.1'
;Returns:
: '''Out:''' ''list''
:: List of channels e.g. ['Chan_A']
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
ups_chans = res.channelsUpstream(node)
# print to console
print('Channels upstream of {0}:'.format(node))
for chan in ups_chans:
print(chan)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==format==
<b>format ()</b><br>
Returns the result format i.e. '2013' or '2016'<br>
;Parameters:
: -
;Returns:
: '''Format:''' ''str''
:: Format of results e.g. '2016'
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
format = res.format()
# print to console
print('Results format: {0}'.format(format)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==getAdverseGradients==
<b>getAdverseGradients ()</b><br>
Returns locations of any adverse water level or energy level (if result type available) gradients in long profile. Note getLongProfileData method must be called prior to calling this method<br>
;Parameters:
: -
;Returns:
: '''Adverse water level gradients:''' ''tuple''
:: ( ''list'' x data, ''list'' y data ) e.g. ([0, 5], [10, 12])
: '''Adverse energy level gradients:''' ''tuple''
:: ( ''list'' x data, ''list'' y data ) e.g. ([0, 5], [11, 14])
'''Example'''
<pre>
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
 
# get long profiles and add to plot
chan = 'Chan_A'
timestep = 1.0
result_types = ['Bed Level', 'H'] # get both water level and bed level
for rt in result_types:
err_lp, mess_lp, data = res.getLongProfileData(timestep, rt, chan)
# check for error before plotting
if not err_lp:
x, y = data
ax.plot(x, y, label=rt)
else:
# error occurred getting long profile data, print error message to console
print(mess_lp)
 
# check for adverse gradients
adv = res.getAdverseGradients()
for i, data in enumerate(adv):
x, y = data
 
# get appropriate label
# getAdverseGradients() returns 2 datasets:
# first dataset adverse water level
# second dataset adverse energy level
if i == 0: # first dataset
label = 'Adverse Water Levels'
else: # second dataset
label = 'Adverse Energy Levels'
 
# add to plot as points
ax.plot(x, y, label=label, marker='o', linestyle='None')
# add legend to plot
lines, labs = ax.get_legend_handles_labels()
ax.legend(lines, labs)
# show plot
fig.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==getLongProfileData==
<b>getLongProfileData (</b> Timestep, Result Type, Channel ID, [optional] 2nd Channel ID <b>)</b><br>
Returns long profile data from a given channel to another channel. If no second channel is specified will continue until no further channels downstream<br>
;Parameters:
: '''Timestep:''' ''float'' or ''str''
:: Output timestep to plot. Can be a value e.g. 1.5 or string e.g. 'max'
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Channel ID:''' ''str''
:: Starting channel for long profile e.g. 'Chan_A'
: '''2nd Channel ID:''' ''str''
:: Optional downstream channel ID to stop the long profile at e.g. 'Chan_D'
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Data:''' ''tuple''
:: '''x data points:''' ''list''
::: '''data point:''' ''float''
:: '''y data points:''' ''list''
::: '''data point:''' ''float''
:::: e.g. ([0, 1, 2, 3, 4, 5], [10, 20, 30, 20, 10, 0]
'''Example'''
<pre>
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
 
# get long profiles and add to plot
chan = 'Chan_A'
timestep = 1.0
result_types = ['Bed Level', 'H'] # get both water level and bed level
for rt in result_types:
err_lp, mess_lp, data = res.getLongProfileData(timestep, rt, chan)
# check for error before plotting
if not err_lp:
x, y = data
ax.plot(x, y, label=rt)
else:
# error occurred getting long profile data, print error message to console
print(mess_lp)
 
# add legend to plot
lines, labs = ax.get_legend_handles_labels()
ax.legend(lines, labs)
# show plot
fig.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==getLongProfileTimeOfMaximum==
<b>getLongProfileTimeOfMaximum ()</b><br>
Returns the time of maximum water level along the current profile. Note getLongProfileData method must be called prior to calling this method<br>
;Parameters:
: -
;Returns:
: '''x data:''' ''list''
:: x points for plotting time of max along long profile e.g. [0, 1, 2, 3, 4, 5]
: '''y data:''' ''list''
:: y points for plotting time of max along long profile e.g. [10, 11, 12, 13, 14, 15]
'''Example'''
<pre>
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
 
# get long profiles and add to plot
chan = 'Chan_A'
timestep = 'max' # maximum result along profile
result_types = ['Bed Level', 'H'] # get both water level and bed level
for rt in result_types:
err_lp, mess_lp, data = res.getLongProfileData(timestep, rt, chan)
# check for error before plotting
if not err_lp:
x, y = data
ax.plot(x, y, label=rt)
else:
# error occurred getting long profile data, print error message to console
print(mess_lp)
 
# also plot time of max water level on secondary axis
tom_x, tom_y = res.getLongProfileTimeOfMaximum()
ax2 = ax.twinx() # initialise secondary axis
 
# add to plot on secondary axis
ax2.plot(tom_x, tom_y, label='Time of Maximum', linestyle='--')
# combine axis legends and add to plot
lines, labs = ax.get_legend_handles_labels()
lines2, labs2 = ax2.get_legend_handles_labels()
comb_lines = lines + lines2
comb_labs = labs + labs2
ax.legend(comb_lines, comb_labs)
# show plot
fig.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==getPipes==
<b>getPipes ()</b><br>
Returns any pipe or culvert data in the current profile. Note getLongProfileData method must be called prior to calling this method<br>
;Parameters:
: -
;Returns:
: '''Pipe list:''' ''list''
:: '''Vertex list:''' ''list''
::: '''Vertex coords:''' ''tuple''
:::: e.g. [[(0, 0), (0, 1), (1, 1), (1, 0)], [(1, 1), (1, 2), (2, 2), (2, 1)]]
'''Example'''
<pre>
import matplotlib.pyplot as plt # plotting library
from matplotlib.patches import Polygon # will add pipes and culverts as polygon
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
 
# get long profiles and add to plot
chan = 'Chan_A'
timestep = 1.0
result_types = ['Bed Level', 'H'] # get both water level and bed level
for rt in result_types:
err_lp, mess_lp, data = res.getLongProfileData(timestep, rt, chan)
# check for error before plotting
if not err_lp:
x, y = data
ax.plot(x, y, label=rt)
else:
# error occurred getting long profile data, print error message to console
print(mess_lp)
 
# get pipes and culverts and add to plot
pipes = res.getPipes()
for pipe in pipes: # loop through all pipes
polygon = Polygon(pipe, facecolor='0.9', edgecolor='0.5', label='Culverts and Pipes')
ax.add_patch(polygon)
# add legend to plot
lines, labs = ax.get_legend_handles_labels()
 
# filter duplicate legend items
# for example if there is more than one pipe or culvert we only want that to appear once in the legend
unique_lines = []
unique_labels = []
for i, label in enumerate(labs):
if label not in unique_labels:
unique_labels.append(label)
unique_lines.append(lines[i])
ax.legend(unique_lines, unique_labels)
# show plot
fig.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==getTimeSeriesData==
<b>getTimeSeriesData (</b> Element, Result Type, [optional] domain <b>)</b><br>
Returns time series data for the given element (1D node, 1D channel, 2D PO, RL) for a the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')<br>
;Parameters:
: '''Element:''' ''str''
:: Channel, Node, PO, or RL element to get time series data for
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Domain:''' ''str''
:: Optional input to specify which domain the Element belongs in. If left as default (None) and there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements.
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Data:''' ''tuple''
:: '''x data points:''' ''list''
:: '''y data points:''' ''list''
::: e.g. ([0, 1, 2, 3, 4, 5], [10, 20, 30, 20, 10, 0]
'''Examples'''<br>
Plotting results for multiple elements on the same graph
<pre>
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
 
# get time series data and add to plot
elements = ['Chan_A', 'Chan_B', 'Chan_C']
result_type = 'Q'
for element in elements:
err_ts, mess_ts, data = res.getTimeSeriesData(element, result_type)
# check for error before plotting
if not err_ts:
x, y = data
ax.plot(x, y, label=element)
else:
# error occurred getting time series data, print error message to console
print(mess_ts)
# add legend to plot
lines, labs = ax.get_legend_handles_labels()
ax.legend(lines, labs)
# show plot
plt.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
Plotting water level and velocity on the same plot using a secondary axis
<pre>
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before continuing
if not err:
# initialise plot
fig, ax = plt.subplots()
# initialise secondary axis
ax2 = ax.twinx()
 
# get time series data and add to plot
elements = ['Chan_A', 'Chan_B', 'Chan_C']
result_types = ['Q', 'V']
for element in elements:
for result_type in result_types:
err_ts, mess_ts, data = res.getTimeSeriesData(element, result_type)
# check for error before plotting
if not err_ts:
x, y = data
label = '{0}: {1}'.format(result_type, element)
 
# plot Q on first axis, V on secondary axis
if result_type == 'Q':
# first axis
ax.plot(x, y, label=label)
else: # must be 'V'
# secondary axis
ax2.plot(x, y, label=label, linestyle='--')
else:
# error occurred getting time series data, print error message to console
print(mess_ts)
# combine primary and secondary axis items and add to legend
lines, labs = ax.get_legend_handles_labels()
lines2, labs2 = ax2.get_legend_handles_labels()
lines_comb = lines + lines2
labs_comb = labs + labs2
ax.legend(lines_comb, labs_comb)
# show plot
fig.show()
else:
# did not load correctly, print error message to console
print(message)
</pre>
Saving a separate plot for each element as a *.png
<pre>
import os # library for operating system file and directory manipulation
import matplotlib.pyplot as plt # plotting library
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# save directory for figures
save_dir = r'C:TUFLOW\results\figures'
 
# check results were loaded successfully before continuing
if not err:
# get time series data and add to plot
elements = ['Chan_A', 'Chan_B', 'Chan_C']
result_type = 'Q'
for element in elements:
err_ts, mess_ts, data = res.getTimeSeriesData(element, result_type)
# check for error before plotting
if not err_ts:
# initialise plot
fig, ax = plt.subplots()
x, y = data
ax.plot(x, y, label=element)
# add legend to plot
lines, labs = ax.get_legend_handles_labels()
ax.legend(lines, labs)
 
# add axis labels
ax.set_xlabel('Time (hrs)')
ax.set_ylabel('Flow (m3/s)')
 
# save fig
save_name = 'Figure_{0}_{1}.png'.format(element, result_type)
save_path = os.path.join(save_dir, save_name)
fig.savefig(save_path)
 
else:
# error occurred getting time series data, print error message to console
print(mess_ts)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==load==
<b>load (</b> Filepath <b>)</b><br>
Loads result file. File formats are *.tpc for TUFLOW release 2016 and later, or *.info for TUFLOW release 2013<br>
;Parameters:
: '''Filepath:''' ''str''
:: Full path to *.tpc or *.info
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, returns True if an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurs
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check if an error occurred
if err:
# print message
print(message)
</pre>
 
==longProfileResultTypes==
<b>longProfileResultTypes ()</b><br>
Returns list of available long profile result types<br>
;Parameters:
: -
;Returns:
: '''out:''' ''list''
:: List of available result types for long profile plotting
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
lp_rt = res.longProfileResultTypes()
# print to console
print('Available long profile result types:')
for rt in lp_rt:
print(rt)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==maximum==
<b>maximum (</b> Element, Result Type, [optional] Domain <b>)</b><br>
Returns the maximum result value for the given element (1D node, 1D channel, 2D PO, RL) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')<br>
;Parameters:
: '''Element:''' ''str''
:: Channel, Node, or RL element to get maximum result for. PO results do not currently support getting maximum value.
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Domain:''' ''str''
:: Optional input to specify which domain the Element belongs in. If left as default (None) and there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements.
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Maximum''' ''float''
:: Maximum result
'''Examples'''<br>
Maximum flow in a channel
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
channel = 'Chan_A'
maximum = res.maximum(channel, 'Q')
# print to console
print('Max flow at {0} is {1} m3/s'.format(channel, maximum)
else:
# did not load correctly, print error message to console
print(message)
</pre>
Maximum water level at a node
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
maximum = res.maximum(channel, 'H')
# print to console
print('Max water level at {0} is {1} mRL'.format(node, maximum)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==maximumTimestepChange==
<b>maximumTimestepChange (</b> Element, Result Type, [optional] Domain <b>)</b><br>
Returns the maximum change in result value in one timestep for the given element (RL elements supported only) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')<br>
;Parameters:
: '''Element:''' ''str''
:: RL element to get maximum change in a single timestep for. Currently RL elements are the only supported type.
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Domain:''' ''str''
:: Optional input to specify which domain the Element belongs in. If left as default (None) and there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements.
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Maximum''' ''float''
:: Maximum value change in any given timestep
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
loc = 'Reporting Location 1'
tsMaximum = res.maximumTimestepChange(channel, 'Q')
# print to console
print('Max flow change in any given timestep at {0} is {1} m3/s'.format(loc, tsMaximum)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==name==
<b>name ()</b><br>
Returns the name of the result<br>
;Parameters:
: -
;Returns:
: '''Name:''' ''str''
:: Name of results
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
name = res.name()
# print to console
print('Results from {0} loaded successfully.format(name))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==nodeCount==
<b>nodeCount ()</b><br>
Returns the total number of nodes in the results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of nodes
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node_count = res.nodeCount()
# print to console
print('{0} nodes in results.format(node_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==nodeDownstream==
<b>nodeDownstream (</b> Channel ID <b>)</b><br>
Returns the name of the downstream node of a given channel<br>
;Parameters:
: '''Channel ID:''' ''str''
:: Name or ID of channel
;Returns:
: '''Node ID:''' ''str''
:: Name or ID of downstream node
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
channel = 'Chan_A'
dns_node = res.nodeDownstream(channel)
# print to console
print('Downstream node of channel {0} is {1}'.format(channel, dns_node))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==nodeResultTypes==
<b>nodeResultTypes ()</b><br>
Returns a list of all the available result types for nodes<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of available result types for nodes e.g. ['H', 'E']
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
result_types = res.nodeResultTypes()
# print to console
print('Available result types for nodes are:)
for result_type in result_types:
print(result_type)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==nodes==
<b>nodes ()</b><br>
Returns a list of all the node IDs in the results<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of all nodes names in results e.g. ['Chan_A.1', 'Chan_A.2', 'Chan_B.2', 'Chan_C.2'...]
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
nodes = res.nodes()
# print to console
print('Nodes:)
for node in nodes:
print(node)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==nodeUpstream==
<b>nodeUpstream (</b> Channel ID <b>)</b><br>
Returns the name of the upstream node of a given channel<br>
;Parameters:
: '''Channel ID:''' ''str''
:: Name or ID of channel
;Returns:
: '''Node ID:''' ''str''
:: Name or ID of upstream node
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
channel = 'Chan_A'
ups_node = res.nodeUpstream(channel)
# print to console
print('Upstream node of channel {0} is {1}'.format(channel, ups_node))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==poNames==
<b>poNames ()</b><br>
Returns a list of all the available Plot Output names <br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of all PO names in results e.g. ['Loc_1', 'Loc_2', 'Loc_3']
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
po_names = res.poNames()
# print to console
print('PO Names:)
for po_name in po_names:
print(po_name)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==poResultTypes==
<b>poResultTypes ()</b><br>
Returns a list of all the available Plot Output result types<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of available result types for Plot Outputs e.g. ['Q', 'H', 'V']
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
result_types = res.poResultTypes()
# print to console
print('Available result types for Plot Outputs are:)
for result_type in result_types:
print(result_type)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlCount==
<b>rlCount ()</b><br>
Returns the total number of Reporting Locations (of all geometry type) in the results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of RL objects
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
rl_count = res.rlCount()
# print to console
print('{0} RL objects in results.format(node_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlLineCount==
<b>rlLineCount ()</b><br>
Returns the total number of line Reporting Locations in the results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of RL Line objects
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
rl_line_count = res.rlLineCount()
# print to console
print('{0} RL Line objects in results.format(node_line_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlNames==
<b>rlNames ()</b><br>
Returns a list of all the Reporting Location names<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of all RL names in results e.g. ['Reporting_Loc_1', 'Reporting_Loc_2', 'Reporting_Loc_3']
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
rl_names = res.rlNames()
# print to console
print('RL Names:)
for rl_name in rl_names:
print(rl_name)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlPointCount==
<b>rlPointCount ()</b><br>
Returns the total number of point Reporting Locations in the results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of RL Point objects
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
rl_point_count = res.rlPointCount()
# print to console
print('{0} RL Point objects in results.format(node_point_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlRegionCount==
<b>rlRegionCount ()</b><br>
Returns the total number of region Reporting Locations in the results<br>
;Parameters:
: -
;Returns:
: '''Count:''' ''int''
:: Number of RL Region objects
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
rl_region_count = res.rlRegionCount()
# print to console
print('{0} RL Region objects in results.format(node_region_count))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==rlResultTypes==
<b>rlResultTypes ()</b><br>
Returns a list of all the available Reporting Location result types<br>
;Parameters:
: -
;Returns:
: '''Out:''' ''list''
:: List of available result types for Reporting Locations e.g. ['Q', 'H', 'Vol']
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
result_types = res.rlResultTypes()
# print to console
print('Available result types for Reporting Locations are:)
for result_type in result_types:
print(result_type)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==source==
<b>source ()</b><br>
Returns the full file path to the source result file (*.tpc or *.info)<br>
;Parameters:
: -
;Returns:
: '''Source:''' ''str''
:: Full path to result file
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
source = res.source()
# print to console
print('Successfully loaded result from {0}.format(source))
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==timeOfMaximum==
<b>timeOfMaximum (</b> Element, Result Type, [optional] Domain <b>)</b><br>
Returns the time of maximum for the given element (1D node, 1D channel, 2D PO, RL) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')<br>
;Parameters:
: '''Element:''' ''str''
:: Channel, Node, or RL element to get maximum result for. PO results do not currently support getting time of maximum.
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Domain:''' ''str''
:: Optional input to specify which domain the Element belongs in. If left as default (None) and there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements.
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Time of Maximum:''' ''float''
:: Time of maximum
'''Example'''
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
node = 'Chan_A.1'
tom = res.timeOfMaximum(channel, 'H')
# print to console
print('Time of max water level at {0} is {1} hrs'.format(channel, tom)
else:
# did not load correctly, print error message to console
print(message)
</pre>
 
==timeOfMaximumTimestepChange==
<b>timeOfMaximumTimestepChange (</b> Element, Result Type, [optional] Domain <b>)</b><br>
Returns the time of maximum for change in result value in one timestep for the given element (RL elements supported only) for the given result type. If there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements unless 'domain' variable is explicitly specified (domain options: '1D', '2D', 'RL')<br>
;Parameters:
: '''Element:''' ''str''
:: RL element to get time of maximum change in a single timestep for. Currently RL elements are the only supported type.
: '''Result Type:''' ''str''
:: Result type e.g. 'H'
: '''Domain:''' ''str''
:: Optional input to specify which domain the Element belongs in. If left as default (None) and there are duplicate names between 1D, PO, or RL elements it will give priority return to 1D elements first, PO elements second, and lastly RL elements.
;Returns:
: '''Error:''' ''bool''
:: Returns False on success, True when an error occurred
: '''Message:''' ''str''
:: Accompanying error message if an error occurred
: '''Maximum''' ''float''
:: Time of maximum value change
'''Example'''<br>
<pre>
import pytuflow
 
# Initialise result class
res = pytuflow.ResData()
 
# load .tpc result file
path = r'C:\TUFLOW\results\M04_5m_001.tpc'
err, message = res.load(path)
 
# check results were loaded successfully before calling function
if not err:
loc = 'Reporting Location 1'
tom_ts = res.timeOfMaximumTimestepChange(channel, 'Q')
# print to console
print('Time of max flow change in any given timestep at {0} is {1} hrs'.format(loc, tom_ts)
else:
# did not load correctly, print error message to console
print(message)
</pre>