# Path: crates_contrib/utils.py
# def write_columns(fname, *args, **kwargs):
#     """Write out arrays as columns to the file fname.
# 
#     Parameters
#     ----------
#     fname : str
#         The name of the output file.
#     *args
#         The column data to write out (NumPy arrays, Python lists, a
#         dictionary, or NumPy structured array).  A single column is
#         assumed to have a consistent data type, and columns will be
#         padded to match the longest column length (the pad value
#         depends on the column type; e.g. 0 for numeric columns and the
#         empty string for string columns). If a single argument is
#         given then it can be a dictionary, or structured array, from
#         which the columns are taken.
#     colnames : list, optional
#         When the input is a dictionary or structured array, this is
#         a list of columns to use (and so should match or be a subset
#         of the column names or keys). If no names are given then the
#         columns will be called 'col1', 'col2', up to 'coln' (where n
#         is the number of arrays sent in).
#     format : {'text', 'fits', 'simple', 'dtf', 'raw'}, optional
#         The format of the file. The default is 'text'. The 'simple'
#         argument has the same meaning as 'text'. See "ahelp dmascii"
#         for more information on the ASCII file formats supported by
#         CIAO tools and libraries, including Crates.
#     sep : character, optional
#         The column separator to use when the format is not 'fits'.
#         The default is ' '.
#     comment : character, optional
#         Comment lines start with this when the format is not 'fits'.
#         The default is '#'.
#     clobber : bool, optional
#       If the output file exists then it will be overwritten (if True)
#       or cause the routine to raise an IOError (when set to False).
#       The default is True.
# 
#     See Also
#     --------
#     make_table_crate, write_arrays
# 
#     Examples
#     --------
# 
#     >>> write_columns('cols.dat', a, b)
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'])
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'],
#     ...               format='fits')
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'],
#     ...               clobber=False)
# 
#     >>> d = { "x": a, "y": b, "z": a + b }
#     >>> write_columns('cols.dat', d)
#     >>> write_columns('cols.dat', d, colnames=["x", "y"])
# 
#     """
# 
#     # We repeat some usage checks that make_table_crate() does so that
#     # we give a more user-friendly error message than if it came from a
#     # routine the user did not directly call.
#     #
#     if len(args) == 0:
#         raise TypeError("write_columns() takes at least two arguments (1 given)")
# 
#     fmt = kwargs.pop("format", "text")
# 
#     try:
#         kernel = __kernel_mapping[fmt]
#     except KeyError:
#         raise TypeError("format argument sent '{}', must be one of {}".format(fmt, ", ".join(__kernel_mapping.keys())))
# 
#     colsep = kwargs.pop("sep", " ")
#     if len(colsep) != 1:
#         raise TypeError("sep argument must be a single character, sent '{}'".format(colsep))
# 
#     comment = kwargs.pop("comment", "#")
#     if len(comment) != 1:
#         raise TypeError("comment argument must be a single character, sent '{}'".format(comment))
# 
#     clobber = kwargs.pop("clobber", True)
# 
#     for k in kwargs.keys():
#         if k != "colnames":
#             raise TypeError("write_columns() got an unexpected keyword argument '{}'".format(k))
# 
#     cr = make_table_crate(*args, **kwargs)
# 
#     oname = fname
#     if kernel is not None:
#         # In CIAO 4.4 release 1, keywords are written out for ASCII formats
#         # when the comment option is set. Since most people will use the
#         # default comment option we avoid this by only including the comment
#         # value if not '#'; this is a hack. Is this still true?
#         if comment == '#':
#             oname += "[opt kernel={},sep='{}']".format(kernel, colsep)
#         else:
#             oname += "[opt kernel={},sep='{}',comment={}]".format(kernel, colsep, comment)
# 
#     cr.write(oname, clobber=clobber)


import sherpa.astro.ui as s
import numpy as np
from sherpa.plot import HistogramPlot, backend
from sherpa.utils.err import IdentifierErr, ArgumentErr
from crates_contrib.utils import write_columns
        and ewidth the bin width, all in keV.
    norm : number or None
        Multiply the flux values by this value, if set.

    Notes
    -----
    The format needed for ChART2 is three columns - low and
    high energy values along with the spectrum - and can be in
    any Data-Model supported format (so both ASCII and FITS).
    This is different to version 1, which required a 2 column
    ASCII file.
    """

    if outfile is None:
        raise ValueError("The outfile argument must be specified")

    # For now restrict to just these two forms (write_columns supports
    # a few more).
    if format not in ['text', 'fits']:
        raise ValueError("The format argument must be 'text' or " +
                         "'fits', not '{}'".format(format))

    # Chart2 uses
    #  - 3 columns (energ_lo, energ_hi, spectrum)
    #  - can accept ascii or FITS format
    #
    (elo, ehi, flux) = get_chart_spectrum(id=id, elow=elow,
                                          ehigh=ehigh, ewidth=ewidth,
                                          norm=norm)
    colnames = ['elo', 'ehi', 'spectrum']

# Path: crates_contrib/utils.py
# def write_columns(fname, *args, **kwargs):
#     """Write out arrays as columns to the file fname.
# 
#     Parameters
#     ----------
#     fname : str
#         The name of the output file.
#     *args
#         The column data to write out (NumPy arrays, Python lists, a
#         dictionary, or NumPy structured array).  A single column is
#         assumed to have a consistent data type, and columns will be
#         padded to match the longest column length (the pad value
#         depends on the column type; e.g. 0 for numeric columns and the
#         empty string for string columns). If a single argument is
#         given then it can be a dictionary, or structured array, from
#         which the columns are taken.
#     colnames : list, optional
#         When the input is a dictionary or structured array, this is
#         a list of columns to use (and so should match or be a subset
#         of the column names or keys). If no names are given then the
#         columns will be called 'col1', 'col2', up to 'coln' (where n
#         is the number of arrays sent in).
#     format : {'text', 'fits', 'simple', 'dtf', 'raw'}, optional
#         The format of the file. The default is 'text'. The 'simple'
#         argument has the same meaning as 'text'. See "ahelp dmascii"
#         for more information on the ASCII file formats supported by
#         CIAO tools and libraries, including Crates.
#     sep : character, optional
#         The column separator to use when the format is not 'fits'.
#         The default is ' '.
#     comment : character, optional
#         Comment lines start with this when the format is not 'fits'.
#         The default is '#'.
#     clobber : bool, optional
#       If the output file exists then it will be overwritten (if True)
#       or cause the routine to raise an IOError (when set to False).
#       The default is True.
# 
#     See Also
#     --------
#     make_table_crate, write_arrays
# 
#     Examples
#     --------
# 
#     >>> write_columns('cols.dat', a, b)
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'])
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'],
#     ...               format='fits')
#     >>> write_columns('cols.dat', a, b, colnames=['x','y'],
#     ...               clobber=False)
# 
#     >>> d = { "x": a, "y": b, "z": a + b }
#     >>> write_columns('cols.dat', d)
#     >>> write_columns('cols.dat', d, colnames=["x", "y"])
# 
#     """
# 
#     # We repeat some usage checks that make_table_crate() does so that
#     # we give a more user-friendly error message than if it came from a
#     # routine the user did not directly call.
#     #
#     if len(args) == 0:
#         raise TypeError("write_columns() takes at least two arguments (1 given)")
# 
#     fmt = kwargs.pop("format", "text")
# 
#     try:
#         kernel = __kernel_mapping[fmt]
#     except KeyError:
#         raise TypeError("format argument sent '{}', must be one of {}".format(fmt, ", ".join(__kernel_mapping.keys())))
# 
#     colsep = kwargs.pop("sep", " ")
#     if len(colsep) != 1:
#         raise TypeError("sep argument must be a single character, sent '{}'".format(colsep))
# 
#     comment = kwargs.pop("comment", "#")
#     if len(comment) != 1:
#         raise TypeError("comment argument must be a single character, sent '{}'".format(comment))
# 
#     clobber = kwargs.pop("clobber", True)
# 
#     for k in kwargs.keys():
#         if k != "colnames":
#             raise TypeError("write_columns() got an unexpected keyword argument '{}'".format(k))
# 
#     cr = make_table_crate(*args, **kwargs)
# 
#     oname = fname
#     if kernel is not None:
#         # In CIAO 4.4 release 1, keywords are written out for ASCII formats
#         # when the comment option is set. Since most people will use the
#         # default comment option we avoid this by only including the comment
#         # value if not '#'; this is a hack. Is this still true?
#         if comment == '#':
#             oname += "[opt kernel={},sep='{}']".format(kernel, colsep)
#         else:
#             oname += "[opt kernel={},sep='{}',comment={}]".format(kernel, colsep, comment)
# 
#     cr.write(oname, clobber=clobber)


# Path: sherpa_contrib/chart.py
import sherpa.astro.ui as s
import numpy as np
from sherpa.plot import HistogramPlot, backend
from sherpa.utils.err import IdentifierErr, ArgumentErr
from crates_contrib.utils import write_columns

        and ewidth the bin width, all in keV.
    norm : number or None
        Multiply the flux values by this value, if set.

    Notes
    -----
    The format needed for ChART2 is three columns - low and
    high energy values along with the spectrum - and can be in
    any Data-Model supported format (so both ASCII and FITS).
    This is different to version 1, which required a 2 column
    ASCII file.
    """

    if outfile is None:
        raise ValueError("The outfile argument must be specified")

    # For now restrict to just these two forms (write_columns supports
    # a few more).
    if format not in ['text', 'fits']:
        raise ValueError("The format argument must be 'text' or " +
                         "'fits', not '{}'".format(format))

    # Chart2 uses
    #  - 3 columns (energ_lo, energ_hi, spectrum)
    #  - can accept ascii or FITS format
    #
    (elo, ehi, flux) = get_chart_spectrum(id=id, elow=elow,
                                          ehigh=ehigh, ewidth=ewidth,
                                          norm=norm)
    colnames = ['elo', 'ehi', 'spectrum']

    write_columns(outfile, elo, ehi, flux, colnames=colnames,