Reading SWAT output.hru files in R

The output.hru file is one of the most important output files of the SWAT (Soil and Water Assessment Tool) simulation model. It contains daily or monthly (depending on how you configured your simulation) time series of about 70 variables that are computed within the HRUs (hydrologic response units) of your simulation. The HRUs are the basic simulation units of SWAT, so the output.hru file is about the most detailed output you can get from the model.

The output.hru file tends to be a huge text table, hard formatted with fixed columns (i.e., Fortran style). It mixes monthly and annual values of the output variables, and it is sometimes a pain to read. I wrote a little R function for reading SWAT output.hru files, and I thought it could be useful for somebody. It can be easily adapted to other SWAT output files or versions (I am still using SWAT 2005 here).

Usage: swat_readOutputhru(file,col=NULL,hru=NULL,year=NULL,lulc=NULL,ver=2012)

Arguments:

file Character. Direction of the output.hru file.
col Optional. Vector of column names or column numbers to be read from the output.hru file.
hru Optional. Numeric, id of the HRU to be read. It can also be a vector of numeric ids.
year Optional. Numeric, year to be read. It can also be a vector of years.
lulc Optional. Character, id of a specific land use / land cover (LULC).
ver Optional. Numeric, version of SWAT being used. Defaults to 2012.

Result: a list object with monthly values stored as mon and annual values in anu.

Here’s the function:

swat_readOutputhru <- function(file,col=NULL,hru=NULL,year=NULL,lulc=NULL,ver=2012) {

	# format of the .hru file (SWAT 2012)
	fmt=list(var=c('LULC','HRU','GIS','SUB','MGT','MON','AREA','PRECIP','SNOFALL','SNOMELT','IRR',
		'PET','ET','SW_INIT','SW_END','PERC','GW_RCHG','DA_RCHG','REVAP','SA_IRR','DA_IRR','SA_ST',
		'DA_ST','SURQ_GEN','SURQ_CNT','TLOSS','LATQ','GW_Q','WYLD','DAILYCN','TMP_AV','TMP_MX',
		'TMP_MN','SOL_TMP','SOLAR','SYLD','USLE','N_APP','P_APP','NAUTO','PAUTO','NGRZ','PGRZ',
		'NCFRT','PCFRT','NRAIN','NFIX','F-MN','A-MN','A-SN','F-MP','AO-LP','L-AP','A-SP','DNIT',
		'NUP','PUP','ORGN','ORGP','SEDP','NSURQ','NLATQ','NO3L','NO3GW','SOLP','P_GW','W_STRS',
		'TMP_STRS','N_STRS','P_STRS','BIOM','LAI','YLD','BACTP','BACTLP',
		'WTAB','WTABELO','SNO_HRU','CMUP_KGH','CMTOT_KGH','QTILE','TNO3','LNO3','GW_Q_D','LATQ_CNT'),
		col=c(4,5,9,5,5,5,rep(10,79)))
	if (ver==2009) {
		fmt$var <- fmt$var[1:80]
		fmt$col <- fmt$col[1:80]
	}
	if (ver==2005) {
		fmt$var <- fmt$var[1:75]
		fmt$col <- fmt$col[1:75]
	}
	if (class(w)=='numeric') {
		col <- fmt$var[w]
	}

	# select columns
	if (!is.null(col)) {
		if (!('MON' %in% col)) {
			col <- c('MON',col)
		}
		if (!('LULC' %in% col)) {
			col <- c('LULC',col)
		}
		if (!('HRU' %in% col)) {
			col <- c('HRU',col)
		}
		w <- fmt$var %in% col
		fmt$var <- fmt$var[w]
		fmt$col <- ifelse(w,fmt$col,-fmt$col)
	}

	# read file, rearrange table
	res <- read.fwf(file,fmt$col,
		head=F,skip=9,encoding='latin1',
		strip.white=TRUE,nrow=-1,buffersize=20000)
	colnames(res) <- fmt$var
	res <- res[order(res$HRU),]

	# select hrus by number or by lulc
	if (!is.null(hru)) {
		res <- res[res$HRU>=min(hru) & res$HRU<=max(hru),]
	}
	if (!is.null(lulc)) {
		res <- res[res$LULC==lulc,]
	}

	# monthly and annual tables
	mon <- res[res$MON<=12,]
	anu <- res[res$MON>12,]
	colnames(anu) <- sub('MON','YEA',colnames(anu))
	w <- which(mon$HRU==mon$HRU[1] & mon$MON==mon$MON[1])
	ww <- c((w-1)[-1],nrow(mon))
	years <- min(anu$YEA):max(anu$YEA)
	mon$YEA <- NA
	for (i in 1:length(w)) {
		mon[w[i]:ww[i],][,'YEA'] <- years[i]
	}

	# select years
	if (!is.null(year)) {
		mon <- mon[mon$YEA>=min(year) & mon$YEA<=max(year),]
		anu <- anu[anu$YEA>=min(year) & anu$YEA<=max(year),]
	}
	
	# rearrange
	rownames(mon) <- rownames(anu) <- NULL
	w <- which(colnames(mon)=='MON')
	ww <- which(colnames(mon)=='YEA')
	mon <- mon[,c(colnames(mon)[c(1:w)],'YEA',colnames(mon)[-c(1:w,ww)])]

	# go
	return(list(mon=mon,anu=anu))
}

Example:

file <- './Scenarios/Default/TxtInOut/output.hru'
w <- c('PRECIP','SNOFALL','SNOMELT','IRR','ET',
    'SW_END','SA_ST','DA_ST','WYLD','REVAP')
# the following also works:
# w <- c(1,2,6,8,9,10,11,13,15,22,23,25,26,27,28,29)

# read the whole file
r <- swat_readOutputhru(file,col=w)

# read just one hru and one year
r <- swat_readOutputhru(file,col=w,hru=14,year=2009)

r$mon
r$anu

2 Comments

  • What would you add to the code and where, if you are running a multi-year simulation on a daily time step with more than 50 sub-basins and 500 HRUs to summarize outputs to monthly and annual values? Gracias!

  • sbegueria wrote:

    Hi Johan. I incorporated this code into higher-level functions to read and process SWAT output files, but they very much depend on i) which version of the model you’re using, and; ii) how did you configure the daily outputs. I will however upload these functions just in case they are of help to someone, if I find time for it…

Leave a Reply

Your email is never shared.Required fields are marked *