Skip to content

Commit

Permalink
Merge branch 'master' of github.com:kadrlica/ugali
Browse files Browse the repository at this point in the history
  • Loading branch information
kadrlica committed Sep 11, 2017
2 parents 3a53564 + 3e4a8d9 commit 9ec876b
Show file tree
Hide file tree
Showing 24 changed files with 3,255 additions and 430 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ notifications:
email: false

# Setup dependencies and install package
# -c sherpa is needed for pyfits
install:
- wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- conda create -q -n travis-env python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib astropy healpy pyyaml emcee nose pyfits -c fermipy -c astropy
- conda create -q -n travis-env python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib astropy healpy pyyaml emcee nose pyfits fitsio -c conda-forge -c jochym -c kadrlica
- source activate travis-env
- export UGALIDIR="$HOME/.ugali"
- python setup.py -q install --isochrones --isochrones-path $UGALIDIR
Expand Down
6 changes: 3 additions & 3 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import ugali

def test_isochrone():
import ugali.analysis.isochrone
iso = ugali.analysis.isochrone.Padova()
import ugali.isochrone
iso = ugali.isochrone.Padova()
print iso

def test_kernel():
Expand All @@ -24,6 +24,6 @@ def test_source():
print source

def test_factory():
import ugali.analysis.isochrone as isochrone
import ugali.isochrone as isochrone
import ugali.analysis.kernel as kernel
pass
5 changes: 4 additions & 1 deletion tests/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import subprocess
import tempfile

# ADW: Is there any reason to do this in CI?
__test__ = False

# Not all that robust...
try:
GITBASE = basename(dirname(subprocess.check_output('git config --get remote.origin.url',shell=True).split(':')[1]))
Expand Down Expand Up @@ -66,7 +69,7 @@ def test_zip_install():
call_setup_py()
call_chdir(os.path.expandvars('$HOME'))
call_cmd('rm -rf %s'%tempdir)

def test_pip_install():
tempdir = tempfile.mkdtemp()
call_chdir(tempdir)
Expand Down
69 changes: 60 additions & 9 deletions tests/test_isochrone.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,94 @@
#!/usr/bin/env python
"""
Test isochrone functionality
Test isochrone functionality. These tests require that ugali has been
installed with the '--isochrones' option.
"""
import os
import numpy as np

import ugali.analysis.isochrone as isochrone
from ugali import isochrone

default_kwargs = dict(age = 12,metallicity = 0.0002, distance_modulus = 18)
test_kwargs = dict(age = 13, metallicity = 0.0008, distance_modulus = 16)
# Default parameters
default_kwargs = dict(age=12,metallicity=0.0002, distance_modulus=18)
# Alternate parameters
alt_kwargs = dict(age=10, metallicity=0.0001, distance_modulus=16)
# Parameter abbreviations
abbr_kwargs = dict(a=10, z=0.0001, mod=17)

padova = ['Padova','Bressan2012','Marigo2017']
dotter = ['Dotter','Dotter2008','Dotter2016']
isochrones = padova + dotter
survey = ['des','sdss']

def set_parameters(name):
iso = isochrone.factory(name,**default_kwargs)

# Test that parameters are set in construction
for k,v in default_kwargs.items():
assert getattr(iso,k) == v

for k,v in test_kwargs.items():
# Test that parameters are set through setattr
for k,v in alt_kwargs.items():
setattr(iso,k,v)
assert getattr(iso,k) == v

# Test that parameters are set through setp
for k,v in default_kwargs.items():
iso.setp(k,v)
assert getattr(iso,k) == v

iso.sample()

def test_padova(): set_parameters('Padova')
def test_dotter(): set_parameters('Dotter')
def test_exists():
""" Check that the isochrone directory exists. """
isodir = isochrone.get_iso_dir()
assert os.path.exists(isodir)
assert len(os.listdir(isodir))


def test_abbr(name='Padova'):
""" Test that parameters can be set by abbreviation. """
iso = isochrone.factory(name,**abbr_kwargs)

for k,v in abbr_kwargs.items():
setattr(iso,k,v)

for k,v in abbr_kwargs.items():
iso.setp(k,v)


def test_padova():
for name in padova:
set_parameters(name)

def test_dotter():
for name in dotter:
set_parameters(name)


def test_composite():
isochrones = [
dict(name='Padova',**default_kwargs),
dict(name='Dotter',**default_kwargs)
]
iso = isochrone.factory("Composite",isochrones=isochrones)

iso.distance_modulus = test_kwargs['distance_modulus']
assert iso.distance_modulus == test_kwargs['distance_modulus']
iso.distance_modulus = alt_kwargs['distance_modulus']
assert iso.distance_modulus == alt_kwargs['distance_modulus']

assert np.all(iso.age == np.ones(len(isochrones))*default_kwargs['age'])
assert np.all(iso.metallicity == np.ones(len(isochrones))*default_kwargs['metallicity'])

iso.sample()

def test_surveys():
""" Create isochrones with different surveys """
for s in survey:
for name in ['Dotter2016']:
iso = isochrone.factory(name,survey=s)

if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description=__doc__)
args = parser.parse_args()

6 changes: 5 additions & 1 deletion ugali/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
del get_versions

# ADW: Is this a good idea?
import ugali.analysis.isochrone as isochrone
#import ugali.analysis.isochrone as isochrone
#from ugali import isochrone
import ugali.analysis.kernel as kernel
import ugali.analysis.source as source

# Hack for backward compatibitility with: ugali.analysis.isochrone
#sys.modules['ugali.analysis.isochrone'] = __import__('ugali.isochrone')
1 change: 1 addition & 0 deletions ugali/analysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
objects :
mask :
"""

13 changes: 8 additions & 5 deletions ugali/analysis/color_lut.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
"""
DEPRECATD 2017-09-01: ADW: This code is deprecated (probably long
before the quoted date).
Functions to create and use a look-up table for the signal color probability distribution function.
"""

Expand All @@ -9,7 +12,7 @@

import ugali.utils.config
import ugali.utils.binning
import ugali.analysis.isochrone
import ugali.isochrone

from ugali.utils.logger import logger

Expand All @@ -35,8 +38,8 @@ def writeColorLUT2(config,
if isochrone is None:
isochrones = []
for ii, name in enumerate(config.params['isochrone']['infiles']):
isochrones.append(ugali.analysis.isochrone.Isochrone(config, name))
isochrone = ugali.analysis.isochrone.CompositeIsochrone(isochrones, config.params['isochrone']['weights'])
isochrones.append(ugali.isochrone.Isochrone(config, name))
isochrone = ugali.isochrone.CompositeIsochrone(isochrones, config.params['isochrone']['weights'])
if distance_modulus_array is None:
distance_modulus_array = config.params['color_lut']['distance_modulus_array']
if delta_mag is None:
Expand Down Expand Up @@ -196,8 +199,8 @@ def writeColorLUT(config,
if isochrone is None:
isochrones = []
for ii, name in enumerate(config.params['isochrone']['infiles']):
isochrones.append(ugali.analysis.isochrone.Isochrone(config, name))
isochrone = ugali.analysis.isochrone.CompositeIsochrone(isochrones, config.params['isochrone']['weights'])
isochrones.append(ugali.isochrone.Isochrone(config, name))
isochrone = ugali.isochrone.CompositeIsochrone(isochrones, config.params['isochrone']['weights'])
if distance_modulus_array is None:
distance_modulus_array = config.params['color_lut']['distance_modulus_array']
if delta_mag is None:
Expand Down
2 changes: 1 addition & 1 deletion ugali/analysis/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ugali.analysis.model import Model, Parameter
from ugali.analysis.kernel import factory as kernelFactory
from ugali.analysis.isochrone import factory as isochroneFactory
from ugali.isochrone import factory as isochroneFactory

class Richness(Model):
"""Dummy model to hold the richness, which is not directly connected
Expand Down
11 changes: 11 additions & 0 deletions ugali/isochrone/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python
"""
Module for dealing with Isochrones.
"""
from ugali.isochrone.model import get_iso_dir
from ugali.isochrone.composite import factory
from ugali.isochrone.composite import CompositeIsochrone, Padova, Dotter
from ugali.isochrone.parsec import Bressan2012, Marigo2017
from ugali.isochrone.dartmouth import Dotter2008
from ugali.isochrone.mesa import Dotter2016

153 changes: 153 additions & 0 deletions ugali/isochrone/composite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/env python
"""
Generic python script.
"""
__author__ = "Alex Drlica-Wagner"

import sys
import os
from collections import OrderedDict as odict
from functools import wraps

import numpy as np

from ugali.analysis.model import Model, Parameter
from ugali.utils.logger import logger
from ugali.isochrone.model import IsochroneModel, Isochrone
from ugali.isochrone.mesa import Dotter2016
from ugali.isochrone.parsec import Marigo2017

class CompositeIsochrone(IsochroneModel):
_params = odict([
('distance_modulus', Parameter(15.0, [10.0, 30.0]) ),
])
_mapping = odict([
('mod','distance_modulus'),
('a','age'),
('z','metallicity'),
])

defaults = (IsochroneModel.defaults) + (
('type','PadovaIsochrone','Default type of isochrone to create'),
('weights',None,'Relative weights for each isochrone'),
)

def __init__(self, isochrones, **kwargs):
super(CompositeIsochrone,self).__init__(**kwargs)

self.isochrones = []
for i in isochrones:
if isinstance(i,Isochrone):
iso = i
else:
name = i.pop('name',self.type)
#iso = isochroneFactory(name=name,**i)
iso = factory(name=name,**i)
# Tie the distance modulus
iso.params['distance_modulus'] = self.params['distance_modulus']
self.isochrones.append(iso)

if self.weights is None: self.weights = np.ones(len(self.isochrones))
self.weights /= np.sum(np.asarray(self.weights))
self.weights = self.weights.tolist()

if len(self.isochrones) != len(self.weights):
msg = 'Length of isochrone and weight arrays must be equal'
raise ValueError(msg)

def __getitem__(self, key):
return self.isochrones[key]

def __str__(self,indent=0):
ret = super(CompositeIsochrone,self).__str__(indent)
ret += '\n{0:>{2}}{1}'.format('','Isochrones:',indent+2)
for i in self:
ret += '\n{0}'.format(i.__str__(indent+4))
return ret

@property
def age(self):
return np.array([i.age for i in self])

@property
def metallicity(self):
return np.array([i.metallicity for i in self])

def composite_decorator(func):
"""
Decorator for wrapping functions that calculate a weighted sum
"""
@wraps(func)
def wrapper(self, *args, **kwargs):
total = []
for weight,iso in zip(self.weights,self.isochrones):
subfunc = getattr(iso,func.__name__)
total.append(weight*subfunc(*args,**kwargs))
return np.sum(total,axis=0)
return wrapper

def sample(self, **kwargs):
samples = [iso.sample(**kwargs) for iso in self.isochrones]
for weight,sample in zip(self.weights,samples):
sample[1] *= weight

return np.hstack(samples)

def separation(self, *args, **kwargs):
separations = [iso.separation(*args,**kwargs) for iso in self.isochrones]
return np.nanmin(separations,axis=0)

def todict(self):
ret = super(CompositeIsochrone,self).todict()
ret['isochrones'] = [iso.todict() for iso in self.isochrones]
return ret

@composite_decorator
def stellar_mass(self, *args, **kwargs): pass

@composite_decorator
def stellar_luminosity(self, *args, **kwargs): pass

@composite_decorator
def observable_fraction(self, *args, **kwargs): pass

@composite_decorator
def observableFractionX(self, *args, **kwargs): pass

@composite_decorator
def signalMMD(self, *args, **kwargs): pass

# ADW: For temporary backwards compatibility
stellarMass = stellar_mass
stellarLuminosity = stellar_luminosity
observableFraction = observable_fraction

# ADW: It would be better if the factory were in isochrone.__init__
# but then you get into a circular import situation with the
# CompositeIsochrone. This is an unfortunate design decision...

# ADW: It'd be better for us to move away from generic aliases...
class Dotter(Dotter2016): pass
class Padova(Marigo2017): pass
class Composite(CompositeIsochrone): pass

def factory(name, **kwargs):
from ugali.utils.factory import factory

module = 'ugali.isochrone'
# First try this module
try: return factory(name, module=__name__, **kwargs)
except KeyError: pass
try: return factory(name, module=module+'.composite', **kwargs)
except KeyError: pass
# Then try parsec
try: return factory(name, module=module+'.parsec', **kwargs)
except KeyError: pass
# Then try mesa
try: return factory(name, module=module+'.mesa', **kwargs)
except KeyError: pass
# Then try desd
try: return factory(name, module=module+'.dartmouth', **kwargs)
except KeyError: pass

raise KeyError('Unrecognized class: %s'%name)
Loading

0 comments on commit 9ec876b

Please sign in to comment.