configuration.py 9.13 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: utf-8 -*-
"""
Copyright (C) 2012 Centre de données Astrophysiques de Marseille
Licensed under the CeCILL-v2 licence - see Licence_CeCILL_V2-en.txt

@author: Yannick Roehlly <yannick.roehlly@oamp.fr>

"""


import atpy
import configobj
import pkg_resources
import pkgutil
15
import collections
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import numpy as np
from textwrap import wrap
from ..data import Database
from ..sed.modules import common as modules
from ..stats import common as analysis


def list_modules(package_name):
    """Lists the modules available in a package

    Parametres
    ----------
    package_name : string
        Name of the package (e.g. pcigale.sed.modules).

    Returns
    -------
    module_name : array of strings
        List of the available modules.

    """
    directory = pkg_resources.resource_filename(package_name, '')
    module_names = [name for _, name, _ in pkgutil.iter_modules([directory])]
    if 'common' in module_names:
        module_names.remove('common')

    return module_names


Yannick Roehlly's avatar
Yannick Roehlly committed
45
46
47
48
49
50
def evaluate_description(description):
    """Evaluate a description from the config file as a list.

    The description is read from the config file by configobj that transforms
    coma separated value in a list. From this description, this function try
    to evaluate the desired list of values:
51
52
53
    - If the description is a string beginning with 'eval ', then its content
      (without 'eval ') is evaluated as Python code and its result returned.
      An array is expected.
Yannick Roehlly's avatar
Yannick Roehlly committed
54
55
56
57
    - If the description is a list beginning by 'range', the start, step and
      stop values are then expected and the range is evaluated.
    - Then the function tries to evaluate the description as a Numpy array of
      float and returns the mere list if this fails.
58
59
60

    Parametres
    ----------
Yannick Roehlly's avatar
Yannick Roehlly committed
61
    description : string or list
62
63
64
65
        The description to be evaluated.

    Returns
    -------
66
     results : list
Yannick Roehlly's avatar
Yannick Roehlly committed
67
        The evaluated list of values.
68
69
70

    """

Yannick Roehlly's avatar
Yannick Roehlly committed
71
72
    if not type(description) == list:
        description = [description]
73

74
75
76
77
78
    if description[0].startswith('eval '):
        results = eval(description[0][4:])
        # If the evaluation lead to a single value, we put it in a list.
        if not isinstance(results, collections.Iterable):
            results = [results]
Yannick Roehlly's avatar
Yannick Roehlly committed
79
80
81
82
83
84
85
86
87
88
    elif description[0] == 'range':
        start = float(description[1])
        step = float(description[2])
        stop = float(description[3])
        results = np.arange(start, stop, step, float)
    else:
        try:
            results = np.array(description, float)
        except ValueError:
            results = description
89

Yannick Roehlly's avatar
Yannick Roehlly committed
90
    return results
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204


class Configuration(object):
    """This class manages the configuration of pcigale.
    """

    def __init__(self, filename="pcigale.ini"):
        """Initialise a pcigale configuration.

        Parametres
        ----------
        filename : string
            Name of the configuration file (pcigale.conf by default).

        """
        self.config = configobj.ConfigObj(filename,
                                          write_empty_values=True,
                                          indent_type='  ')

    def create_blank_conf(self):
        """Create the initial configuration file

        Write the initial pcigale configuration file where the user can state
        which data file to use, which modules to use for the SED creation, as
        well as the method selected for statistical analysis.

        """

        self.config['data_file'] = ""
        self.config.comments['data_file'] = wrap(
            "File containing the observation data to be fitted. Each flux "
            "column must have the name of the corresponding filter, the "
            "error columns are suffixed with '_err'. The values must be "
            "in mJy.")

        self.config['sed_modules'] = []
        self.config.comments['sed_modules'] = [""] + wrap(
            "Order of the modules use for SED creation. Available modules : "
            + ', '.join(list_modules('pcigale.sed.modules')) + ".")

        self.config['analysis_method'] = ""
        self.config.comments['analysis_method'] = [""] + wrap(
            "Method used for statistical analysis. Available methods: "
            + ', '.join(list_modules('pcigale.stats')) + ".")

        self.config.write()

    def generate_conf(self):
        """Generate the full configuration file

        Reads the user entries in the initial configuration file and add the
        configuration options of all selected modules as well as the filter
        selection based on the filters identified in the data table file.

        """

        # Getting the list of the filters available in pcigale database
        base = Database()
        filter_list = base.get_filter_list()[0]
        base.close()

        # Finding the known filters in the data table
        obs_table = atpy.Table(self.config['data_file'])
        column_list = []
        for column in obs_table.columns:
            filter_name = column[:-4] if column.endswith('_err') else column
            if filter_name in filter_list:
                column_list.append(column)

        # Check that we don't have an error column without the associated flux
        for column in column_list:
            if column.endswith('_err') and (column[:-4] not in column_list):
                raise StandardError("The observation table as a {} column "
                                    "but no {} column.".format(column,
                                                               column[:-4]))

        self.config['column_list'] = column_list
        self.config.comments['column_list'] = [""] + wrap(
            "List of the columns in the observation data file to use for "
            "the fitting.")

        # SED creation modules configurations. For each module, we generate
        # the configuration section from its parametre list.
        self.config['sed_creation_modules'] = {}
        self.config.comments['sed_creation_modules'] = ["", ""] + wrap(
            "Configuration of the SED creation modules.")

        for module_name in self.config['sed_modules']:
            self.config["sed_creation_modules"][module_name] = {}
            sub_config = self.config["sed_creation_modules"][module_name]

            for name, (typ, unit, description, default) in \
                    modules.get_module(module_name).parametre_list.items():
                if default is None:
                    default = ''
                sub_config[name] = default
                sub_config.comments[name] = wrap(description)

            self.config['sed_creation_modules'].comments[module_name] = [
                modules.get_module(module_name).comments]

        # Configuration for the analysis method
        self.config['analysis_configuration'] = {}
        self.config.comments['analysis_configuration'] = ["", ""] + wrap(
            "Configuration of the statistical analysis method.")
        module_name = self.config['analysis_method']
        for name, (typ, unit, desc, default) in \
                analysis.get_module(module_name).parametre_list.items():
            if default is None:
                default = ''
            self.config['analysis_configuration'][name] = default
            self.config['analysis_configuration'].comments[name] = wrap(desc)

        self.config.write()
Yannick Roehlly's avatar
Yannick Roehlly committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

    def get_conf(self):
        """Returns a dictionnary for the session configuration.

        Returns
        -------
        configuration['data_file'] : string
            File containing the observations to fit.
        configuration['column_list'] : list of strings
            List of the columns of data_file to use in the fitting.
        configuration['sed_modules'] : list of strings
            List of the modules (in the right order) used to create the SEDs.
        configuration['sed_modules_params'] : list of dictionaries
            Configuration parametres for each module. To each parametre, the
            dictionnary associates a list of possible values (possibly only
            one).
        configuration['analysis_method'] : string
            Statistical analysis module used to fit the data.
        configuration['analysis_methode_params'] : dictionnary
            Parametres for the statistical analysis module. To each parametre
            is associated a list of possible values.
        """
        configuration = {}

        for section in ['data_file', 'column_list', 'sed_modules',
                        'analysis_method']:
            configuration[section] = self.config[section]

        # Parsing the SED modules parametres
        configuration['sed_modules_params'] = []
        for module in self.config['sed_modules']:
            module_params = {}
Yannick Roehlly's avatar
Yannick Roehlly committed
237
238
            for key, value in \
                    self.config['sed_creation_modules'][module].items():
Yannick Roehlly's avatar
Yannick Roehlly committed
239
240
241
242
243
                module_params[key] = evaluate_description(value)
            configuration['sed_modules_params'].append(module_params)

        # Parsing the statistical analysis parametres
        configuration['analysis_methode_params'] = {}
Yannick Roehlly's avatar
Yannick Roehlly committed
244
        for key, value in self.config['analysis_configuration'].items():
Yannick Roehlly's avatar
Yannick Roehlly committed
245
246
247
248
            configuration['analysis_methode_params'][key] = \
                evaluate_description(value)

        return configuration