# $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $ # # Copyright (C) 1999, 2001 by Jan-Oliver Wagner # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # DESCRIPTION: # This script generates some .att files from various input # files. The procedure for 'river.att' is the most complex one. # This function will compare the flow of the discharges # (derived from poulation and domestic flow) with the mean flow of # the corresponding receiving stretch. # If the flow of the discharge is higher than the river flow # a warning is printed. function checkFlowPlausibility() { for (DischID in DischIDlist) { pop = disch[DischID,"Pop"] # [-] effluentFlow = disch[DischID, "Flow"] # [m^3/s] StretchID = disch[DischID, "StretchID"] qmean = rivernet[StretchID, "QMean"] # [m^3/s] if (effluentFlow > qmean) { printf("WARNING: Discharge %s (ID=%s):\n", disch[DischID,"Name"], DischID) >> LOG printf("\tPopulation = %d\n", pop) >> LOG printf("\tEffluent flow (%f [m^3/s]) higher than\n", effluentFlow) >> LOG printf("\tmean river flow (%f [m^3/s])\n", qmean) >> LOG printf("\t=> factor = %f\n", qmean == 0 ? 0 : effluentFlow / qmean) >> LOG count_downstr = split(downstream[StretchID], down, " ") printf("\t%d downstream segments found\n", count_downstr) >> LOG if (count_downstr == 1) { qmean_downstream = rivernet[down[1], "QMean"] # [m^3/s] if (0 + qmean_downstream != 0.0) { printf("\tFactor for the downstream segment (ID=%d, QMean=%f) = %f\n", down[1], qmean_downstream, effluentFlow / qmean_downstream) >> LOG } } continue } if ((qmean - effluentFlow) == 0) { printf("Discharge %s: Mean river flow and effluent flow are equal (=%f).\n", disch[DischID,"Name"], qmean) >> LOG continue } if (effluentFlow == 0) { printf("Warning: Discharge %s (ID=%s): Effluent flow is zero.\n", disch[DischID,"Name"], DischID) >> LOG continue } # else printf("Dilution of discharge %s plausible (dilution factor = %f).\n", disch[DischID,"Name"], (qmean - effluentFlow) / effluentFlow) >> LOG printf("\tdilution factor: (Mean River flow - Effluent flow)/Effluent Flow\n") >> LOG } } function checkFlowPlausibility2() { for (DischID in DischIDlist) { StretchID = disch[DischID,"StretchID"] # compute effluent flow pop = disch[DischID,"Pop"] # [-] effluentFlow = disch[DischID, "Flow"] # [m^3/s] # get upstream segments riverQ = 0 # [m^3/s] count_upstr = split(upstream[StretchID], up, " ") # during automatic discharge to rivernet connection routine # some stretches were splitted and hence the upstream segment # naturally has the same attribute (except the length of course). # In such a case the stretches upstream of the upstream segment should # be considered. indirectUpstreamUsed = 0 if (count_upstr == 1) { # of course only the case when there is just 1 upstream segment. if ((rivernet[StretchID,"QMean"] == rivernet[up[1],"QMean"]) && (rivernet[StretchID,"Q5"] == rivernet[up[1],"Q5"]) && (rivernet[StretchID,"vMean"] == rivernet[up[1],"vMean"]) && (rivernet[StretchID,"v5"] == rivernet[up[1],"v5"])) { StretchID = up[1] indirectUpstreamUsed = 1 } } # compute sum of upstream flow(s) riverQ = 0 # [m^3/s] count_upstr = split(upstream[StretchID], up, " ") for (i = 1;i <= count_upstr; i ++) { upQ[i] = rivernet[up[i], "QMean"] riverQ += upQ[i] } # retrieve flow of receiving stretch qmean = rivernet[StretchID, "QMean"] # [m^3/s] Qsum = effluentFlow + riverQ if (Qsum > qmean) { printf("WARNING:") >> LOG } else { printf("PLAUSIBLE:") >> LOG } printf(" Discharge %s (ID=%s):\n", disch[DischID,"Name"], DischID) >> LOG printf("\tPopulation = %d [m^3/s]\n", pop) >> LOG printf("\tEffluent flow = %f [m^3/s]\n", effluentFlow) >> LOG printf("\tReceiving StretchID = %d\n", StretchID) >> LOG if (indirectUpstreamUsed == 1) { printf("\t\tattention: taken the segment upstream of this one as basis for upstream segments!\n") >> LOG } printf("\tmean flow of receiving stretch = %f [m^3/s]\n", qmean) >> LOG printf("\tupstream segments and their mean flows:\n") >> LOG printf("\t\tID\tQMean\n") >> LOG for (i = 1;i <= count_upstr; i ++) { printf("\t\t%d\t%f\n", up[i], upQ[i]) >> LOG } printf("\tQsum = \tSum(upstream mean flows) + Effluent Flow = %f\n", Qsum) >> LOG printf("\t=> factor Qsum / Qmean = %f\n", qmean == 0 ? 0 : Qsum / qmean) >> LOG } } # This function scan for bifurcation situations. # This is necessary, because the two neighbors in such a # situation needs each others ID as the up_2 information # (required by the simulator). # A pre-scan is necessary for this job - it cannot be done # linear within the function 'writeRiverAtt'. function scanBifurcations() { countBifurcation = 0 for (StretchID in StretchIDlist) { num = split(downstream[StretchID], down_segs, " ") if (num == 1) { # nothing to do } if (num == 2) { # bifurcation! countBifurcation ++ bifurcations[countBifurcation,0] = StretchID bifurcations[countBifurcation,1] = down_segs[1] bifurcations[countBifurcation,2] = down_segs[2] rivernet[down_segs[1],"neighbour"] = down_segs[2] rivernet[down_segs[2],"neighbour"] = down_segs[1] } if (num > 2) { # terrible bifurcation: stop! printf("ERROR: %dple bifurcation: From segment %s to segments %s!\n", num, StretchID, downstream[StretchID]) > "/dev/stderr" printf("ERROR: %dple bifurcation: From segment %s to segments %s!\n", num, StretchID, downstream[StretchID]) >> LOG } } printf("\tNumber of bifurcations: %d\n", countBifurcation) >> LOG if (countBifurcation > 0) { printf("\tList of bifurcations:\n") >> LOG for (i = 0;i < countBifurcation;i ++) { printf("Stretch %d splits up in %s and %s\n", bifurcations[i,0], bifurcations[i,1], bifurcations[i,2]) >> LOG } printf("\tEnd of bifurcation list\n") >> LOG } } function writeRiverAtt() { printf("File river.att processed by %s at %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $\n", ENVIRON["USER"], strftime()) >> LOG printf("Writing table with following columns:\n") >> LOG printf("StretchID(-),type(-),IsDisch(0|1),up1(-),up2(-),rivclass(-),QMean(m^3/s),Q5(m^3/s),vMean(m/s),v5(m/s),RealLength(m),depthMean(m),depth5(m),Name(-)\n") >> LOG printf("#StretchID,type,IsDisch,up1,up2,rivclass,QMean,Q5,vMean,v5,RealLength,depthMean,depth5,Name\n") > "river.att" # now fill the columns with the data newcountRiv = countRiv; first_stretch = countOcean = -1; for (StretchID in StretchIDlist) { # first get the upstream segments count_upstr = split(upstream[StretchID], up, " ") # some debugging (used during development) # printf("count_upstr: %d; stretchid %d; up[1] = %d; upstream[%d] = %s\n",count_upstr, StretchID,up[1], StretchID, upstream[StretchID]) >> LOG # new zero-length stretch must be generated if there are 3 upstream stretches! if (count_upstr == 3) { max ++; # new stretch id to be generated # include information about lakes isLake = 0 for (i = 1; i <= countLakes;i++) { if (StretchID == LakeIDList[i] ) { printf("%d,2,0,%d,%d,%d,0,0,0,0,0,0,0,Zero Stretch (triple confluence)\n", max, up[2], up[3], LAKE_CLASS) > "river.att"; isLake = 1 break; } } if (isLake == 0) { printf("%d,2,0,%d,%d,1,0,0,0,0,0,0,0,Zero Stretch (triple confluence)\n", max, up[2], up[3]) > "river.att"; } count_upstr = 2; up[2] = max; newcountRiv ++; } # if there are too many upstream stretches (>= 4) then error and exit! if (count_upstr > 2) { printf("Panic: count_upstr = %d -", count_upstr) > "/dev/stderr"; printf(" too many upstream segments ") > "/dev/stderr"; printf(" (lid = %d)\n", StretchID) > "/dev/stderr"; exit; } # now get the downstream segments count_downstr = split(downstream[StretchID], down, " ") # is this an end-stretch (i.e. going into the ocean)? if (count_downstr == 0) { first_stretch = StretchID; count_downstr = 1 down[1] = countOcean # create an 'ocean' stretch printf("%d,2,0,%d,%d,1,0,0,0,0,0,0,0,Zero Stretch (ocean)\n", countOcean, StretchID, -- countOcean) > "river.att"; newcountRiv ++; } # if there are more than 2 downsteam stretches: stop! if (count_downstr > 2) { printf("Panic: count_downstr = %d -", count_downstr) > "/dev/stderr"; printf(" too many downstream segments") > "/dev/stderr"; printf(" (lid = %d\n", StretchID) > "/dev/stderr"; exit; } # determine whether this stretch is actually a lake isLake = 0 for (i = 1;i <= countLakes;i ++) { if (StretchID == LakeIDList[i] ) { isLake = 1 break; } } # start now writing columns (row by row) # seg_ID printf("%d,", StretchID) > "river.att" # type (1 = nothing special, 0 = end of tree (source), # 2 = confluence, -2 = bifurcation) # (note: type += 8 for lakes for OUT-VERSION == 2.0) # The tests for count_downstr == 0 are not needed, # because any end-segment is connected to a virtual # segment (negative ID, see above). stretch_type = -1; if (count_upstr == 1 && count_downstr == 1) stretch_type = 1; if (count_upstr == 1 && count_downstr == 2) stretch_type = 1; #if (count_upstr == 2 && count_downstr == 0) # stretch_type = 2; if (count_upstr == 2 && count_downstr == 1) stretch_type = 2; if (count_upstr == 2 && count_downstr == 2) stretch_type = 2; #if (count_upstr == 0 && count_downstr == 0) # stretch_type = 0; if (count_upstr == 0 && count_downstr == 1) stretch_type = 0; if (count_upstr == 0 && count_downstr == 2) stretch_type = 0; if (rivernet[StretchID,"neighbour"] != "") { stretch_type = -2; up[2] = rivernet[StretchID,"neighbour"] } if (stretch_type == -1) { printf("Panic: stretch_type (lid %d) can not be identified ", StretchID) > "/dev/stderr"; printf("(count_upstr = %d, count_downstr = %d\n", count_upstr, count_downstr) > "/dev/stderr"; exit; } # in case it is a lake and we are heading for GREAT-ER 2.0, # the type id is increased by 8 if (OUTVERSION == "2.0" && isLake == 1) { stretch_type = stretch_type + 8 } printf("%d,", stretch_type) > "river.att"; # disch_pres (1 = discharge point is present, # 0 = no discharge point present) disch_pres = 0; for (DischID in DischIDlist) { if (disch[DischID,"StretchID"] == StretchID) disch_pres = 1; } printf("%d,", disch_pres) > "river.att"; # up_1 printf("%d,", up[1]) > "river.att"; # up_2 printf("%d,", up[2]) > "river.att"; # rivclass if (isLake == 0) { printf("1,") > "river.att"; } else { printf("%d,", LAKE_CLASS) > "river.att"; } # flow_m if (rivernet[StretchID,"QMean"] == "" || rivernet[StretchID,"Q5"] == "") { printf("Warning: incomplete flow data for lid = %d\n", StretchID) > "/dev/stderr" ; } printf("%f,", rivernet[StretchID,"QMean"]) > "river.att"; # flow_5% printf("%f,", rivernet[StretchID,"Q5"]) > "river.att"; # flow velocity (mean) printf("%f,", rivernet[StretchID,"vMean"]) > "river.att"; # flow velocity 5%ile printf("%f,", rivernet[StretchID,"v5"]) > "river.att"; # length printf("%f,", rivernet[StretchID,"RealLength"]) > "river.att"; # depth_mean printf("%f,", rivernet[StretchID,"depthMean"]) > "river.att"; # depth_5% printf("%f,", rivernet[StretchID,"depth5"]) > "river.att"; # Name printf("%s", rivernet[StretchID,"Name"]) > "river.att"; printf("\n") > "river.att"; } # Finally write the dead-end ocean segment printf("%d,0,0,0,0,1,0,0,0,0,0,0,0,Zero Stretch (dead end ocean)\n", countOcean) > "river.att"; close ("river.att"); } function writeDischargeTab() { printf("File disch.att processed by %s at %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $\n", ENVIRON["USER"], strftime()) >> LOG printf("Writing table with following columns:\n") >> LOG printf("DischID(-),StretchID(-),MarketID(-),Pop(-),DWF(m^3/s),flow_dom(L/cap/d),flow_nondo(m^3/s),flow_runof(m^3/s),Flow(m^3/s),treated(-),WWTPID(-),wwtp_frac(-),Name(-)\n") >> LOG printf("#DischID,StretchID,MarketID,Pop,DWF,flow_dom,flow_nondo,flow_runof,Flow,treated,WWTPID,wwtp_frac,Name\n") > "disch.att" for (DischID in DischIDlist) { printf("%d,%d,%d,%d,%f,%f,%f,%f,%f,%f,%d,%f,%s\n", DischID, disch[DischID,"StretchID"], disch[DischID,"MarketID"], disch[DischID,"Pop"], disch[DischID,"DWF"], # flow_dom: ((disch[DischID,"Pop"] == 0 || disch[DischID,"Pop"] == "") ? 42 : disch[DischID,"Flow"] * 86400 * 1000 / disch[DischID,"Pop"]), 0, 0, # flow_nondo, flow_runof disch[DischID, "Flow"], disch[DischID,"treated"], disch[DischID,"WWTPID"], disch[DischID,"wwtp_frac"], disch[DischID, "Name"]) > "disch.att"; } close("disch.att"); } function writeRiverClassTab() { printf("File rivclass.att processed by %s at %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $\n", ENVIRON["USER"], strftime()) >> LOG printf("Writing table with following columns:\n") >> LOG printf("RivClassID(-),mode,IsLake(0|1),cor_riv_cs(-),SS_mean(mg/L),SS_stdev(mg/L),k_SS_mean(1/h),k_SS_stdev(1/h),f_oc(-),v_sed_grow(mm/a),eps_sed(-),ro_SS_sed(kg/L),X(mg/L),DO_mean(mgO2/L),DO_setdev(mgO2/L),BOD_mean(mgO2/L),BOD_stdev(mgO2/L),pH_mean(-),pH_stdev(-),a_rivclass(-),k_light(1/m)\n") >> LOG printf("#RivClassID,mode,IsLake,cor_riv_cs,SS_mean,SS_stdev,k_SS_mean,k_SS_stdev,f_oc,v_sed_grow,eps_sed,ro_SS_sed,X,DO_mean,DO_setdev,BOD_mean,BOD_stdev,pH_mean,pH_stdev,a_rivclass,k_light\n") > "rivclass.att"; printf("1,1,0,0.6,20,5,0.01,0.005,0.5,5,0.3,2,5,5,2,20,5,7,1,1,1\n") > "rivclass.att"; printf("%d,1,1,0.6,20,5,0.01,0.005,0.5,5,0.3,2,5,5,2,20,5,7,1,1,1\n", LAKE_CLASS) > "rivclass.att"; close("rivclass.att"); printf("\tNumber of datasets: 2\n") >> LOG } function writeWWTPTab() { printf("File wwtp.att processed by %s at %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $\n", ENVIRON["USER"], strftime()) >> LOG printf("Writing table with following columns:\n") >> LOG printf("WWTPID(-),mode,WWTPtype(0|1|2|3),prim_pres(1|0),cap_prim(-),depth_prim(m),v_over_pri(m/s),R_SS_prim(-),depth_sec(m),v_over_sec(m/s),R_SS_sec(-),capa_sec(-),nr_PE(cap),SS_ML(mg_dwt/L),recycle(-),a_biomass(-),depth_ML(m),B_X(g_BOD/g_dwt/d),Y(g_dwt/g_BOD),DO(mgO2/L),aer_type(0|1),f_aerobic(-),f_anoxic(-),f_anaerob(-),eps_N(-),eps_DN(-),WWTPdescr\n") >> LOG printf("#WWTPID,mode,WWTPtype,prim_pres,cap_prim,depth_prim,v_over_pri,R_SS_prim,depth_sec,v_over_sec,R_SS_sec,capa_sec,nr_PE,SS_ML,recycle,a_biomass,depth_ML,B_X,Y,DO,aer_type,f_aerobic,f_anoxic,f_anaerob,eps_N,eps_DN,WWTPdescr\n") > "wwtp.att" # this is a primary only plant: printf("10,1,1,1,5,3,0.0005,0.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,primary only plant\n") > "wwtp.att" # this is an AS without primary: printf("15,1,2,0,0,3,0.0005,0.8,3,0.000556,0.99,3,-1,4000,1,1,2,0.25,0.25,5,1,1,0,0,1,0,activated sludge plant without primary\n") > "wwtp.att" # this is an AS + primary plant: printf("20,1,2,1,5,3,0.0005,0.8,3,0.000556,0.99,3,-1,4000,1,1,2,0.25,0.25,5,1,1,0,0,1,0,activated sludge plant + primary\n") > "wwtp.att" # this is a TF plant without primary: printf("25,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,trickling filter plant without primary\n") > "wwtp.att" # this is a TF plant + primary: printf("30,1,3,1,5,3,0.0005,0.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,trickling filter plant + primary\n") > "wwtp.att" close("wwtp.att"); } BEGIN { if (LOG == "") { # check for logfile print "Panic: No log-file specified" > "/dev/stderr" exit 1 } FS = "," # set field separator LAKE_CLASS = 2 # which of the river class entries describes a lake? countComment = countEmpty = countIgnored = 0 countRiv = countDisch = max = countLakes = 0 currentFile = "" } { if (FILENAME != currentFile) { printf("Now starting to process %s by %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $ at %s:\n", FILENAME, ENVIRON["LOGNAME"], strftime()) >> LOG currentFile = FILENAME } } /^#/ { comments[countComment] = $0 ; countComment ++ ; next } # skip comments (of '.rna' and '.dsd' and '.lks') /^$/ { countEmpty ++ ; next } # skip empty lines /from-tos/ { topo = "from-tos" } /to-froms/ { topo = "to-froms" } NF == 1 { # this is '.lks'. # the stretches that are actually lakes are stored in the vector LakeIDs countLakes++ LakeIDList[countLakes] = $1 printf("\t%d", LakeIDList[countLakes]) >> LOG next } NF == 2 { # upstream/downstreams and downstream/upstreams file if (topo == "from-tos") { downstream[$1] = $2 } else { upstream[$1] = $2 } next } (NF == 9) { # hits the '.rna' file if ($1 > max) max = $1 StretchIDlist[$1] = $1 rivernet[$1,"QMean"] = $2 rivernet[$1,"Q5"] = $3 # equations for flow velocity estimation from: # GREAT-ER Working Note No. 5, IH, Wallingford, UK if (rivernet[$1,"QMean"] == 0.0) { vMean = v5 = 0 } else { vMean = exp(-0.583*log(10)) * exp(0.283*log(rivernet[$1,"QMean"])) v5 = exp(-0.583*log(10)) * exp(0.283*log(rivernet[$1,"QMean"])) * exp(0.495*log(rivernet[$1,"Q5"]/rivernet[$1,"QMean"])) } rivernet[$1,"vMean"] = ($4 == "" ? vMean : $4) rivernet[$1,"v5"] = ($5 == "" ? v5 : $5) rivernet[$1,"RealLength"] = $6 # depth estimation: Regime Theory (Simons & Albertson, 1960) # Simons DB & Albertson NL (1960) Uniform water conveyance # channels in alluvial materials. ASCE, Vol 86, No. HY5, 1960. if (rivernet[$1,"QMean"] == 0.0) { depthMean = depth5 = 0 } else { rMean = 0.57 * exp(0.36 * log(rivernet[$1,"QMean"])) r5 = 0.57 * exp(0.36 * log(rivernet[$1,"Q5"])) dMean = (rMean > 2.13 ? 0.610 + 0.93 * rMean : 1.21 * rMean) d5 = (r5 > 2.13 ? 0.610 + 0.93 * r5 : 1.21 * r5) } rivernet[$1,"depthMean"] = ($7 == "" ? dMean : $7) rivernet[$1,"depth5"] = ($8 == "" ? d5 : $8) rivernet[$1,"Name"] = $9 countRiv ++ next } (NF == 10) { # hits the '.dsd' file (has actually only 9 columns, # but would conflict with .rna otherwise DischIDlist[$1] = $1 disch[$1,"StretchID"] = $8 disch[$1,"MarketID"] = 1 disch[$1,"Pop"] = $4 if ($5 == 0 || $5 == "") { disch[$1,"DWF"] = 0 } else { disch[$1,"DWF"] = $5 / 86400.0 # m^3/d -> m^3/s } if ($6 == 0 || $6 == "") { disch[$1,"Flow"] = 0 } else { disch[$1,"Flow"] = $6 / 86400.0 # m^3/d -> m^3/s } # correct Flow if lower than DWF if (disch[$1, "DWF"] > disch[$1, "Flow"]) { disch[$1, "Flow"] = disch[$1, "DWF"] } disch[$1,"treated"] = 1 # see writeWWTPTab() for the following IDs if ($7 == "PS") disch[$1,"WWTPID"] = 10 if ($7 == "AS") disch[$1,"WWTPID"] = 20 if (($7 != "PS") && ($7 != "AS")) disch[$1,"WWTPID"] = 30 disch[$1,"wwtp_frac"] = 1 disch[$1,"Name"] = $9 countDisch ++ next } { ignored[countIgnored] = $0 ; countIgnored ++ } END { printf("All files processed by %s via $Id: generateAttTables.awk,v 1.4 2003-04-14 15:32:49 jan Exp $ at %s:\n", ENVIRON["LOGNAME"], strftime()) >> LOG printf("\tNumber of empty lines: %d\n", countEmpty) >> LOG printf("\tNumber of comment lines: %d\n", countComment) >> LOG if (countComment > 0) { printf("\tList of comment lines:\n") >> LOG for (i = 0;i < countComment;i ++) { printf("%s\n", comments[i]) >> LOG } printf("\tEnd of comment list\n") >> LOG } printf("\tNumber of ignored lines: %d\n", countIgnored) >> LOG if (countIgnored > 0) { printf("\tList of ignored lines:\n") >> LOG for (i = 0;i < countIgnored;i ++) printf("%s\n", ignored[i]) >> LOG printf("\tEnd of ignore list\n") >> LOG } checkFlowPlausibility(); checkFlowPlausibility2(); scanBifurcations(); writeRiverAtt(); writeDischargeTab(); writeRiverClassTab(); writeWWTPTab(); printf("\n") >> LOG }