# -------------------------------------------------------------------------
# Name: Output
# Purpose: Output as timeseries, netcdf,
#
# Author: PB
# Created: 5/08/2016
# CWatM is licensed under GNU GENERAL PUBLIC LICENSE Version 3.
# -------------------------------------------------------------------------
import difflib # to check the closest word in settingsfile, if an error occurs
import math
import os
import string
import sys
from decimal import Decimal
import numpy as np
from netCDF4 import Dataset, num2date, date2num, date2index
from . import globals
from .messages import *
from cwatm.hydrological_modules.routing_reservoirs.routing_sub import *
from cwatm.management_modules.checks import *
from cwatm.management_modules.data_handling import *
from cwatm.management_modules.replace_pcr import *
[docs]class outputTssMap(object):
"""
Main CWatM output system class handling time series and map output generation.
This class manages all output operations for the CWatM hydrological model, including
NetCDF map writing, time series extraction at gauge points, CSV/TSS file generation,
and progress reporting. It handles various temporal aggregations (daily, monthly,
annual) and spatial aggregations (point values, catchment averages/sums).
**Global variables**
=================================== ========== ====================================================================== =====
Variable [self.var] Type Description Unit
=================================== ========== ====================================================================== =====
dirUp Array river network in upstream direction --
meteo Array store all meteo data in memeory for warm start (eg calibration) compl
sampleAdresses List outflowpoints as 1D index --
outpoints List output points (Gauges) --
noOutpoints Number number of output points --
evalCatch Array indeces of a subbasin in the mask --
catcharea Array catchment area of the subbaSIN m2
netcdfasindex Flag save netcdf file in a compressed way - for splitting runs in several b bool
firstout Number discharge of the first gauge m3/s
discharge Array Channel discharge m3/s
cellArea Array Area of cell m2
=================================== ========== ====================================================================== =====
Attributes
----------
var : object
Reference to model variable container with hydrological state variables
model : object
Reference to main CWatM model instance
Notes
-----
The output system supports multiple output formats:
- NetCDF maps for spatial data with temporal aggregation
- Time series files (TSS/CSV) for gauge point data
- Text dumps for debugging and analysis
- Progress monitoring for GUI integration
Output timing is controlled by dateVar configuration and supports:
- Daily outputs
- Month-end, monthly totals, monthly averages
- Annual outputs with various aggregations
- Simulation-total aggregations
"""
def __init__(self, model):
"""
Initialize CWatM output system with model reference.
Parameters
----------
model : object
Main CWatM model instance containing variable container and configuration
Notes
-----
Sets up references to model variables and configuration needed for output
operations. The actual output configuration is handled in the initial() method.
"""
self.var = model.var
self.model = model
[docs] def initial(self):
"""
Initialize output system configuration, gauge locations, and file structures.
This method sets up the complete output system including:
- Processing gauge coordinates and creating sample addresses
- Configuring catchment boundaries for area-based aggregations
- Setting up NetCDF and time series file structures
- Validating output variable names and timing specifications
- Initializing progress reporting system
Notes
-----
Must be called before any dynamic output operations. Processes settings
file configuration to determine output locations, variables, and timing.
Creates catchment delineation for gauges requiring area-based statistics.
Validates all output variable names against available model variables.
"""
def getlocOutpoints(out):
"""
Extract gauge locations from output point map and convert to geographic coordinates.
Parameters
----------
out : numpy.ndarray
1D compressed array with gauge IDs at corresponding cell locations,
zero for non-gauge cells
Returns
-------
dict
Dictionary mapping gauge IDs to compressed array indices for fast lookup
list
List of x,y coordinates in model projection [x1, y1, x2, y2, ...] for all gauges
Notes
-----
Converts from compressed array indices to geographic coordinates using
mask information and cell size. Coordinates represent cell centers in
the model's spatial reference system.
"""
sampleAdresses = {}
outp = []
# allpoints = np.where(maskinfo["mask"].data == False)
allpoints = np.where(maskinfo["mask"] == False)
for i in range(maskinfo['mapC'][0]):
if out[i] > 0:
sampleAdresses[out[i]] = i
outx = allpoints[1][i] * maskmapAttr['cell'] + maskmapAttr['x'] + maskmapAttr['cell'] / 2
outy = maskmapAttr['y'] - allpoints[0][i] * maskmapAttr['cell'] - maskmapAttr['cell'] / 2
outp.append(outx)
outp.append(outy)
return sampleAdresses, outp
def appendinfo(out, sec, name, type, ismap):
"""
Configure output specifications for maps or time series based on settings.
Parameters
----------
out : dict
Output configuration dictionary to populate with file information
sec : str
Settings file section name (e.g., 'OUTPUT', 'INITIAL', 'ENVIRONMENTAL')
name : str
Base output identifier ('_out_tss_' or '_out_map_')
type : str
Temporal aggregation type ('daily', 'monthly', 'annual', etc.)
ismap : bool
True for NetCDF map output, False for time series output
Notes
-----
Creates file paths and metadata structures for each configured output.
For maps: sets up NetCDF file paths and variable creation flags.
For time series: configures CSV or TSS file formats based on settings.
Validates output directory existence and creates error messages for
missing paths.
"""
key = sec.lower() + name + type
if key in out:
if out[key][0] != "None":
i = 0
for var in out[key]:
info = []
if os.path.exists(outDir[sec]):
if ismap:
info.append(os.path.join(outDir[sec], str(var) + "_" + type + ".nc"))
#vars(self.var)[var+"_"+type] = 0
# creates a var to sum/ average the results e.g. self.var.Precipitation_monthtot
info.append(var)
info.append(False)
else:
# TimeoutputTimeseries(binding[tss], self.var, outpoints, noHeader=Flags['noheader'])
# info.append(os.path.join(outDimpontr[sec], str(var) + "_daily.tss"))
newcsvformat = True
suffix = ".csv"
if 'reportOldTss' in option:
newcsvformat = not(checkOption('reportOldTss'))
if not(newcsvformat):
suffix = ".tss"
name = os.path.join(outDir[sec], str(var) + "_" + type + suffix)
# info.append(TimeoutputTimeseries2(name, self.var, outpoints, noHeader=False))
info.append(name)
info.append(var)
# flag set True for writing times series in csv format
info.append(newcsvformat)
else:
msg = "Error 220: Checking output file path \n"
raise CWATMFileError(outDir[sec], msg)
placeholder = []
info.append(placeholder)
if ismap:
info.append(type) # set type to create variable later on first timestep
out[key][i] = info
i += 1
# ------------------------------------------------------------------------------
# if a geotif is used it can be a local map or a global
localGauges = returnBool('GaugesLocal')
where = "Gauges"
outpoints = cbinding(where)
# globals.inZero = np.zeros(maskinfo['mapC'])
coord = cbinding(where).split() # could be gauges, sites, lakeSites etc.
if len(coord) % 2 == 0:
compress_arange = np.arange(maskinfo['mapC'][0])
arange = decompress(compress_arange).astype(int)
# outpoints = valuecell( coord, outpoints)
col, row = valuecell(coord, outpoints, returnmap=False)
self.var.sampleAdresses = {}
for i in range(len(col)):
self.var.sampleAdresses[i + 1] = arange[row[i], col[i]]
self.var.outpoints = list(map(float, outpoints.split(" ")))
else:
if os.path.exists(outpoints):
outpoints = loadmap(where, local=localGauges).astype(np.int64)
else:
if len(coord) == 1:
msg = "Error 221: Checking output-points file\n"
else:
msg = "Error 129: Coordinates are not pairs\n"
raise CWATMFileError(outpoints, msg, sname="Gauges")
# self.var.Tss[tss] = TimeoutputTimeseries(cbinding(tss), self.var, outpoints, noHeader=Flags['noheader'])
outpoints[outpoints < 0] = 0
self.var.sampleAdresses, self.var.outpoints = getlocOutpoints(outpoints) # for key in sorted(mydict):
self.var.noOutpoints = len(self.var.sampleAdresses)
# catch = subcatchment1(self.var.dirUp,outpoints,self.var.UpArea1)
# check if catchment area calculation is necessary
calcCatch = False
for s in filter(lambda x: "areaavg" in x, outTss.keys()):
calcCatch = True
for s in filter(lambda x: "areasum" in x, outTss.keys()):
calcCatch = True
if calcCatch:
self.var.evalCatch = {}
self.var.catcharea = {}
for key in sorted(self.var.sampleAdresses):
outp = globals.inZero.copy()
outp[self.var.sampleAdresses[key]] = key
self.var.evalCatch[key] = catchment1(self.var.dirUp, outp)
self.var.catcharea[key] = np.bincount(self.var.evalCatch[key], weights=self.var.cellArea)[key]
# ------------------------------------------------------------------------------
# report TSS
# loop through all the section with output variables
for sec in outsection:
for type2 in outputTypTss2:
for type in outputTypTss:
if type2 == "tss":
type = type
else:
type = type2 + "_" + type
appendinfo(outTss, sec, "_out_tss_",type, False)
# report MAPs
metaNetCDF()
# loop through all the section with output variables
for sec in outsection:
# daily output, monthly total monthly average,
for type in outputTypMap:
# map or tss, section, type = daily, monthly ....
appendinfo(outMap,sec, "_out_map_",type, True)
# check if timing of output is in outputTypTss (globals.py)
for out in list(outTss.keys()):
if not(out.split('_')[-1] in outputTypTss):
msg = "Error 130: Output is not possible!\n"
msg += "\""+out +"\" is not one of these: daily, monthend, monthtot, monthavg, annualend, annualtot, annualavg"
raise CWATMError(msg)
if not(out.split('_')[-2] in outputTypTss2):
msg = "Error 131: Output is not possible!\n"
msg += "\""+out +"\" is not one of these: TSS for point value, AreaSum for sum of area, AreaAvg for average of area"
raise CWATMError(msg)
# save netcdf as index maps (not lar/lon) but only the valid cells
self.var.netcdfasindex = False
if "netcdfasindex" in option:
self.var.netcdfasindex = checkOption('netcdfasindex')
[docs] def dynamic(self, ef = False):
"""
Execute dynamic output operations for current time step.
This is the main output execution method called at each time step to:
- Write NetCDF maps with appropriate temporal aggregation
- Extract and accumulate time series data at gauge points
- Handle monthly/annual aggregation and reset cycles
- Update progress reporting for GUI and console output
- Manage file writing for completed aggregation periods
Parameters
----------
ef : bool, optional
Environmental flow flag indicating if processing environmental flow
calculations, by default False
Notes
-----
Processes all configured outputs based on current date and timing rules.
Handles temporal aggregation by accumulating values and writing outputs
at appropriate intervals (month-end, year-end, etc.). Updates progress
displays and manages memory by resetting aggregation variables after
writing outputs.
"""
def firstout(map):
"""
Extract value from the first configured output point for progress reporting.
Parameters
----------
map : numpy.ndarray
1D compressed array containing values to sample
Returns
-------
float
Value at the first gauge location, used for console progress display
Notes
-----
Used primarily for displaying discharge values during model execution
to provide feedback on simulation progress. Always samples from the
lowest-numbered gauge ID for consistency.
"""
first = sorted(list(self.var.sampleAdresses))[0]
value = map[self.var.sampleAdresses[first]]
return value
def checkifvariableexists(name, vari, space):
"""
Validate that requested output variable exists in model variable space.
Parameters
----------
name : str
Context name for error reporting (output section name)
vari : str
Variable name to validate, may include array indexing
space : list
List of available variable names in model variable container
Raises
------
CWATMError
If variable does not exist in variable space, with suggestion
for closest matching variable name
Notes
-----
Provides helpful error messages with closest variable name matches
using difflib fuzzy string matching when requested variable is not found.
Handles array-indexed variables by checking base variable name.
"""
if not (vari in space):
closest = difflib.get_close_matches(vari, space)
if not closest: closest = ["- no match -"]
msg = "Error 132: Variable \"" + vari + "\" is not defined in \""+ name+"\"\n"
msg += "Closest variable to this name is: \"" + closest[0] + "\""
raise CWATMError(msg)
def sample3(expression, map, daymonthyear):
"""
Sample values at gauge points and accumulate for time series output.
Parameters
----------
expression : list
Output configuration containing [filename, variable, format_flag, data_list, type]
map : numpy.ndarray
1D compressed array with values to sample at gauge locations
daymonthyear : int
Temporal aggregation level: 0=daily, 1=monthly, 2=annual
Returns
-------
list
Updated expression with accumulated time series data
Notes
-----
Handles three types of spatial aggregation:
- Point values: Direct sampling at gauge coordinates
- Area averages: Catchment-weighted mean values
- Area sums: Catchment-weighted total values
Accumulates values during simulation and writes complete time series
to file at the end of the simulation period. Supports both CSV and
traditional TSS formats.
"""
#if dateVar['checked'][dateVar['currwrite'] - 1] >= daymonthyear:
# using a list with is 1 for monthend and 2 for year end to check for execution
value = []
#tss.split('_')[-2]
# if inputmap is not an array give out error message
if not (hasattr(map, '__len__')):
msg = "No values in: " + expression[1] + "\nCould not write: " + expression[0]
print(CWATMWarning(msg))
return expression
for key in sorted(self.var.sampleAdresses):
if expression[0].split('_')[-2] in ['areaavg','areasum']:
# value from catchment
v = np.bincount(self.var.evalCatch[key], weights = map * self.var.cellArea)[key]
if expression[0].split('_')[-2] == 'areaavg':
if self.var.catcharea[key] == 0:
v = 0.
else:
v = v / self.var.catcharea[key]
else: # from single cell
v = map[self.var.sampleAdresses[key]]
value.append(v)
expression[3].append(value)
if dateVar['laststep']:
if expression[2]:
writeTssFileNew(expression, daymonthyear)
else:
writeTssFile(expression, daymonthyear)
return expression
def sample4(expression, what, daymonthyear):
"""
Collects outputpoint value to write it into a time series file
calls function :meth:`management_modules.writeTssFile`
:param expression: array of outputpoint information
:param map: 1D array of data
:param daymonthyear: day =0 , month =1 , year =2
:return: expression
"""
#if dateVar['checked'][dateVar['currwrite'] - 1] >= daymonthyear:
# using a list with is 1 for monthend and 2 for year end to check for execution
value = []
#tss.split('_')[-2]
map10 = []
for i in range(self.var.numberSnowLayers):
w = what +"["+str(i)+"]"
map10.append(eval(w))
# if inputmap is not an array give out error message
if not (hasattr(map10, '__len__')):
msg = "No values in: " + expression[1] + "\nCould not write: " + expression[0]
print(CWATMWarning(msg))
return expression
ii = 0
for key in sorted(self.var.sampleAdresses):
if self.var.sampleAdresses[key] < 0:
v = -999
else:
v = map10[self.var.elepoint[ii]][self.var.sampleAdresses[key]]
value.append(v)
ii += 1
expression[3].append(value)
if dateVar['laststep']:
if expression[2]:
writeTssFileNew(expression, daymonthyear)
else:
writeTssFile(expression, daymonthyear)
return expression
def writeTssFile(expression, daymonthyear):
"""
Write traditional TSS format time series file with PCRaster-style header.
Parameters
----------
expression : list
Output configuration with filename, variable info, and accumulated data
daymonthyear : int
Temporal filter level: 0=daily, 1=monthly, 2=annual
Notes
-----
Creates traditional PCRaster TSS format files with:
- Metadata header with model version and run information
- Column count and timestep numbering
- Fixed-width numeric formatting
- Missing value handling with 1e31 sentinel
Only outputs timesteps matching the specified temporal aggregation level
based on the dateVar checking system.
"""
outputFilename = expression[0]
#if expression[2]: now new csv if true
writeFileHeader(outputFilename,expression)
outputFile = open(outputFilename, "a")
assert outputFile
if len(expression[3]):
numbervalues = len(expression[3][0])
for timestep in range(dateVar['intSpin'], dateVar['intEnd'] + 1):
if dateVar['checked'][timestep - dateVar['intSpin']] >= daymonthyear:
#if dateVar['checked'][timestep - 1] >= daymonthyear:
row = ""
row += " %8g" % timestep
for i in range(numbervalues):
value = expression[3][timestep-1][i]
if isinstance(value, Decimal):
row += " 1e31"
else:
row += " %14g" % value
row += "\n"
outputFile.write(row)
outputFile.close()
def writeTssFileNew(expression, daymonthyear):
"""
Write modern CSV format time series file with date headers.
Parameters
----------
expression : list
Output configuration with filename, variable info, and accumulated data
daymonthyear : int
Temporal filter level: 0=daily, 1=monthly, 2=annual
Notes
-----
Creates modern CSV format files with:
- Comma-separated values
- Date column in DD/MM/YYYY format
- Human-readable headers with gauge coordinates
- Model version and run metadata
Date formatting adjusts to aggregation level (daily dates, month start
for monthly data, year start for annual data). Preferred format for
modern applications and data analysis.
"""
outputFilename = expression[0]
if expression[2]:
writeFileHeaderNew(outputFilename,expression)
outputFile = open(outputFilename, "a")
else:
outputFile = open(outputFilename, "w")
assert outputFile
if len(expression[3]):
numbervalues = len(expression[3][0])
for timestep in range(dateVar['intSpin'], dateVar['intEnd'] + 1):
if dateVar['checked'][timestep - dateVar['intSpin']] >= daymonthyear:
date1 = dateVar['dateBegin'] + datetime.timedelta(days=timestep - 1)
if "month" in os.path.split(outputFilename)[1]:
date1 = date1.replace(day=1)
if "annual" in os.path.split(outputFilename)[1]:
date1 = date1.replace(day=1,month=1)
row = date1.strftime('%d/%m/%Y')
for i in range(numbervalues):
value = expression[3][timestep-1][i]
if isinstance(value, Decimal):
row += ",1e31"
else:
row += ",%10g" % value
row += "\n"
outputFile.write(row)
outputFile.close()
def writeFileHeaderNew(outputFilename, expression):
"""
Write CSV-style header with metadata and gauge coordinates.
Parameters
----------
outputFilename : str
Full path to output CSV file
expression : list
Output configuration containing gauge information and metadata
Notes
-----
Creates comprehensive CSV header with:
- Model run metadata (settings file, execution time, version info)
- Git branch and hash information for reproducibility
- Longitude coordinates row for all gauges
- Latitude coordinates row for all gauges
- Column headers with gauge identifiers (G1, G2, etc.)
Header provides all information needed to interpret time series data
and reproduce the model run that generated the output.
"""
outputFile = open(outputFilename, "w")
# header
# outputFile.write("timeseries " + self._spatialDatatype.lower() + "\n")
header = "Timeseries," + "settingsfile: " + os.path.realpath(settingsfile[0]) + ",Runnning date: " + xtime.ctime(
xtime.time())
header += ",CWATM: " + versioning['exe'] + " Git-Branch:" + versioning['git']["git_branch"] + " Hash:" + versioning['git']["git_hash"]
header += "\n"
outputFile.write(header)
loc = self.var.outpoints
xrow = "xloc"
yrow = "yloc"
head = "Date"
for x in loc[::2]:
xrow = xrow +"," + "%#.4f" % round(x, 4)
xrow = xrow + "\n"
for y in loc[1::2]:
yrow = yrow +"," + "%#.4f" % round(y, 4)
yrow = yrow + "\n"
for i in range(len(loc[::2])):
head = head +",G" + str(i+1)
head = head + "\n"
outputFile.write(xrow)
outputFile.write(yrow)
outputFile.write(head)
outputFile.close()
def writeFileHeader(outputFilename, expression):
"""
Write PCRaster TSS-style header with run metadata and gauge count.
Parameters
----------
outputFilename : str
Full path to output TSS file
expression : list
Output configuration containing gauge information and metadata
Notes
-----
Creates traditional PCRaster TSS header format with:
- Single line metadata (settings, date, version, git info)
- Number of data columns (timestep + gauge columns)
- Column identifiers starting with 'timestep'
- Gauge IDs as column headers
Maintains compatibility with PCRaster and traditional CWatM
time series processing tools.
"""
outputFile = open(outputFilename, "w")
# header
# outputFile.write("timeseries " + self._spatialDatatype.lower() + "\n")
header = "timeseries " + " settingsfile: " + os.path.realpath(settingsfile[0]) + " date: " + xtime.ctime(xtime.time())
header += " CWATM: " + versioning['exe'] + ", " +versioning['lastdate']
try:
import git
header += "git commit " + git.Repo(search_parent_directories=True).head.object.hexsha
except:
ii = 1
header += "\n"
outputFile.write(header)
if len(expression[3]):
numbervalues = len(expression[3][0]) + 1
else: numbervalues = 0
outputFile.write(str(numbervalues) + "\n")
outputFile.write("timestep\n")
for key in sorted(self.var.sampleAdresses):
outputFile.write(str(key) + "\n")
outputFile.close()
# ************************************************************
# ***** WRITING RESULTS: TIME SERIES *************************
# ************************************************************
# xxx=catchmenttotal(self.var.SurfaceRunForest * self.var.PixelArea, self.var.Ldd) * self.var.InvUpArea
# self.var.Tss['DisTS'].sample(xxx)
# self.report(self.Precipitation,cbinding('TaMaps'))
def sample_maptotxt(expression, map):
"""
Export spatial map data to text file for debugging and analysis.
Parameters
----------
expression : list
Output configuration containing filename and variable information
map : numpy.ndarray
1D compressed array with spatial data to export
Notes
-----
Creates simple text dump files with:
- Model run metadata header
- Variable name and cell count information
- One value per line for all valid cells
- Values scaled by 1000 and rounded to 3 decimal places
Used primarily for total aggregation outputs and debugging
spatial patterns. Files have .txt extension regardless of
original output filename.
"""
size = map.shape[0]
outputFilename = os.path.splitext(expression[0])[0] + ".txt"
outputFile = open(outputFilename, "w")
outputFile.write("Map_dump " + " settingsfile: " + os.path.realpath(settingsfile[0]) + " date: " + xtime.ctime(xtime.time()) + "\n")
outputFile.write("Parameter: " + expression[1] + "\n")
outputFile.write("Number of cells: " + str(size) + "\n")
for i in range(size):
v = "%.3f\n" % round(1000. * map[i],3)
outputFile.write(v)
outputFile.close()
# ************************************************************
# ***** WRITING RESULTS: MAPS ******************************
# ************************************************************
# print '----------------#'
varname = None
varnameCollect = []
# set this tru if only the valid cell are stored in netcdf
nindex = self.var.netcdfasindex
if dateVar['curr'] >= dateVar['intSpin'] or ef:
for map in list(outMap.keys()):
for i in range(outMap[map].__len__()):
if outMap[map][i] != "None":
netfile = outMap[map][i][0]
flag = outMap[map][i][2]
# flag to create netcdf or to write
varname = outMap[map][i][1]
type = outMap[map][i][4]
# to use also variables with index from soil e.g. actualET[2]
if '[' in varname:
checkname = varname[0:varname.index("[")]
varname2 = varname.replace("[", "_").replace("]", "_")
else:
checkname = varname
varname2 = varname
checkifvariableexists(map,checkname, list(vars(self.var).keys()))
varnameCollect.append(varname2)
inputmap = 'self.var.' + varname
inputmap2 = 'self.var.' + varname2
# create variable after it is checked on the first timestep
# creates a var to sum/ average the results e.g. self.var.Precipitation_monthtot
if dateVar['curr'] == dateVar['intSpin']:
vars(self.var)[varname2 + "_" + type] = 0
if map[-5:] == "daily":
outMap[map][i][2] = writenetcdf(netfile, varname,"", "undefined", eval(inputmap), dateVar['currDate'],dateVar['currwrite'],
flag, True, dateVar['diffdays'],netcdfindex=nindex)
if map[-8:] == "monthend":
if dateVar['checked'][dateVar['currwrite'] - 1]>0:
outMap[map][i][2] = writenetcdf(netfile, varname, "_monthend", "undefined", eval(inputmap), dateVar['currDate'], dateVar['currMonth'],
flag,True,dateVar['diffMonth'],netcdfindex=nindex)
if map[-8:] == "monthtot":
# sum up daily value to monthly values
vars(self.var)[varname2 + "_monthtot"] = vars(self.var)[varname2 + "_monthtot"] + eval(inputmap)
if map[-8:] == "monthavg":
vars(self.var)[varname2 + "_monthavg"] = vars(self.var)[varname2 + "_monthavg"] + eval(inputmap)
if map[-4:] == "once":
if (returnBool('calc_ef_afterRun') == False) or (dateVar['currDate'] == dateVar['dateEnd']):
# either load already calculated discharge or at the end of the simulation
outMap[map][i][2] = writenetcdf(netfile, varname,"", "undefined", eval(inputmap),
dateVar['currDate'], dateVar['currwrite'], flag, False,netcdfindex=nindex)
if map[-7:] == "12month":
if (returnBool('calc_ef_afterRun') == False) or (dateVar['currDate'] == dateVar['dateEnd']):
# either load already calculated discharge or at the end of the simulation
flag1 = False # create new netcdf file
for j in range(12):
in1 = inputmap + '[' +str(j) + ']'
date1 = datetime.datetime(dateVar['dateEnd'].year, j+1, 1, 0, 0)
outMap[map][i][2] = writenetcdf(netfile, varname,"", "undefined", eval(in1), date1, j+1,
flag1, True,12,netcdfindex=nindex)
flag1 = True # now append to netcdf file
# if end of month is reached
if dateVar['checked'][dateVar['currwrite'] - 1]>0:
if map[-8:] == "monthtot":
outMap[map][i][2] = writenetcdf(netfile, varname,"_monthtot", "undefined", eval(inputmap2+ "_monthtot"), dateVar['currDate'],
dateVar['currMonth'], flag, True, dateVar['diffMonth'],dateunit="months",netcdfindex=nindex)
if map[-8:] == "monthavg":
#days = calendar.monthrange(dateVar['currDate'].year, dateVar['currDate'].month)[1]
avgmap = vars(self.var)[varname2 + "_monthavg"] / dateVar['daysInMonth']
outMap[map][i][2] = writenetcdf(netfile, varname,"_monthavg", "undefined", avgmap,dateVar['currDate'], dateVar['currMonth'],
flag, True,dateVar['diffMonth'],dateunit="months",netcdfindex=nindex)
if map[-9:] == "annualend":
if dateVar['checked'][dateVar['currwrite'] - 1]==2:
outMap[map][i][2] = writenetcdf(netfile, varname,"_annualend", "undefined", eval(inputmap), dateVar['currDate'], dateVar['currYear'],
flag,True,dateVar['diffYear'], dateunit="years", netcdfindex=nindex)
if map[-9:] == "annualtot":
vars(self.var)[varname2 + "_annualtot"] = vars(self.var)[varname2 + "_annualtot"] + vars(self.var)[varname]
if map[-9:] == "annualavg":
vars(self.var)[varname2 + "_annualavg"] = vars(self.var)[varname2 + "_annualavg"] + eval(inputmap)
if dateVar['checked'][dateVar['currwrite'] - 1]==2:
if map[-9:] == "annualtot":
outMap[map][i][2] = writenetcdf(netfile, varname,"_annualtot", "undefined", eval(inputmap2+ "_annualtot"), dateVar['currDate'], dateVar['currYear'], flag, True,
dateVar['diffYear'], dateunit="years", netcdfindex=nindex)
if map[-9:] == "annualavg":
days = 366 if calendar.isleap(dateVar['currDate'].year) else 365
avgmap = vars(self.var)[varname2 + "_annualavg"] / days
outMap[map][i][2] = writenetcdf(netfile, varname,"_annualavg", "undefined", avgmap, dateVar['currDate'], dateVar['currYear'], flag, True,
dateVar['diffYear'],dateunit="years", netcdfindex=nindex)
if map[-8:] == "totaltot":
if dateVar['curr'] >= dateVar['intSpin']:
vars(self.var)[varname2 + "_totaltot"] = vars(self.var)[varname2 + "_totaltot"] + vars(self.var)[varname]
if dateVar['currDate'] == dateVar['dateEnd']:
# at the end of simulation write this map
outMap[map][i][2] = writenetcdf(netfile, varname,"_totaltot", "undefined", eval(inputmap2 + "_totaltot"),
dateVar['currDate'], dateVar['currwrite'], flag, False, netcdfindex=nindex)
if map[-8:] == "totalavg":
if dateVar['curr'] >= dateVar['intSpin']:
vars(self.var)[varname2 + "_totalavg"] = vars(self.var)[varname2 + "_totalavg"] + vars(self.var)[varname]/ float(dateVar['diffdays'])
if dateVar['currDate'] == dateVar['dateEnd']:
# at the end of simulation write this map
outMap[map][i][2] = writenetcdf(netfile, varname,"_totalavg", "undefined", eval(inputmap2 + "_totalavg"),
dateVar['currDate'], dateVar['currwrite'], flag, False, netcdfindex=nindex)
if map[-8:] == "totalend":
if dateVar['currDate'] == dateVar['dateEnd']:
# at the end of simulation write this map
vars(self.var)[varname2 + "_totalend"] = vars(self.var)[varname]
outMap[map][i][2] = writenetcdf(netfile, varname,"_totalend","undefined", vars(self.var)[varname],
dateVar['currDate'], dateVar['currwrite'], flag, False, netcdfindex=nindex)
# ************************************************************
# ***** WRITING RESULTS: TIME SERIES *************************
# ************************************************************
self.var.firstout = firstout(self.var.discharge)
if Flags['gui']:
# if CWatM is started from a GUI - update the progress clock
if hasattr(self.var, 'meteo') and hasattr(self.var.meteo, 'progress_clock'):
# Calculate progress percentage based on dates
total_days = dateVar['intEnd'] - dateVar['intStart'] + 1
current_day = dateVar['curr'] - dateVar['intStart'] + 1
progress_percent = min(100, max(0, int((current_day / total_days) * 100)))
self.var.meteo.progress_clock.setValue(progress_percent)
if Flags['loud']:
print("\r%-6i %10s %10.2f " %(dateVar['currStart'],dateVar['currDatestr'],self.var.firstout), end='')
sys.stdout.flush()
else:
if not(Flags['check']):
if (Flags['quiet']) and (not(Flags['veryquiet'])):
sys.stdout.write(".")
if (not(Flags['quiet'])) and (not(Flags['veryquiet'])):
print("\r%d " % dateVar['currStart'],end ='')
sys.stdout.flush()
# report TSS
for tss in list(outTss.keys()):
for i in range(outTss[tss].__len__()):
# loop for each variable in a section
if outTss[tss][i] != "None":
varname = outTss[tss][i][1]
what = 'self.var.' + outTss[tss][i][1]
# to use also variables with index from soil e.g. actualET[2]
if '[' in varname:
checkname = varname[0:varname.index("[")]
varname2 = varname.replace("[", "_").replace("]", "_")
what2 = what.replace("[", "_").replace("]", "_")
else:
checkname = varname
varname2 = varname
what2 = what
checkifvariableexists(tss, checkname, list(vars(self.var).keys()))
varnameCollect.append(varname2)
if tss[-5:] == "daily":
# what = 'self.var.' + reportTimeSerieAct[tss]['outputVar'][0]
# how = reportTimeSerieAct[outTss[tss][0][0]]['operation'][0]
# if how == 'mapmaximum':
# changed = compressArray(mapmaximum(decompress(eval(what))))
# what = 'changed'
# if how == 'total':
# changed = compressArray(catchmenttotal(decompress(eval(what)) * self.var.PixelAreaPcr,self.var.Ldd) * self.var.InvUpArea)
# what = 'changed'
# print i, outTss[tss][i][1], what
if checkOption('reportsnowstations',True):
if not (Flags['calib']):
outTss[tss][i] = sample4(outTss[tss][i],what,0)
else:
outTss[tss][i] = sample3(outTss[tss][i], eval(what), 0)
if tss[-8:] == "monthend":
# reporting at the end of the month:
outTss[tss][i] = sample3(outTss[tss][i], eval(what), 1)
if tss[-8:] == "monthtot":
# if monthtot is not calculated it is done here
if (varname2 + "_monthtotTss") in vars(self.var):
#vars(self.var)[varname2 + "_monthtotTss"] = vars(self.var)[varname2 + "_monthtotTss"] + vars(self.var)[varname]
vars(self.var)[varname2 + "_monthtotTss"] = vars(self.var)[varname2 + "_monthtotTss"] + eval(what)
else:
#vars(self.var)[varname2 + "_monthtotTss"] = vars(self.var)[varname]
vars(self.var)[varname2 + "_monthtotTss"] = eval(what)
outTss[tss][i] = sample3(outTss[tss][i], eval(what2 + "_monthtotTss"), 1)
if tss[-8:] == "monthavg":
if (varname + "_monthavgTss") in vars(self.var):
vars(self.var)[varname2 + "_monthavgTss"] = vars(self.var)[varname2 + "_monthavgTss"] + eval(what)
else:
vars(self.var)[varname2 + "_monthavgTss"] = 0
vars(self.var)[varname2 + "_monthavgTss"] = vars(self.var)[varname2 + "_monthavgTss"] + eval(what)
avgmap = vars(self.var)[varname2 + "_monthavgTss"] / dateVar['daysInMonth']
outTss[tss][i] = sample3(outTss[tss][i], avgmap, 1)
if tss[-9:] == "annualend":
# reporting at the end of the month:
outTss[tss][i] = sample3(outTss[tss][i], eval(what), 2)
if tss[-9:] == "annualtot":
if (varname2 + "_annualtotTss") in vars(self.var):
vars(self.var)[varname2 + "_annualtotTss"] = vars(self.var)[varname2 + "_annualtotTss"] + eval(what)
else:
vars(self.var)[varname2 + "_annualtotTss"] = eval(what)
outTss[tss][i] = sample3(outTss[tss][i], eval(what2 + "_annualtotTss"), 2)
if tss[-9:] == "annualavg":
if (varname + "_annualavgTss") in vars(self.var):
vars(self.var)[varname2 + "_annualavgTss"] = vars(self.var)[varname2 + "_annualavgTss"] + eval(what)
else:
vars(self.var)[varname2 + "_annualavgTss"] = eval(what)
avgmap = vars(self.var)[varname2 + "_annualavgTss"] /dateVar['daysInYear']
#outTss[tss][i][0].sample2(decompress(avgmap), 2)
outTss[tss][i] = sample3(outTss[tss][i], avgmap, 2)
if tss[-8:] == "totaltot":
if dateVar['curr'] >= dateVar['intSpin']:
if (varname2 + "_totaltotTss") in vars(self.var):
vars(self.var)[varname2 + "_totaltotTss"] = vars(self.var)[varname2 + "_totaltotTss"] + eval(what)
else:
vars(self.var)[varname2 + "_totaltotTss"] = eval(what)
if dateVar['currDate'] == dateVar['dateEnd']:
#outTss[tss][i] = sample_maptotxt(outTss[tss][i], eval(what + "_totaltotTss"))
sample_maptotxt(outTss[tss][i], eval(what2 + "_totaltotTss"))
if tss[-8:] == "totalavg":
if dateVar['curr'] >= dateVar['intSpin']:
if (varname2 + "_totalavgTss") in vars(self.var):
vars(self.var)[varname2 + "_totalavgTss"] = vars(self.var)[varname2 + "_totalavgTss"] + eval(what) / float(dateVar['diffdays'])
else:
vars(self.var)[varname2 + "_totalavgTss"] = eval(what) / float(dateVar['diffdays'])
if dateVar['currDate'] == dateVar['dateEnd']:
#outTss[tss][i] = sample_maptotxt(outTss[tss][i], eval(what + "_totalavgTss"))
sample_maptotxt(outTss[tss][i], eval(what2 + "_totalavgTss"))
# if end of month is reached all monthly storage is set to 0
#if not(varname is None):
for varname in varnameCollect:
if dateVar['checked'][dateVar['currwrite'] - 1] > 0:
if (varname + "_monthtot") in vars(self.var):
vars(self.var)[varname + "_monthtot"] = 0
if (varname + "_monthavg") in vars(self.var):
vars(self.var)[varname + "_monthavg"] = 0
if (varname + "_monthtotTss") in vars(self.var):
vars(self.var)[varname + "_monthtotTss"] = 0
if (varname + "_monthavgTss") in vars(self.var):
vars(self.var)[varname + "_monthavgTss"] = 0
if dateVar['checked'][dateVar['currwrite'] - 1] == 2:
if (varname + "_annualtot") in vars(self.var):
vars(self.var)[varname + "_annualtot"] = 0
if (varname + "_annualavg") in vars(self.var):
vars(self.var)[varname + "_annualavg"] = 0
if (varname + "_annualtotTss") in vars(self.var):
vars(self.var)[varname + "_annualtotTss"] = 0
for ii in range(self.var.noOutpoints):
if (varname + "_annualtotTss"+str(ii)) in vars(self.var):
vars(self.var)[varname + "_annualtotTss"+str(ii)] = 0
if (varname + "_annualavgTss") in vars(self.var):
vars(self.var)[varname + "_annualavgTss"] = 0