Setup multiplier parameters for SFR segment data.  Just handles the
    standard input case, not all the cryptic SFR options.  Loads the dis, bas, and sfr files
    with flopy using model_ws.  However, expects that apply_sfr_seg_parameters() will be called
    from within model_ws at runtime.

    Parameters
    ----------
        nam_file : str
            MODFLOw name file.  DIS, BAS, and SFR must be available as pathed in the nam_file
        model_ws : str
            model workspace for flopy to load the MODFLOW model from
        OR
        nam_file : flopy.modflow.mf.Modflow
            flopy modflow model object
        par_cols : list(str)
            segment data entires to parameterize
        tie_hcond : bool
            flag to use same mult par for hcond1 and hcond2 for a given segment.  Default is True
        include_temporal_pars : bool
            flag to include spatially-global multipliers for each par_col for each stress period.  Default is False

    Returns
    -------
        df : pandas.DataFrame
            a dataframe with useful parameter setup information

    Note
    ----
        the number (and numbering) of segment data entries must consistent across
        all stress periods.
        writes <nam_file>+"_backup_.sfr" as the backup of the original sfr file
        skips values = 0.0 since multipliers don't work for these

def setup_sfr_seg_parameters(nam_file, model_ws='.', par_cols=["flow", "runoff", "hcond1", "pptsw"],
                             tie_hcond=True, include_temporal_pars=False):
    """Setup multiplier parameters for SFR segment data.  Just handles the
    standard input case, not all the cryptic SFR options.  Loads the dis, bas, and sfr files
    with flopy using model_ws.  However, expects that apply_sfr_seg_parameters() will be called
    from within model_ws at runtime.

    Parameters
    ----------
        nam_file : str
            MODFLOw name file.  DIS, BAS, and SFR must be available as pathed in the nam_file
        model_ws : str
            model workspace for flopy to load the MODFLOW model from
        OR
        nam_file : flopy.modflow.mf.Modflow
            flopy modflow model object
        par_cols : list(str)
            segment data entires to parameterize
        tie_hcond : bool
            flag to use same mult par for hcond1 and hcond2 for a given segment.  Default is True
        include_temporal_pars : bool
            flag to include spatially-global multipliers for each par_col for each stress period.  Default is False

    Returns
    -------
        df : pandas.DataFrame
            a dataframe with useful parameter setup information

    Note
    ----
        the number (and numbering) of segment data entries must consistent across
        all stress periods.
        writes <nam_file>+"_backup_.sfr" as the backup of the original sfr file
        skips values = 0.0 since multipliers don't work for these

    """

    try:
        import flopy
    except Exception as e:
        return
    if par_cols is None:
        par_cols = ["flow", "runoff", "hcond1", "pptsw"]
    if tie_hcond:
        if "hcond1" not in par_cols or "hcond2" not in par_cols:
            tie_hcond = False

    if isinstance(nam_file,flopy.modflow.mf.Modflow) and nam_file.sfr is not None:
        m = nam_file
        nam_file = m.namefile
        model_ws = m.model_ws
    else:
        # load MODFLOW model # is this needed? could we just pass the model if it has already been read in?
        m = flopy.modflow.Modflow.load(nam_file,load_only=["sfr"],model_ws=model_ws,check=False,forgive=False)
    #make backup copy of sfr file
    shutil.copy(os.path.join(model_ws,m.sfr.file_name[0]),os.path.join(model_ws,nam_file+"_backup_.sfr"))

    #get the segment data (dict)
    segment_data = m.sfr.segment_data
    shape = segment_data[list(segment_data.keys())[0]].shape
    # check
    for kper,seg_data in m.sfr.segment_data.items():
        assert seg_data.shape == shape,"cannot use: seg data must have the same number of entires for all kpers"
    seg_data_col_order = list(seg_data.dtype.names)
    # convert segment_data dictionary to multi index df - this could get ugly
    reform = {(k, c): segment_data[k][c] for k in segment_data.keys() for c in segment_data[k].dtype.names}
    seg_data_all_kper = pd.DataFrame.from_dict(reform)
    seg_data_all_kper.columns.names = ['kper', 'col']

    # extract the first seg data kper to a dataframe
    seg_data = seg_data_all_kper[0].copy() # pd.DataFrame.from_records(seg_data)

    #make sure all par cols are found and search of any data in kpers
    missing = []
    cols = par_cols.copy()
    for par_col in par_cols:
        if par_col not in seg_data.columns:
            missing.append(par_col)
            cols.remove(par_col)
        # look across all kper in multiindex df to check for values entry - fill with absmax should capture entries
        else:
            seg_data.loc[:, par_col] = seg_data_all_kper.loc[:, (slice(None), par_col)].abs().max(level=1, axis=1)
    if len(missing) > 0:
        warnings.warn("the following par_cols were not found in segment data: {0}".format(','.join(missing)), PyemuWarning)
        if len(missing) >= len(par_cols):
            warnings.warn("None of the passed par_cols ({0}) were found in segment data.".
                          format(','.join(par_cols)), PyemuWarning)
    seg_data = seg_data[seg_data_col_order]  # reset column orders to inital
    seg_data_org = seg_data.copy()
    seg_data.to_csv(os.path.join(model_ws, "sfr_seg_pars.dat"), sep=',')

    #the data cols not to parameterize
    # better than a column indexer as pandas can change column orders
    idx_cols=['nseg', 'icalc', 'outseg', 'iupseg', 'iprior', 'nstrpts']
    notpar_cols = [c for c in seg_data.columns if c not in cols+idx_cols]

    #process par cols
    tpl_str, pvals = [], []
    for par_col in cols:
        prefix = par_col
        if tie_hcond and par_col == 'hcond2':
            prefix = 'hcond1'
        if seg_data.loc[:, par_col].sum() == 0.0:
            print("all zeros for {0}...skipping...".format(par_col))
            #seg_data.loc[:,par_col] = 1
        else:
            seg_data.loc[:, par_col] = seg_data.apply(lambda x: "~    {0}_{1:04d}   ~".
                                                      format(prefix, int(x.nseg)) if float(x[par_col]) != 0.0
                                                      else "1.0", axis=1)

            org_vals = seg_data_org.loc[seg_data_org.loc[:, par_col] != 0.0, par_col]
            pnames = seg_data.loc[org_vals.index, par_col]
            pvals.extend(list(org_vals.values))
            tpl_str.extend(list(pnames.values))

    pnames = [t.replace('~','').strip() for t in tpl_str]
    df = pd.DataFrame({"parnme":pnames,"org_value":pvals,"tpl_str":tpl_str},index=pnames)
    df.drop_duplicates(inplace=True)
    if df.empty:
        warnings.warn("No sfr segment parameters have been set up, either none of {0} were found or all were zero.".
                      format(','.join(par_cols)), PyemuWarning)
        return df

    # set not par cols to 1.0
    seg_data.loc[:, notpar_cols] = "1.0"

    #write the template file
    write_df_tpl(os.path.join(model_ws,"sfr_seg_pars.dat.tpl"), seg_data, sep=',')



    #make sure the tpl file exists and has the same num of pars
    parnme = parse_tpl_file(os.path.join(model_ws,"sfr_seg_pars.dat.tpl"))
    assert len(parnme) == df.shape[0]

    #set some useful par info
    df.loc[:,"pargp"] = df.parnme.apply(lambda x: x.split('_')[0])
    df.loc[:,"parubnd"] = 1.25
    df.loc[:,"parlbnd"] = 0.75
    hpars = df.loc[df.pargp.apply(lambda x: x.startswith("hcond")),"parnme"]
    df.loc[hpars,"parubnd"] = 100.0
    df.loc[hpars, "parlbnd"] = 0.01

    if include_temporal_pars:
        # include only stress periods that are explicitly listed in segment data
        pnames,tpl_str = [],[]
        tmp_df = pd.DataFrame(data={c:1.0 for c in cols},index=list(m.sfr.segment_data.keys()))
        tmp_df.sort_index(inplace=True)
        tmp_df.to_csv(os.path.join(model_ws,"sfr_seg_temporal_pars.dat"))
        for par_col in cols:
            print(par_col)
            parnme = tmp_df.index.map(lambda x: "{0}_{1:04d}_tmp".format(par_col,int(x)))
            tmp_df.loc[:,par_col] = parnme.map(lambda x: "~   {0}  ~".format(x))
            tpl_str.extend(list(tmp_df.loc[:,par_col].values))
            pnames.extend(list(parnme.values))
        write_df_tpl(filename=os.path.join(model_ws,"sfr_seg_temporal_pars.dat.tpl"),df=tmp_df)
        pargp = [pname.split('_')[0]+"_tmp" for pname in pnames]
        tmp_df = pd.DataFrame(data={"parnme":pnames,"pargp":pargp},index=pnames)
        tmp_df.loc[:,"org_value"] = 1.0
        tmp_df.loc[:,"tpl_str"] = tpl_str
        df = df.append(tmp_df)

    # write the config file used by apply_sfr_pars()
    with open(os.path.join(model_ws, "sfr_seg_pars.config"), 'w') as f:
        f.write("nam_file {0}\n".format(nam_file))
        f.write("model_ws {0}\n".format(model_ws))
        f.write("mult_file sfr_seg_pars.dat\n")
        f.write("sfr_filename {0}\n".format(m.sfr.file_name[0]))
        if include_temporal_pars:
            f.write("time_mult_file sfr_seg_temporal_pars.dat\n")


    # set some useful par info
    df.loc[:, "pargp"] = df.parnme.apply(lambda x: x.split('_')[0])
    df.loc[:, "parubnd"] = 1.25
    df.loc[:, "parlbnd"] = 0.75
    hpars = df.loc[df.pargp.apply(lambda x: x.startswith("hcond")), "parnme"]
    df.loc[hpars, "parubnd"] = 100.0
    df.loc[hpars, "parlbnd"] = 0.01

    return df