# hspfmodel.py
#
# David J. Lampert (djlampert@gmail.com)
#
# Purpose: Contains the HSPFModel class that can be used to store data and
# generate UCI and WDM files for an HSPF simulation.
#
# last updated: 09/16/2015
#

from .wdmutil        import WDMUtil
from .postprocessor  import Postprocessor
from .perlnd         import Perlnd
from .implnd         import Implnd
from .rchres         import Rchres
from .specialactions import SpecialAction

import os, sys, datetime, time, math, hspf

class HSPFModel:
    """
    A class that stores all pertinent information about a watershed for
    building a model for the Hydrologic Simulation Program in Fortran (HSPF).
    """

    def __init__(self, 
                 units = 'Metric',
                 ):
        """
        Initialize the model and point to the lib3.0 library.
        """

        # unit system (English or Metric)

        self.units = units

        # set up dictionaries of external timeseries for the model 
        # (append as needed)

        self.inflows         = {}  # flows into the watershed
        self.precipitations  = {}  # precipitation
        self.evaporations    = {}  # potential evapotranspiration
        self.temperatures    = {}  # temperature
        self.dewpoints       = {}  # dewpoint
        self.windspeeds      = {}  # wind speed
        self.solars          = {}  # solar radiation
        self.flowgages       = {}  # NWIS daily flow gage stations
        self.snowfalls       = {}  # NCDC snowfall depth
        self.snowdepths      = {}  # NCDC snowpack depth
        self.waterquality    = {}  # NWIS water quality data
        self.wateryields     = {}  # water yielded from snowpack
        self.rains           = {}  # rain (NOT snow)
        self.snowcovers      = {}  # snow cover fraction
        self.ices            = {}  # water depth of ice

        # a list of special actions

        self.specialactions = []

    def build_from_watershed(self, 
                             watershed, 
                             filename,
                             print_file = None, 
                             binary_file = None, 
                             tstep = 60, 
                             outlev = 4, 
                             spout = 2, 
                             units = 2, 
                             print_level = 5, 
                             ifraction = 0.5, 
                             evap_multiplier = 1., 
                             landuseyear = None, 
                             verbose = False,
                             ):
        """
        Builds a model from an instance of the Watershed class.

        watershed       -- an instance of the watershed class
        start           -- beginning date for simulation
        end             -- end of simulation
        ucifile         -- name given to the UCI file generated by this object
        wdminfile       -- name of the input (meteorology) WDM file
        wdmoutfile      -- name of the WDM file for output data
        print_file      -- name of optional text output file
        binary_file     -- name of optional binary output file
        tstep           -- time step size in minutes (defaults to one hour)
        outlev          -- run interpreter output level (1-10)
        spout           -- special action output level (1-10)
        units           -- english (1) or metric (2)
        print_level     -- output level in optional print_file
        ifraction       -- fraction of developed land assumed to be impervious
        evap_multiplier -- potential evapotranspiration / pan evaporation
        """

        # name from the watershed instance (max 33 characters)

        self.description = watershed.name[:33]

        # paths to working directory for the simulation

        self.filename = filename

        # path to the messagefile

        directory = os.path.dirname(hspf.__file__)
        self.messagepath = '{}/pyhspf/core/hspfmsg.wdm'.format(directory)

        # add the output levels and units

        self.tstep       = tstep
        self.outlev      = outlev
        self.spout       = spout
        self.print_level = print_level
        self.print_file  = print_file
        self.binary_file = binary_file

        # set up the parameters for the perlnds

        self.evap_multiplier = evap_multiplier
        self.ifraction       = ifraction

        # attach the "updown" dictionary for mass linkages to the model

        self.updown = watershed.updown

        # attach the subbasins to the model

        self.subbasins = watershed.subbasins

        # attach the inlets and outlets to the model

        self.inlets  = watershed.inlets
        self.outlets = watershed.outlets

        # specify the land use year

        self.landuseyear = landuseyear

        # build it

        self.build()

    def build_from_existing(self, 
                            hspfmodel, 
                            filename, 
                            print_file = None, 
                            binary_file = None, 
                            tstep = 60, 
                            outlev = 4, 
                            spout = 2, 
                            units = 2, 
                            print_level = 5, 
                            landuseyear = None, 
                            verbose = False,
                            ):
        """
        Builds a model from another instance of the HSPFModel class (for 
        running similar models or submodels).

        hspfmodel       -- an HSPFModel class instace with all needed data
        start           -- beginning date for simulation
        end             -- end of simulation
        ucifile         -- name given to the UCI file generated by this object
        wdminfile       -- name of the input (meteorology) WDM file
        wdmoutfile      -- name of the WDM file for output data
        print_file      -- name of optional text output file
        binary_file     -- name of optional binary output file
        tstep           -- time step size in minutes (defaults to one hour)
        outlev          -- run interpreter output level (1-10)
        spout           -- special action output level (1-10)
        units           -- english (1) or metric (2) -- English NOT implemented
        print_level     -- output level in optional print_file
        ifraction       -- fraction of developed land assumed to be impervious
        evap_multiplier -- potential evapotranspiration / pan evaporation
        """

        # name from the watershed instance

        self.description = hspfmodel.description

        # paths to working directory for the simulation

        self.filename = filename

        # path to the messagefile

        directory = os.path.dirname(hspf.__file__)
        self.messagepath = '{}/pyhspf/core/hspfmsg.wdm'.format(directory)

        # add the output levels and units
        
        self.tstep       = tstep
        self.outlev      = outlev
        self.spout       = spout
        self.print_level = print_level
        self.print_file  = print_file
        self.binary_file = binary_file

        # set the time stepping

        self.tcode, self.tsstep = self.get_timestep(self.tstep)

        # set up the parameters for the perlnds

        self.evap_multiplier = hspfmodel.evap_multiplier
        self.ifraction       = hspfmodel.ifraction

        # attach the "updown" dictionary for mass linkages to the model

        self.updown = hspfmodel.updown

        # attach the subbasins to the model

        self.subbasins = hspfmodel.subbasins

        # attach the inlets and outlets to the model

        self.inlets  = hspfmodel.inlets
        self.outlets = hspfmodel.outlets

        # specify the land use year

        self.landuseyear = hspfmodel.landuseyear

        # specify the special actions

        self.specialactions = hspfmodel.specialactions

        # specify the perlnds, implnds, rchreses, landtypes, landuse

        self.perlnds   = hspfmodel.perlnds
        self.implnds   = hspfmodel.implnds
        self.rchreses  = hspfmodel.rchreses
        self.landtypes = hspfmodel.landtypes
        self.landuse   = hspfmodel.landuse

        # specify the network

        self.updown = hspfmodel.updown

        # set up the time series

        # set up the time series for the whole watershed

        self.watershed_timeseries = {}

        # set up the time series specific to each the subbasin

        self.subbasin_timeseries = {}

        # set up the time series specific to each landuse category

        self.landuse_timeseries = {}

        # set up the time series specific to each operation

        self.operation_timeseries = {}

    def build(self):
        """Calculates everything needed to run HSPF."""
       
        # check time step size

        if 1440 % self.tstep != 0: 
            print('warning: specified time step is invalid, converting')
            while 1440 % self.tstep != 0: self.tstep -= 1

        # calculate tsstep and tcode

        self.tcode, self.tsstep = self.get_timestep(self.tstep)
        
        # order operations from upstream to downstream using updown dictionary
        
        # divide the subbasins into rows and put them on the chart
        # start at the bottom to organize the linkages better

        rows = [self.outlets]

        top = False
        while not top:
            rows.insert(0, [])
            for next in rows[1]:
                for subbasin in self.updown:
                    if self.updown[subbasin] == next: 
                        rows[0].append(subbasin)
            if len(rows[0]) == 0:
                rows.remove(rows[0])
                top = True

        subbasin_network = []
        for row in rows: 
            for comid in row: subbasin_network.append(comid)

        # keep track of the land segments in each subbasin

        self.landtypes = {}

        # make the operations

        r = 1  # the number assigned to the current rchres and ftable
        p = 1  # the number assigned to the current perlnd
        i = 1  # the number assigned to the current implnd

        self.rchreses = []
        self.perlnds  = []
        self.implnds  = []

        for comid in subbasin_network:

            subbasin = self.subbasins[comid]

            self.landtypes[comid] = {}

            # assign the reach

            delth = subbasin.reach.maxelev - subbasin.reach.minelev
            
            self.rchreses.append(Rchres(r, 
                                        subbasin.name,
                                        subbasin.reach.name, 
                                        subbasin.reach.slopelen, 
                                        delth, 
                                        flow     = subbasin.reach.flow, 
                                        velocity = subbasin.reach.velocity, 
                                        ftable   = subbasin.reach.ftable, 
                                        dam      = subbasin.reach.dam)
                                 )

            self.landtypes[comid]['Reach'] = self.rchreses[-1]
            r += 1
        
            # use the first year if the user doesn't specify

            if self.landuseyear is None: 
                landuse = subbasin.landuse[min(subbasin.landuse.keys())]
            else: 
                landuse = subbasin.landuse[self.landuseyear]

            for landtype, area in landuse.items():

                if landtype == 'Developed':
                    
                    # add an implnd

                    impervious_area = self.ifraction * area
                    pervious_area = (1 - self.ifraction) * area

                    self.implnds.append(Implnd(i, 
                                               subbasin.name, 
                                               impervious_area,
                                               subbasin.flowplane.length,
                                               subbasin.flowplane.slope,
                                               subbasin.flowplane.avgelev,
                                               subbasin.flowplane.centroid[1])
                                        )

                    self.landtypes[comid]['Impervious'] = self.implnds[-1]
                    i += 1
                    
                else: pervious_area = area

                # add the perlnd

                self.perlnds.append(Perlnd(p, 
                                           subbasin.name, 
                                           landtype, 
                                           pervious_area,
                                           subbasin.flowplane.length,
                                           subbasin.flowplane.slope,
                                           subbasin.flowplane.avgelev, 
                                           round(subbasin.reach.minelev),
                                           subbasin.flowplane.centroid[1])
                                    )

                self.landtypes[comid][landtype] = self.perlnds[-1]
                p += 1           

        # make a list of all the landuse types specified

        self.landuse = list(set([l for c in self.landtypes 
                                 for l in self.landtypes[c] if l != 'Reach']))

        # set up the time series for the whole watershed

        self.watershed_timeseries = {}

        # set up the time series specific to each the subbasin

        self.subbasin_timeseries = {}

        # set up the time series specific to each landuse category

        self.landuse_timeseries = {}

        # set up the time series specific to each operation

        self.operation_timeseries = {}

    def add_timeseries(self, 
                       tstype, 
                       identifier, 
                       start_date, 
                       data, 
                       tstep = 60,
                       ):
        """
        Adds a timeseries of type "tstype" with a unique "identifier"
        and list of data with time step "tstep" (in minutes) and a list of
        data "data" to the model. The units should be appropriate for the
        model simulation or comparison.
        """

        # inflow is assumed to be volumetric; e.g., Mm3 (ROVOL) not m3/s (RO)

        if   tstype == 'inflow':        d = self.inflows
        elif tstype == 'precipitation': d = self.precipitations
        elif tstype == 'evaporation':   d = self.evaporations
        elif tstype == 'temperature':   d = self.temperatures
        elif tstype == 'dewpoint':      d = self.dewpoints
        elif tstype == 'wind':          d = self.windspeeds
        elif tstype == 'solar':         d = self.solars
        elif tstype == 'flowgage':      d = self.flowgages
        elif tstype == 'snowfall':      d = self.snowfalls
        elif tstype == 'snowdepth':     d = self.snowdepths
        elif tstype == 'wateryield':    d = self.wateryields
        elif tstype == 'rain':          d = self.rains
        elif tstype == 'snowcover':     d = self.snowcovers
        elif tstype == 'ice':           d = self.ices
        else: 
            print('warning: unknown time series type specified')

        d[identifier] = (start_date, tstep, data)

    def assign_watershed_timeseries(self, tstype, identifier):
        """Assigns a timeseries to the whole watershed."""

        self.watershed_timeseries[tstype] = identifier

    def assign_subbasin_timeseries(self, tstype, comid, identifier):
        """Assigns a timeseries to a subbasin."""

        if tstype not in self.subbasin_timeseries:
            self.subbasin_timeseries[tstype] = {}
        self.subbasin_timeseries[tstype][comid] = identifier

    def assign_landuse_timeseries(self, tstype, landuse, identifier):
        """Assigns a timeseries to a landuse category."""

        if tstype not in self.landuse_timeseries:
            self.landuse_timeseries[tstype] = {}
        self.landuse_timeseries[tstype][landuse] = identifier

    def assign_operation_timeseries(self, tstype, subbasin, otype, identifier):
        """Assigns a timeseries to an operation."""

        if tstype not in self.operation_timeseries:
            self.operation_timeseries[tstype] = {}
        if subbasin not in self.operation_timeseries[tstype]:
            self.operation_timeseries[tstype] = {}
        self.operation_timeseries[tstype][subbasin][otype] = identifier

    def add_special_action(self, action, subbasin, landtype, date, 
                           recurrence = 'annual', value = None):
        """Adds special action "action" to the operation defined by the 
        subbasin identifier and landtype. The recurrence is defaulted to
        every year, and the SpecialActions instance assumes a default value
        for the parameter if it is not provided."""

        self.specialactions.append(SpecialAction(action, subbasin, landtype,
                                                 date, self.tstep, self.units,
                                                 recurrence, value))

    def get_timestep(self, tstep):
        """Gets the value of tstep and tcode based on the time step size in 
        minutes."""

        if tstep % 60 != 0 or tstep < 60: # use minutes
            tcode  = 2
            tsstep = tstep
        elif tstep < 1440:                # use hours
            tcode = 3
            tsstep = tstep // 60
        elif tstep == 1440:
            tcode = 4
            tsstep = 1
        else:
            print('warning: maximum HSPF time step is 1 day\n')
            tcode = None
            tstep = None

        return tcode, tsstep

    def build_wdminfile(self, verbose = False):
        """Builds the input WDM file for an HSPF simulation."""

        # file path

        self.wdminfile = '{}_in.wdm'.format(self.filename)

        # keep track of the dsns

        self.dsns = []

        wdm = WDMUtil(verbose = verbose, messagepath = self.messagepath)
        wdm.open(self.wdminfile, 'w')
 
        # attributes that don't change

        HUC8 = self.description

        attributes = {'TGROUP': 5,          # pointers units-same as tcode
                      'TSFILL': 0.,         # fill value for no data
                      'COMPFG': 2,          # compression (1 = yes, 2 = no)
                      'VBTIME': 1,          # variable time step (always 1) 
                      'IDSCEN': 'INPUT',    # data type
                      'STANAM': HUC8,       # description
                      }

        # assign unique id for each dataset

        n = 100

        # add all the inflow data

        attributes['TSFORM'] = 2
        attributes['TSTYPE'] = 'FLOW'
        attributes['IDCONS'] = 'FLOW'

        for f in self.inflows:

            start_date, tstep, data = self.inflows[f]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = f
            
            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)
            self.dsns.append([n, f, 'FLOW'])

            n += 1

        # add all the precipitation data

        attributes['TSTYPE'] = 'PREC'
        attributes['TSFORM'] =  2
        attributes['IDCONS'] = 'PREC'

        for p in self.precipitations:

            start_date, tstep, data = self.precipitations[p]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = p

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)
            self.dsns.append([n, p, 'PREC'])

            n += 1

        # add the evaporation data

        attributes['TSTYPE'] = 'EVAP'
        attributes['IDCONS'] = 'EVAP'

        for e in self.evaporations:

            start_date, tstep, data = self.evaporations[e]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = e

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)
            self.dsns.append([n, e, 'EVAP'])

            n += 1

        # add the temperature data

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'TOBS'
        attributes['IDCONS'] = 'TEMP'

        for t in self.temperatures:

            start_date, tstep, data = self.temperatures[t]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = t

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, t, 'TOBS'])

            n += 1

        # add the dewpoint data

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'DEWT'
        attributes['IDCONS'] = 'DTMG'

        for t in self.dewpoints:

            start_date, tstep, data = self.dewpoints[t]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = t

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, t, 'DEWT'])

            n += 1

        # add the wind speed data

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'WIND'
        attributes['IDCONS'] = 'WIND'

        for t in self.windspeeds:

            start_date, tstep, data = self.windspeeds[t]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = t

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, t, 'WIND'])

            n += 1

        # add the solar radiation data

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'SOLR'
        attributes['IDCONS'] = 'SOLR'

        for t in self.solars:

            start_date, tstep, data = self.solars[t]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['DESCRP'] = t

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, t, 'SOLR'])

            n += 1

        attributes['TSFORM'] = 2
        attributes['TSTYPE'] = 'WYIE'
        attributes['IDCONS'] = 'WYIELD'

        for subbasin in self.wateryields:

            start_date, tstep, data = self.wateryields[subbasin]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['STAID '] = subbasin

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, subbasin, 'WYIE'])

            n += 1

        attributes['TSFORM'] = 2
        attributes['TSTYPE'] = 'RAIN'
        attributes['IDCONS'] = 'RAIN'

        for subbasin in self.rains:

            start_date, tstep, data = self.rains[subbasin]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['STAID '] = subbasin

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, subbasin, 'RAIN'])

            n += 1

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'SCOV'
        attributes['IDCONS'] = 'SCOV'

        for subbasin in self.snowcovers:

            start_date, tstep, data = self.snowcovers[subbasin]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['STAID '] = subbasin

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, subbasin, 'SCOV'])

            n += 1

        attributes['TSFORM'] = 3
        attributes['TSTYPE'] = 'ICE'
        attributes['IDCONS'] = 'ICE'

        for subbasin in self.ices:

            start_date, tstep, data = self.ices[subbasin]

            # calculate tsstep and tcode

            tcode, tsstep = self.get_timestep(tstep)

            # update attributes

            attributes['TSSTEP'] = tsstep
            attributes['TCODE '] = tcode
            attributes['TSBYR '] = start_date.year
            attributes['STAID '] = subbasin

            wdm.create_dataset(self.wdminfile, n, attributes)
            wdm.add_data(self.wdminfile, n, data, start_date)

            self.dsns.append([n, subbasin, 'ICE'])

            n += 1

        # close it up

        wdm.close(self.wdminfile)
        wdm.close_message()

    def set_states(self, states, hydrology = False, snow = False):
        """Set states."""

        # set hydrology states

        if hydrology:

            for p in self.perlnds:

                p.set_pwat_state(CEPS  = states[p.subbasin][p.landtype]['CEPS'],
                                 SURS  = states[p.subbasin][p.landtype]['SURS'],
                                 UZS   = states[p.subbasin][p.landtype]['UZS'], 
                                 IFWS  = states[p.subbasin][p.landtype]['IFWS'],
                                 LZS   = states[p.subbasin][p.landtype]['LZS'], 
                                 AGWS  = states[p.subbasin][p.landtype]['AGWS'])

            # set the implnd iwater states

            for i in self.implnds:

                i.set_iwat_state1(RETS = states[i.subbasin][i.landtype]['RETS'])
                i.set_iwat_state1(SURS = states[i.subbasin][i.landtype]['SURS'])

            # set the rchres states

            for r in self.rchreses:

                r.VOL = states[r.subbasin][r.landtype]['VOL']

        # set the snow states

        if snow:

            for o in (self.perlnds + self.implnds):

                o.set_snow_init1(packsnow =
                                 states[o.subbasin][o.landtype]['PACKF'],
                                 packice  =
                                 states[o.subbasin][o.landtype]['PACKI'],
                                 packwatr =
                                 states[o.subbasin][o.landtype]['PACKW']
                                 )

    def add_atemp(self):

        for o in self.perlnds + self.implnds:
            o.ATMP = True
            o.set_atemp_dat()

    def add_snow(self, depth = 0., density = 0.08):
        """Adds the SNOW module to the simulation. Key calibration parameters
        are gage catch efficiency factor (SNOWCF) and the degree day melt
        factor (KMELT).  Also, initial pack depth is placed here as simulations
        often begin in winter (hence non-zero)."""

        for o in self.perlnds + self.implnds:
            o.SNOW = True
            o.set_ice_flag()
            o.set_snow_flags()
            o.set_snow_parm1()
            o.set_snow_parm2()
            o.set_snow_init1(packsnow = depth * density, RDENPF = density)
            o.set_snow_init2()

    def add_hydrology(self):
        """Adds the PWAT and IWAT modules to the PERLNDs and IMPLNDs and the
        HYDR module to the RCHRESs."""

        if   self.units == 'Metric':  u = 2
        elif self.units == 'English': u = 1

        for p in self.perlnds: 
            p.PWAT = True
            p.set_pwat_parms(self.units)
            if p.SNOW: p.CSNO = 1
            else:      p.CSNO = 0

        # set up the parameters for the implnds

        for i in self.implnds: 
            i.IWAT = True
            i.set_iwat_parms(self.units)
            if i.SNOW: i.CSNO = 1
            else:      i.CSNO = 0

        # set up the parameters for the rchreses

        for r in self.rchreses: 
            r.HYFG = True
            r.set_hydr_parms(self.units)

    def add_sediment(self):

        for p in self.perlnds:
            p.SED = True
            p.set_sed_parm1()
            p.set_sed_parm2()
            p.set_sed_parm3()
            p.set_sed_stor()

        for i in self.implnds:

            i.SLD = True
            i.set_sld_parm1()
            i.set_sld_parm2()
            i.set_sld_stor()

        for r in self.rchreses:
            r.SDFG = True
            r.set_sandfg()
            r.set_genparm()
            r.set_sand_pm()
            r.set_silt_pm()
            r.set_clay_pm()
            r.set_ssed_init()
            r.set_bed_init()

    def run(self, verbose = True):
        """Runs a simulation. Optionally allows specification of the path
        to the message file, which if spawning multiple processes may
        improve stability."""

        if self.messagepath is None:
            directory = os.path.dirname(hspf.__file__)
            self.messagepath = '{}/pyhspf/core/hspfmsg.wdm'.format(directory)

        if verbose: 
            print('attempting HSPF simulation for {}\n'.format(self.ucifile))

        start = time.time()

        # make a copy of the message file to guard against corrupting the
        # original (source of many headaches)

        retcode = hspf.hsppy(self.ucifile, self.messagepath)

        if retcode > 0 and verbose: 
            print('successfully completed simulation in %.1f' %
                  (time.time() - start), 'seconds\n')

    def warmup(self, 
               start, 
               days = 365, 
               iterations = 1, 
               atemp = False, 
               snow = False, 
               hydrology = False, 
               sediment = False,
               verbose = False
               ):
        """
        Warms up the values of the state variables by running the several
        iterations of the specified number of days (default runs the first year
        twice).
        """

        warmup = []

        if snow:      warmup.append('snow_state')
        if hydrology: warmup.append('water_state')

        # run the specified period

        end = start + datetime.timedelta(days = days)

        for i in range(iterations):

            # run the simulation

            self.build_uci(warmup, start, end, atemp = atemp, snow = snow, 
                           hydrology = hydrology, sediment = sediment,
                           verbose = verbose)
            self.run(verbose = verbose)

            # get the state variables in the perlnds

            p = Postprocessor(self, dates = (start, end))
            states = p.get_states()
            p.close()

            # set the states

            self.set_states(states, hydrology = hydrology, snow = snow)

    def build_uci(self, 
                  targets, 
                  start, 
                  end, 
                  states = None, 
                  atemp = False, 
                  snow = False, 
                  hydrology = False, 
                  sediment = False, 
                  verbose = False,
                  ):
        """
        Builds the User Control Input (UCI) file for an HSPF Simulation.
        """

        if verbose: print('generating the UCI file from the watershed data\n')

        # file types and name; see HSPF documentation for more explanation  
        # ftypes are the different file classes for HSPF I/O (echo, WDM, etc)
        # funits are the Fortran numbers assigned to the files (10-98)
        # fnames are the names or paths to the files

        self.ucifile    = '{}.uci'.format(self.filename)
        self.wdmoutfile = '{}_out.wdm'.format(self.filename)

        # echo file for input file processing name (assumed same as uci)

        self.echofile = self.ucifile[:-4] + '.ech'
        
        # external target names

        self.targets = targets

        self.ftypes = ['MESSU',               'WDM1',          'WDM2']
        self.fnames = [self.echofile, self.wdminfile, self.wdmoutfile]
        self.funits = [10,                        11,              12]

        if self.print_file is not None: 
            self.ftypes.append('')
            self.fnames.append(self.print_file)
            self.print_no = self.funits[-1] + 1
            self.funits.append(self.print_no)

        if self.binary_file is not None:
            self.ftypes.append('BINO')
            self.fnames.append(self.binary_file)
            self.bin_no = self.funits[-1] + 1
            self.funits.append(self.bin_no)

        # set the initial states if provided (in dictionary form from the
        # Postprocessor class)

        if states is not None: self.set_states(states)

        # "RUN" marks the start of the execution

        lines = ['RUN', '']

        # add the GLOBAL block

        lines = lines + self.global_block(1, start, end)

        # add the FILES block

        lines = lines + self.files_block()

        # add the OPN SEQUENCE block

        lines = lines + self.opn_sequence_block()

        # add SPEC-ACTIONS block

        lines = lines + self.spec_actions_block()

        # add the PERLND block

        lines = lines + self.perlnd_block(hydrology = hydrology, 
                                          atemp = atemp, snow = snow, 
                                          sediment = sediment)

        # add the IMPLND block

        lines = lines + self.implnd_block(hydrology = hydrology, atemp = atemp,
                                          snow = snow, sediment = sediment)

        # add the RCHRES block if needed

        if hydrology or sediment:

            lines = lines + self.rchres_block(hydrology = hydrology, 
                                              sediment = sediment)

        # add the EXT SOURCES block

        lines = lines + self.ext_sources_block()

        # add the SCHEMATIC and MASS-LINK blocks if needed

        if hydrology or sediment:

            lines = lines + self.schematic_block()

            lines = lines + self.masslink_block(hydrology = hydrology,
                                                sediment = sediment)

        # add the EXT TARGETS block

        lines = lines + self.ext_targets_block(targets, start.year,
                                               verbose = verbose)

        # add the FTABLES block for the RCHRESES if needed

        if hydrology or sediment:

            lines = lines + self.ftables_block()

        # "close" the simulation info

        lines.append('END RUN')

        # write the lines to the uci file

        with open(self.ucifile, 'w') as f:
        
            for line in lines: f.write(line + '\n')

    def add_ext_targets(self, operations, wdm, year, n, otype, group, var, 
                        tsform, tstype, idcons, func, tcode, tsstep,
                        sub1 = 1, sub2 = 1):
        """Utility routine to shorten the external target addition."""
    
        # the values of these attributes don't change

        attributes = {'TSBYR ': year,       # starting year of data
                      'TGROUP': 5,          # pointers units-same as tcode
                      'TSFILL': 0.,         # fill value for no data
                      'COMPFG': 2,          # compression (1 = yes, 2 = no)
                      'VBTIME': 1,          # variable time step (always 1)
                      'IDSCEN': 'SIM',      # data type
                      }

        # time series specific attributes

        attributes['TSTYPE'] = tstype
        attributes['TSFORM'] = tsform
        attributes['IDCONS'] = idcons
        attributes['TSSTEP'] = tsstep
        attributes['TCODE '] = tcode
       
        if   self.units == 'English': tsys = 'ENGL'
        elif self.units == 'Metric':  tsys = 'METR'

        # add a line for each operation's target

        lines = []
        for o in operations:
            if   otype == 'PERLND': attributes['DESCRP'] = o.landtype
            elif otype == 'IMPLND': attributes['DESCRP'] = 'Impervious'
            elif otype == 'RCHRES': attributes['DESCRP'] = 'Reach'
            attributes['STAID '] = str(o.subbasin)

            t = otype, o.operation, group, var, sub1, sub2, func, n
            l = '{:<6s}{:4d} {:<6s} {:6s}{:2d}{:2d}{:>14s} WDM2{:6d}'.format(*t)
            lines.append(l + ' {:6s} 1 {:4} AGGR REPL'.format(tstype, tsys))

            # add the dataset to the WDM file

            wdm.create_dataset(self.wdmoutfile, n, attributes)
            n+=1

        return lines

    def ext_targets_block(self, 
                          targets, 
                          year, 
                          verbose = False,
                          ):
        """
        Adds the EXT TARGETS block to a UCI file and creates the output WDM 
        file.
        """
        
        lines = ['EXT TARGETS',
                 '<-Volume-> <-Grp> <-Member-><--Mult-->Tran <-Volume->' +
                 ' <Member> Tsys Aggr Amd ***',
                 '<Name>   x        <Name> x x<-factor->strg <Name>' +
                 '   x <Name>qf  tem strg strg***']
    
        wdm = WDMUtil(verbose = verbose, messagepath = self.messagepath)
        wdm.open(self.wdmoutfile, 'w')

        # dataset numbers are assigned by reach in order (subject to revision)
        # keep track of dsns in a dictionary

        n = 1

        # keep up with flows

        if 'reach_flow' in targets:

            otype  = 'RCHRES'    # HSPF operation type
            group  = 'HYDR'      # HSPF group
            var    = 'RO'        # HSPF variable name
            tsform = 3           # 1-mean, 2-total, 3-instant
            tstype = 'FLOW'      # time series type
            idcons = 'RO'        # constituent id
            func   = 'SAME'      # transform function
            tcode  = self.tcode  # 2-mins, 3-hours, 4-days 
            tsstep = self.tsstep # number of units per step

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with reach outflow volume

        if 'reach_outvolume' in targets:

            otype  = 'RCHRES'
            group  = 'HYDR'
            var    = 'ROVOL'
            tstype = 'VOL'
            tsform = 1
            idcons = 'ROVOL'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)        

        # keep up with reach TSS components (daily)

        if 'reach_tss' in targets:

            otype  = 'RCHRES'
            group  = 'SEDTRN'
            var    = 'SSED'
            tstype = 'CONC'
            tsform = 1
            func   = 'AVER'
            tcode  = 4
            tsstep = 1

            # for this one need to change the subscripts from the default
            # total: sub1 = 4

            idcons = 'TSS'

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 4)
            lines = lines + new
            n    += len(new)

        if 'reach_total_sediment' in targets:

            otype  = 'RCHRES'
            group  = 'SEDTRN'
            var    = 'RSED'
            tstype = 'CONC'
            tsform = 1
            func   = 'AVER'
            tcode  = 4
            tsstep = 1

            # for this one need to change the subscripts from the default
            # total (bed + water column): sub1 = 10

            idcons = 'RSED'

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 10)
            lines = lines + new
            n    += len(new)

        if 'reach_solids' in targets:

            otype  = 'RCHRES'
            group  = 'SEDTRN'
            var    = 'SSED'
            tstype = 'CONC'
            tsform = 1
            func   = 'AVER'
            tcode  = 4
            tsstep = 1

            # for this one need to change the subscripts from the default
            # sand:  sub1 = 1
            # silt:  sub1 = 2
            # clay:  sub1 = 3
            # total: sub1 = 4

            # for now i have all three separate, using IDCONS to identify
            # which one is which

            idcons = 'SAND'
            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 1)
            lines = lines + new
            n    += len(new)

            idcons = 'SILT'
            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 2)
            lines = lines + new
            n    += len(new)

            idcons = 'CLAY'
            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 3)
            lines = lines + new
            n    += len(new)

        if 'reach_sediment_in' in targets:

            otype  = 'RCHRES'
            group  = 'SEDTRN'
            var    = 'ISED'
            tstype = 'CONC'
            tsform = 1
            idcons = 'ISED'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 4)
            lines = lines + new
            n    += len(new)

        if 'reach_sediment_out' in targets:

            otype  = 'RCHRES'
            group  = 'SEDTRN'
            var    = 'ROSED'
            tstype = 'CONC'
            tsform = 1
            idcons = 'ROSED'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep, sub1 = 4)
            lines = lines + new
            n    += len(new)

        if 'shear' in targets:

            otype  = 'RCHRES'
            group  = 'HYDR'
            var    = 'TAU'
            tsform = 1
            tstype = 'TAU'
            idcons = 'TAU'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1

            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with land segment groundwater recharge

        if 'groundwater' in targets:

            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'IGWI'
            tsform = 1
            tstype = 'FLOW'
            idcons = 'IGWI'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with land segment evapotranspiration
    
        if 'evaporation' in targets:
    
            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'TAET'
            tsform = 2
            tstype = 'EVAP'
            idcons = 'TAET'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            otype  = 'IMPLND'
            group  = 'IWATER'
            var    = 'IMPEV'
            tsform = 2
            tstype = 'EVAP'
            idcons = 'IMPEV'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with moisture supplied (precipitation w/snow adjustment)

        if 'supply' in targets:
    
            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'SUPY'
            tsform = 2
            tstype = 'SUPY'
            idcons = 'SUPY'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            otype  = 'IMPLND'
            group  = 'IWATER'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with land segment surface runoff
    
        if 'runoff' in targets:

            # surface runoff

            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'SURO'
            tsform = 2
            tstype = 'FLOW'
            idcons = 'SURO'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            # interflow runoff

            var    = 'IFWO'
            idcons = var

            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            # active groundwater outflow

            var    = 'AGWO'
            idcons = var

            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            # impervious surface runoff

            otype  = 'IMPLND'
            group  = 'IWATER'
            var    = 'SURO'
            tsform = 2
            tstype = 'FLOW'
            idcons = 'SURO'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # land segment temperature (daily)
    
        if 'temperature' in targets:
    
            otype  = 'PERLND'
            group  = 'ATEMP'
            var    = 'AIRTMP'
            tsform = 1
            tstype = 'ATMP'
            idcons = 'AIRT'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            otype  = 'IMPLND'
            group  = 'ATEMP'
            var    = 'AIRTMP'
            tsform = 1
            tstype = 'ATMP'
            idcons = 'AIRT'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with hydrology state variables (daily)
    
        if 'water_state' in targets:
    
            # interception storage
    
            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'CEPS'
            tsform = 1
            tstype = 'VOL '
            idcons = 'CEPS'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # surface storage
    
            var    = 'SURS'
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # upper zone storage
    
            var    = 'UZS '
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # interflow storage
    
            var    = 'IFWS'
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
            
            # lower zone storage
    
            var    = 'LZS '
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # active groundwater storage
    
            var    = 'AGWS'
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
        
            # impervious retention storage
    
            otype  = 'IMPLND'
            group  = 'IWATER'
            var    = 'RETS'
            tstype = 'VOL '
            idcons = var
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious surface storage
    
            var    = 'SURS'
            idcons = var
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # reach volume
    
            otype  = 'RCHRES'
            group  = 'HYDR'
            var    = 'VOL'
            tstype = 'VOL'
            idcons = tstype
    
            new = self.add_ext_targets(self.rchreses, wdm, year, n, otype, 
                                       group, var, tsform, tstype, idcons, func,
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # total water stored
    
        if 'water_total' in targets:
    
            otype  = 'PERLND'
            group  = 'PWATER'
            var    = 'PERS'
            tsform = 1
            tstype = 'VOL '
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            otype  = 'IMPLND'
            group  = 'IWATER'
            var    = 'IMPS'
            tsform = 1
            tstype = 'VOL '
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
            idcons = var
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
        # snow state variables (water equivalent depth)
    
        if 'snow_state' in targets:
    
            # snow + ice water equivalent depth
    
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'PACKF'
            tsform = 1
            tstype = 'PACKF'
            idcons = 'PACKF'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep) 
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # pack ice (water equivalent)
    
            otype  = 'PERLND'
            var    = 'PACKI'
            tstype = var
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # pack water (water equivalent depth)
    
            otype  = 'PERLND'
            var    = 'PACKW'
            tstype = var
            idcons = var
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
        # land segment snow pack depth (water equivalent depth)
    
        if 'snowdepth' in targets:
    
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'PACK'
            tsform = 1
            tstype = 'PACK'
            idcons = 'PACK'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
        # land segment snow pack depth (real depth)
    
        if 'snowpack' in targets:
    
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'PDEPTH'
            tsform = 1
            tstype = 'PDEP'
            idcons = 'PDEP'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)

        # land segment snow fall depth (real depth)
    
        if 'snowfall' in targets:

            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'SNOWF'
            tsform = 2
            tstype = 'SNOF'
            idcons = 'SNOF'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)

        # land segment snow melt yield
    
        if 'snowmelt' in targets:
    
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'WYIELD'
            tsform = 2
            tstype = 'WYIE'
            idcons = 'WYIE'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # land segment snow cover fraction
    
        if 'snowcover' in targets:
    
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'SNOCOV'
            tsform = 1
            tstype = 'SCOV'
            idcons = 'SCOV'
            func   = 'AVER'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)
   
        if 'rain' in targets:
 
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'RAINF'
            tsform = 1
            tstype = 'RAIN'
            idcons = 'RAIN'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)

        if 'ice' in targets:
 
            otype  = 'PERLND'
            group  = 'SNOW'
            var    = 'PACKI'
            tsform = 1
            tstype = 'ICE'
            idcons = 'ICE'
            func   = 'AVER'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)

        # keep up with land segment surface runoff
    
        if 'erosion' in targets:

            # pervious erosion

            otype  = 'PERLND'
            group  = 'SEDMNT'
            var    = 'SOSED'
            tsform = 2
            tstype = 'SOSED'
            idcons = 'SOSED'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

            # impervious erosion

            otype  = 'IMPLND'
            group  = 'SOLIDS'
            var    = 'SOSLD'
            tsform = 2
            tstype = 'SOSLD'
            idcons = 'SOSLD'
            func   = 'SAME'
            tcode  = self.tcode
            tsstep = self.tsstep
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)

        # land segment detached sediment
    
        if 'detached_sediment' in targets:
    
            otype  = 'PERLND'
            group  = 'SEDMNT'
            var    = 'DETS'
            tsform = 1
            tstype = 'DETS'
            idcons = 'DETS'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.perlnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, 
                                       tcode, tsstep)
            lines = lines + new
            n    += len(new)
    
            # impervious
    
            otype  = 'IMPLND'
            group  = 'SOLIDS'
            var    = 'SLDS'
            tsform = 1
            tstype = 'SLDS'
            idcons = 'SLDS'
            func   = 'AVER'
            tcode  = 4
            tsstep = 1
    
            new = self.add_ext_targets(self.implnds, wdm, year, n, otype, group,
                                       var, tsform, tstype, idcons, func, tcode,
                                       tsstep)
            lines = lines + new
            n    += len(new)

        # close the wdmeditor

        wdm.close(self.wdmoutfile)
        wdm.close_message()
    
        # finish up 
    
        lines = lines + ['END EXT TARGETS', '']
    
        return lines

    def time_format(self, date, end = False):
        """Takes an instance of datetime.datetime class and returns a date and 
        time as a string compatible with HSPF."""

        if end and date.hour == 0:
            date = date + datetime.timedelta(days = -1)

        h = date.strftime('%H')
        m = date.strftime('%M')

        yr = date.strftime('%Y')
        mo = date.strftime('%m')
        da = date.strftime('%d')

        return ('%s/%s/%s %s:%s' % (yr, mo, da, h, m))

    def format_tstep(self):
        """Takes a time step in minutes and formats it to HH:MM"""

        hours   = self.tstep // 60
        minutes = self.tstep - 60 * hours

        return ('%02d:%02d' % (hours, minutes))

    def global_block(self, run_flag, start, end):
        """Makes the "GLOBAL" block of the UCI file for HSPF."""

        if   self.units == 'English': u = 1
        elif self.units == 'Metric':  u = 2
        
        return  ['GLOBAL',
                 '  UCI created by PyHSPF for %s at %s' % 
                 (self.description, str(datetime.datetime.now())[:-10]),
                 '  START       %s  END    %s' % 
                 (self.time_format(start), 
                  self.time_format(end, end = True)),
                 '  RUN INTERP OUTPT LEVELS    %d    %d' % 
                 (self.outlev, self.spout),
                 ('  RESUME     0 RUN     %d' % run_flag +
                  '                    UNIT SYSTEM    %d  IHM    0' % u),
                 'END GLOBAL', '']

    def files_block(self):
        """Makes the "FILES" block of the UCI file for HSPF."""

        lines = ['FILES',
                 '<FILE>  <UN#>***<----FILE NAME-----------------' + 
                 '-------------------------------->']
    
        for ftype, funit, fname in zip(self.ftypes, self.funits, self.fnames):
            lines.append('{0:<6}  {1:>5}   {2:<62}'.format(ftype, funit, fname))
            
        lines = lines + ['END FILES', '']

        return lines

    def opn_sequence_block(self):
        """Makes the "OPN SEQUENCE" block of the UCI file for HSPF."""

        lines = ['OPN SEQUENCE',
                 '    INGRP              INDELT ' + self.format_tstep()]

        for perlnd in self.perlnds:
            lines.append('      PERLND     %3d' % perlnd.operation)
             
        for implnd in self.implnds:
            lines.append('      IMPLND     %3d' % implnd.operation)
             
        for rchres in self.rchreses:
            lines.append('      RCHRES     %3d' % rchres.operation)

        lines = lines + ['    END INGRP', 'END OPN SEQUENCE', '']

        return lines

    def bin(self, activity):
        """Returns 1 if active or 0 otherwise."""

        if activity: return 1
        else:        return 0

    def spec_actions_block(self):
        """Makes the SPEC-ACTIONS block if any special actions are specified."""

        lines = []

        if len(self.specialactions) > 0:

            lines += ['SPEC-ACTIONS']
            
            lines += [action.get_action(self.landtypes)
                      for action in self.specialactions]

            lines += ['END SPEC-ACTIONS', '']

        return lines

    def perlnd_block(self, hydrology = False, atemp = False, snow = False, 
                     sediment = False):
        """Makes the "PERLND" block of the UCI file for HSPF. Note that 
        this block is not complete as there are many optional blocks for 
        PERLND. Updating should be relatively simple."""

        output_interval = 1

        # add the flags for the modules

        lines = ['PERLND',
                 '  ACTIVITY',
                 '*** <PLS >               Active Sections' + 
                 '                               ***',
                 '*** x -  x ATMP SNOW PWAT  SED  PST  PWG ' +
                 'PQAL MSTL PEST NITR PHOS TRAC ***']

        for p in self.perlnds:

            # convert flags to 1/0

            flags = (p.ATMP, p.SNOW, p.PWAT, p.SED, p.PST, p.PWG, p.PQAL, 
                     p.MSTL, p.PEST, p.NITR, p.PHOS, p.TRAC)

            lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' % 
                         ((p.operation,) + tuple(self.bin(f) for f in flags)))

        lines = lines + ['  END ACTIVITY', '']

        # make the print line; see the HSPF instructions for more info
        # on the print output level if changes are desired

        if self.print_file is not None:

            lines = lines + ['  PRINT-INFO',
                             '*** < PLS>' + 23 * ' ' + 'Print-flags' + 27 * 
                             ' ' + 'PIVL  PYR',
                             '*** x  - x ATMP SNOW PWAT  SED  PST  PWG PQAL ' + 
                             'MSTL PEST NITR PHOS TRAC']

            for p in self.perlnds:
                
                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' %
                             ((p.operation,) + 
                              tuple(self.print_level for i in range(12)) +
                              (output_interval, 12)))

            lines = lines + ['  END PRINT-INFO', '']

        # add the binary info line

        if self.binary_file is not None:

            lines = lines + ['  BINARY-INFO',
                             '*** < PLS>' + 15 * ' ' + 'Binary Output Flags' + 
                             27 * ' ' + 'BIVL  BYR',
                             '*** x  - x ATMP SNOW PWAT  SED  PST  PWG ' +
                             'PQAL MSTL PEST NITR PHOS TRAC']

            for p in self.perlnds:
                
                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' %
                             ((p.operation,) + 
                              (self.print_level for i in range(12)) +
                              (output_interval, 12)))

            lines = lines + ['  END BINARY-INFO', '']

        # add the general info line

        lines = lines + ['  GEN-INFO',
                         '***             Name' + 18 * ' ' + 
                         'Unit-systems   Printer BinaryOut',
                         '*** <PLS >' + 32 * ' ' + 
                         't-series Engl Metr Engl Metr',
                         '*** x -  x                                 ' +
                         'in  out']

        if self.print_file is not None:  pf = self.print_no
        else:                            pf = 0

        if self.binary_file is not None: b  = self.binary_no
        else:                            b  = 0

        if   self.units == 'English': 
            ot = pf, 0, b, 0
            u  = 1
        elif self.units == 'Metric':  
            ot = 0, pf, 0, b
            u  = 2

        for p in self.perlnds:        

            description = '{} {}'.format(p.subbasin, p.landtype)

            line = ('{0:>5}     {1:<21}'.format(p.operation, description[:20]) +
                    '             %d    %d' % (u, u) +
                    '{0:>5}{1:>5}{2:>5}{3:>5}'.format(*ot))
            lines.append(line)

        lines = lines + ['  END GEN-INFO', '']

        # add pwater (if simulating hydrology)

        if hydrology:

            # add the flags for pwater

            lines = lines + ['  PWAT-PARM1',
                             '*** <PLS >                   Flags',
                             '*** x -  x CSNO RTOP UZFG  VCS  VUZ  VNN VIFW ' + 
                             'VIRC  VLE IFFC  HWT IRRG']

            for p in self.perlnds:

                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' % 
                             p.get_pwat_parm1())

            lines = lines + ['  END PWAT-PARM1', '']

            # add the infiltration/overland flow parameters

            lines = lines + ['  PWAT-PARM2',
                             '*** < PLS>    FOREST      LZSN    INFILT      ' +
                             'LSUR     SLSUR     KVARY     AGWRC']

            if self.units == 'English':

                lines = lines + ['*** x  - x                (in)   (in/hr)' + 
                                 '      (ft)              (1/in)   (1/day)']

            elif self.units == 'Metric':
                
                lines = lines + ['*** x  - x                (mm)   (mm/hr)' + 
                                 '       (m)              (1/mm)   (1/day)']

            for p in self.perlnds:

                #N, FOREST, LZSN, INFILT, LSUR, SLSUR, KVARY, AGWRC

                parms = p.get_pwat_parm2()

                lines.append('%5d%15.3f%10.2f%10.4f%10.1f%10.6f%10.3f%10.3f' % 
                             parms)

            lines = lines + ['  END PWAT-PARM2', '']

            # add the evapotranspiration parameters

            lines = lines + ['  PWAT-PARM3',
                             '*** < PLS>    PETMAX    PETMIN    INFEXP    ' +
                             'INFILD    DEEPFR    BASETP    AGWETP']

            if self.units == 'English':

                lines = lines + ['*** x  - x   (deg F)   (deg F)']

            elif self.units == 'Metric':

                lines = lines + ['*** x  - x   (deg C)   (deg C)']

            for p in self.perlnds:

                parms = p.get_pwat_parm3()

                # N, PETMAX, PETMIN, INFEXP, INFILD, DEEPFR, BASETP, AGWETP

                lines.append('%5d%15.1f%10.1f%10.2f%10.2f%10.2f%10.2f%10.2f' % 
                             parms)

            lines = lines + ['  END PWAT-PARM3', '']

            # PWAT-PARM4 upper zone

            lines = lines + ['  PWAT-PARM4',
                             '*** <PLS >     CEPSC      UZSN      NSUR' +
                             '     INTFW       IRC     LZETP']

            if self.units == 'English':

                lines = lines + ['*** x -  x      (in)      (in)' +
                                 '                       (1/day)']

            elif self.units == 'Metric':

                lines = lines + ['*** x -  x      (mm)      (mm)' +
                                 '                       (1/day)']

            for p in self.perlnds:

                # N, CEPSC, UZSN, NSUR, INTFW, IRC, LZETP

                parms = p.get_pwat_parm4()

                lines.append('%5d%15.4f%10.4f%10.4f%10.4f%10.6f%10.4f' %
                             parms)

            lines = lines + ['  END PWAT-PARM4', '']

            # add the PWAT-PARM5 lines

            lines = lines + ['  PWAT-PARM5',
                             '*** <PLS>                 ' +
                             '                                            ***',
                             '*** x -  x       FZG      FZGL']

            for p in self.perlnds: 
                lines.append('%5d%15d%10d' % p.get_pwat_parm5())

            lines = lines + ['  END PWAT-PARM5', '']

            # add the PWAT-PARM6 lines if needed

            lines = lines + ['  PWAT-PARM6',
                             '*** x -  x     MELEV      BELV    GWDATM       ' +
                             'PCW       PGW      UPGW']

            if self.units == 'English':

                lines = lines + ['***             (ft)      (ft)      (ft)']

            elif self.units == 'Metric':

                lines = lines + ['***              (m)       (m)       (m)']

            for p in self.perlnds: 
                lines.append('%5d%15d%10d%10d%10.3f%10.3f%10.3f' %
                             p.get_pwat_parm6())

            lines = lines + ['  END PWAT-PARM6', '']

            # add the PWAT-PARM7 lines if needed

            lines = lines + ['  PWAT-PARM7',
                             '*** x -  x    STABNO      SRRC     SREXP     ' +
                             'IFWSC     DELTA    UELFAC    LELFAC']

            if self.units == 'English':

                lines = lines + ['***                      (/hr)' +
                                 '                (in)      (in)']

            elif self.units == 'Metric':

                lines = lines + ['***                      (/hr)' +
                                 '                (mm)      (mm)']

            for p in self.perlnds: 
                lines.append('%5d%15d%10.3f%10.3f%10.3f%10.3f%10.2f%10.2f' %
                             p.get_pwat_parm7())

            lines = lines + ['  END PWAT-PARM7', '']

            # add the state variable initial values

            if   self.units == 'English': u = '(in)'
            elif self.units == 'Metric':  u = '(mm)'
                
            lines = lines + ['  PWAT-STATE1',
                             '*** < PLS>  PWATER state variables {}'.format(u),
                             '*** x  - x      CEPS      SURS       UZS  ' +
                             '    IFWS       LZS      AGWS      GWVS']

            for p in self.perlnds:
                lines.append('%5d%15.4f%10.4f%10.4f%10.4f%10.4f%10.4f%10.4f' % 
                             p.get_pwat_state())

            lines = lines + ['  END PWAT-STATE1', '']

            # add the monthly variable parameters if needed

            mon_names =['INTERCEP', 'UZSN', 'MANNING', 'INTERFLW','IRC',
                        'LZETPARM']
            mon_descs =['Interception storage capacity at start of each ' +
                        'month {}'.format(u),
                        'Upper zone storage at start of each month {}'.format(u),
                        'Mannings n at start of each month',
                        'Interflow inflow parameter for start of each month',
                        'Interflow recession constant at start of each month',
                        'Lower zone evapotranspiration parameter at start ' +
                        'of each month']

            # add the monthly varible parameters that are constant if needed

            VCSs  = [p.VCS  == 0 for p in self.perlnds] # interception
            VUZs  = [p.VUZ  == 0 for p in self.perlnds] # upper zone
            VNNs  = [p.VNN  == 0 for p in self.perlnds] # manning's n
            VIFWs = [p.VIFW == 0 for p in self.perlnds] # interflow infiltration
            VIRCs = [p.VIRC == 0 for p in self.perlnds] # interflow recession
            VLEs  = [p.VLE  == 0 for p in self.perlnds] # lower zone ET

            mon_flags = [VCSs, VUZs, VNNs, VIFWs, VIRCs, VLEs]
            p_flags   = [any(f) for f in 
                         zip(VCSs, VUZs, VNNs, VIFWs, VIRCs, VLEs)]

            for pflags, name, desc in zip(mon_flags, mon_names, mon_descs):

                if any([not f for f in pflags]): # monthly variable

                    lines = lines + ['  MON-' + name,
                                     '*** <PLS >  ' + desc,
                                     '*** x -  x  JAN  FEB  MAR  APR  MAY  ' +
                                     'JUN  JUL  AUG  SEP  OCT  NOV  DEC']

                    for p, flag in zip(self.perlnds, pflags):
                
                        if not flag and name != 'UZSN':

                            values = tuple(p.get_monthly(name))
                            lines.append('%5d' % p.operation +
                                         '%10.2f%5.2f%5.2f%5.2f' % values[:4]  +
                                         '%5.2f%5.2f%5.2f%5.2f'  % values[4:8] +
                                         '%5.2f%5.2f%5.2f%5.2f'  % values[8:])

                        elif not flag:

                            values = tuple(p.get_monthly(name))
                            lines.append('%5d' % p.operation +
                                         '%10.1f%5.1f%5.1f%5.1f' % values[:4]  +
                                         '%5.1f%5.1f%5.1f%5.1f'  % values[4:8] +
                                         '%5.1f%5.1f%5.1f%5.1f'  % values[8:])

                    lines = lines + ['  END MON-' +  name, '']

        # add the temp (if needed)

        if atemp:

            lines = lines + ['  ATEMP-DAT', '*** <PLS >     ELDAT    AIRTEMP']

            if self.units == 'English': 
                lines.append('*** x -  x      (ft)    (deg F)')
            if self.units == 'Metric': 
                lines.append('*** x -  x       (m)    (deg C)')

            for p in self.perlnds: 
                lines.append('%5d%15.1f%10.1f' % p.get_atemp_dat())

            lines = lines + ['  END ATEMP-DAT', '']

        # add the snow (if needed)

        if snow:

            lines = lines + ['  ICE-FLAG', '*** <PLS > Ice-', '*** x -  x flag']

            for p in self.perlnds: lines.append('%5d%10d' % 
                                                p.get_ice_flag())

            lines = lines + ['  END ICE-FLAG', '', '  SNOW-FLAGS']

            for p in self.perlnds: 
                lines.append('%5d%10d%5d' % p.get_snow_flags())

            lines = lines + ['  END SNOW-FLAGS',
                             '',
                             '  SNOW-PARM1',
                             '*** < PLS>       LAT     MELEV     SHADE    ' +
                             'SNOWCF    COVIND     KMELT     TBASE',
                             '*** x  - x   degrees       (m)              ' +
                             '            (mm)  (mm/d.C)       (C)']

            for p in self.perlnds: 
                
                # N, LAT, MELEV, SHADE, SNOWCF, COVIND, KMELT, TBASE

                parms = p.get_snow_parm1()

                lines.append('%5d%15.2f%10.1f%10.3f%10.3f%10.1f%10.1f%10.1f' % 
                             parms)

            lines = lines + ['  END SNOW-PARM1',
                             '',
                             '  SNOW-PARM2',
                             '*** <PLS >     RDCSN     TSNOW    SNOEVP    ' +
                             'CCFACT    MWATER    MGMELT',
                             '*** x -  x             (deg C)              ' +
                             '                  (mm/day)']

            for p in self.perlnds: 
                lines.append('%5d%15.3f%10.1f%10.3f%10.2f%10.3f%10.3f' % 
                             p.get_snow_parm2())

            lines = lines + ['  END SNOW-PARM2',
                             '',
                             '  SNOW-INIT1',
                             '*** <PLS > Pack-snow  Pack-ice Pack-watr    ' +
                             'RDENPF      DULL    PAKTMP',
                             '*** x -  x      (mm)      (mm)      (mm)    ' +
                             '                   (deg C)']

            for p in self.perlnds: 
                lines.append('%5d%15.2f%10.2f%10.2f%10.2f%10.1f%10.1f' % 
                             p.get_snow_init1())

            lines = lines + ['  END SNOW-INIT1',
                             '',
                             '  SNOW-INIT2',
                             '*** <PLS >    COVINX    XLNMLT    SKYCLR',
                             '*** x -  x      (mm)      (mm)']

            for p in self.perlnds: 
                lines.append('%5d%15.1f%10.1f%10.3f' % 
                             p.get_snow_init2())

            lines = lines + ['  END SNOW-INIT2', '']

        # add the sediment transport

        if sediment:

            lines = lines + ['  SED-PARM1',
                             '*** <PLS >  Sediment parameters 1',
                             '*** x -  x  CRV VSIV SDOP']

            for p in self.perlnds: 
                lines.append('%5d%10d%5d%5d' % p.get_sed_parm1())

            lines = lines + ['  END SED-PARM1',
                             '',
                             '  SED-PARM2',
                             '*** <PLS >      SMPF      KRER      JRER     ' +
                             'AFFIX     COVER      NVSI',
                             '***            (/day)           (kg/ha.dy)']

            for p in self.perlnds: 
                lines.append('%5d%15.2f%10.3f%10.1f%10.3f%10.1f%10.1f'
                             % p.get_sed_parm2())
                         
            lines = lines + ['  END SED-PARM2',
                             '',
                             '  SED-PARM3',
                             '*** <PLS >  Sediment parameter 3',
                             '*** x -  x      KSER      JSER      KGER      ' +
                             'JGER']

            for p in self.perlnds: 
                lines.append('%5d%15.3f%10.2f%10.2f%10.2f' % p.get_sed_parm3())

            lines = lines + ['  END SED-PARM3', '']

#
# need to add monthly flag section, for now omitted
#

            lines = lines + ['  SED-STOR',
                             '*** <PLS >  Detached sediment storage (tonne/ha)',
                             '*** x -  x      DETS']

            for p in self.perlnds: lines.append('%5d%15.3f' % p.get_sed_stor())

            lines = lines + ['  END SED-STOR', '']

        lines = lines[:-1] + ['END PERLND', '']

        return lines

    def implnd_block(self, hydrology = True, atemp = False, snow = False, 
                     sediment = False):
        """Adds the block of lines for the implnds to a UCI file."""

        output_interval = 1

        # add activity flags

        lines = ['IMPLND',
                 '  ACTIVITY',
                 '*** <ILS >               Active Sections',
                 '*** x -  x ATMP SNOW IWAT  SLD  IWG IQAL']

        for i in self.implnds:

            flags = [atemp, snow, hydrology, sediment, i.IWG, i.IQAL]
            
            lines.append('%5d%10d%5d%5d%5d%5d%5d' % 
                         ((i.operation,) + tuple(self.bin(f) for f in flags)))

        lines = lines + ['  END ACTIVITY', '']

        # add print/binary output flags

        if self.units == 'English':  u = 1
        elif self.units == 'Metric': u = 2

        if self.print_file is not None:

            lines = lines + ['  PRINT-INFO', 
                             '*** <ILS > ******** Print-flags ******** PIVL' +
                             '  PYR',
                             '*** x -  x ATMP SNOW IWAT  SLD  IWG IQAL ' +
                             '*********']

            for i in self.implnds:

                flags = ((i.operation,) + 
                         tuple([self.print_level for i in range(6)]) + 
                         (output_interval, 12))
                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d' % flags)

            lines = lines + ['  END PRINT-INFO', '']

        if self.binary_file is not None:

            lines = lines + ['  BINARY-INFO',
                             '*** <ILS > **** Binary-Output-flags **** BIVL' +
                             '  BYR',
                             '*** x -  x ATMP SNOW IWAT  SLD  IWG IQAL ' +
                             '*********']
        
            for i in self.implnds:

                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d' % 
                             (i.operation, 2, 2, 2, 2, 2, 2,output_interval,12))

            lines = lines + ['  END BINARY-INFO', '']

        # add the GEN-INFO

        lines = lines + ['  GEN-INFO',
                         '***             Name             Unit-systems' +
                         '   Printer BinaryOut',
                         '*** <ILS >                           t-series ' +
                         'Engl Metr Engl Metr',
                         '*** x -  x                            in  out']

        if self.print_file is not None:  pf = self.print_no
        else:                            pf = 0

        if self.binary_file is not None: b  = self.binary_no
        else:                            b  = 0

        if   self.units == 'English': t = pf, 0, b, 0
        elif self.units == 'Metric':  t = 0, pf, 0, b

        for i in self.implnds:

            description = '{} impervious'.format(i.subbasin)

            line = ('{0:>5}     {1:<21}'.format(i.operation, description) +
                    '        %d    %d' % (u, u) +
                    '{0:>5}{1:>5}{2:>5}{3:>5}'.format(*t))
            lines.append(line)

        lines = lines + ['  END GEN-INFO', '']

        # add iwater (if needed)

        if hydrology:

            # add the iwat-parm1 block

            lines = lines + ['  IWAT-PARM1',
                             '*** <ILS >        Flags',
                             '*** x -  x CSNO RTOP  VRS  VNN RTLI']

            for i in self.implnds: 
                lines.append('%5d%10d%5d%5d%5d%5d' % i.get_iwat_parm1())

            lines = lines + ['  END IWAT-PARM1', '']

            # add the iwat-parm2 block

            lines = lines + ['  IWAT-PARM2',
                             '*** <ILS >      LSUR     SLSUR      NSUR     ' +
                             'RETSC']

            if self.units == 'English':

                lines = lines + ['*** x -  x      (ft)                     ' +
                                 '     (in)']

            elif self.units == 'Metric':

                lines = lines + ['*** x -  x       (m)                     ' +
                                 '     (mm)']

            for i in self.implnds: 
                lines.append('%5d%15.2f%10.5f%10.5f%10.2f' % i.get_iwat_parm2())

            lines = lines + ['  END IWAT-PARM2', '']

            # add the iwat-parm3 block

            if   self.units == 'English':

                lines = lines + ['  IWAT-PARM3',
                                 '*** <ILS >    PETMAX    PETMIN',
                                 '*** x -  x   (deg F)   (deg F)']

            elif self.units == 'Metric':

                lines = lines + ['  IWAT-PARM3',
                                 '*** <ILS >    PETMAX    PETMIN',
                                 '*** x -  x   (deg C)   (deg C)']

            for i in self.implnds: 
                lines.append('%5d%15.1f%10.1f' % i.get_iwat_parm3())

            lines = lines + ['  END IWAT-PARM3', '']

            # add the initial values of the state variables

            if self.units == 'English':

                lines = lines + ['  IWAT-STATE1',
                                 '*** <ILS >  IWATER state variables (in)',
                                 '*** x -  x      RETS      SURS']

            if self.units == 'Metric':

                lines = lines + ['  IWAT-STATE1',
                                 '*** <ILS >  IWATER state variables (mm)',
                                 '*** x -  x      RETS      SURS']

            for i in self.implnds: 
                lines.append('%5d%15.3f%10.3f' % i.get_iwat_state1())

            lines = lines + ['  END IWAT-STATE1', '']

        # add the temp (if needed)

        if atemp:

            lines = lines + ['  ATEMP-DAT',
                             '*** <PLS >     ELDAT    AIRTEMP']

            if self.units == 'English': 
                lines.append('*** x -  x      (ft)    (deg F)')
            if self.units == 'Metric': 
                lines.append('*** x -  x       (m)    (deg C)')

            for i in self.implnds: 
                lines.append('%5d%15.1f%10.1f' % tuple(i.get_atemp_dat()))

            lines = lines + ['  END ATEMP-DAT', '']

        # add the snow (if needed)

        if snow:

            lines = lines + ['  ICE-FLAG', '*** <PLS > Ice-', '*** x -  x flag']

            for i in self.implnds: lines.append('%5d%10d' % 
                                                tuple(i.get_ice_flag()))

            lines = lines + ['  END ICE-FLAG', '', '  SNOW-FLAGS']

            for i in self.implnds: 
                lines.append('%5d%10d%5d' % tuple(i.get_snow_flags()))

            lines = lines + ['  END SNOW-FLAGS',
                             '',
                             '  SNOW-PARM1',
                             '*** < PLS>       LAT     MELEV     SHADE    ' +
                             'SNOWCF    COVIND     KMELT     TBASE',
                             '*** x  - x   degrees       (m)              ' +
                             '            (mm)  (mm/d.C)       (C)']

            for i in self.implnds: 
                lines.append('%5d%15.2f%10.1f%10.3f%10.3f%10.1f%10.1f%10.1f' % 
                             tuple(i.get_snow_parm1()))

            lines = lines + ['  END SNOW-PARM1',
                             '',
                             '  SNOW-PARM2',
                             '*** <PLS >     RDCSN     TSNOW    SNOEVP    ' +
                             'CCFACT    MWATER    MGMELT',
                             '*** x -  x             (deg C)              ' +
                             '                  (mm/day)']

            for i in self.implnds: 
                lines.append('%5d%15.3f%10.1f%10.3f%10.2f%10.3f%10.3f' % 
                             tuple(i.get_snow_parm2()))

            lines = lines + ['  END SNOW-PARM2',
                             '',
                             '  SNOW-INIT1',
                             '*** <PLS > Pack-snow  Pack-ice Pack-watr    ' +
                             'RDENPF      DULL    PAKTMP',
                             '*** x -  x      (mm)      (mm)      (mm)    ' +
                             '                   (deg C)']

            for i in self.implnds: 
                lines.append('%5d%15.2f%10.2f%10.2f%10.2f%10.1f%10.1f' % 
                             tuple(i.get_snow_init1()))

            lines = lines + ['  END SNOW-INIT1',
                             '',
                             '  SNOW-INIT2',
                             '*** <PLS >    COVINX    XLNMLT    SKYCLR',
                             '*** x -  x      (mm)      (mm)']

            for i in self.implnds: 
                lines.append('%5d%15.1f%10.1f%10.3f' % 
                             tuple(i.get_snow_init2()))

            lines = lines + ['  END SNOW-INIT2', '']

        # add the sediment transport

        if sediment:

            lines = lines + ['  SLD-PARM1',
                             '*** <ILS >     Flags',
                             '*** x -  x VASD VRSD SDOP']

            for i in self.implnds: 
                lines.append('%5d%10d%5d%5d' % i.get_sld_parm1())

            lines = lines + ['  END SLD-PARM1',
                             '',
                             '  SLD-PARM2',
                             '***             KEIM      JEIM    ACCSDP    ' +
                             'REMSDP',
                             '*** <ILS >                    (tne/ha.d)    ' +
                             '(/day)',
                             '*** x -  x']

            for i in self.implnds: 
                lines.append('%5d%15.3f%10.3f%10.4f%10.3f' % i.get_sld_parm2())
        
            lines = lines + ['  END SLD-PARM2',
                             '',
                             '  SLD-STOR',
                             '*** <ILS >  Solids storage (tonne/ha)',
                             '*** x -  x']

            for i in self.implnds: lines.append('%5d%15.3f' % i.get_sld_stor())

            lines = lines + ['  END SLD-STOR', '']

        # finish up

        lines = lines[:-1] + ['END IMPLND', '']

        return lines

    def rchres_block(self, hydrology = False, sediment = False):
        """Adds the block of lines for the reaches for HSPF to a UCI file."""

        output_interval = 1
        lines = ['RCHRES',
                 '  ACTIVITY',
                 '*** RCHRES  Active sections',
                 '*** x -  x HYFG ADFG CNFG HTFG SDFG GQFG OXFG NUFG PKFG PHFG']

        for r in self.rchreses:

            if any([r.CNFG, r.HTFG, r.SDFG, r.GQFG, r.OXFG, r.NUFG, r.PKFG, 
                    r.PKFG, r.PHFG]): 
                r.ADFG = True

            flags =  [r.HYFG, r.ADFG, r.CNFG, r.HTFG, r.SDFG, r.GQFG, 
                      r.OXFG, r.NUFG, r.PKFG, r.PHFG]

            lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d' % 
                         ((r.operation,) + tuple(self.bin(f) for f in flags)))

        lines = lines + ['  END ACTIVITY', '']

        if self.print_file is not None:  pf = self.print_no
        else:                            pf = 0

        if self.binary_file is not None: b  = self.binary_no
        else:                            b  = 0

        if self.print_file is not None:

            lines = lines + ['  PRINT-INFO',
                             '*** RCHRES  Printout level flags',
                             '*** x -  x HYDR ADCA CONS HEAT  SED  GQL ' +
                             'OXRX NUTR PLNK PHCB PIVL  PYR']
                     
            for r in self.rchreses:

                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' % 
                             ((r.operation,) + 
                              tuple(self.print_level for i in range(10)) +
                              (output_interval, 12)))

            lines = lines + ['  END PRINT-INFO', '']

        if self.binary_file is not None:

            lines = lines + ['  BINARY-INFO',
                             '*** RCHRES  Binaryout level flags',
                             '*** x -  x HYDR ADCA CONS HEAT  SED  GQL ' +
                             'OXRX NUTR PLNK PHCB PIVL  PYR']

            for r in self.rchreses:

                lines.append('%5d%10d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d%5d' % 
                             ((r.operation,) + tuple(2 for i in range(10)) +
                              (output_interval, 12)))

            lines = lines + ['  END BINARY-INFO', '']

        lines = lines + ['  GEN-INFO',
                         '***              Name        Nexits   Unit Systems' +
                         '   Printer      BinaryOut',
                         '*** RCHRES                               t-series' +
                         '  Engl Metr LKFG Engl Metr',
                         '*** x -  x                                 in  out']

        for r in self.rchreses:

            description = '{0} {1}'.format(r.subbasin, r.name)[:20]

            if self.print_file is None:  pf = 0
            else:                        pf = self.print_no

            if self.binary_file is None: b  = 0
            else:                        b  = self.binary_no

            if r.lake: l = 1
            else:      l = 0

            if   self.units == 'English': t = r.nexits, 1, 1, pf, 0, l, b, 0
            elif self.units == 'Metric':  t = r.nexits, 2, 2, 0, pf, l, 0, b

            line = ('{0:>5}     {1:<20}'.format(r.operation, description) +
                    '%5d%10d%5d%5d%5d%5d%5d%5d' % t)
            lines.append(line)

        lines = lines + ['  END GEN-INFO', '']

        # add the HYDR-PARM1 block

        lines = lines + ['  HYDR-PARM1',
                         '***         Flags for HYDR section',
                         '***RC HRES  VC A1 A2 A3  ODFVFG for each *** ' +
                         'ODGTFG for each     FUNCT  for each',
                         '*** x  - x  FG FG FG FG  possible   exit *** ' + 
                         'possible   exit     possible   exit'
                         ]

        for r in self.rchreses:

            vs = r.get_hydr_parm1()
            lines.append('%5d%9d%3d%3d%3d%5d%3d%3d%3d%3d%8d' % vs[:11] +
                         '%3d%3d%3d%3d%8d%3d%3d%3d%3d' % vs[11:])

        lines = lines + ['  END HYDR-PARM1', '']

        # add the HYDR-PARM2 block

        lines = lines + ['  HYDR-PARM2',
                         '*** RCHRES FDSN FTBN       LEN     DELTH     STCOR' +
                         '        KS      DB50']

        if self.units == 'English':

            lines.append('*** x -  x                (mi)      (ft)      (ft)' +
                         '                (in)')

        elif self.units == 'Metric':
            
            lines.append('*** x -  x                (km)       (m)       (m)' +
                         '                (mm)')
                    
        for r in self.rchreses:

            lines.append('%5d%10d%5d%10.2f%10.2f%10.2f%10.2f%10.2f' % 
                         r.get_hydr_parm2())

        lines = lines + ['  END HYDR-PARM2', '']

        # add the HYDR-INIT block

        lines = lines + ['  HYDR-INIT',
                         '***         Initial conditions for HYDR section',
                         '***RCHRES        VOL  CAT Initial value  of COLIND' +
                         '     initial  value  of OUTDGT']

        if self.units == 'English':

            lines.append('*** x  - x     ac-ft      for each possible   exit' +
                         '    for each possible exit,m3')

        if self.units == 'Metric':

            lines.append('*** x  - x       Mm3      for each possible   exit' +
                         '    for each possible exit,m3')

        for r in self.rchreses:

            lines.append('%5d%15.3f%10d%5d%5d%5d%5d%10d%5d%5d%5d%5d' % 
                         r.get_hydr_init())

        lines = lines + ['  END HYDR-INIT', '']

        # add the sediment transport

        if sediment:

            lines = lines + ['  SANDFG', '*** RCHRES', '*** x -  x SNDFG']

            for r in self.rchreses: lines.append('%5d%10d' % r.get_sandfg())

            lines = lines + ['  END SANDFG', 
                             '',
                             '  SED-GENPARM',
                             '*** RCHRES    BEDWID    BEDWRN       POR',
                             '*** x -  x       (m)       (m)']

            for r in self.rchreses: 
                lines.append('%5d%15.2f%10.2f%10.2f' % r.get_genparm())

            lines = lines + ['  END SED-GENPARM',
                             '',
                             '  SAND-PM',
                             '*** RCHRES         D         W       ' +
                             'RHO     KSAND    EXPSND',
                             '*** x -  x       (mm)  (mm/sec)  (gm/cm3)']

            for r in self.rchreses: 
                lines.append('%5d%15.3f%10.2f%10.2f%10.2f%10.2f' % 
                             r.get_sand_pm())

            lines = lines + ['  END SAND-PM',
                             '',
                             '  SILT-CLAY-PM',
                             '*** RCHRES         D         W       RHO     ' +
                             'TAUCD     TAUCS         M',
                             '***             (mm)  (mm/sec)   (gm/cm3)  ' +
                             '(kg/m2)   (kg/m2)(kg/m2.dy)']

            for r in self.rchreses:
                lines.append('%5d%15.4f%10.4f%10.2f%10.2f%10.2f%10.2f' %
                             r.get_silt_pm())

            lines = lines + ['  END SILT-CLAY-PM',
                             '',
                             '  SILT-CLAY-PM',
                             '*** RCHRES         D         W       RHO     ' +
                             'TAUCD     TAUCS         M',
                             '***             (mm)  (mm/sec)   (gm/cm3)  ' +
                             '(kg/m2)   (kg/m2)(kg/m2.dy)']

            for r in self.rchreses:
                lines.append('%5d%15.4f%10.4f%10.2f%10.2f%10.2f%10.2f' %
                             r.get_clay_pm())
        
            lines = lines + ['  END SILT-CLAY-PM',
                             '',
                             '  SSED-INIT',
                             '*** RCHRES     Suspended sed concs (mg/l)',
                             '*** x -  x      Sand      Silt      Clay']

            for r in self.rchreses: 
                lines.append('%5d%15.2f%10.2f%10.2f' % r.get_ssed_init())

            lines = lines + ['  END SSED-INIT',
                             '',
                             '  BED-INIT',
                             '*** RCHRES    BEDDEP  Initial bed composition',
                             '*** x -  x       (m)      Sand      Silt' +
                             '      Clay']

            for r in self.rchreses: 
                lines.append('%5d%15.2f%10.3f%10.3f%10.3f' % r.get_bed_init())

            lines = lines + ['  END BED-INIT', '']

        # finish up

        lines = lines[:-1] + ['END RCHRES', '']

        return lines

    def ext_sources_block(self, reach = False, atemp = False, snow = False, 
                          hydrology = False, sediment = False, verbose = False):
        """Adds the block of lines for the external data sources (timeseries)
        for an HSPF model."""

        # units

        if   self.units == 'English': units = 'ENGL'
        elif self.units == 'Metric':  units = 'METR'
                
        lines = ['EXT SOURCES',
                 '<-Volume-> <Member> SsysSgap<--Mult-->Tran ' +
                 '<-Target vols> <-Grp> <-Member-> ***',
                 '<Name>   x <Name> x tem strg<-factor->strg <Name>' +
                 '   x   x        <Name> x x ***']

        # operations

        otypes, nmins, nmaxs = [], [], []

        if len(self.perlnds) > 0: 
            otypes.append('PERLND')
            nmins.append(min([p.operation for p in self.perlnds]))
            nmaxs.append(max([p.operation for p in self.perlnds]))
        if len(self.implnds) > 0: 
            otypes.append('IMPLND')
            nmins.append(min([i.operation for i in self.implnds]))
            nmaxs.append(max([i.operation for i in self.implnds]))
        if len(self.rchreses) > 0 and reach: 
            otypes.append('RCHRES')
            nmins.append(min([r.operation for r in self.rchreses]))
            nmaxs.append(max([r.operation for r in self.rchreses]))

        # assign watershed-wide timeseries

        for tstype, identifier in self.watershed_timeseries.items():

            if tstype == 'precipitation':

                group  = 'EXTNL'
                dtype  = 'PREC'
                gtype  = 'PREC'
                factor = '1.00'

            elif tstype == 'evaporation':

                group  = 'EXTNL'
                dtype  = 'EVAP'
                factor = '{:5.3f}'.format(self.evap_multiplier)

            elif tstype == 'temperature':

                group  = 'EXTNL'
                dtype  = 'TOBS'
                gtype  = 'GATMP'
                factor = '1.00'

            elif tstype == 'dewpoint':

                group  = 'EXTNL'
                dtype  = 'DEWT'
                factor = '1.00'

            elif tstype == 'wind':

                group  = 'EXTNL'
                dtype  = 'WIND'
                factor = '1.00'

            elif tstype == 'solar':

                group  = 'EXTNL'
                dtype  = 'SOLR'
                factor = '1.00'

            else: dtype = None

            # find the dsn

            for dsn, comid, ts in self.dsns:

                if comid == identifier and ts == dtype: break

            # format and add the lines

            for otype, nmin, nmax in zip(otypes, nmins, nmaxs):

                if otype == 'RCHRES' and tstype == 'evaporation':
                    gtype = 'POTEV'
                elif tstype == 'evaporation':
                    gtype = 'PETINP'

                if otype == 'RCHRES' and tstype == 'dewpoint':
                    gtype = 'DEWTMP'
                elif tstype == 'dewpoint': 
                    gtype  = 'DTMPG'

                if otype == 'RCHRES' and tstype == 'wind':
                    gtype = 'WIND'
                elif tstype == 'wind': 
                    gtype  = 'WINMOV'

                if tstype == 'solar': 
                    gtype  = 'SOLRAD'

                if dtype is not None:
                    v1 = (dsn, dtype, units, factor)
                    v2 = (otype, nmin, nmax, group, gtype)
                    l = ('WDM1{0:>6} {1:<9}{2:}ZERO{3:>10}SAME '.format(*v1)
                         + '{0:.6}{1:>4}{2:>4} {3:<6} {4:<6}'.format(*v2))
                    lines.append(l)

        # subbasin timeseries

        for tstype, d in self.subbasin_timeseries.items():

            if tstype == 'precipitation':

                group  = 'EXTNL'
                dtype  = 'PREC'
                gtype  = 'PREC'
                factor = '1.00'

            elif tstype == 'evaporation':

                group  = 'EXTNL'
                dtype  = 'EVAP'
                factor = '{:5.3f}'.format(self.evap_multiplier)

            elif tstype == 'temperature':

                group  = 'EXTNL'
                dtype  = 'TOBS'
                gtype  = 'GATMP'
                factor = '1.00'

            elif tstype == 'dewpoint':

                group  = 'EXTNL'
                dtype  = 'DEWT'
                factor = '1.00'

            else: dtype = None

            # for each subbasin, assign timeseries to all operations

            for subbasin, identifier in d.items():

                # find the dsn

                if dtype is not None:

                    for dsn, comid, ts in self.dsns:

                        if comid == identifier and ts == dtype: break

                    # assign to all operations

                    for o in self.perlnds + self.implnds:

                        if (o.landtype in self.landtypes[subbasin] and 
                            o.subbasin == subbasin):

                            if   o.landtype == 'Reach':      otype = 'RCHRES'
                            elif o.landtype == 'Impervious': otype = 'IMPLND'
                            else:                            otype = 'PERLND'

                            if o.landtype == 'Reach' and tstype == 'dewpoint':
                                gtype = 'DEWTMP'
                            elif tstype == 'dewpoint': 
                                gtype  = 'DTMPG'

                            if o.landtype == 'Reach' and tstype =='evaporation':
                                gtype = 'POTEV'
                            elif tstype == 'evaporation':
                                gtype = 'PETINP'

                            v1 = (dsn, dtype, units, factor)
                            v2 = (otype, o.operation, '     ', group, gtype)

                            l=('WDM1{0:>6} {1:<9}{2:}ZERO{3:>10}'.format(*v1)
                               +'SAME {0:.6}{1:>4}{2}{3:<6} {4:<6}'.format(*v2))

                            lines.append(l)

        # landuse timeseries

        for tstype, ldict in self.landuse_timeseries.items():

            for landtype, identifier in ldict.items():

                # append time series type as needed

                if tstype == 'evaporation':

                    group  = 'EXTNL'
                    dtype  = 'EVAP'
                    factor = '{:5.3f}'.format(self.evap_multiplier)

                # find the dsn

                for dsn, comid, ts in self.dsns:

                    if comid == identifier and ts == dtype: break

                # assign to all operations

                for o in self.perlnds + self.implnds:

                    if o.landtype == landtype:
                  
                        if   o.landtype == 'Reach':      otype = 'RCHRES'
                        elif o.landtype == 'Impervious': otype = 'IMPLND'
                        else:                            otype = 'PERLND'

                        if o.landtype == 'Reach' and tstype == 'evaporation':
                            gtype = 'POTEV'
                        elif tstype == 'evaporation':
                            gtype = 'PETINP'

                        v1 = (dsn, dtype, units, factor)
                        v2 = (otype, o.operation, '     ', group, gtype)

                        l = ('WDM1{0:>6} {1:<9}{2:}ZERO{3:>10}'.format(*v1)
                             + 'SAME {0:.6}{1:>4}{2}{3:<6} {4:<6}'.format(*v2))

                        lines.append(l)
     
        # operation timeseries

        for tstype, tdict in self.operation_timeseries.items(): 

            if tstype == 'inflow':

                group  = 'INFLOW'
                dtype  = 'FLOW'
                factor = '1.00'
                gtype  = 'IVOL'

            if tstype == 'wateryield':

                group  = 'SNOW'
                dtype  = 'WYIE'
                factor = '1.00'
                gtype  = 'WYIELD'

            if tstype == 'rain':

                group  = 'SNOW'
                dtype  = 'RAIN'
                factor = '1.00'
                gtype  = 'RAINF'

            if tstype == 'snowcover':

                group  = 'SNOW'
                dtype  = 'SCOV'
                factor = '1.00'
                gtype  = 'SNOCOV'

            if tstype == 'ice':

                group  = 'SNOW'
                dtype  = 'ICE'
                factor = '1.00'
                gtype  = 'PACKI'

            for subbasin, sdict in tdict.items():

                for landtype, identifier in sdict.items():

                    if   landtype == 'Impervious': otype = 'IMPLND'
                    elif landtype == 'Reach':      otype = 'RCHRES'
                    else:                          otype = 'PERLND'

                    n = self.landtypes[subbasin][landtype].operation

                    # find the dsn

                    for dsn, comid, ts in self.dsns:

                        if comid == identifier and ts == dtype: break

                    v1 = (dsn, dtype, units, factor)
                    v2 = (otype, n, '     ', group, gtype)

                    l = ('WDM1{0:>6} {1:<9}{2:.4}ZERO{3:>10}'.format(*v1)
                         + 'SAME {0:.6}{1:>4}{2}{3:<6} {4:<6}'.format(*v2))

                    lines.append(l)

        lines = lines + ['END EXT SOURCES', '']

        return lines

    def schematic_block(self):
        """Adds the SCHEMATIC block to a UCI file."""

        # add the title block

        lines = ['SCHEMATIC',
                 '<-Volume->                  <--Area-->     ' +
                 '<-Volume->  <ML#> ***       <sb>',
                 '<Name>   x                  <-factor->     <Name>   x' +
                 '        ***        x x']

        # add the linkages between the reaches

        comids = [r.subbasin for r in self.rchreses]

        for comid in self.updown:

            # check to see if it is an outlet

            if comid not in self.outlets:

                # find the upstream and downstream reach operation numbers

                up   = self.rchreses[comids.index(comid)]
                down = self.rchreses[comids.index(self.updown[comid])]

                # add the line (note reach-reach connections are now assumed to
                # be MASS-LINK 1)

                lines.append('RCHRES%4d%28d     RCHRES%4d%7d' % 
                             (up.operation, 1, down.operation, 1))

        # add the linkages from the perlnds and implnds to the reaches

        for r in self.rchreses:

        # find all the perlnds that feed the reach

            for p in self.perlnds:

                if p.subbasin == r.subbasin:

                    # add the line (note perlnd-reach connections are now 
                    # assumed to be MASS-LINK 2)

                    lines.append('PERLND%4d%28.3f     RCHRES%4d%7d' % 
                                 (p.operation, p.area, r.operation, 2))

            # find all the implnds that feed the reach

            for i in self.implnds:

                if i.subbasin == r.subbasin:

                    # add the line (note implnd-reach connections are now 
                    # assumed to be MASS-LINK 3)
                
                    lines.append('IMPLND%4d%28.3f     RCHRES%4d%7d' %
                                 (i.operation, i.area, r.operation, 3))

        lines = lines + ['END SCHEMATIC', '']

        return lines

    def masslink_block(self, hydrology = True, sediment = False, 
                       per_sed_fs = [0.01, 0.75, 0.24],
                       imp_sed_fs = [0.10, 0.46, 0.44]):
        """Makes the MASS-LINK block for a UCI file. The assumption is that 
        reach-reach connections are ML 1, perlnd-reach connections are ML 2, 
        implnd-reach connections are ML 3. Also assumes units of areas are
        km2 for metric and acres for English."""

        lines = ['MASS-LINK',
                 '  MASS-LINK        1',
                 '<-Volume-> <-Grp> <-Member-><--Mult-->     ' +
                 '<-Target vols> <-Grp> <-Member->  ***',
                 '<Name>            <Name> x x<-factor->     <Name>' +
                 '                <Name> x x  ***']

        lines.append('RCHRES     ROFLOW                          ' +
                     'RCHRES         INFLOW         ')

        lines = lines + ['  END MASS-LINK    1', 
                         '',
                         '  MASS-LINK        2',
                         '<-Volume-> <-Grp> <-Member-><--Mult-->     ' +
                         '<-Target vols> <-Grp> <-Member->  ***',
                         '<Name>            <Name> x x<-factor->     <Name>' +
                         '                <Name> x x  ***']

        # conversion factors

        if self.units == 'English':  
            fact  = 0.08333
            sfact = 1
        elif self.units == 'Metric': 
            fact  = 0.001
            sfact = 100
            
        if hydrology: 
            lines.append('PERLND     PWATER PERO {:>15.5f}     '.format(fact) +
                         'RCHRES         INFLOW IVOL    ')

        if sediment:  

            if self.units == 'English':  
                fs = per_sed_fs
            elif self.units == 'Metric': 
                fs = [f * sfact for f in per_sed_fs]

            lines.append('PERLND     SEDMNT SOSED  1  %10.3f' % fs[0] +
                         '     RCHRES         INFLOW ISED   1 ')
            lines.append('PERLND     SEDMNT SOSED  1  %10.3f' % fs[1] +
                         '     RCHRES         INFLOW ISED   2 ')
            lines.append('PERLND     SEDMNT SOSED  1  %10.3f' % fs[2] +
                         '     RCHRES         INFLOW ISED   3 ')

        lines = lines + ['  END MASS-LINK    2',
                         '',
                         '  MASS-LINK        3',
                         '<-Volume-> <-Grp> <-Member-><--Mult-->     ' +
                         '<-Target vols> <-Grp> <-Member->  ***',
                         '<Name>            <Name> x x<-factor->     <Name>' +
                         '                <Name> x x  ***']

        if hydrology: 
            lines.append('IMPLND     IWATER SURO {:>15.5f}     '.format(fact) +
                         'RCHRES         INFLOW IVOL    ')

        if sediment:  

            if self.units == 'English':  
                fs = imp_sed_fs
            elif self.units == 'Metric': 
                fs = [f * sfact for f in imp_sed_fs]

            lines.append('IMPLND     SOLIDS SOSLD  1  %10.3f' % fs[0] +
                         '     RCHRES         INFLOW ISED   1 ')
            lines.append('IMPLND     SOLIDS SOSLD  1  %10.3f' % fs[1] +
                         '     RCHRES         INFLOW ISED   2 ')
            lines.append('IMPLND     SOLIDS SOSLD  1  %10.3f' % fs[2] +
                         '     RCHRES         INFLOW ISED   3 ')

        lines = lines + ['  END MASS-LINK    3',
                         'END MASS-LINK',
                         '']

        return lines

    def ftables_block(self):
        """Adds the FTABLE blocks to the UCI file."""

        lines = ['FTABLES']

        for r in self.rchreses:

            ftable = [row for row in r.get_ftable() if row[-1] < 10**7]

            lines = lines + ['  FTABLE    %3d' % r.operation,
                             ' rows cols                               ***',
                             '%5d%5d' % (len(ftable), len(ftable[0])),
                             '     depth      area    volume  outflow1 ***']

            for row in ftable:
                lines.append('{:10.4g} {:9.4g} {:9.4g} {:9.4g}'.format(*row))

            lines.append('  END FTABLE%3d' % r.operation)
            lines.append('')

        lines = lines[:-1] + ['END FTABLES', '']

        return lines

