setup a karhuenen-Loeve based parameterization for a given
    geostatistical structure.

    Parameters
    ----------
    num_eig : int
        number of basis vectors to retain in the reduced basis
    sr : flopy.reference.SpatialReference

    struct : str or pyemu.geostats.Geostruct
        geostatistical structure (or file containing one)

    array_dict : dict
        a dict of arrays to setup as KL-based parameters.  The key becomes the
        parameter name prefix. The total number of parameters is
        len(array_dict) * num_eig

    basis_file : str
        the name of the PEST-format binary file where the reduced basis will be saved

    tpl_file : str
        the name of the template file to make.  The template
        file is a csv file with the parameter names, the
        original factor values,and the template entries.
        The original values can be used to set the parval1
        entries in the control file

    Returns
    -------
    back_array_dict : dict
        a dictionary of back transformed arrays.  This is useful to see
        how much "smoothing" is taking place compared to the original
        arrays.

    Note
    ----
    requires flopy

    Example
    -------
    ``>>>import flopy``

    ``>>>import pyemu``

    ``>>>m = flopy.modflow.Modflow.load("mymodel.nam")``

    ``>>>a_dict = {"hk":m.lpf.hk[0].array}``

    ``>>>ba_dict = pyemu.helpers.kl_setup(10,m.sr,"struct.dat",a_dict)``

def kl_setup(num_eig,sr,struct,prefixes,
             factors_file="kl_factors.dat",islog=True, basis_file=None,
             tpl_dir="."):
    """setup a karhuenen-Loeve based parameterization for a given
    geostatistical structure.

    Parameters
    ----------
    num_eig : int
        number of basis vectors to retain in the reduced basis
    sr : flopy.reference.SpatialReference

    struct : str or pyemu.geostats.Geostruct
        geostatistical structure (or file containing one)

    array_dict : dict
        a dict of arrays to setup as KL-based parameters.  The key becomes the
        parameter name prefix. The total number of parameters is
        len(array_dict) * num_eig

    basis_file : str
        the name of the PEST-format binary file where the reduced basis will be saved

    tpl_file : str
        the name of the template file to make.  The template
        file is a csv file with the parameter names, the
        original factor values,and the template entries.
        The original values can be used to set the parval1
        entries in the control file

    Returns
    -------
    back_array_dict : dict
        a dictionary of back transformed arrays.  This is useful to see
        how much "smoothing" is taking place compared to the original
        arrays.

    Note
    ----
    requires flopy

    Example
    -------
    ``>>>import flopy``

    ``>>>import pyemu``

    ``>>>m = flopy.modflow.Modflow.load("mymodel.nam")``

    ``>>>a_dict = {"hk":m.lpf.hk[0].array}``

    ``>>>ba_dict = pyemu.helpers.kl_setup(10,m.sr,"struct.dat",a_dict)``

    """

    try:
        import flopy
    except Exception as e:
        raise Exception("error import flopy: {0}".format(str(e)))
    assert isinstance(sr,flopy.utils.SpatialReference)
    # for name,array in array_dict.items():
    #     assert isinstance(array,np.ndarray)
    #     assert array.shape[0] == sr.nrow
    #     assert array.shape[1] == sr.ncol
    #     assert len(name) + len(str(num_eig)) <= 12,"name too long:{0}".\
    #         format(name)

    if isinstance(struct,str):
        assert os.path.exists(struct)
        gs = pyemu.utils.read_struct_file(struct)
    else:
        gs = struct
    names = []
    for i in range(sr.nrow):
        names.extend(["i{0:04d}j{1:04d}".format(i,j) for j in range(sr.ncol)])

    cov = gs.covariance_matrix(sr.xcentergrid.flatten(),
                               sr.ycentergrid.flatten(),
                               names=names)

    eig_names = ["eig_{0:04d}".format(i) for i in range(cov.shape[0])]
    trunc_basis = cov.u
    trunc_basis.col_names = eig_names
    #trunc_basis.col_names = [""]
    if basis_file is not None:
        trunc_basis.to_binary(basis_file)
    trunc_basis = trunc_basis[:,:num_eig]
    eig_names = eig_names[:num_eig]

    pp_df = pd.DataFrame({"name":eig_names},index=eig_names)
    pp_df.loc[:,"x"] = -1.0 * sr.ncol
    pp_df.loc[:,"y"] = -1.0 * sr.nrow
    pp_df.loc[:,"zone"] = -999
    pp_df.loc[:,"parval1"] = 1.0
    pyemu.pp_utils.write_pp_file(os.path.join("temp.dat"),pp_df)


    eigen_basis_to_factor_file(sr.nrow,sr.ncol,trunc_basis,factors_file=factors_file,islog=islog)
    dfs = []
    for prefix in prefixes:
        tpl_file = os.path.join(tpl_dir,"{0}.dat_kl.tpl".format(prefix))
        df = pyemu.pp_utils.pilot_points_to_tpl("temp.dat",tpl_file,prefix)
        shutil.copy2("temp.dat",tpl_file.replace(".tpl",""))
        df.loc[:,"tpl_file"] = tpl_file
        df.loc[:,"in_file"] = tpl_file.replace(".tpl","")
        df.loc[:,"prefix"] = prefix
        df.loc[:,"pargp"] = "kl_{0}".format(prefix)
        dfs.append(df)
        #arr = pyemu.geostats.fac2real(df,factors_file=factors_file,out_file=None)
    df = pd.concat(dfs)
    df.loc[:,"parubnd"] = 10.0
    df.loc[:,"parlbnd"] = 0.1
    return pd.concat(dfs)