Commit 46b9544e authored by Yannick Roehlly's avatar Yannick Roehlly

Create configuration modules and pcigale script

parent 7365c803
......@@ -6,3 +6,69 @@ Licensed under the CeCILL-v2 licence - see Licence_CeCILL_V2-en.txt
@author: Yannick Roehlly <>
import argparse
from .session.configuration import Configuration
def init(config):
"Create a blank configuration file."
print("The initial configuration file was created. Please complete it "
"with the data file name and the pcigale modules to use.")
def genconf(config):
"Generate the full configuration."
print("The configuration file has been updated. Please complete the "
"various module parametres and the data file columns to use in "
"the analysis.")
def check(config):
"Check the configuration."
raise NotImplementedError()
def run(config):
"Run the analysis."
raise NotImplementedError()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--conf-file', dest='config_file',
help="Alternative configuration file to use.")
subparsers = parser.add_subparsers(help="List of commands")
init_parser = subparsers.add_parser('init', help=init.__doc__)
genconf_parser = subparsers.add_parser('genconf', help=genconf.__doc__)
check_parser = subparsers.add_parser('check', help=check.__doc__)
run_parser = subparsers.add_parser('run', help=run.__doc__)
args = parser.parse_args()
if args.config_file:
config = Configuration(args.config_file)
config = Configuration()
if args.parser == 'init':
elif args.parser == 'genconf':
elif args.parser == 'check':
elif args.parser == 'run':
# -*- 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 <>
# -*- 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 <>
import atpy
import configobj
import pkg_resources
import pkgutil
import numpy as np
from textwrap import wrap
from 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
package_name : string
Name of the package (e.g. pcigale.sed.modules).
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:
return module_names
def read_array_description(description):
"""Read an array description from configuration file
Interpret a string (from the configuration file) as a Numpy array
definition. The string can be either a Numpy command beginning with 'np.'
and evaluated (may be dangerous), or a range definition beginning with
'range' and followed by the start value, the stop value and the step (the
stop value is included if reached), or a list of values separated with
The returned array is always an array of floats.
description : string
The description to be evaluated.
array : array of floats
The evaluated array.
if description[:3] == 'np.':
array = eval(description)
value_list = description.split(' ')
if value_list[0] == 'range':
start = float(value_list[1])
step = float(value_list[3])
stop = float(value_list[2]) + step # to include stop value
array = np.arange(start, stop, step, float)
array = np.array(value_list, dtype=float)
return array
class Configuration(object):
"""This class manages the configuration of pcigale.
def __init__(self, filename="pcigale.ini"):
"""Initialise a pcigale configuration.
filename : string
Name of the configuration file (pcigale.conf by default).
self.config = configobj.ConfigObj(filename,
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')) + ".")
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]
# 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:
# 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,
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 \
if default is None:
default = ''
sub_config[name] = default
sub_config.comments[name] = wrap(description)
self.config['sed_creation_modules'].comments[module_name] = [
# 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 \
if default is None:
default = ''
self.config['analysis_configuration'][name] = default
self.config['analysis_configuration'].comments[name] = wrap(desc)
# -*- 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 <>
import itertools
import collections
def param_dict_combine(dictionary):
"""Given a dictionary associating to each key an array, returns all the
possible dictionaries associating a single element to each key.
dictionary : dict
Dictionary associating an array to its (or some of its) keys.
combination_list : list of dictionaries
List of dictionaries with the same keys but associating one element
to each.
# We make a copy of the dictionary as we are modifying it.
dictionary = dict(dictionary)
# First, we must ensure that all values are lists; when a value is a
# single element, we put it in a list.
# We must take a special care of strings, because they are iterable.
for key, value in dictionary.items():
if ((not isinstance(value, collections.Iterable)) or
isinstance(value, basestring)):
dictionary[key] = [value]
# We use itertools.product to make all the possible combinations from the
# value lists.
key_list = dictionary.keys()
value_array_list = [dictionary[key] for key in key_list]
combination_list = [dict(zip(key_list, combination)) for combination in
return combination_list
......@@ -11,7 +11,6 @@ from setuptools import setup, find_packages
from import build
class custom_build(build):
def run(self):
# Build the database.
......@@ -21,21 +20,28 @@ class custom_build(build):
# Proceed with the build
entry_points = {
'console_scripts': ['pcigale = pcigale:main']
name = "pcigale",
version = "0.1a",
packages = find_packages(exclude=["database_builder"]),
install_requires = ['numpy', 'scipy', 'sqlalchemy'],
requires = ['numpy','scipy', 'sqlalchemy'],
install_requires=['numpy', 'scipy', 'sqlalchemy'],
requires=['numpy', 'scipy', 'sqlalchemy', 'atpy'],
cmdclass={"build": custom_build},
package_data = {'': ['*.db']},
package_data={'': ['*.db']},
author="Yannick Roehlly",
description="Python Code Investigating Galaxy Emission",
keywords="astrophysics, galaxy, SED fitting"
author = "Yannick Roehlly",
author_email = "",
description = "Python Code Investigating Galaxy Emission",
license = "CeCILL-V2",
keywords = "astrophysics, galaxy, SED fitting"
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment