pax_global_header00006660000000000000000000000064147643566640014537gustar00rootroot0000000000000052 comment=6c344013a1925fd520c638e4fe5cb21c0211a0ab sncosmo-2.12.1/000077500000000000000000000000001476435666400133035ustar00rootroot00000000000000sncosmo-2.12.1/.github/000077500000000000000000000000001476435666400146435ustar00rootroot00000000000000sncosmo-2.12.1/.github/workflows/000077500000000000000000000000001476435666400167005ustar00rootroot00000000000000sncosmo-2.12.1/.github/workflows/run_tests.yml000066400000000000000000000040331476435666400214510ustar00rootroot00000000000000name: Tests on: push: branches: - master tags: - '*' pull_request: workflow_dispatch: jobs: tests: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - name: Code style checks os: ubuntu-latest python: 3.x toxenv: codestyle - name: Build the docs os: ubuntu-latest python: 3.x toxenv: build_docs - name: Latest Python with minimal dependencies os: ubuntu-latest python: 3.x toxenv: py3 - name: Python 3.11 with full coverage os: ubuntu-latest python: 3.11 toxenv: py311-alldeps-cov - name: Python 3.8 with oldest supported version of all dependencies os: ubuntu-latest python: 3.8 toxenv: py38-oldestdeps - name: Python 3.9 (MacOS X) with all optional dependencies os: macos-latest python: 3.9 toxenv: py39-alldeps - name: Python 3.10 (Windows) with all optional dependencies os: windows-latest python: "3.10" toxenv: py310-alldeps - name: Python 3.11 with all optional dependencies os: ubuntu-latest python: "3.11" toxenv: py311-alldeps - name: Download all of the builtins os: ubuntu-latest python: 3.x toxenv: builtins steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install Python dependencies run: python -m pip install --upgrade tox - name: Run tests run: tox ${{ matrix.toxargs }} -e ${{ matrix.toxenv }} - name: Upload coverage to codecov if: contains(matrix.toxenv,'-cov') uses: codecov/codecov-action@v3 with: file: ./coverage.xml sncosmo-2.12.1/.github/workflows/upload_to_pypi.yml000066400000000000000000000035651476435666400224630ustar00rootroot00000000000000name: Build and upload to PyPI on: release: types: [published] jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 name: Install Python with: python-version: '3.x' - name: Build wheels # For very recent Python versions, wheels from e.g. numpy might not be # available yet which can cause the build to fail. Keep going, and upload # the wheels for all of the previous versions when that happens. continue-on-error: true uses: pypa/cibuildwheel@v2.1.1 - uses: actions/upload-artifact@v4 with: name: wheels-${{ matrix.os }} path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 name: Install Python with: python-version: '3.x' - name: Install build run: python -m pip install build - name: Build sdist run: python -m build --sdist - uses: actions/upload-artifact@v4 with: name: sdist path: dist/*.tar.gz upload_pypi: name: Upload to PyPI needs: [build_wheels, build_sdist] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4.1.7 with: name: sdist path: dist - uses: actions/download-artifact@v4.1.7 with: path: dist merge-multiple: true # put contents of each wheel folder into dist/ pattern: wheels-* - uses: pypa/gh-action-pypi-publish@v1.8.11 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} sncosmo-2.12.1/.github/workflows/verify_builtins_downloads.yml000066400000000000000000000014161476435666400247140ustar00rootroot00000000000000# Test that builtins download every week # Downloads ~1 GB of data and runs in a few minutes (Nov 5, 2021) name: Builtins download check # Controls when the workflow will run on: schedule: # https://crontab.guru/#5_1_1_*_* # cant use special cron keys like @weekly in GitHub Actions - cron: '5 1 * * 1' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: builtins: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.9 - name: Install Python dependencies run: python -m pip install --upgrade tox - name: Run tests run: tox -e builtins sncosmo-2.12.1/.gitignore000066400000000000000000000012601476435666400152720ustar00rootroot00000000000000# Compiled files *.py[co] # Ignore .c files by default to avoid including generated code. If you want to # add a non-generated .c extension, use `git add -f filename.c`. *.c *.so # Packages *.egg *.egg-info dist build .eggs parts bin var sdist develop-eggs .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports htmlcov .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg # Documentation docs/_build docs/api docs/examples docs/modules # Other generated files MANIFEST # editors *~ .vscode # pycharm files .idea # ipython notebooks *.ipynb # misc helpers misc/Make.user misc/*.o misc/*.d misc/test-salt2model # testing .cache .pytest_cache sncosmo-2.12.1/.readthedocs.yaml000066400000000000000000000010361476435666400165320ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Required build: os: ubuntu-lts-latest tools: python: "latest" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF formats: - pdf # Set requirements required to build your docs python: install: - method: pip path: . extra_requirements: - docs sncosmo-2.12.1/CITATION.cff000066400000000000000000000047231476435666400152030ustar00rootroot00000000000000cff-version: 1.2.0 message: If you use SNCosmo, please cite it using these metadata. title: SNCosmo abstract: Python library for supernova cosmology repository-code: https://github.com/sncosmo/sncosmo license: BSD-3-Clause doi: 10.5281/zenodo.592747 authors: - given-names: Kyle family-names: Barbary affiliation: University of California, Berkeley orcid: https://orcid.org/0000-0002-2532-3696 - given-names: Stephen family-names: Bailey affiliation: Lawrence Berkeley National Laboratory - given-names: Geert family-names: Barentsen affiliation: NASA - given-names: Tom family-names: Barclay - given-names: Rahul family-names: Biswas affiliation: Oskar Klein Centre orcid: https://orcid.org/0000-0002-5741-7195 - given-names: Kyle family-names: Boone affiliation: University of Washington orcid: https://orcid.org/0000-0002-5828-6211 - given-names: Matt family-names: Craig affiliation: Minnesota State University Moorhead - given-names: Ulrich family-names: Feindt affiliation: Oskar Klein Centre - given-names: Brian family-names: Friesen affiliation: Lawrence Berkeley National Laboratory - given-names: Danny family-names: Goldstein - given-names: Saurabh W. family-names: Jha - given-names: David O. family-names: Jones orcid: https://orcid.org/0000-0002-6230-0151 affiliation: University of Santa Cruz - given-names: Florian family-names: Mondon - given-names: Seméli family-names: Papadogiannakis - given-names: Daniel family-names: Perrefort orcid: https://orcid.org/0000-0002-3988-4881 - given-names: Justin family-names: Pierel affiliation: Space Telescope Science Institute - given-names: Steve family-names: Rodney affiliation: University of South Carolina - given-names: Benjamin family-names: Rose affiliation: Duke University orcid: https://orcid.org/0000-0002-1873-8973 - given-names: Clare family-names: Saunders affiliation: Princeton University orcid: https://orcid.org/0000-0002-4094-2102 - given-names: Brigitta family-names: Sipőcz affiliation: California Institute of Technology/IPAC - given-names: Caroline family-names: Sofiatti - given-names: Rollin C. family-names: Thomas affiliation: Lawrence Berkeley National Laboratory/NERSC - given-names: Jakob family-names: van Santen - given-names: Maria family-names: Vincenzi - given-names: David family-names: Wang affiliation: University of Washington - given-names: Michael family-names: Wood-Vasey affiliation: University of Pittsburgh sncosmo-2.12.1/CODE_OF_CONDUCT.md000066400000000000000000000060761476435666400161130ustar00rootroot00000000000000# SNCosmo Code of Conduct We are committed to providing a strong and enforced code of conduct and expect everyone in our community to follow these guidelines when interacting with others in all forums. Our goal is to keep ours a positive, inclusive, successful, and growing community. The community of participants in open source Astronomy projects is made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences success and continued growth. As members of the community, - We pledge to treat all people with respect and provide a harassment- and bullying-free environment, regardless of sex, sexual orientation and/or gender identity, disability, physical appearance, body size, race, nationality, ethnicity, and religion. In particular, sexual language and imagery, sexist, racist, or otherwise exclusionary jokes are not appropriate. - We pledge to respect the work of others by recognizing acknowledgment/citation requests of original authors. As authors, we pledge to be explicit about how we want our own work to be cited or acknowledged. - We pledge to welcome those interested in joining the community, and realize that including people with a variety of opinions and backgrounds will only serve to enrich our community. In particular, discussions relating to pros/cons of various technologies, programming languages, and so on are welcome, but these should be done with respect, taking proactive measure to ensure that all participants are heard and feel confident that they can freely express their opinions. - We pledge to welcome questions and answer them respectfully, paying particular attention to those new to the community. We pledge to provide respectful criticisms and feedback in forums, especially in discussion threads resulting from code contributions. - We pledge to be conscientious of the perceptions of the wider community and to respond to criticism respectfully. We will strive to model behaviors that encourage productive debate and disagreement, both within our community and where we are criticized. We will treat those outside our community with the same respect as people within our community. - We pledge to help the entire community follow the code of conduct, and to not remain silent when we see violations of the code of conduct. We will take action when members of our community violate this code such as contacting the current maintainers (all messages sent related to code of conduct violations will be treated with the strictest confidence) or talking privately with the person. This code of conduct applies to all community situations online and offline, including mailing lists, forums, social media, conferences, meetings, associated social events, and one-to-one interactions. Parts of this code of conduct have been adapted from the Astropy, Numfocus, and Space Telescope codes of conduct. http://www.astropy.org/code_of_conduct.html https://www.numfocus.org/about/code-of-conduct/ https://github.com/spacetelescope/style-guides/blob/master/templates/CODE_OF_CONDUCT.md sncosmo-2.12.1/CONTRIBUTING.md000066400000000000000000000001771476435666400155410ustar00rootroot00000000000000Details on how to contribute can be found in the [documentation](https://sncosmo.readthedocs.io/en/stable/contributing.html). sncosmo-2.12.1/LICENSE.rst000066400000000000000000000027411476435666400151230ustar00rootroot00000000000000Copyright (c) 2012-2016, SNCosmo Developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sncosmo-2.12.1/MANIFEST.in000066400000000000000000000002541476435666400150420ustar00rootroot00000000000000include README.md include CITATION.md include LICENSE.rst include pyproject.toml recursive-include sncosmo *.pyx recursive-exclude sncosmo *.c global-exclude __pycache__ sncosmo-2.12.1/README.md000066400000000000000000000017741476435666400145730ustar00rootroot00000000000000SNCosmo ======= *Python library for supernova cosmology* [![Build Status](https://github.com/sncosmo/sncosmo/actions/workflows/run_tests.yml/badge.svg)](https://github.com/sncosmo/sncosmo/actions/workflows/run_tests.yml) [![Coverage Status](https://codecov.io/gh/sncosmo/sncosmo/branch/master/graph/badge.svg?token=jMdsK7kgPb)](https://codecov.io/gh/sncosmo/sncosmo) [![Documentation Status](https://readthedocs.org/projects/sncosmo/badge/?version=latest)](https://sncosmo.readthedocs.io/en/latest/?badge=latest) [![PyPI](https://img.shields.io/pypi/v/sncosmo.svg?style=flat-square)](https://pypi.python.org/pypi/sncosmo) [![Anaconda](https://anaconda.org/conda-forge/sncosmo/badges/version.svg)](https://anaconda.org/conda-forge/sncosmo) [![ASCL](https://img.shields.io/badge/ascl-1611.017-blue.svg?colorB=262255)](https://ascl.net/1611.017) **Documentation:** http://sncosmo.readthedocs.io **Citing sncosmo:** [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.592747.svg)](https://doi.org/10.5281/zenodo.592747) sncosmo-2.12.1/benchmarks/000077500000000000000000000000001476435666400154205ustar00rootroot00000000000000sncosmo-2.12.1/benchmarks/cubic_interpolator.py000077500000000000000000000041071476435666400216660ustar00rootroot00000000000000#!/usr/bin/env python """ Quick script to show difference between RectBivariateSpline and the thing used in snfit. """ import time import numpy as np from scipy.interpolate import RectBivariateSpline from sncosmo.interpolation import BicubicInterpolator import matplotlib.pyplot as plt def f_true(x, y): return np.sin(x)[:, None] * np.cos(0.25 * y) def compare(): x = np.array([-1., 0., 2., 4., 5., 6., 6.5, 7.]) y = np.array([1., 2., 3., 4., 5.]) z = f_true(x, y) f = BicubicInterpolator(x, y, z) xs = np.linspace(x[0], x[-1], 100) ys = np.linspace(y[0], y[-1], 100) zs_true = f_true(xs, ys) zs = f(xs, ys) zs_diff = zs - zs_true # compare to BivariateSpline f2 = RectBivariateSpline(x, y, z, kx=3, ky=3) zs2 = f2(xs, ys) zs2_diff = zs2 - zs_true # set min,max for diffs vmin = zs_diff.min() vmax = zs_diff.max() f, ax = plt.subplots(nrows=3, ncols=2, figsize=(10., 10.)) ax[0,0].imshow(zs_true) ax[0,0].set_title('True function') ax[0,1].set_visible(False) ax[1,0].imshow(zs) ax[2,0].imshow(zs_diff, vmin=vmin, vmax=vmax) ax[1,0].set_title('BicubicInterpolator (snfit)') ax[2,0].set_title('difference') ax[1,1].imshow(zs2) ax[2,1].imshow(zs2_diff, vmin=vmin, vmax=vmax) ax[1,1].set_title('RectBivariateSpline (scipy)') ax[2,1].set_title('difference') f.savefig('cubic_interpolator_comparison.png') def timeit(): x = np.arange(100) y = np.arange(1000) z = np.sin(x)[:, None] * np.cos(0.25 * y) f1 = BicubicInterpolator(x, y, z) f2 = RectBivariateSpline(x, y, z, kx=3, ky=3) # Test data. In typical use, we'll have ~15 lightcurve points and # ~100 bandpass points (y) xs = np.linspace(10., 90., 15) ys = np.linspace(200., 400., 100) nloops = 10000 for (name, f) in (('BicubicInterpolator', f1), ('RectBivariateSpline', f2)): t0 = time.time() for _ in range(nloops): f(xs, ys) t = time.time() - t0 print('{} : {:.3f} us per evaluation'.format(name, t/nloops * 1e6)) timeit() sncosmo-2.12.1/benchmarks/models.py000077500000000000000000000024471476435666400172670ustar00rootroot00000000000000#!/usr/bin/env python """Run benchmarks for model synthetic photometry""" import time from collections import OrderedDict import numpy as np import sncosmo delim = 61 * "-" # test data ndata = 100 # make divisible by 4! dates = np.linspace(-15., 40., ndata) bands = np.array((ndata//4) * ['desg', 'desr', 'desi', 'sdssg']) niter = 100 # models f99dust = sncosmo.F99Dust(3.1) models = OrderedDict([ ('salt2', sncosmo.Model(source='salt2')), ('hsiao', sncosmo.Model(source='hsiao')), ('salt2+f99dust', sncosmo.Model(source='salt2', effects=[f99dust], effect_names=['mw'], effect_frames=['obs'])), ('hsiao+f99dust', sncosmo.Model(source='hsiao', effects=[f99dust], effect_names=['mw'], effect_frames=['obs'])) ]) print("\nbandflux(band_array, time_array) [4 des bands]:") print(delim) print("Model n=1 n=10 n=100") print(delim) for name, model in models.items(): print('{:15s}'.format(name), end='') for idx in [0, range(10), range(100)]: d = dates[idx] b = bands[idx] time1 = time.time() for i in range(niter): model.bandflux(b, d) time2 = time.time() time_sec = (time2 - time1) / niter print("%10.5f" % (time_sec * 1000.), end='') print(" ms per call") sncosmo-2.12.1/checkstyle000077500000000000000000000005341476435666400153710ustar00rootroot00000000000000#!/usr/bin/env bash # to the default ignore list, we add # # E402 module level import not at top of file # Reason: in __init__.py, we need to import things in a certain order # # E741 ambiguous variable name # Reason: Sometimes 'l' is a fine variable name. # pycodestyle --ignore=E121,E123,E126,E226,E24,E704,W503,W504,E402,E741 sncosmo sncosmo-2.12.1/codecov.yml000066400000000000000000000001401476435666400154430ustar00rootroot00000000000000comment: off ignore: - "sncosmo/tests/*" - "sncosmo/conftest.py" - "sncosmo/__init__.py" sncosmo-2.12.1/docs/000077500000000000000000000000001476435666400142335ustar00rootroot00000000000000sncosmo-2.12.1/docs/Makefile000066400000000000000000000112271476435666400156760ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest #This is needed with git because git doesn't create a dir if it's empty $(shell [ -d "_static" ] || mkdir -p _static) help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR) -rm -rf api -rm -rf examples -rm -rf modules html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." sncosmo-2.12.1/docs/_examples/000077500000000000000000000000001476435666400162105ustar00rootroot00000000000000sncosmo-2.12.1/docs/_examples/README.txt000066400000000000000000000000561476435666400177070ustar00rootroot00000000000000.. autogenerated_examples: Examples ======== sncosmo-2.12.1/docs/_examples/plot_custom_fitter.py000066400000000000000000000042251476435666400225120ustar00rootroot00000000000000""" ================================ Using a custom fitter or sampler ================================ How to use your own minimizer or MCMC sampler for fitting light curves. SNCosmo has three functions for model parameter estimation based on photometric data: `sncosmo.fit_lc`, `sncosmo.mcmc_lc` and `sncosmo.nest_lc`. These are wrappers around external minimizers or samplers (respectively: iminuit, emcee and nestle). However, one may wish to experiment with a custom fitting or sampling method. Here, we give a minimal example of using the L-BFGS-B minimizer from scipy. """ import numpy as np from scipy.optimize import fmin_l_bfgs_b import sncosmo model = sncosmo.Model(source='salt2') data = sncosmo.load_example_data() # Define an objective function that we will pass to the minimizer. # The function arguments must comply with the expectations of the specfic # minimizer you are using. def objective(parameters): model.parameters[:] = parameters # set model parameters # evaluate model fluxes at times/bandpasses of data model_flux = model.bandflux(data['band'], data['time'], zp=data['zp'], zpsys=data['zpsys']) # calculate and return chi^2 return np.sum(((data['flux'] - model_flux) / data['fluxerr'])**2) # starting parameter values in same order as `model.param_names`: start_parameters = [0.4, 55098., 1e-5, 0., 0.] # z, t0, x0, x1, c # parameter bounds in same order as `model.param_names`: bounds = [(0.3, 0.7), (55080., 55120.), (None, None), (None, None), (None, None)] parameters, val, info = fmin_l_bfgs_b(objective, start_parameters, bounds=bounds, approx_grad=True) print(parameters) ##################################################################### # The built-in parameter estimation functions in sncosmo take care of # setting up the likelihood function in the way that the underlying # fitter or sampler expects. Additionally, they set guesses and bounds # and package results up in a way that is as consistent as # possible. For users wishing use a custom minimizer or sampler, it # can be instructive to look at the source code for these functions. sncosmo-2.12.1/docs/_examples/plot_custom_source.py000066400000000000000000000111741476435666400225160ustar00rootroot00000000000000""" =========================== Creating a new Source class =========================== Extending sncosmo with a custom type of Source. A ``Source`` is something that specifies a spectral timeseries as a function of an arbitrary number of parameters. For example, the SALT2 model has three parameters (``x0``, ``x1`` and ``c``) that determine a unique spectrum as a function of phase. The ``SALT2Source`` class implements the behavior of the model: how the spectral time series depends on those parameters. If you have a spectral timeseries model that follows the behavior of one of the existing classes, such as ``TimeSeriesSource``, great! There's no need to write a custom class. However, suppose you want to implement a model that has some new parameterization. In this case, you need a new class that implements the behavior. In this example, we implement a new type of source model. Our model is a linear combination of two spectral time series, with a parameter ``w`` that determines the relative weight of the models. """ import numpy as np from scipy.interpolate import RectBivariateSpline import sncosmo class ComboSource(sncosmo.Source): _param_names = ['amplitude', 'w'] param_names_latex = ['A', 'w'] # used in plotting display def __init__(self, phase, wave, flux1, flux2, name=None, version=None): self.name = name self.version = version self._phase = phase self._wave = wave # ensure that fluxes are on the same scale flux2 = flux1.max() / flux2.max() * flux2 self._model_flux1 = RectBivariateSpline(phase, wave, flux1, kx=3, ky=3) self._model_flux2 = RectBivariateSpline(phase, wave, flux2, kx=3, ky=3) self._parameters = np.array([1., 0.5]) # initial parameters def _flux(self, phase, wave): amplitude, w = self._parameters return amplitude * ((1.0 - w) * self._model_flux1(phase, wave) + w * self._model_flux2(phase, wave)) ######################################################################## # ... and that's all that we need to define!: A couple class attributes # (``_param_names`` and ``param_names_latex``, an ``__init__`` method, # and a ``_flux`` method. The ``_flux`` method is guaranteed to be passed # numpy arrays for phase and wavelength. # # We can now initialize an instance of this source from two spectral time # series: #Just as an example, we'll use some undocumented functionality in # sncosmo to download the Nugent Ia and 2p templates. Don't rely on this # the `DATADIR` object, or these paths in your code though, as these are # subject to change between version of sncosmo! from sncosmo.builtins import DATADIR phase1, wave1, flux1 = sncosmo.read_griddata_ascii( DATADIR.abspath('models/nugent/sn1a_flux.v1.2.dat')) phase2, wave2, flux2 = sncosmo.read_griddata_ascii( DATADIR.abspath('models/nugent/sn2p_flux.v1.2.dat')) # In our __init__ method we defined above, the two fluxes need to be on # the same grid, so interpolate the second onto the first: flux2_interp = RectBivariateSpline(phase2, wave2, flux2)(phase1, wave1) source = ComboSource(phase1, wave1, flux1, flux2_interp, name='sn1a+sn2p') ########################################################################## # We can get a summary of the Source we created: print(source) ########################################################################## # Get a spectrum at phase 10 for different parameters: from matplotlib import pyplot as plt wave = np.linspace(2000.0, 10000.0, 500) for w in (0.0, 0.2, 0.4, 0.6, 0.8, 1.0): source.set(w=w) plt.plot(wave, source.flux(10., wave), label='w={:3.1f}'.format(w)) plt.legend() plt.show() ########################################################################## # The w=0 spectrum is that of the Ia model, the w=1 spectrum is that of # the IIp model, while intermediate spectra are weighted combinations. # # We can even fit the model to some data! model = sncosmo.Model(source=source) data = sncosmo.load_example_data() result, fitted_model = sncosmo.fit_lc(data, model, ['z', 't0', 'amplitude', 'w'], bounds={'z': (0.2, 1.0), 'w': (0.0, 1.0)}) sncosmo.plot_lc(data, model=fitted_model, errors=result.errors) ########################################################################## # The fact that the fitted value of w is closer to 0 than 1 indicates that # the light curve looks more like the Ia template than the IIp template. # This is generally what we expected since the example data here was # generated from a Ia template (although not the Nugent template!). sncosmo-2.12.1/docs/_examples/plot_lc_fit.py000066400000000000000000000045441476435666400210670ustar00rootroot00000000000000""" ===================== Fitting a light curve ===================== This example shows how to fit the parameters of a SALT2 model to photometric light curve data. First, we'll load an example of some photometric data. """ import sncosmo data = sncosmo.load_example_data() print(data) ##################################################################### # An important additional note: a table of photometric data has a # ``band`` column and a ``zpsys`` column that use strings to identify # the bandpass (e.g., ``'sdssg'``) and zeropoint system (``'ab'``) of # each observation. If the bandpass and zeropoint systems in your data # are *not* built-ins known to sncosmo, you must register the # corresponding `~sncosmo.Bandpass` or `~sncosmo.MagSystem` to the # right string identifier using the registry. # create a model model = sncosmo.Model(source='salt2') # run the fit result, fitted_model = sncosmo.fit_lc( data, model, ['z', 't0', 'x0', 'x1', 'c'], # parameters of model to vary bounds={'z':(0.3, 0.7)}) # bounds on parameters (if any) ##################################################################### # The first object returned is a dictionary-like object where the keys # can be accessed as attributes in addition to the typical dictionary # lookup like ``result['ncall']``: print("Number of chi^2 function calls:", result.ncall) print("Number of degrees of freedom in fit:", result.ndof) print("chi^2 value at minimum:", result.chisq) print("model parameters:", result.param_names) print("best-fit values:", result.parameters) print("The result contains the following attributes:\n", result.keys()) ################################################################## # The second object returned is a shallow copy of the input model with # the parameters set to the best fit values. The input model is # unchanged. sncosmo.plot_lc(data, model=fitted_model, errors=result.errors) ####################################################################### # Suppose we already know the redshift of the supernova we're trying to # fit. We want to set the model's redshift to the known value, and then # make sure not to vary `z` in the fit. model.set(z=0.5) # set the model's redshift. result, fitted_model = sncosmo.fit_lc(data, model, ['t0', 'x0', 'x1', 'c']) sncosmo.plot_lc(data, model=fitted_model, errors=result.errors) sncosmo-2.12.1/docs/_helpers/000077500000000000000000000000001476435666400160345ustar00rootroot00000000000000sncosmo-2.12.1/docs/_helpers/bandpass_page.py000066400000000000000000000107221476435666400211770ustar00rootroot00000000000000"""Generate a restructured text document that describes built-in bandpasses and save it to this module's docstring for the purpose of including in sphinx documentation via the automodule directive.""" from sncosmo.bandpasses import _BANDPASSES, _BANDPASS_INTERPOLATORS __all__ = [] # so that bandpass_table is not documented. # string.ascii_letters in py3 ASCII_LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' bandpass_meta = _BANDPASSES.get_loaders_metadata() table_delim = " ".join([15 * '=', 120 * '=', 8 * '=', 20 * '=', 24 * '=']) table_colnames = ("{0:15} {1:120} {2:8} {3:20} {4:24}" .format('Name', 'Description', 'Data URL', 'Retrieved', 'Reference')) urlnums = {} def bandpass_table(setname): """Return a string containing a rst table of bandpasses in the set.""" lines = [table_delim, table_colnames, table_delim] allrefs = [] for m in bandpass_meta: if ( m['filterset'] != setname or # special case of ztf position-dependent bandpass (setname == 'ztf' and 'ZTF-II IN2P3 participation group' in m['description']) ): continue reflink = '' urllink = '' retrieved = '' if 'reference' in m: reflink = '[{0}]_'.format(m['reference'][0]) if m['reference'] not in allrefs: allrefs.append(m['reference']) if 'dataurl' in m: dataurl = m['dataurl'] if dataurl not in urlnums: if len(urlnums) == 0: urlnums[dataurl] = 0 else: urlnums[dataurl] = max(urlnums.values()) + 1 urllink = '`{0}`_'.format(ASCII_LETTERS[urlnums[dataurl]]) if 'retrieved' in m: retrieved = m['retrieved'] lines.append("{0!r:15} {1:120} {2:8} {3:20} {4:24}".format( m['name'], m['description'], urllink, retrieved, reflink)) lines.append(table_delim) lines.append("") for refkey, ref in allrefs: lines.append(".. [{0}] {1}".format(refkey, ref)) return "\n".join(lines) # ----------------------------------------------------------------------------- # Build the module docstring # Get the names of all filtersets setnames = [] for m in bandpass_meta: setname = m['filterset'] if setname not in setnames and setname not in ('hsc', 'megacam6'): setnames.append(setname) # For each set of bandpasses, write a heading, the table, and a plot. lines = [] for setname in setnames: lines.append("") lines.append(setname) lines.append(len(setname) * "-") lines.append("") lines.append(bandpass_table(setname)) lines.append(""" .. plot:: from bandpass_plot import plot_bandpass_set plot_bandpass_set({0!r}) """.format(setname)) # Bandpass interpolators bandpass_interpolator_meta = _BANDPASS_INTERPOLATORS.get_loaders_metadata() setnames = {'megacampsf'} for setname in setnames: names = [m['name'] for m in bandpass_interpolator_meta if m['filterset'] == setname] lines.append("") lines.append(setname) lines.append(len(setname) * "-") lines.append("") lines.append("These are radially-variable bandpasses. To get a Bandpass at a given radius, use ``band = sncosmo.get_bandpass('megacampsf::g', 13.0)``") lines.append("") lines.append(""" .. plot:: from bandpass_plot import plot_bandpass_interpolators plot_bandpass_interpolators({0!r}) """.format(names)) # General bandpass interpolators setnames = {'ztf', 'megacam6', 'hsc'} for setname in setnames: names = [m['name'] for m in bandpass_interpolator_meta if m['filterset'] == setname] lines.append("") lines.append(setname) lines.append(len(setname) * "-") lines.append("") lines.append("These are focalplane variable bandpasses. To get a Bandpass at a given (x, y, sensor_id) position on the focal plane, use ``band = sncosmo.get_bandpass('{0}', x=0, y=0, sensor_id=1)``. To get the averaged Bandpass, use ``band = sncosmo.get_bandpass('{0}')``".format(names[0])) lines.append("") lines.append(""" .. plot:: from bandpass_plot import plot_general_bandpass_interpolators plot_general_bandpass_interpolators({0!r}) """.format(setname)) # URL links accumulated from all the tables. for url, urlnum in urlnums.items(): lines.append(".. _`{0}`: {1}".format(ASCII_LETTERS[urlnum], url)) lines.append("") __doc__ = "\n".join(lines) sncosmo-2.12.1/docs/_helpers/bandpass_plot.py000066400000000000000000000113661476435666400212460ustar00rootroot00000000000000"""Helper function to plot a set of bandpasses in sphinx docs.""" import numpy as np from matplotlib import rc from matplotlib import pyplot as plt import sncosmo cmap = plt.get_cmap('viridis') def plot_bandpass_set(setname, label_prefix=''): """Plot the given set of bandpasses.""" rc("font", family="serif") bandpass_meta = sncosmo.bandpasses._BANDPASSES.get_loaders_metadata() fig = plt.figure(figsize=(9, 3)) ax = plt.axes() nbands = 0 for m in bandpass_meta: if ( m['filterset'] != setname or # special case of ZTF position-dependent bandpasses 'ztf::' in m['name'] ): continue b = sncosmo.get_bandpass(m['name']) # add zeros on either side of bandpass transmission wave = np.zeros(len(b.wave) + 2) wave[0] = b.wave[0] wave[1:-1] = b.wave wave[-1] = b.wave[-1] trans = np.zeros(len(b.trans) + 2) trans[1:-1] = b.trans ax.plot(wave, trans, label=label_prefix + m['name']) nbands += 1 ax.set_xlabel("Wavelength ($\\AA$)") ax.set_ylabel("Transmission") ncol = 1 + (nbands-1) // 9 # 9 labels per column ax.legend(loc='upper right', frameon=False, fontsize='small', ncol=ncol) # Looks like each legend column takes up about 0.125 of the figure. # Make room for the legend. xmin, xmax = ax.get_xlim() xmax += ncol * 0.125 * (xmax - xmin) ax.set_xlim(xmin, xmax) plt.tight_layout() plt.show() def plot_bandpass_interpolators(names): # we'll figure out min and max wave as we go. minwave = float('inf') maxwave = 0. fig, axes = plt.subplots(nrows=len(names), ncols=1, figsize=(9., 2.5*len(names)), squeeze=True, sharex=True) for i in range(len(names)): bi = sncosmo.bandpasses._BANDPASS_INTERPOLATORS.retrieve(names[i]) radii = np.linspace(bi.minpos(), bi.maxpos()-0.000001, 8) for r in radii: band = bi.at(r) # update min,max wave minwave = min(minwave, band.minwave()) maxwave = max(maxwave, band.maxwave()) wave = np.linspace(band.minwave(), band.maxwave(), 1000) trans = band(wave) label = ("radius = {:4.1f}cm".format(r) if (r == radii[0] or r == radii[-1]) else None) axes[i].plot(wave, trans, color=cmap((r - bi.minpos())/ (bi.maxpos() - bi.minpos())), label=label) axes[i].legend(loc='upper right') axes[i].set_ylabel("Transmission") axes[i].text(0.03, 0.92, names[i], transform=axes[i].transAxes, va='top', ha='left') axes[-1].set_xlabel("Wavelength ($\\AA$)") plt.tight_layout() plt.show() def plot_general_bandpass_interpolators(name): if name == 'hsc': return plot_bandpass_set(name, label_prefix='averaged ') names = [ m['name'] for m in sncosmo.bandpasses._BANDPASS_INTERPOLATORS.get_loaders_metadata() if m['filterset'] == name] # we'll figure out min and max wave as we go. minwave = float('inf') maxwave = 0. fig, axes = plt.subplots(nrows=len(names), ncols=1, figsize=(9., 2.5*len(names)), squeeze=True, sharex=True) for i in range(len(names)): bi = sncosmo.bandpasses._BANDPASS_INTERPOLATORS.retrieve(names[i]) b = sncosmo.bandpasses._BANDPASSES.retrieve(names[i]) # add zeros on either side of bandpass transmission wave = np.zeros(len(b.wave) + 2) wave[0] = b.wave[0] wave[1:-1] = b.wave wave[-1] = b.wave[-1] trans = np.zeros(len(b.trans) + 2) trans[1:-1] = b.trans axes[i].plot(wave, trans, label='average') x = 1000 y = 1000 nsensors = len(bi.transforms._to_focalplane) for n, sid in enumerate(np.linspace(1, nsensors-1, 4, dtype=int)): band = bi.at(x=x, y=y, sensor_id=sid) # update min,max wave minwave = min(minwave, band.minwave()) maxwave = max(maxwave, band.maxwave()) wave = np.linspace(band.minwave(), band.maxwave(), 1000) trans = band(wave) label = 'x={}, y={}, sensor_id={}'.format(x, y, sid) axes[i].plot(wave, trans, label=label, color=cmap(n / 4), linestyle='dotted') axes[i].legend(loc='upper right') axes[i].set_ylabel("Transmission") axes[i].text(0.03, 0.92, names[i], transform=axes[i].transAxes, va='top', ha='left') axes[-1].set_xlabel("Wavelength ($\\AA$)") plt.tight_layout() plt.show() sncosmo-2.12.1/docs/_helpers/magsystem_page.py000066400000000000000000000023171476435666400214160ustar00rootroot00000000000000"""Generate a restructured text document that describes built-in magsystems and save it to this module's docstring for the purpose of including in sphinx documentation via the automodule directive.""" import string from sncosmo.magsystems import _MAGSYSTEMS lines = ['', ' '.join([10*'=', 60*'=', 35*'=', 15*'=']), '{0:10} {1:60} {2:35} {3:15}' .format('Name', 'Description', 'Subclass', 'Spectrum Source')] lines.append(lines[1]) urlnums = {} for m in _MAGSYSTEMS.get_loaders_metadata(): urllink = '' description = '' if 'description' in m: description = m['description'] if 'url' in m: url = m['url'] if url not in urlnums: if len(urlnums) == 0: urlnums[url] = 0 else: urlnums[url] = max(urlnums.values()) + 1 urllink = '`{0}`_'.format(string.ascii_letters[urlnums[url]]) lines.append("{0!r:10} {1:60} {2:35} {3:15}" .format(m['name'], description, m['subclass'], urllink)) lines.extend([lines[1], '']) for url, urlnum in urlnums.items(): lines.append('.. _`{0}`: {1}'.format(string.ascii_letters[urlnum], url)) lines.append('') __doc__ = '\n'.join(lines) sncosmo-2.12.1/docs/_helpers/photdata_aliases_table.py000066400000000000000000000023331476435666400230630ustar00rootroot00000000000000from sncosmo.photdata import PHOTDATA_ALIASES # Generate docstring: table of aliases # Descriptions for docstring only. _photdata_descriptions = { 'time': 'Time of observation in days', 'band': 'Bandpass of observation', 'flux': 'Flux of observation', 'fluxerr': 'Gaussian uncertainty on flux', 'zp': 'Zeropoint corresponding to flux', 'zpsys': 'Magnitude system for zeropoint', 'fluxcov': 'Covariance between observations (array; optional)' } _photdata_types = { 'time': 'float', 'band': 'str', 'flux': 'float', 'fluxerr': 'float', 'zp': 'float', 'zpsys': 'str', 'fluxcov': 'ndarray' } lines = [ '', ' '.join([10 * '=', 60 * '=', 50 * '=', 50 * '=']), '{0:10} {1:60} {2:50} {3:50}' .format('Column', 'Acceptable aliases (case-independent)', 'Description', 'Type') ] lines.append(lines[1]) for colname in PHOTDATA_ALIASES: alias_list = ', '.join([repr(a) for a in PHOTDATA_ALIASES[colname]]) line = '{0:10} {1:60} {2:50} {3:50}'.format( colname, alias_list, _photdata_descriptions[colname], _photdata_types[colname]) lines.append(line) lines.extend([lines[1], '']) __doc__ = '\n'.join(lines) sncosmo-2.12.1/docs/_helpers/source_page.py000066400000000000000000000035471476435666400207130ustar00rootroot00000000000000"""Generate a restructured text document that describes built-in sources and save it to this module's docstring for the purpose of including in sphinx documentation via the automodule directive.""" import string from sncosmo.models import _SOURCES lines = [ '', ' '.join([30*'=', 7*'=', 10*'=', 27*'=', 30*'=', 7*'=', 20*'=']), '{0:30} {1:7} {2:10} {3:27} {4:30} {5:7} {6:50}'.format( 'Name', 'Version', 'Type', 'Subclass', 'Reference', 'Website', 'Notes') ] lines.append(lines[1]) urlnums = {} allnotes = [] allrefs = [] for m in _SOURCES.get_loaders_metadata(): reflink = '' urllink = '' notelink = '' if 'note' in m: if m['note'] not in allnotes: allnotes.append(m['note']) notenum = allnotes.index(m['note']) notelink = '[{0}]_'.format(notenum + 1) if 'reference' in m: reflink = '[{0}]_'.format(m['reference'][0]) if m['reference'] not in allrefs: allrefs.append(m['reference']) if 'url' in m: url = m['url'] if url not in urlnums: if len(urlnums) == 0: urlnums[url] = 0 else: urlnums[url] = max(urlnums.values()) + 1 urllink = '`{0}`_'.format(string.ascii_letters[urlnums[url]]) lines.append("{0!r:30} {1!r:7} {2:10} {3:27} {4:30} {5:7} {6:50}" .format(m['name'], m['version'], m['type'], m['subclass'], reflink, urllink, notelink)) lines.extend([lines[1], '']) for refkey, ref in allrefs: lines.append('.. [{0}] `{1}`__'.format(refkey, ref)) lines.append('') for url, urlnum in urlnums.items(): lines.append('.. _`{0}`: {1}'.format(string.ascii_letters[urlnum], url)) lines.append('') for i, note in enumerate(allnotes): lines.append('.. [{0}] {1}'.format(i + 1, note)) lines.append('') __doc__ = '\n'.join(lines) sncosmo-2.12.1/docs/_logo/000077500000000000000000000000001476435666400153325ustar00rootroot00000000000000sncosmo-2.12.1/docs/_logo/linkout.svg000066400000000000000000000143241476435666400175440ustar00rootroot00000000000000 image/svg+xml Community sncosmo-2.12.1/docs/_logo/make_logo.py000077500000000000000000000016261476435666400176510ustar00rootroot00000000000000#!/usr/bin/env python """Make a 500x500 pixel png of the Hsiao spectrum at phase = 0""" from matplotlib import cm import matplotlib.pyplot as plt import sncosmo cmap = plt.get_cmap('gist_rainbow') model = sncosmo.get_source('hsiao') wave = model._wave flux = model.flux(0., wave) wmin = 2600. wmax = 7400. mask = (wave > wmin) & (wave < wmax) wave = wave[mask] flux = flux[mask] cscale = (wmax - wave[1:]) / (wmax - wmin) colors = cmap(cscale) fig = plt.figure(figsize=(1., 1.), facecolor=(0., 0., 0., 0.)) ax = plt.axes([0., 0., 1., 1.], axisbg=(0.,0., 0., 0.)) for i in range(len(cscale)): d0, d1 = wave[i], wave[i+1] f0, f1 = flux[i], flux[i+1] plt.fill([d0, d0, d1, d1], [0., f0, f1, 0.], color=colors[i]) plt.xlim(wmin, wmax) plt.ylim(0., 1.05*max(flux)) plt.axis('off') plt.savefig('spectral.png', dpi=500., transparent=True) plt.savefig('spectral_white_bkg.png', dpi=500., transparent=False) sncosmo-2.12.1/docs/_logo/sncosmo_banner.svg000066400000000000000000000775301476435666400210750ustar00rootroot00000000000000 image/svg+xml sncosmo Community A Python Library for Supernova Cosmology sncosmo-2.12.1/docs/_logo/sncosmo_logo_docs.svg000066400000000000000000000265721476435666400216000ustar00rootroot00000000000000 image/svg+xml sncosmo:docs Community sncosmo-2.12.1/docs/_logo/sncosmo_logo_spectral.svg000066400000000000000000000757341476435666400224710ustar00rootroot00000000000000 image/svg+xml sncosmo Community sncosmo-2.12.1/docs/_logo/sncosmo_logo_word.svg000066400000000000000000000270441476435666400216160ustar00rootroot00000000000000 image/svg+xml sncosmo Community sncosmo-2.12.1/docs/_logo/spectral.png000066400000000000000000000346611476435666400176670ustar00rootroot00000000000000PNG  IHDRߊsBIT|d pHYsLLu IDATxyeg]w5RTUR!!$d&$@B# a0AmV#-vխkI+Vhum(0{?9{{uVUݳ~:}$IP@4@4@4@4@4@4@4@4@cz˵Qғ%=9r$IPAvJTUt9Iju+C2v$0_@F:+J]=Qi~#~(dLr]ٔ#VS-W$}88:[ Gj$\_=SG/{tTvFc_J= t ;31MyÄtQrY0)Np̴ $wJc1,ː.QKҼJ`StER}~(tKMm՚/U,`N2Q_{N4}S@Ǡ9=nsr@Ǡm9=~W+:8DcP@COtK` m` @ǠC9Ccz;\ -Iϒfٗ,; ?NT%o@Gt R] D.-ٚp hzTg3rO!БJ =6C#Lvm{N6#P@_@~:68'$:':Rznz*{ %Q]ұ3@G_!3@G_ckvw]tp@G_@?zGl@t tՋ+m-.-i՘ s:DDo|}w-N@D2 ˴|4B!W5I@Ow $.qL5: 'RP>z!S;DCW\=m3@$R\NJ"!.P :$->Y셚+>%萤54t(@4>]t(@Tю)y><RzJ~`9 ,=4v=Qȉ:,>iwqe KLTzK԰5`fCj`;@C*Cw:@C"tHq!w(@TfWw@ :j t(@T:CP+!D863 \ C>~Xg]P=pGH"<Ecu@a:֗:mtz5!:;^ f*:=t(@u nPlA }%X%:DJ~CtT~Nt[Xƭ5v'>w@(>N@e(>N@e蠇 @j~@ù]u/MKA织:@#=?;:@#2=忞;#N%^+ y,G; =gB\.W"Q@?tTm-w:FzP)::DWԹ\m '.]@D 'BZ7%]N] ^-W5tG͇h,.ղI jtyS"V^='V# Z%Pg?U39qybIJ:"mV9@zuJgpPze>N@FzD&=3 ~&U=rH5tLN0㸌:=tȈ@yOJ?鸆>iz{/ߣ= tq3PIqtrx.x'P$[@#kX33=Q)ܧ=KkNǐ;q\T3'AG@ LUQ:d@G`{{t+k!}~&Vl\X@Be='zGKRlqmz`Q@zx8YUU =<+Iz .{腗sjs}[ *=^綝UBK\{9/[@B>ĹkUœƖـؠ gW}M]TЅ~{iW>Sa >>= zX'x&%v{&yW ϫSg^.סGh #êN[UbL9BPIp٢~:|  {TB ea=yUL|7d%ڬ@)z!>{x~zr7lJ!yy =).`s~ESx8txₗO=/`['$VuU 7= R畑ڽY*U-@S@[(&Mқ 9x M&YtvfP(T)3@{@[{w~9 | S~g!$]Zb/Oa.\Ֆy}(@odV  f(Czd}?9*j 0"!"tg{sZ:(@crs^E׵VnmSVoӴwmGy2@5anG$Y0= ߁^dȽj^s>Ks_)*G P=V)?t٣'g>r>9=FA`]"G R;)=xC~_55Is:=힯?=t_˿&:-\/u@ zo. YWxxlIXw:BD"Hﵰ5~>K<ퟵ%Ik2ٴ&v衟Z謹ŎRtϮ0 :(Uu=@<퉏ZS?*$j' Ip49'W~do]cW{=t^?i(@/Tql={%b}g"%og2v/]HzUWw}a68?P_׹~k.}?:r-3vw9BW!y?ϢKK_J"s٣$OWgbY~.E]2:PU[B59Nc:ԭDC @{tmX3)΁ή%= 7ݯCyzު[m_Ő{GOp|=ȅ@@.'VtBCwdzz&dBE9>2Ltt?qp㋟^ޫ{-.(@^Y^uz"H{{`G`˰WkuZ8OtCyI\ҳpkrD` 'ų;\c@'}mZâ2tQl Tnx@?;C7fg@>I0֬@ 1f;Цw22@'x]]}+eL;O|}X@@>\թK@O{SN\B(K1  8ݟЛ2qsy]# !GY 5'nO^9nt>bGzSy\sRJn ZZGRg (ݏX˰REY/^IkwR ׎]@CښsY :JU;6$k<93KCY]~Q% 9&K_k cݏ*zSzS[?0{yԖt򹤀C(ݏ*Л2>mtiN_Ibr!w?艎 PG5]#:t?b~]&@UB'@wiyY]6{؇'_p@woOĶjUyV޲ #,:7=c`vY롏^/?5@ ^m1G]tU=C_5xWN|jC8 == #vu*ƋD;tiNKMzjb*>zs/mrWT$: ;$]ͪ Oꡯ VKm-ߔe9Q%Z}hjKC-q[:/,C|SRwV7=d鄩M&饾l)M3X&=TCRоh&z[_-r_ ̪y=lh&$ki=sC*߫@?% V@^={cJ LyܳЏ\E{cf!W߁3g{r豉fnԆ5L7v.I]6`UZg.a |y~k13X!鹱KH%Jۺ:ׂoUu 1k$==\I:X!Ώ]Bz@|3hUVm`FaJkhvutnU W~a*= 6I~Ѧz3C5E#W tIJ=͎GmZ1GӖo:cWM &H;2w{fICk^s7}<TR>iis6[g6M?z}?5v^/qΖ=wCj?^Kc2'?dVuS]J{6a*ʿ׫6ý3Rݾ tp<[{+j5 W心{n1˱ to=wfT?3ڜxpp _FW"J_}x)خ #U ]SI4eƍ5|5zP-FkIXBF]MX𺀅Ȯ.Q_L7UW-;GЫ| &|dލ]jimO-GUYFw}[޵GΌ]DOUSo UϏ&[Գyf#k1>+cͱ O}KS zKzys{}X%'MH t*j4[`V?n_³=a ÒݦTy]˵|[y| YK[@9˽v?cl%}i¦nc Zo>rv4/2TVzR;zdݼ!a״nGLӭe'l+sGP0S@Vwu`:צ,CHҹtkfL?WF~}#U@d >aJSa{l$,2?Hҳ= vbv̼Jz"6.%C~''űL$7jp{G ZMS •dOCLh=vȫ|^Ւ ?9M7$Qe}%6gL'=::2( O.J%7i}d&i=y:Y6AXe\!.x. *]D'k]s|vbQ%=^?_ZݤW{*a+ݮ@G=EK2_{;X@QS+=Ig näz}&<lx`e-HMU#^?g^7z}ig:ZLodh= ތ?7i{}fzI^]IzhcL7f ؁egZviB%]LK-iV_WK/ ::/9h#jtw㫿Gۆ/{,eҐuuܫoM7urwת;cc;>j>M7sp6Ot\b}_@_Qˤy?ZZYsQbo{w(bz{}İ՞wRm6'2*e]QvIzj#3?zz^&'\+蒮wpvlk؝}m^n9?F ;^Oknhdu!0v83?ޣjJ/e-Xp <[W7ЇV#QW#@M85\5Z8EҚ!~JSG\zא{ֲ;Qy_rp2cɎ^-D7iŘ7v cZTDs``Y6ʠ!= «vI#d$]b_"tϫUbU;=tMxWxUvC,dI_(vaW~LgHgKk6'(\"%II:Z=̋{}AMܠ&c&{}4`+!l$XJ|VKV=ztZwL!7sQ}ްp^ʠaV|Oq-JmOt:7OkA7M9> a}r+ Ef\3X=А#\0~n9gڑ?w~t--Y:[6qG>GfCÓQJ+~2µSgNzzM"԰«ޒx=QڴaM  ~w>3.LV+O{zK?364.me>ZRۤ۝4mqBz;Լ܁gj`ŰŏL޿O;#^Fwk7]&^Rpp,.9 c8x:з9*qf"Io440ŲaQ#Hϓc[;_iGfoGo[@&t|rK}&X6>*߸4=o.4d7:h џ ŭ l `8wTMSjzxSH6=z~|-r_bUx6vصq^tqq޿d7:3'ݟ^`ߦ+mt{35d' #z$5eiitgA@t.]IƮ Ĵk?V+_P?|J[ 79Z>)~0LNE;  )%@>]I:]A mUNOt3I9/~UoҒ^^n?uָl Ej1 ti㏩ʾS% ̩91Oy|&skavF|(y@@I银+? +uVd K=^[}os2U/ η1nsp*doVn;@ >#$m30)94I/K+wtŸ2K/w~|Fe o&ӕ5C,+uJXh|h[CRgKO{?~In @G5I>VuZ{9DS'۲ʖtkKZ#iM[ZՒ.\+}FOvC\)~Uo3C‘7j:}&0t|L:vkܙ?97J{-7P U>z~$to\|H6^ lK[V+N^yM$b ڨI0MQ4nlveI巏{(YMm޴^iϼt,x*Lz@3U9YzDJ>v@֒4'=8}hrI,fL@?6P;*?SVqKz}9iu#N k\%I!I^6SPu%x=c8IICyϽaW ҧ=qkb tovT͡ n{HR&-#to|+}s%]= ޳;LYGi)[ʎ;e+wMs}CvD_HpF}+I7qBzo}**^5`P t`f$*΂$]mھIU\X=VnRIɼ_ w|vKc5*НOfdIzpdN{[ p-iu}X@-Hه P:3LmUx#s<5@?}x!I )0ٽx ܳWeC ?,pw%utC"eYerz^Iz`H")X ΰ)H}5ojQK8@4{3oWzuۄ':&ݭ.@Mm][4eߔ.<|((u2D#_tqK: . 1.ɞ/IAIv5:׺㖇Bwh@Ye5]bEsvI~dtNPLJ g/l%M$I}sp(D1 fw{1;+lUlҵnIگDlZH@2zȻʶz_KzNZ DIW-p_TD?Rդ'zl暖uNK`{p$%m7c=p$A7vǸc/lIZ`L>-A*G0cJuYvBFJ6@k,[DQ=@&>.YɉdI7}~k;ȄYY[#^7L {x C> Mc7?,HZМjo CuQ1ٺyCF-> 3脂煛.4#`mb/n yN_(Q=XXIqPmۂmC@OLk]_fXzy_"鰤úO_1:K?5`V~#G!S-H:$}&J3G0N{B"G!tPW,w(Glu^EIzL#П0>.ƄQzOaEL\;6DZy"D>?vRf՗gު("ѓdK=uߜzbwy=|*JtLsCz|^͕:?ߍ]Ft[)k,Z{2uu|=@@;Yo{kFoό]J9C=PSx)>kq>cEw~ٱjk>}DzJ:ږإ4 @ lcU>N"/vuR@zr^O%2IOًciSD-%>i.\p VSG'؅ԙ@osPMz>^K\gآzrkL &<6Frݫ] 0d7;[WG'^!9$Sܒ4{œKCOW;{#/̾NZ0Z ~VߒY[0q o/yp/bAq{O2Q]n1_aߠ_uKn$ ˂F2Efݼq~[*B: z:p׳$<1\ĒH:m]JՕKb6i,Gq\%v)UWk= [Z|v#gK>*+v2 ҇H=9X!1C/Gk{S: {eR@ݾ mt_ `S&ڟ6ū(gϋ]JU tڅAo:i;~mǧUIWA}\ﲟ]J* =.-s? TNԛveĖ=k%$~9f;=fC[LzڏqwB(*uuDld V`UjkC]w٣zm]Kӿ/+$m_ `4A:~տyvPϳ.Ʌ,? HÏ5~vhA=-3n.,~*:wzKt ښ@`XWuH*K&C'L̦#ڧGԺ~SO"5~/V)`:$uxK$}Mn_*&ߎYT@tex%: s}UҴ@,H]%vYW{O t DCz#gv*UQ[?A{w4VtA謹mAM |6 JzL/ZKξ9si@/+]7LK$utÚ#j^+[1=$mr!Q[I}R+[ن=e.7&Г(Ȫ%[m8";nܽq~N@n4!ۑm}C3|@ Iȶwd;.tƊߚ3]kI$;۲=sC?SU-v#F+$ͿJv!iG;ԴyIj|@vLҪwu+%@uUgNp܈@%JC&HBZ{}q e z8e2>.b-/I7.ӖZO;soFt@t@t@t@t@t@t@t@t@t@t@t@t@t@t@t@t@t@t@t|P8IENDB`sncosmo-2.12.1/docs/_logo/spectral_white_bkg.png000066400000000000000000000362231476435666400217060ustar00rootroot00000000000000PNG  IHDRߊsBIT|d pHYsLLu IDATx{}ggPTIXQd%J9.)e%J#'Dĩ쒙.J,XH]HA .@q!N`],^;;;t=ۗrySյ39N!@ FH@ tr@ tr@ tr@ tr@ tr@ tr@ tr@ tr@ t.=uP; BAڝJҾKoj5ii4/>*7["|GOF@p:CCTsi/59:$Io՝~x3BstH&gs~ߡ.O*mF@$i5dv^ :Y`9x@#q:;#)8f8 !)ޔ@&㞲6m`@)=kef/Х ss1Gڌ Ct]O9ff4zƯ:1vS\v;osA=OkS$bR_n?\_舽\Wppz=~,r̩v aGC t@r:]:ݬ0tx׮#{Ĕ;@*up:#tH@6З֓Gg:1-#СR@@CR)AaIgz*\s1B#~]/x0)! /gCjZ0l!@ZFR  Æ@rI] )V@ljN@lr '#ЇJ҅hCnÃ*q- >}ȕ= t@|i~t:@vCnvmTz=^@6CnӃSd> Aob0 !@gBD7!@r$č,( Ô;Cb,0C.Jq#_O,8rZF%j>FHZ-d>"ЃĵL0C.*w~q>m[d@tB;CtO#t@ԑAz@tGjOJcoۗ4 DF{R(=}*i`Bq@ʲ3Cthty4yM[>: 3If7|tV@$G$oln=GL@4'^}N:rIiYڰ{$rhtOLMHm:Gd7O#>֥zzJ_dv$Yեx0WK$+ =k >3&L?Ŵ{@^{~d"Ki@OAQQ>!&G&:B :Jkd#t@OAy358JJ: Ѵ;0l[gwظV`WbarR{S  ݱ3u긹k)w@wYs@gB!dr~"kOS-ʽ=vk?kqm>v7G #[2N7|8i.5~GX ض"l̺0L![vC67bF6> `0ݡI׌[ݻ)>ed #lsq=C#E#XU t.P;5RmLei>='}4B?fbmi,{"g@;ݡ}f{/4\ԤeݡE ˾@@,-REsL=Cd(GUI6?CA;b0܌(:懝gRiwA;rC񮙵@?쮭%Ikf ЮÉ@wdj=ة@y"vJ7I'2tp", (ZW_Km@w=Ρ/Yr_Jija u# S5Y ŴaH. Sk1=k]{h Ѵ;0,lNDM;Ks--e0É#/=dڱ=k#tIou;:uDz8du^0=0|nx@bҥM~[/v2EeZ៛$i`~#xp@Քz@y>OF T 2W#(O\+aKe"CssCG 2/3o9nΦT2}A{dq#tϓ<@[vm7D/d0_M% x@EqdkMMHn[Qmf8Mn[{*A^6w)@hᱦu#t)#-ZvVEq* tӳ'DF[0У˞r]ZPBvQ: (г@)Ĕ)0УH*G˜1F0nʀzHNjR=K];4=t0n_}@^$Q5~[#skL[V/%F#-z>v!Jz~ԭK](.1ݰjEZHnauj}fح@?/Q\Sa3JF&WlK&]ma@rak;̢rF賥_?,/&0CaSv){¢~r)w]M1Їb^~YWz|5F\" K3s9B{qkQ^_up@7)̏۬Vڋ$8DTIk?̔{Yn_dt.1-J4}QCܶ^m;vn'( S\bni@hJ}nS!f) $CA<uFFD䎥蕎GURU hQ:Gt|YWFdQ:Gt|a?>kqM! rzmH>^?z!@>qR{P@Z^xAԽRܪ9_ѾmCҦ. <\(z^@=F=IS^WKv/w2ڇ؃{Z l?_WYG*-dmlWb,|$Y.t۶vaU7\Vtӕ&ԨԴּ_QMw4 Lzqž1Ŕz`LzYˆ:^wݦܑ~7GJ_˪02IE ]a ?k]茶o $h†:@>[UrJ5>G69KUƹP=u\מO*M}S/rnuh9pR+HH1qeyI %Oʪ~Woܡx(^54\0{)kAA[폦mYS>t]z:jtU׻{ F̷Jt.U]cV]j+{u|·0nSz 焺\Y轐a%ŭk%uXc#H"`DǰS咙\1B'=o?zMk)GQcU鴮 ~"' AEc.nS (ڎ{geh)ǔ\#c 8j'}~@҉D׸E{/.禭#ns_IѤoffg~FoD oI> oMJ o5bǔRz@Xvgt23. -Esmr_tIZdN𑘫zP{5f:.ǥե ;Bg6 {3posޔڤ,}6)P޺t\{y݇L{'@&IZ4߽ \ @^zH,F[s^)Y|~ y8V-=qz`UK'eZ}'[NQlyRo^?t'(P }t-q70(nj&~ uմ` '{W@xA u A~n6mVO2:-X@@ b=/bMEi@Ϝo[nbw}Ux`7\Q|T P:ѣLgymfc:fc C)IDATɋ@b筇-nz&t<='ݶ}W?ci-zuE!']=l\tt>cġzǒ4?A/"W{/N3osǙ?#; +fO@6A]*yiv~m lFOS,˴jVa1( *@if)vC-ZפzsG)kךggUi;rtƯu~[xpΦl[kiNodi_}mq>y=dH-%bT:k?Y#`:lp[*ޫ?Vp+6vQ{Ea2ִa0j>Km6 rΉdfg;٭k& \q_!CWԵWB]Km^6_ ڜ4iB\hW2m_z.L2I! Ʈ峪z@e<~{_`aqWč.&̷ N^{O1=֔&ST7\[ ;RN{4; ݗPLYI};w?VwNwmȈA>,DH' ^_qX_˜Ԅ^ҳyT |wn#7znWI'UL|n'bt P ^ҵS& gQY zܕ<wVjHJ*F}jZּ6-6yoܮb3+HW]ǝګgk.|/)vC^ %Iyfm Sڣjo F]7X;v@kmpnޒqíU[Ќԭ:ڥQ.}z q}V/Tkjmu5):G)*?~N=.8$]} ڞv&N{ݮ]rOĭݡ_kuS¼6H;^`K6B{uLgoq?kCRYF/kb@e9}= }%mfd`ĆV95>VԴq=gYW3PS.dv ƨ}LkUI4UIzUilM}|+8{PJo[(ZPpuUg:n0zۮZJ] Nj]u$rB??f{,;q-iEU}8baWRu*4KFۚn_(sQO:-wSi,᯺ƴ{KmErV#Gbh]0>rs`Eqt~ .z55UCTr+kC/\g2\/QWȯ>U;)kE紷k3HgEWGtJ55UNtRPclkmmݧoUVI/~u 9ӹ}Q_;:-}B]jT K|>6K0"H3_ׇtQ+zSe>ݚឯм&S^qWXf}SDO"35*5zD BU;h9[oC=LtD"&E:1DyY)@y㾎;>!ᆶCO4wSMJn7K55ݩ*H-~BXO> '`dK:OY @~N*~ƂMpXZ$4efUi@;'?@?>ER3{xrQm=}_7jG-|%@w)F>_}fЫ߈tCߠ^So7Wq*@cwuVŴʪuwoPUMwvWOjYg$袞5t5 37n,+N|nMRM_շ~5֫Pn߯4ޏwtkb8 r[QrNGnI=9-tRkEm{kA#qԯIl>nV8iWվ|NP^;sAۡ|KQ=feK51<,Z%ZmoҍjsO_e3] Iw$Ihy7vLQ NU/Z^EժOiZvM͔; *腶Mn곅̴HQ[􏜴5$&ͯek5='tTGK)Q5*oNA>Q:FwW8} Io95ZR6-Ԭ磻$U/:kw:Ilqڜ Eݯ@+自iYzׯn@~ _I;USY$M(Pf[dUefrTjm)Zv5IK[iv1߶@L+}QSYt T}[꺪O(RҮOUk}yS@,7޲b(ղPUV/Oޯ@~{R~,9 @,9:%5݋oݒ TPMRI ޓ6>$Vszz r5"+yS֮*;¼s⿴E-whEJ]ø]MiƆ^4]j[OP}Tn?0@O$]|L)k~W_Z`)uK?uE^?f׭ X tR@wWU2uG0Q}x`@yuȯU]0~͢>A Ih`Zy׫]Nvf:+t@_'߷KZ  Vͫ@~Wcf.T|u`@94rgR/ֆ̵`mkV1cZӗ]KT|`Sj՛ $-ftTYKQz!"k `Ě.&ƺnOޑnjk#{>n0V zA/\؛r7_ʺ0ԓ꒖~nZت$AeUm@c0s/*&KOmR6SڛU]JܷHK7oY `U  s6zUe@P_M{JS|.z1uW'bKZSw+;#Oٸ* GN=#TKKq&a0c>~@q M\Eq8n|dUI@!B%WUNG-ۤZ)WZܲf'!T%mJișƯCkEg"fYZIHIKr~#Cȣ9E\׼DPtoۇ!d>" @MaA\7eߕO3XLV5i'Ϳ!{RvOuH+F=@ bObZ?)i"̎'^ h^ =I`t݋\3ѷK[;ӿv/rl_4z5z {..]} @?0 i%4z9FTr.^@@-2zZߌKߔi$WضP/Đ@ҋ?-MvOr@ sZUWĠ@ҡ_ߕvOr\יn[(w~[0d@_mDumڇ%Og@6IQq=3+{  K[MoZ!./݋2/֌] Ӡ))}MGl;"/^d@:$UPyDҮcTҊ}݆Ƭ͉@!8r5*3^ iv@RV$Ij*1I{9fRi>ImݽGiMzi"Sz[_P%vO2#y/ҋ?ۊ==';U%=WEf$'*Rm@W/=qX{ e#\kWus㲩÷JKi{ɒx&`KgzjL?%K߭`5iE\)V^ʹ{d$QzI;n;@ұ{i[\U0@W큾;a*TzPQ4 @/qKzyRHZ=R@?Uw-IS iwz.X s>j> R=J@UKN];SO꒞tiђͲ4VsuljnI5M b['i _Q\h޲EKzL 蛁tcR -5޴WA~U7\:2C? |#.T+O|8힤"|i0F$-꠪ZL+~K#t}=q@)ꤖ?nOK{~%^8.ЫSP{#vW]ɦ@K7ʹ{L@?b8EAҪ^H0yNKAH.Џ0:dPM뤍{cU@?J/v݁,ڨJQi"{Czr7]Xf]MҦԟ+p4BU v{ %=Yw> -M\֤9JJyHZ)J[z{d@εeX*J ;?8&C\ ү}3EBi*}_K }gմ{4@gA`X%MLHIT>-RxERs<텽 #+?-_(}H!OdHMtG[I1#*GSR)"}c;nt|@?D]~vDzΚ8+HIפSK{k 5Fgǥ-oYmw9F%ݽGzAkl~{Rxc @GOzߨtw7?7 OI#~K]{ ibJ[ {bڻQ\.}]Dz)oIIB@i.L+@?I5iߐ~S=rm?bK ~N:+.z"+H3,}߹6/ٯ7 (Hr_ʵ~ve@,il\J_6Bg(U@EdKs!LTTڵksv/n"223f :t@)ej8r{.,۞={pss'N윯BfvO&Mq&L TP]|y\bڵklٲ6mJݳ=oʕYz5̙3'?Pcm:`~ 11tFBHΝܹ3o>>>$$..uY=gϞ4j0ӳlgxW֭رcTXu: f͚ٓ}zΟ?ƍMm8ٳM۽{~-gܺuKӼys:uD#(dtXvr%MN0իgts>ƍӡC(I L< 2vX8@6mpvvαGG(;wfС&,,yqM}ʔ)@~X~=:ubҥ>}gϲvZʖ-  ؾ};*Uݝ +W$**7Rre5Ϟ=o޿?}io߾4l0W5hjKU\g}>I&Io(֮]^ѣhтo+V^sXˆӄ(ItJs!D?WWW|||hڴ)'N`ݺuߓĉ͎X??3f0d -2K6m4ԩcc͛7ӣGvƘ̈́ O%C4Bm 4ɓ<8;;3h ,Mǒeg1c4!9ID{2L<س`c*714>M:0'n݊^ÃxV\IVr|gjIB!4cD,jVZ刈z=7Zsp^~efΜn@vײJEgks^^^'ߥBlohh-NE˖-UJT=ԥKWUJJ:zԩi-^?Ye*///>3ײ&<<ܜ'66VլYS)ԩSRJӧO>nСV/B89s ޶ȉx饗uXgmۨUir駟,>>k׮q]6mƍzyvuueΜ99r#"BUz4}vKX~=U\K5[gr HKK3ONNҥKe)zc?>Æ CsN R*^zL2vĪU|}!Q(ڵ+s_^z̟?ʕ+Oͳm9g4կ[nR͖ \\鸸0x`V^ -wr1ti)_>ӧOӡC2ԩSvիc(zl9gĉݻ駟婅ܲeرcMSw6<<\-\д?DRA}e_x>|[n۸kg)b$iAzŋ BҥK,Y???f͚Ş={t|Af=82m[}VqeR ,DIbDӓ &B"Ν;׏m۶M6r;,;32'f9;;|[v`-f!J-.(:VZEǎMJ.m^ngv6Yvώ;F@@geZ b$wncqqq̞=_Ɏ;dMvy)+c7oL*UL4,sa-. KlÃ1 QBsڵ޽{4lؐ-ZrˈRb%$"B!Ќ<B!f$B!f${nBCC8q"w:!ELsNS-!"?LHrr!8 G]_9spuCBQDY3.]b 20_yӒE)y4 k׮EsfڵWvصkWׯO@@iii*U/77߱!(4[& 4iRJ)^ եKh"DGG[*JHHPeʔQׯ_7nX2:tRJ[kCkqY7niӦ9!B)ƍ ɉ1cСCΝ;ǧ~ 7\z5OsV(M۵kj/Ǐȑ#WWW̙Ñ#GL=">(+Vˍ7vZ}7oʕ+V۷^zj;!";ѣhтo+VnXnq6lɓ')_<͚5yyڟBgz2e ڵ#((UV\2ԩS'5*Uěoɘ1c|GL6gW3ބB(P"ry!<>)S?rѨQ#B! K7nSNۛ}aÆ|+**~#F.b`0 /u(B!D݇z/*VXsriz+VZ /uB!DJ͛7Yx1x xmav%~S!*%ޅBQ<9|eU!B_!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3!B3&Oҥ :ue˖YbpBBBԩ:uW^vTQvAjؽ{i֭[ѣ;w& ˗k;vPrevE=ҥ +VNӱb |||4TakK.%** gggt:iɓuӼys4VYYt)+WM6r5mR MQ:vHDDY5֭[CGTT! !(6HfL_ϟ?7nlԩS W^:t^! ! IZ,7n\픔"ͣC1l0ذa6oߞ.]w^vӧYBS\\III[P@Ξ=oMHJJ2k3bt@ bɒ%Uag&Mbܹ.]ZP }aԩZ{.۶kf|j(5k͛7}G[GfX$"nbߴL2$''9s&i;!!mۚ0a'O4mgook!3G/σ;t 9Yn'Od@ڵ:G/w*SHbcc8p ͍'N}6oiӦlذsY8r5rN777ݻŏc&#tOi8^LupBy<믿ի)UqqqZSjgvN-&(Q^:=zSNr]ѢE ֮]ٳgMm'MĈ#pvv&99K%!B|$&&2uTwɓ5jgΜ6<<\@fE"Rti~m0` 0m9#G+"#uĪUh"/ţH]9Y|7~-(r8pd%ь( Ga@bBQr:"]`00~xhӦ vT!MHn;w. @@@f;ӽ{,ݻ^{ 777t:ӧO'$$tƏY&=˖-c„ Ydʭ;2l0O ,+WaÆAϞ=aPDqmM‹rP\Ss>(¾իG~HHH`ܸqٳ^äS#!!AV^͛7ڵkk۶={Y-Zu֙{>vjݦWIRM#J)qFիW/Rz^TUF yfR6mRJR.]RJ)զMuaR6mJ)>3ծ];UZZj߾9rdc|޽[.]:ʏ:ud_۵kΝkTڵ͎wS֫z:N*Fbo6yB}Tnv|F_"q*WWW?*zWl{~#,LIIQwV˖-S+WT{Qw,L/^T*99Y)ǕΝ;ՓO>ij矫}*tRׯ_7;gIIǰ~Uvmce0vܩ*U?nݺ_|QݸqC u޽l?#Y?X1f?։HÆ ղeL{SIϩyB-TM \,[OUzUmP NsdN>,+ŋg fͬ]>c6mڵkywiٲ%}69ʼ#<ȑ#Mݺu+ˢwcǎnݺ3p@/^,5DC aΜ9S~}+]fRJ-[3?0իOHHȲ ܃.dLed,N MoQ ?ږ=]hD8\{m+=e҆L>S^Wov~BRJzDa]zU)e?#dSNYM~GŪGңZjW&?j:})8cK_j:{s:VŋU:u Śd zSONBµn:ϟT\`ٸq#7nݻO޽{`fmYbz46l`փPpS0"#㴢1shχ2 !’X=$$*T wرc;>!J&M?ׯݺuc͚5;Ð!CXnaaat RSS8u[חvkceԨQۗ3fƲexw̎t%ffJ~>UL|95I#3'emaV_œO>IBB;wf͚5Y䅭RL\BTT;5kҥKmrQ[KII>`zʖ-ܹs-| g˖-PL6md֮$/ 6M: aCQti>Cylr~{G?dʹnݚnݺh">szAr_ ɇ,fߟb |f7FO<+G{Dj֬ɐ!C >}׳~J*Exx8C]> IDAT%::ڬNcŊh<R^(wȑ#ff̘uXyĪU,~o .o DpXLBmd ,'Ok͜?(*Uo 4ClN !JFsN:vHhh(k׮Z% .RkS*o :t"ܥ-ut,XryGLLLaƓ/jբVZ픔ʕ+gzSoQ\9 WohQ2ԭ[۷k(dX`dO(e|uڠALB,dGO믿N׮]rQ !}<5HC f7E(Rg"orIJJܹsQ㏌gرGa}dlJ֚"ϴZkFXu`"95i;72V {I)j&rͬG$<<8lBV8p c߾}v0/=z4 .|:v\|#Bm.J|_=8 ~f!Jg}_DFFoN?wߥ]vZę#Q4iM6TZW׬gΜeUфڶmkvTW@[{Ʊ(8 Zd.sE^{2<t!6#7www<@ӦMM,*T@F]L4@BBB{.sg׮]Y"/Z .pY [Q~BCC ߟ jݜF j~T5Y]Smf="o&,Zk׮ѽ{wƍO?mh:'|ĉc /`ݤI1b$''tRN]{v]Ŀ!M_ZBZG'D9ꫯry6mJvh0EQqVs^43er2^{Np԰Ec7r ͚5Ņ_8;;kݕJ[³kaP3ÖsNb8e3*mE QrشiQjX`=#YP(.Кa/_27!2k|>>&eڵҵkW6l #((aKYϭCй>4 Np߰t_IkÙY|PC"z 8pz =?^{Ж:%MFsjc\_fȚ5k%11~ Xz-:vHѣ/Zn0?~ySO=w} Zjѷo_ӶfRRRqƦ}~~~OvUk1 #ot$DZ8 akt:\\Otqq!--Mè ^8H)sGPX=88`]t)lZ3 <<(miSq8W 8"(({j]#i8hMͩ,aEcKkͤfiFf8siߩShݺ]Eđx8sX-5'Ц+|87΢:JM3}/=IXcHQai/2o<[{9kˆמ(Nό>/wRV fGBAmbE_qgS\c#h Ce6БYb{mc.* WXkfĉ/_ *֚;v,u%88xb4%܁V=ǏgpAVBsi4‹PeOsrHP(=" 0`ӶN3-WR|<0v5*æ)BmNcOmu8C?q(|ŢGDX7@ax 7U|vt8^qƉä:1IDfx;;0 8f]!4 EwjÓa:[H%VHvAjؽ{6ᄄЩS':uD^-<  k0yK^G5km۶%$$siEI~ǗJ-dlp9Zt)QQQ8;;,Lӱb yl!)rn[P3gkH.\ѣGٿ? w8*:Lm(zyRh)%%V бcG Fݺ9=-T\3_x%KC7d-RE{ǚ5kL3g03`H Cɗhl0#JSt`VZn;uTBCCի)d–нY埥-ekj!p<׮]ٳ=z;ҥKnݪuX6uDPOjJHУM QFq->c/^L8qb,___ڷoO.]ػw/]vԬYS ,s!G*e%;1J y|y>:[KXrSDN]\\:HvELL 8q"WEa.2?, G(t&>}п&OСCp[Fa}d^{,ZkVoTzO6+T Nug¾-kSe55) mR>>l2{XXr5C럌iM,!h5)~Y3KD J*<YߨQ#z)[Yk&%%,駟 34%wΫ| Qf".s4ԥ֡EG3UPD!1KDZhq9s&}˗/7;[nv 1 xxܯB568cMaxb9vB%%L:#5'GD5j*<8DxWgOqUׁ%8E1qqqZo ~b WPx,/̦n߾VZ K"B鉓IIITf\1))Uj,{,l61 qAtmCqT_누-8ڢw{d/ ⸍BQ<ɬ7~ VʿCfH\\ƍ̙3lْ+R\9:uDddd`E͚5̙3q"N&{V MM/g+yǠo^L />!6mD`` 7o:O,m[bdӖ԰:ƏjvL٣{Çs&Oo޼ZĘg٭5s_Ƥe,rT+V!/#1zeڷ{!]i‰0;+ 23r(Ou(b|,s%`5uLg1G4Q\0*QPf=":uu{E^{.}˃ܮ53vXΝ;Gpp0zŋ yȺhpb82|gp` T]'Y S~{qlEYf >(*?BvƅZT:M a`I$Ѽ6^Ƒ2KDN:Yr9-]kF'3|XH3@b0 \o4@sdw'N_iӦljLXaQ;$'.836SOjRe1affBBBظqi;55k׮кuk\T{`B;ǟ[:L}7^Ahm/;FkytRe6gكE4ÎHCS>g/ Z# I&;pU{9J*ʼn'5kժUc„ ZYi5wX]LT3>iP|(::֮]kOlڗ1&Q!JE-\e='IJ=cxyym6q._֭;w&00adFMll,z?zlٲ L29v:Ondh?> O:*Q|YXb>>>ES\8eUzC2sBX3դKHfh&!!www֯_ϵk5jzE2e<==ٷo~) 55լNcŊܹ;w$mrku$cQNxz%I&>QݻA3fLG1DC *RZ+e'q4"%K3ԩS޽{_QR%-b̑^端bԨQRB"##-/ψ3{o7 rŧ\x1P G4ȣ۳zjz=7ndZdLУePE't&JzF(DcϞ=at4hoܾ};׋M:PzšCM\N510Ts+{A%0902qSEυ:"D25իO-ܺ~:...YFWXbvoߞ.]w^vӧY=Cη@c@Iԧ ׀a+aIxž_(L9J<0&ӡc,xHp4 0r=ԱgϞ{5$Fa}dbmXW c%39^njzgṵ0zL~b M3G^k(:S ܸsҙ)l21$ sysч#&--{zEn߾M&MrB_^ڗʌ'Y`MK7LfԩÏ?ٳy79~8zq)T'''0`[nC1cG]v(Xv-J֭[Y4i#Fٙd.]JF %&[-lv2DWsn[=;~ ԁ M-#ZJII>`zʖ-ܹs[ױG5PRĬSÔ'Te;'Of(y&J)<==qr2PXtd0rH^A=eJisuk W?S+|i4FU<"wN>ٿ?J"<\8MbJ*曔*e4h3f 992esaa " 1޼GnpC|q$fܮ5c00a`00}tzA9S ^ .m_89A:gD_/ Ь*tcL<GVZԪug!%%re;(2felWtBPCvp>׳cǎ,QфhCq3ԣ"nL!PN4 cI!FZ4تDo߾:tLƏHvkd7%%\Is7ag*짔qH x8%? )!/h u*BmQIv2.\kUQ\9ҡ5M%p_^/ YLD޽;v,AAA|8;;bnךILL`0d~'kNz3A7°E?p+e,!S<ރSl" [A\\ @Jv IDAT޸PE7Pژ2>qu6YgLX2_ :'cɘ<8A)r:^,:oqL)rq~']ўmd05j&MiӦ\|Uf0Z,H%8֣$s1 59F D jx)Ȏ;QÆ cȐ!qQ~W\0ymU5 xlLhW2G $]MtHNTePgFtdHN2g8IM4qKRy&%~4M?2k?)OgcRt{$G1i$ ݻ̝;__ǚB/:D'_pl UC3ikIyx,&"ǏˋW_}#GOll˝ܮ5Q%))jՌIIITZizŐ~8Ng|Sq:x5vqeݏzk^Q3x3E) Jg{(S/h4J˟| 'NF^x/ = ziJ }hF7sb^iMm)Bnm}& 0#99ӧO3qD,YBtt4o˕ܮ5FfͲ;u5iAϪ7^ܘF5ԬkV6to8v8`t)jxōO'7Zj1 y뭷pss3{YUV[r)mjƒH[ƢgNQ^nTF*nr1Cd\vEl21 Y^zZ' ) =ilӃl Vpkm^ ) eO>iiz饗,w&MR/RJ*vܙU߾}RJ]tIyyyׯt6ֿg0(T&*Ѳ= >*lFuT]:cڬW slwE%zATUFnSjqwT,>o-&-K&E۵fƎ˹sF׳xbj-c5}J,dZPSPjSf!hbyBYZSVԢqq=S|fv %+y6HXy> hx¦u_xЗfP c Ky:0pDr\Ӕj4MJM<p:G{DMIIIlڴG(D!Ś>Bh:w8J:$b'$NND %)L -]zB榸ڻsMPQv-Ӌϟ?իrJѣGSNt1^pZjexϧuҥK{"n:yfk0[,K]ex|Rka6 e-@+ЕyXxzU؞[oښPV7PX|3rxoѢ:ttҌ7QF97ɪB!p??>oݻ3sL.]JժU% B! bܘ1cK,A4zE~ڵ+~;?waX7oQa !~gf"11>5k`)TSNbŊF&p1iBd,[H6nHÆ ӧa !@ڻ="Nb׮]t{RF bcc 18:!+I{|bH^-[e: {B iB_hժU 8Pj a.~#֩SXl֭3:!I{|:g̜9… ލĻ93s.kdU]۷/O<mڴ᯿[n!((:uw^Fv+l@b+H;@ :fy*JYƭ igx|1k-_LJ_?]\f32*#igxؘ5 UL3C3۷ogĉ J44McРAG&_T[XW[3"]KgHD,篚e;)s_rcNH|D\IؽEڿdߣ 5mƛox@J"Dv\3$"͚5#***'h%773}gȽ#j%g0M'jߦ 5;LAޅVj3)SFI-|qΜ9Á\Q+9O<|J͓+f)e8IED$1/vr[P믓L2ex9w1cw ;!(H$Yy ǜI@$FdHDvJ͚5}ұcvf̘ڵkϻロ{Ȍɓ^(p H吳 /- w;z(/^jժsժUcΝteyBSyp'spTΝ;jf9+Vsι>8C'k<¨ IHO沭#[oez~̙-y?,?Ώ$O2D?$F91µMDfΜ?cw\BB.*UDxx8tN^;p@d;t6yl8Mk'[4IINMJ{3DHcm{QFS][ iXr.K^'ϻ&%%Qn]~{Ç'))I&}vzÇ t^c8^ތr C3w]MyM%Jw%C3mH˖-'&&ra{ І*tH~Q="?<#Gƍ7ѣĠ">>,*EV44NMkpsJBr{Y,\˭ @ZXl !\M{޴ Oӆ*F#HK{kRlYӑ ̙3[fVj(8#ۑw!<-C3u(QA^tt4͚538I1w0[<`$.O?SO aN~㏄ͤ a^>54ۚfĉNZSJ9{0R6nޅ0ڻ_MVW^|7)SÇSr թS{:o0+ƷMf.;U"@Nx7UfΜ9G&p5iBxɓY~=ׯwPq !FڻzDBa.~jF!ƓDD!DD!DD!DD!DD!DD!DD!DD!DD!Ddݺu*U 6d^JJ UT᭷2 2!;dwE6mh֬s7(B!Df|rӻsM@@@yϜ9gVB|LL =?e˖_~aF*O&"-Z_~TX17o]tA7dK:vHٲehܸ176"D!D|rh&M'39X> ᗲjѽ{w4iѣNLDrU/_N>}E'N૯bɬ__~c" JD&Mij>C9"B$:t@r dРA̙3谄iĥKX~= JlDGG;n֬[+V%K:K*ũS2\gj7~رZl llذyq-^`޽c#{OR&-0_Lύ}ӦMYf[5jp9K2e2\'=kfHM6޽y~TP7x#۷DĘԬ,/&fy~x|7/sglbx}rȶmի111&=CDEEpB{9BJVmiӦhтMRL^}UB=" 4`ѢEY~Fnٵ!C0dG$-MIULl6C-[ێ|rhF!A!דM.^>dzj:t@V[);w.ϗM.R>9YލsԨQL6:uPvm^Uf`BM.4RU2?ӧһwo|A ԩS9s1K-6mAAAGDDPNNU:H/Em!&$$'OQlrAflԬYKbX۩R ~z1rHRRR9Wzsq_bb"%JDH>!Ykt"ϧTR~~f͘=7~&.RG_ c{GXXǏ'000ӋSRR={6F>XrI.]DΝԬZSqt"DhKeKTQT\5LD|tCezJDDDٰaØ:u*F"Dv;ԭ[ &uVy .L=gӦM9͆i4j(իWK.DGGE&ByFY&>}3gp]S9IHRR[nXVJ.M:u p0a{/:u I?ӧٸq#Ӹqcٳ'3f̠}ZӧO;/gQn]~G"##iԨQWQFDEEa1Y[uKMӤVdt>ϝ<|&M2e v 0t|6aҥKyw)W &%%˗/saƏOn\rc#}wݻӧٳg)WaU{;@ވΞ={X~=-Z`l޼x';9s̡B tԉmf{wK!|ʔ)?͖De &O|)Rv]~*T[nUZR.\PW\QSof{2LV"?#-_\5kLUXQM2E͛7O~鮩P:qRJ /RJݻGf{^)4MsYhG[nU> S͛7>ջwoUBUZ55tPjT*eR[RoJ]ltd>hѢ.;(C"2cƌ,/u#4vXRժUSEi|[/oL/!O)7߬z-"E8ߠoRjСyٞWW8=oJ}R)VhJJ TJ=^SO;vnUF\޹vFQvm)j%66ݻw{հLVq۷πh|ϡ M GvXi2";._СCV7t 6$44+VЩS'VXAllsW^YFݺuY IDATW2Ss&=vAps6Kkj6~^{ A<'MD||}hΜ9z뭴n'&y]#QŮ66M@6'XJV}ΡC6lqqqlْm۶3PP!4i¼y(]4}saXVOU~a~;]taƍjՊvn: hp|$%%qe ?lh&< m׿j6t|DP. &SR$KnUVDu]TPrJʗ/_ێ2MD1(DC 0V'^u#Բ H__qE",TfſS(uK+V0o!(O>狼@06mZw4-@fM 'a=j='I+m*pfgM_s"[ND>L׮]e5̙3WcܸqEg^vz Ѿ!֔/_gEӬY"FӂIT/F2r|![:IaOJ /}/u{]'z'ʕ+{DϬvy7طosO5رњ]%N2@{^K Ѧ'NQJvC/Eaf(n)rg!2cȥK_z'qjON jү_?MfPbWfT$$@,; j JXAuwʙ*戄mcx՝EJ*Mn+ԇ`MݣPj.ۋi0,zyXbm۶mcʔ),^XN<ɢE29wmm,!MV (]snM^{5n6f͚?C2d>(.J,ɥKǗ.]ʴPwwT Yiu5-Tq Īؽ5m6>Cbbbx7h߾=F"""Rxqo!1 `xg1^ph#ɉ{>siuԌh۶-w橧xVn]>$~*J%G`>¢v=R4Gر5!E LêwK ɖ9H,w7Z1= ]w( ҥKׯ.\ӭ[7vEɒ%+eYڮڈt;#foeر]HV#GY14րPwݱY PX{Z9;*Y-^Q3fpp{> /~ɒ%?~~ƏΝ;]6 j۶mUl{シ[jՊƍCe $K|nOj슡h |O4)j[/l5ªFpZ XO {̚5;u]gرt֍-[ryi_{= m6;FdW?HeVM>Y7G%O?={2rHC4e0]nQWd@M>'E|K &&ѣ O8k#%!ybW"seYVR^~=[laZH b×S!0^QP-eHDO…ݻw}hp= M 3: ZcB,Ӊ{aW2PTR^dt${4F0P=" 6GPou J]!E-F%eqzWtupJHIo“^/,f&ARػHAr}z!){Yf4k֌ڵk3sLCD4^u%X{z't/%u L%ёd.()7eUR ?wy'brЫW/FÇ^:wwqѡ],+tV +m¦}JqՍM%e-`0=Z"~k#WΤդ __g7:K*`=q]4jʕ+eIjA3h>"$A(L嶒ǡrR `<{6Wc/С˗/YflݺN ˭tu0Fk="0+WϽxۍ)Cn+)bC掃̦Q`W018ȁڌ_|Wlݺ*UУG-Z7ltXn>%V/iG~ac+J'Q's]0+)\rd[ت"WnKU)ޢA{hٱ3~уnݺq!֬Y#<‘#Gm#YusˬܺiZ~&Iyeڊ˴RI9&B*b0xDx ߿(V\ @jը_>sI+/M^KV )As|HBӊjzK,6$τ}7MQ)mhIصޱ jۍHiXs0JrI R$A))6B- ן®x}j#_3B$*U'ކ7$na}pYU… l'R&)dّa7l/Bd} B-hų|U{;DS$-ў={2dJ)tB,Ӎ%Zni ,><%端sTRX߮W 0:Kw3<", ۃ)9<||ٓo=2WaNv~H[ $$,kXJX?]W3E JJoe|ѓc S^4:b9J)V+={VZԪUw&ud5A^)V ڻlHxSZ~U7hP.,kt$\+HNrf{}. n 'A獎﹪nPȤIXj\z:u⒛i„ ,Z,^RJK(u+ A(8ZViT_ q:tzAY - g:h/HjJ9&"ͣ|9s_~wy͛7ӳgOO3gd׮]/$hдpC tN#IJ eUZ[n :EEE3Ϥ7;s:M^l׏o. C[50efCG8\fUGd˖-pިjժ,Yիgy77fyP T ChZ lT2#3#4MH C| sx x:G-ێ\=!!cǎFf?p!vE-hݺ5W6:,Q@/Qf%2]&Qk4REGGk׮ IuX5:e>o8~+osq, /^t_ɓ'4UVž}hذ!;+V4:KR Ҟ@nt4-PR1ش׌Iع1\Q.#qGйdg8qB[ 0@=zo?C=c{x֭[<ץK5f̘t{:MiFaJv@_Q0C;U~ÇWgkgJ06uG*JuI&~J3'Z-ێ2N:8q("##)ZheմkΣ + 8]י1c7s۶mrkm]Nޛ0~#NE6I~N`v䓫Z`m<O?~ yoNB>c"""WAAA|oI9дZV&l|p B@@#F0~ ёWhacxE)(^vG(P:Towօu:Bi£2$"61eʔ*7*R/6: Q*BFbzVPRīބX >'WɥY> > 2)旔]r|}w;!#99c]wn!Yf jբvXVKAZ@R&撦bKBhZn೫䔂+OĻ,xV)\)8}đrR1SQB֯g+n\J.CwI Bxx8M6eܹX8,Y⒛ JR_ 4: iVlhuCWIOα4yO+"4 Vv,4Üм CB0Gl,Z5sq{ Uׯ_O˖-|26mbÆ DGGg-ӧգe]$^O!YJ)xշZV`Ѽwiݎ(^8?#-[:<ڵ+W_u^gty, w4:vdU>?p_hJ?]ێrLll,#Gdʔ)yYEGGӢE ?N}[ޘAX`l]I$@v 3#Y%;|2Lrha_uf? g!j 4.sc«$"ahf^ƀ7Ϋ{DZNV"ׂ,Ocwvp?˗/S^=z䢿M?dW[30m+[G@"W2-ZoN:ą  a&V\yG$I ;; L7:V?F6NY5y+ohG=qr8 j;/?'h(U>ǧ 6c_Έ#0aB#5'N_4:|smFѡ֎`m$sI뾛 $̊ A`cEΓ[/dߢ3 H%dٲehFvxw3g1ܒ%Kh۶-ExM;@lyPKj>!&X,޻igb<阇 O`0w?XUp0wC6~&CgʲeAr6fz_3sˆr2=~30u<ȭ'Z ,_aDG4y۷Ӹqc6u|1c_|Z3$PW4hjutmذ ШQ#>cuf64n&Oܞt;ē&&bUk_He7GinfhGJ)ڷoŋ)VwNNFYt)ɰStt`nh:"V⡇ezcX,^ƤUnB,rUo)5)6 PIf-^cǎ1|pkNFG"\Yuز#~z';}<6mă>i FG/#KN:Ů]%K5%$13LtU^,?d}43Ia꿏R(N>jO=E+n\bvy< Dtl `(Y&]]Md^̗ܹVZq%^fzcJ?D!OER ;IVIQ?"P뇕hyڑ72(TxH7a6Q~{zV7q_˖- U8p)7RNF6PDiUk@eaCXiKze_e n(6?X=Iy5Џ`!p% =&0m4 qW)Zj}vWw;p;v㞌N:EYn _| IDATtU(Y IBk p?]'Y-$Y-Dq@;V+G/On,pԮ];{m(o?at#$9 os- +BFP>TLccc߿?< 4we @3g̜93]b&ITCE@2]$E-#A _XvXiG VPRٲe)[811tVM#9ɱdwBd Ai5'LI{`VmΞաr tաnki_E2eׯ6mZj|omxۗ'x6m_q-aOHv8ah= @WgHQ?B$vFZsv! Fcƌŋ|GssLذ/7:8ql8(9_cle(s;*8˹֥="?<%J`ڵ1;vp)6lקQF2uT~in6CsJR='Ih C"Y@ZPM+jtئv)-[ƺuҝ7^3v;|>  8i]9Vtm 9N9MEnm="~1a/NJJ &L`ff}v6li;vYOH:UahZ!U=̖f+Pîv`gv?"p7.,܅EBwi?SF޽;}QЦ[X1&|0 ¥>#̛7//&L_^7x94nfyݰgeVr_#Ƒtu:X VX[([иM uvC))hu9ݱA/ҡ̒={"ORY~7:Ai8:孋^44ahR9qtu)vYtv4nF8дp4Q@Bג`4B 4q}s0Pl7P$!/e94]Y )0Zc-ra<#R*ſu@,+(q(@%DԵ?!HAaD~(6.UKj]cb\#+^v%V\IRRR#`7BYF"Q(dA_%79cA{IBS֭[vOJrqv;SLnoդrUo@e Z[U{;DS$e܋\X/ƹS0a(|{¥Nn.:bBB{3:ŋoaуÇ;'yRIj4ZIB+fp㒄iHxx85ktQBˣndɒ]wywڵ:O}BJ /&ABeVfw0C=YjfƝ=C!C0f̘eFGŋTZyZjܹ3]"II$w I.fP;!I{.* !ΝjgXb;wΣq(@HH!K$w6 a6v3f8&)RUVo_-n\BGd֬44MKyٿg#t-@t]gÆ͚u}tIזBEWُ].R$]V륟Ĭ;s[ n_f4S>_d쎚p`g/I9{DFITTQQQ_ux񢑱Dɒ%INNN7uEJL_'߮'Ӷm#$$0.+5js^x&$(WH&4Jr`oJLBQP!!l'ӘnjfXdIתU+3LX<KXH?#X6 o`^ձdUʔ)S4h/_HsƏψ#<+uɪ7oUV$&&:=SX,>s`fU+FJ9|$&s1Puw I|:/m"+7k._#R~} @n2y_ 8.^r?g !M{@uڌ|"^K솤 ZNv^cx c{m(\ m¢c6 [^ |ٗ+rCm3hI,'z@="-ZC.]q1j(<_#9vJ)>ԩw~K; NH1qΛF4]ұj6t|uMvێӧ:u*O槟~C.TJ?sDlrJD˪D`$%%1i$oN^8|0kޮHZƍcȑ./7&! P!K!oLB#oS3o;ʴB!'U"OҺukׯO?M||a !@ڻ ڵkٲe G6:,!H{;d靯{ر#'ҽ{w̙cpTBw.wDu(QA^jfQklbx'5/&`jhF?O=a!<@ڻ䷉ȏ?Hxx8<ѡ!LڻSC3ڵc˖-~oȑ>|'lٲ,_K)nCoH7nNޅ0ڻ4W^|7)SÇSr թS{:5MdBxN6mʚ5k FڻWHBB:u7ߤVZ }`7o5o&?36 V\6mڰi&ƛ[+Rl@V1فk_[1Q4iʪ%=kf3ܹs& i,Xgz6m"ۻDQAEJ- ;TK**"WT*#Rg\qcK]FQłuA4T>> D"tL ˔lݺqơw8vZ|,,,+W4^X:ؾ}sRRR`kk ???>}r Toj3TȣׯcĈg͚#GO?UitRc(,,ĸqpI5Jq2մ7:MHkEDD@WWW^1RZZ ###(--UkfbذapssƍՊBs}d]X#^(..VOD€@!1Ϛ6R[tt4lllpܹsZۊV55{]#buB$11˖-߿#bPSS#r8::Ď;4hMZQ*8s挰PaG#'̋hW!2i$|64 ~-`K/k׮ԩSHJJR{D½ .rfذa@~~>W\AXXJ:N1jK]`ZgG GH"RiFt|(++CBB~cŊx!]&[t)"""Э[7TWWcΝxE1ݺuCdd$mۆ/ϟGEEUx?G~Ñ#GTzDJ "Lf|}}/355Ń F)@ w](555Baa!ׯرc~^WbѢEAUUj*/SSgQ`/AD{]G5Ɔ |"mkLJqqq8wJ%>Sa60JYp VT g̘3f0j(;wR^^^z٣D$"D=z]Iii;!..YYYZ =Z;Mp-2 )rLp~3áVL>]]͛?`ggq!55:uRδNK$ y!99_Z@QPP\#G FL7&B\X õ$pq3m{K$/Rtў'B"Ǝ`ګ3-ATO>¾>} ''Gg>ahc.Õ C.`UW"GIRRR[[=Vr;ZH1V1Q 3zt2YJJ """m\4L2CNQZ;b~ JQ^^1` Z2y:zt$ǃpiIϡ|CӄM3. 47R|/r@mx_.7-z+ӾiShݺu RWSS#,/Z;Ӣ)F|q^^&OR dؘ mO{bVB1v|ߝ|nO>G bfןλњ-_-BvL[δ@m9smvEEEpΝ%@R?G=@t ! "#[ 7wa5#2amB7n <<\Aʕ+OOOccc .-֬Ydeei W^"D2lobb gY,vLhUM) `ʕx?" /_Jۮ0eM'9&L@pp0#:tرck׮E^^^ _|VZt#"}||tcKK5>fLl-ݒɓ'ٳ-_DyyrXYY?qr9~mwT1&f Tŋ¾!CΝ;sr\6?mll +++9 H`"Y vuǏ7{<;;˖-|88 88Xc?D"|WWW/<<<ӛo'&&&8tZyr]_[>^8a6mݾ},--T1-]T)==lmmIPhMeggL&Z""JHH 777""tQEEkFGYl$Jۢdzm6 ?Cq{>wuc΅C U~WoIZK>''':xb˗/T*JZx1 ׿[o9B $H7&11bjŌ5]{z=秲.FMM ,--4vYbȑQF!33Sk@.ϲiӄmB^z*9::$6"Brr2,Y?33Sxݻk׮!++ +&2wu- _Xx1 J}999Z;ii)T[077Giiْcǎw߅T*ŃT^]Ŷo>L4 {V_ZZ*J L:UeēѧeKJJw~hhh7=zTq]4~k$"DG7Uooo|چ]f!Ҍ&O^zii8ߍ7m۶ Tra_yy9 xPWWa\.<ՈBJJ|v܉ oO]xo=O5JJJ`ee\|ΐ#"'SVVhڵ ={č70|pbaZwذa@~~>W\AXXΩRDdd$.] '''cdA&i5g"77͞=nnnѣJKK wW<&^.Y:4qD:y$UTTPEE-Zbηxb"jNNWhݺuoQEEXnݺE.]!CǏu~ZQb_~}5bqk2Cw.DڵkI" ǕJ%ƒ+d2:vcP www9JOOW{M˟B2d2}g:ԩSLRi߾}믿Rpp0d2PRX8q;/zc1`Uc c c c c c cxg`8q 8… 1k,@DDn߾-֭[픔lܸxةSGfffؾ}; q=%%%_' 88Xc|P(ǏѳgO&f?$"1666'''^yohh@jj*ϟۮ6oތ=z˗/wزe+̙333ocD7eܽ{'Njddt gegg… 011i1`̙*6q0&&ww}hJ^C622IjYYjkk &`„ (((@UUm߾]hlllPSS/..X^^.+//ǏGbb"|||0l0vvv7w<==p\r*++Q__/89߾ZEӛ}Gݱm6Büy{{Xޛe˖ ߿kkkL&C~~p,//2Lvqq r90f@Ϟ=i&30gΜ wܹsUd2>|X% 7\]GhS]]tO^$"]ݺuܹssNa߂ p̙6TTT$|M6!$$DxCLNNƣG?ݻ@#< !55 ,TUU!44TxDȨ.+t oRÇ[o\.GVVyZ&ʘqsKah޽4~x4i]xԔ… L@7o#={H*ԩSq>p&oooJLLlWLEEE;7Qxx8 ׮]KCtOII!LF2>3amm-믓;XJp|ݺu4j(SRaap,==lmmIPiΜ9 rss# vu$ww}"!41_a1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙha1Ƙh?O(fBIENDB`sncosmo-2.12.1/docs/_static/example_spectrum.png000066400000000000000000000260161476435666400217510ustar00rootroot00000000000000PNG  IHDR^;sBIT|d pHYs= IDATx{\e?πG#8b* \4ef}j+Vq3{˶ú[l\ZY( h @a89ܿ?n|y+xg. ..cB`\B:J"0J"0J"0J"0J"0J"0J"0J"0K ,@~p6_W^Ǝs~BXX x6!hsě۷Z-Bjjα&C4442RB1$TJ!CdXFF aОÍQ\\ (//GII.++ܹs_|,Yb!g?H311)))Ǿ}pYsbccO 55R'|s"44(--*g}<Μ9___ݻqF!** o& Զ4!h فk׮ٳq9/i~LT"..Ol޼* k׮*off&v؁ohll^{5\.׹)VRR3A~~Pa+*_P)!(Ú8/^,j qF\~شi/_ޜtx]]]Z̞=[FU$vƿ/ǬYuVxyy!33˖-C}}=,Y<7,, 999B,2 ؑ\;D@t###QiDfg!mVCGRZ EEbGBi JN5`Xm#!%^'R^ 2@qNxݻ@@NbGCi-JNsGh!E׉hF/%yPu"w8spuŋb=JNx?!6xfī?PX<B15-*yxЄ"qxoI XaC#B(:p<%^BErjjj0e444/޽{}'&޺:kW` @76BuMnnn8|0wJBxxzz6#ɐ777!Cs*S J% tPzBS 2Hmm-vnݺ'B`iܧPQ?/*|}]Z^Bs /gϞ:d2DGG#"";w4x~mm-tu\][vZA.n>6BJ+//\ܾ}'Oƣ> O8///aʔ) ðaÄSrVC3-*l~~G((`4irrrtӧOGNNN޽Ġѵ+?VPLy{StBI5W:PZZJ{p14?RP]] DZZ Qp |`p~LFt&H#^RgyM&(W\Ç77żyMMMXbF)dx5Kʪn6?xCV-|M8}q톗R*5#ނ_(8'/!RE;לw`@@s@l$XFI'P/ >~/T`!Qu3ٳ nÙ35Cb%^'?HM=ѫpu׭|)"JNB?#Yo38y"JNBU=ܾ GGIIeԄ$l̹u xQ6mZ"!Jx_ fܼ@@b"pxc$X_`I߾[-/_n]|QuxM_j<xxDkvCLc4zK xkjjQFAP 99 ( `Æ B'ijh͈lb=AZvv6N:(--9'11)))Ǿ}pY!C,GM5x)x깦T*VP(₄2OAc**y(xwƄ p%{:=הJ%Ƒ#GtH#غO]iZixzzoBqZR뵦!5MϵB|Ǹt!8%[G{pӠ/!'kr%ZeJJJtF@f5K#۷ w>v" I\puuE^^j5RRR0Xv&- 8z95xXhK$s ɈG}}=,YB!dx)1{O5[ Ks8zEFFܹsB%*oj2\mM;y4x߾ݔ#\ٕ+@` pl]`ɍ'>RKcQի0tG2UԈK#eBHۉpd~5Lf77^w7x !G#^en5 kZBZ4#^G,2mpw7X!1hAd%%pcѬy=z7BG#^ivY歿{x xS@iJapyS7S 7n_Ѡv±T!G4zyy{܁]&E皵#^[OhJwi sϳԡƀ M&\sij+ܹS詆 Հum-~p &Ǐ#%%~-Fp\pf͛7q-DEE!,, XbF)dr/`Ҋx x[UV1Ϸ:gFEEAVnvٞݻ[ޫM]&Ly x^O*LJ$k"ӞOuĈw i!%^0fxQW{RA7[y'Vb#+ZV#V5h'ޚUšU =d)k(DgKx yÆ/4K6J"1xw7>ږksyyDxd[]nݴ!%^K2} S mLX?7//>L(XϿ x-sF˭!%^JFbѧOzJe?7$8wMՁAY[u5lf'!%^JaUzpqǎcܐY7U–{K+"]V6xQ۞3֟o.Z[f25u q6xEby /2S]3l{BaTa݈w^:[\ϵ ( `Æ B'(SS o}}B.[\`Jޒ=yҷvٵkۆpJ34jzaXbtILLDJJ o>eщJnnb[t̫F` o֝{xZ.Q6L:k"e&^///<t{i(Jj( !!{2DTTz0l#(qνutN ]7nzFaT$sMT| ޖ'MK6VS5Ʀ4apyr552DՖZݺjXNhr(\ϵN?i,0i>lh%e|mk<(_ZOk?=!$kr%ZC.ĚfRmbgF>jo[4CN<o]`v=|x@{/\G_/ 7!4BjBR=r9\]]Z̞=[QQa|)`85l_s,mGNo~mܸr |ħN q6ɈLj# 6w;bnךTC[|Aj*j8ҜR׮Ms4S)JƋWyyL!B\ϵHeվxB05 {K'o%$ΝOc@q19%Kիѣ@t4ߟ#`B\ī?+gۏW7/ۋͭ5t 6իs ݁Ӂ_k'^Xؼ2BCWFR6b0oOAA|uʕW\GOknUfJA#[:wָx0B$Tl㭩ֈ'O^K\\!- U>k̰a|y*B{LYTь8J;ܜ|=dH6K{fX3k %^2`3Nnn{OݻeЖB@G+sXm/#^}X+2/qxE`nī?۞ۗ57kcA!$J"0W[aW]+zmՋlPN{ Plk;o2H %^Yn1R<;?98!^`뇅 }5j M0Miyi|}#{ :Aobb"onqLtdggۖ?oc2W6!DM111pww7{kwGJK-'n |`paj( CLhDDD`ΝFqkL5hQⵍ/cNF[zYĉ8}4v܉^{ PkFMF::djMc/Nx8/d :̾Pw|}}1}t`ذa:hz9 $9}|G׷/pM'KƀL%NNB9\J}d*++ !CDaaK=ZS]/Zj;yאɀw6mQ/766O<RSS1x`4[uhX#y%-s CC=1xQt*ii!TO?dpLZNNሢ.3ˋ󟅉=1xsr6*3yv#}X qJEa,Q(x_p ܸaxsts;44BQPf&aN]wG1x]3Xcb`VE6Pj*oUN_?O 44QQ@Qb$ mkYbwLc\ :  ;[7V ) ~!@W۶O?-v$aҴ&jdZ+ +J8p/oϽӤ 6k6hieR"x`zmtdJūYke~Í{k'[fǫf0qIhiGa(ͩSo:L"JH\(-_n5Il,FQⵃ~m8kCp _Һ;u*%^jvBlذA^ߟ j;}^O>z͵_lkvc߾}8{ѵ^N˖4j= (+ko\yyoO\hH٥RZB pN۩?~mqcd2>)n7Fƣ={!2T*79bp% ڵsQ˜Xz+Jzn߶`VYxXIKjM.5Z19t+^l/tsV듮:p}#d]rh)))kHeQ6'ޘN歷ċ# S=J} {z}GԄYH)WhLK\WWWAV#%%g8:F tcM,AAaĉ/ $$uu&$#F 66 BZEbD/xʫ\qDF<'VjkGL A%*,,LRM1}Ո/18zNHt,﵉H)wHfU3o7l9va͛ Jɀ:wv̵IihUT*^sᥗZݽ em8gygO`v`Rl{QRVYc#?KU:2HJ,mu6l&OnYzP|3Dp0F'ѣōk"`r~s c#ѧNg39^+eg+VG99atl BH˖?7]nŝ;ӻ7pݑшJ񷖣F$|>o''vdD ~۔ <=ѓ.@*Z׮|Ds/pe8x\\?L@wt_}ztT~o#ZHD}/f^~ObSej!.^ow&(b"6|897ldƧM-q'Ǐߍ %14ŋ|Ft4 ĜΝ et8< !Nlo,VV_(Z!?bS> Z|XWۻ|`r~ei5kW=<ٳWc IIIX|~~~ݻ7\\\-.ۂ iͦoM|ȑ\ΗV[.x߿UV!-- ={ѣ1|xhXMOOQ pH;0s&;"vl_Z:iO\;y$=z`̙o iw Ğ~O7<pDK8)8x߁~ko&^c}Ք,d2DGG#"";w4PuK>bO鯼G_|׎XJpo\!kN5jĉBQQL0 6L茻x/q,/s'/&=2) IDATJM[ :ⵦ׃:~>}AbM5z)шI.畻N,jjo4Ȝ9¾#hrz xǎ<ܼyطobeT*TWW*++ ͖1\!t|O~Sźkwm|) Cu5}GtSNشibbbÚ5kзo_̚5 [nEmm-=X[Ԅ+V`Yu``QC ҥ|~[G0kW^xظxy^8\3P;uJ#ĤBy [x&}nu&N:íUsI.C?;'&=dk>1Ǥ+5T$nj,^gЫ_⋶y׎X1]x8y; B7q"o׶=wy8WWEtQ5_y)?BDzuǎ9b" 1pw ߼{d$P_65 b=I| Cׄ={r1[7 44'ޜg?/5K;gc1"άKrX _v˝~ؑv<45bn޺͍ݻxnBqJZ;x0!%Ξ7a?J>^vy'JG<ٳÇG`` >3322P( 6yg/{U@B5LLLDJJ F &` xZRUb!99|$o͋= pv-_h-M+K!)@qPa)wIī@sE*s(Jj( =w 57Ry..@ cZFZB x-54#Gtqj6oB|>%%%1=z4;wSlܸq̙31͛RSSc-X}1^}UG1[z5۲e1L0;v1XEEklldcƌ4RɆZf&L`qag`cz͛75k0KNNfCR4clĈʕ+ǡ駟xpcBDZh"_7jjjCV c 19*7n\c7od>>>LR-_h I$ދ/m۶^)J6jԨ?CqFc j>w߱{1X`` b1f̘aS gϞeSN9VRR"xlĈݻLR&XjŲƌͿ weVǡcǎcMMMQ^^ΦN8|\8*** 1|pVTT߿fϞ>,Hڵk$ '%%]l*++~_*((@Ϟ=1g%^zaʕJKK%sjw ?QHV#-- ?_~<+WO>AQQ_TIS]]{wå L8QL^YYY(--嚶իoeeeHMM$r,]_DOرc$|gؿeesNqNfJrܦ8|||0vXk׮Cmmq:u &L@^)S --M8iL-kr࣍>vcXt)̙sqdffѣ:t(-Z/2/hx衇̛7?.\ 4;wF\\222G}}=ϟu!"" 紵DO7nQXXM6ڵkM.+7n~GW_}|<.._}U966L3f n߾w iii=zq"33hhhѣG#X#9sl}]㖖 (׮] wwwl>&^@qq1 c̜9ɂ? իC!((H8pyܻw1|XgUߏYHH{ꩧXCCqdggO?) ̖-[jC8z(sqqaFjY8(?m!cL*BDj /!/!/!/!/!/!/!c^IENDB`sncosmo-2.12.1/docs/_static/linkout_16.png000066400000000000000000000006121476435666400203610ustar00rootroot00000000000000PNG  IHDR1_sBIT|d pHYsHtEXtSoftwarewww.inkscape.org<IDAT81+qJDĂ"eS"%ވΠYM2ba1(ހXekoWk4:1'U `mHDq43kfskgSp›̋:FЏo  /ye<ҍ}u0[%VW$"zq),fW{A"GB ϐ|vmv1ں[O2s om%N4 CMf"bM$u\bI*LvꔚIENDB`sncosmo-2.12.1/docs/_static/sncosmo.css000066400000000000000000000005351476435666400200570ustar00rootroot00000000000000 @import url("bootstrap-astropy.css"); div.topbar a.brand { background: transparent url("sncosmo_logo_32.png") no-repeat 10px 4px; } div.topbar ul li a.homelink { width: auto; display: block; height: 20px; padding: 5px 25px 5px 10px; background: transparent url("linkout_16.png") no-repeat 10px 10px; background-position: 85px 5px; }sncosmo-2.12.1/docs/_static/sncosmo_banner_96.png000066400000000000000000000301571476435666400217210ustar00rootroot00000000000000PNG  IHDR`OsBIT|d pHYs6mtEXtSoftwarewww.inkscape.org< IDATxwU@JB *&.EQAD)ґJoz ` ޓs7ov}[^l9;;3s=222222:;P7lݝ##### 2h?7]knnaܯ9ѹF˔Q[u_M{fͧ'繽uS7%w7WS@$32222GݕW3BOнgȨ'U2}f]%znu.#2mVп}qP%sm@Wn ˲K=Οd^{U`~[T32222C]ط{ X!sgdddtid>Lz'\zȐQ>O&ɵd&JÕ'أ2dddddԆΓ Wk~rfFFFFFפJ&T2]˲KFFFRC}2gFːQd`i 5Jf#ܰuq#ǎv$ `BV KUus0mgddddt>:YepMvFFFFFjJ&[_X_ 222224?~\l[~.z`7|ۦZrddddd,9e [azȑQ_j2r̚?z"kBRL,{"322224US2f =s=H`{|)U/####TM Zs;`V%y`1jv]>%_FFFFFOw =ve6'=ۜcՒ3#"%=&/UuAC!"+!#0X; VՖ YDFBzs-G!\H7g1Ѳ (m\s,Q Wj9ǹi Y%}@_Y݀|Fқ 6qg݃zD#"+?bJfWrsUsc7F`t>NTը,A; 1%*0%90/S :'033Ѕ |n'/0>@yv;P.d+h`#S7x~ "P&oa{*9GGq!{wou  "WUQ2--砻R[ @DW+U'T2e(d.RS~EfٻCk9s!}Ӂ8t&O Ug!π?3%OuL໔N."fuOIAeE˹gʦ_~DR8 `4W "b\HOL3Nj)Ūd-XC $T*M񇫙}݂n45؋ |FDU&."/ұ{lpT= ҅4cP*xۅŽJBN~Jy }o.$p!^u5` 9JZu!˻ϐ^).$, Yt +su^r 9A }}$ Ѕm*>.Vߺ}kzstNQ#"rV!"A~a8-]p!+`ʻ};SO/` k^ c.FA Y3za}O0sǚ~0@+#]_+O6Nkhq+/_?{. t!kVyV6]stnq! Ѕ|OU= /.d 2kI&'/cǬۿ'p = J媊߻UaJ=9SDNv &@̆\ Ӏ ~SEIGr!ka>w=pW1&r!b&x[ .d b\lWMlzS^v!c~_6m<V" ~ w7p  -|aZLp#La^_7t =?u+'p!+bN9Tgs; _d,jqc}^}ɖXix{JO`B1&U1L 8JmYn;0SNAo+ "LB$Rہw2YKB\A.|y {*i;տYh彂{/\s|߅u!MG~AĹAs\㜞\y~yA镄R | n򖀲O?z.rK;`<ۥ\d9"2 < 3hLwxlLz؛bI3Q1za6[+,8t`  B6.L\y.A׃Dq.dYl9O(-+e:X0X\ܑ6b</(+r2&.,~4^(9 ə6q!"^=\ U棕 DLw!{bmW݅TTEt޶"+<˙BƖZ3P b&A3L{;D| %pY| 7p|Li쑲@ɸ}h}y-訂{! |Y er܅D q_9:MH~\L(n?GA,Ό>*S2.d}~D|Ebf&gMaf] +U{ h)`ۺiB.`lU=^9k|l2m 6MSy] L=J旳/sTLN>оT!3iG AGޏb&jo;ܑZZcw6v|hHEMGV+n?TK%4Qz;zjҵ|쨪!ޤf17&jp;`$R5dz;,FUz#woSx1+7x(^8#7\VUI:d}/r[`9-u>¢MIOŽkZVRUωMb"mj̯,Ǿ]~f}R`z15xmX]NAXم}4+ț|sGy%XE V2xX{7l\(*|b]EópkΪZSz&ϯkl$r&~H=mX2))6qzVɔwBU[Ed_Z1:6cڴ5pt- U`M-U Zx9K 9vf_.)v(wIHGgg*d6V2-S8 ̖1x) k҆fbᾥ{_$ |,"; ՞|6zG\CI/9U30ƅJoW..FZȼC mY1w V5UX XK~&"]dμs'yT9sa-?0uIONI2$Lr f9>D Xa!DUGa;b#%"VEA)ZIj^1AdQ 2 yޅ² L#xҗ|mԲ'ϵC3iɵʛ"Z”5;+z#v*47xYD8ɛcHJ/V29m۠`߀]TJMܑcՙhim.HY66,q|p7=oPշTu7l"lpu}kNg$AAġX:/m98VRHU2on;ݙ &XJҲcu+a"s̗9ylxrΎ>C$bQ%2JDR$mǍOF[60D\Dl lf&,e[.j{)G*}Zp~u- lhAXX׍Y'~gH>' 1ꫪz4V7MKE+nØJ/yI8IJq,Vb$Zх[/tinn*`lڢx2P#%̇[3cc]~dUXkJC.$L+$W)A 2by~ RfJfOa[R8k2%HC>J΀6jMi*"ReCaܤ!R|7LOaC3ɨ/'6y2X\Y4>_iِDQ{V =:oʥM/j@a[[($Λ~_mp۸`X:FUM3 BcWb}o5Jŕk#"j/FU7CO\PO9ccf֋˅B(]V,taWF.-,K⤜`h}\5lf t=1-sz--~M"0j%Bȫ}RϗvG5*OosmmOVӓ/B_ b|*PB`dK3pIemIq4U1KU~HU10u͟NHOWCh%r(ZHb: $h}7TQQ%It-4rҁhZhb,`7wqĭ6 `Sg ebupz?tS-%oاA#ҺLb.L`$_:ߗ}~?db$%6ҒG;)O2bǔLZT'c<~9 #[L6xQXtN3s]/wmj59NDq:?M5#;&7 Y^oƖW9@~ /j|R|X&hOyKbuMmB;V}<خR*A3fs@-ܦmœ3y[Mm  EDn`4u% b3! @p8*:;G{rŬ{eB6L,_柯tQ%g0MFn@72epzU")"GHY#K?ŮMUs54JTNlykplէU ]y4]czcu 9|tJRdәzN>?ҔJhHpU,"r+"aRTA4&BRk'h6pau\h-FeӒT2y:sc{0a\j՗QBEˤ eІ. D~ѭKIKU6f7Dg3뱮EP ^U ͷj; ]r (m{.">5zZj::"WaW\ȡ.O{mB%d` j3+45Z{QׁrU]_*xAϦT y<.U4:x2% % RT)1tOik.{g!>sP:u>h/###c)qgo"rsS=}Ewkj+UFFFF#Oɸn_Q,It3f[>գ#9uꉮLeuP3ݩ<-lda%|%h)hi8`>IIedy^IDATddt2+wU8*Vm2?v/4y.*WDdY,,SU-+)^]MDcUՁ:ѲurWi3͜73f㧜 P2vIy 9vҰA+hȪ@?U-8CDN6P܄ٟcS֨e݅uE^&T58$# lY̟Sթ )-a%-8ZerRidò,lV\e VcNz KB*0UDDdEJ9KWuD3%y=V[噺7Dř"SD|#p-u Z)6Pnnk9Ǻ`z`D#Tu`7{oewvOoQ|N1ї% T5G87bwWՁ26CWe;a c);0h)@D ?z#"a57u)\<oa}VsXVYTuN7-êZl[ovM pnP/|$"_Unt'/cVvUEd7LEU'1<8n ,o=^m̶wSȺ."{c<U Sbǭ RHXblUx\;cu=;TuWV#vX.UWV'B|fj^-"㰼P=' +okNcT r7GIDTDVJ.%. S6ߧmJb)("g`Y^d/ȯbٌ/Rّ9Gg%-%[D>ADN”Ķ\Scc yU/dV_B:Gc/D>."C/s2B͉] >د9[=ߏ6~ /XK1>*"= 8wLkmgc:<Oj:v !ż"K \/ȆE;Eα=XT{-+`~}0+8z`_mW̯pޞ+b53WEdHl-0{g\upByʁhْX: c?K~7S k_p8𑪎QC1UuH49X_Uh{nr 5 ADÜo]X8QUwS:(?mb"{,Z[UGXSm]Uu?Xqo CL塪`=;5#"a ~gU=XUw>Ŀ1g'ڞx}\)T8`c_lN߱7{e`8mP@TuO9"'Sj:UWLkbsU}]UgHo6kVcq 73qDoWC1sغ&=B ab?ᓋsO͙w`&[@AG0Sֱub 3AU]H`/};HoWH[sh,,"+`#{3Ҿo_~;ǧب0hW )~82U3S5L[obWy2룀]l7?5/_ {0v~S)%X?CX=6=a!:&j@{HCId©z[ Xkx9zq>/`S03 ЦRq"fFiBn.V8y&jLU?U_a&KX5&OFr`2/|,"Ed"V'V!$M0{`={}a xS%ocXϰ߱XQ~{ :D'ֿ +"˨ꗘp{`M푧ܼ2Gee&şu0hFk~${cU2+0wDl9S9dJf`,t7}{o1UիTU}\U&c}a)\w h4+M^CTuhjZ{)Ϝ0W%L*>:SUOL{չh%Y=VQ`JTU>=TJe mI:l~l'VWaq`̓R1^bLFɒi~D>b"ґ2꓁y=[{g2y 0ҡc.hNCd Ddذs!^\yVl'%Ь'ywP/*FX"m+2?Y*K^đyښ2ED&"X Fjq?YyT k:xԻb}=@U?z''3{^)s+E$u,-k8{< @U;.~yԢ?("$ZG81~f`-o\eKc3 U"2 *=L-xGn׀PD>WէRy5q󀈌^@c; +X/TU.| 6sN^[Ū,sLߣ1sEX?'7̄.`~bkEl캜)s| s}H^T5,Q""'74l$Ht Z|i"rv:$9G݅u6GO>m,`cKcEB,,TRӪ:VDnS Hwdl/!g5~"2 V8 8FUsFc`O|t 1!dlMؿkXl9HK6THkwib/Z_cO&X⦚hf{bGQ ֳL(l9u} EdG," 02f_Iy{ ^X9^QXӽi8 }-7ltfx !Z,TEbv}U*6G=a{2'QDUv_vTՉ2$ .^Λbm4?'܉pl>p4ocMT.{sA6>8nD^~Ӱy3'wDuWk6=<_SճDcLlGXeM}T՜e3Sq4~ׁfs?q96F~:Þ@]Uw-X(1Y#kocW6ZV+ 4rZbPgTo&vM} }~ï6^#e G2R,f ysgV0K+a~ c94 s;J d>9 393:#Ldd,.aD+&S2Kb41[@ sncosmo-2.12.1/docs/_static/sncosmo_logo_32.png000066400000000000000000000012771476435666400214030ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYstEXtSoftwarewww.inkscape.org<߽!\o1yeQ iFDhVz0{jPߐX36@>XzĹe|s}}T#nZ\C9d; `ru+>^8@ęuykn"P-hrWTBc Q.*{O3xf5rI1t}Bt?~zi#WXuDMAɝ7Y[\[?CAg<j߂[,/wc~`{oJ^,O_(슰4alr!9@M/!O[*f~X60I5С(4!҂m 4b83r (ܝ 65i}6ؐ=]M$S`@R= 7hd[.co+Cۼ',7`_zM;x\1iIENDB`sncosmo-2.12.1/docs/_static/sncosmo_logo_word_32.png000066400000000000000000000042251476435666400224320ustar00rootroot00000000000000PNG  IHDR "p%sBIT|d pHYsaDutEXtSoftwarewww.inkscape.org<IDATxl?V(2@iA~l \H6:N j4b8fLlMĨMСLy 2.`*)\ZhK}}[-ys=<[ OtDJ5REsGɤ7QO/P{1t϶+yrA dבa>E|%M{q'YLՖ6Ӡ-o@X$ 8#iHI$ہ}&g'f FY^k)tʲV@ pZMMc,QY,= z,q,d) dY".,'}{Bk#_\'m4Ž).6,8MY~]dchP'=/$PMIXY lB {X[d, 4S^x_(;o?JRe0(ze vzvLuSdEx=\B[ 2'*/$*U*v$I}IV&߭VJjJhٮD:YnKKr-K7Y}[$wPs<,K,+/Fޜ_'yY,e)H,_m/_-0&\4|`9bYjI>occ;ړWgwdžTƌG,o-@ERS1p6|`<2-I[6%*Fx9`2?0eTu9IާoPOYǖKf/ng$u؋a8IAIm^i{hH${x2y"4mӴNO~dx/ӞZ>zƗMi1caIH&\@UH;}t20Aؗ9Yn 9^*.5m,q+P9V+7~3``j?b:|KbY`Uksi:EH;ÀtBI\`<.;R.9P\4g; N1 VtHH8Ư⛺nyc6cu-Bp¥K!?Π3MU'7]~_mmY n\T B(KϜMW ip+?ñt 11 b q!ⶁtNA #f< YB8 &{[;ޥ-4^ܸιIJ$~?n1͸=]V&`Ar&EƐݸLR<`ck4ԀX[5*A֬`Z]_gt4fKcjeLB> W)LYe6\*.+ ʿ[qҏ{X3dо wXsE\l[RߘTĨ[Cp$ze5`B[I<֯t=,xc9ez*Pe`_ file. Parsed versions of the author list, including BibTeX entries that can be used to cite SNCosmo, are available on `GitHub `_ and `Zenodo `_. sncosmo-2.12.1/docs/bandpass-list.rst000066400000000000000000000001711476435666400175300ustar00rootroot00000000000000.. _list-of-built-in-bandpasses: List of Built-in Bandpasses =========================== .. automodule:: bandpass_page sncosmo-2.12.1/docs/bandpasses.rst000066400000000000000000000047531476435666400171210ustar00rootroot00000000000000********** Bandpasses ********** Constructing a Bandpass ----------------------- Bandpass objects represent the transmission fraction of an astronomical filter as a function of dispersion (photon wavelength, frequency or energy). They are basically simple containers for arrays of these values, with a couple special features. To get a bandpass that is in the registry (built-in):: >>> import sncosmo >>> band = sncosmo.get_bandpass('sdssi') >>> band To create a Bandpass directly, you can supply arrays of wavelength and transmission values: >>> wavelength = [4000., 5000.] >>> transmission = [1., 1.] >>> sncosmo.Bandpass(wavelength, transmission, name='tophatg') By default, the first argument is assumed to be wavelength in Angstroms. To specify a different dispersion unit, use a unit from the `astropy.units` package: >>> import astropy.units as u >>> wavelength = [400., 500.] >>> transmission = [1., 1.] >>> Bandpass(wavelength, transmission, wave_unit=u.nm) Using a Bandpass ---------------- A Bandpass acts like a continuous 1-d function, returning the transmission at supplied wavelengths (always in Angstroms):: >>> band([4100., 4250., 4300.]) array([ 0., 1., 1.]) Note that the transmission is zero outside the defined wavelength range. Linear interpolation is used between the defined wavelengths. Bnadpasses have a few other useful properties. You can get the range of wavelengths where the transmission is non-zero:: >>> band.minwave(), band.maxwave() (4000.0, 5000.0) Or the transmission-weighted effective wavelength: >>> band.wave_eff 4500.0 Or the name: >>> band.name 'tophatg' Adding Bandpasses to the Registry --------------------------------- You can create your own bandpasses and use them like built-ins by adding them to the registry. Suppose we want to register the 'tophatg' bandpass we created: >>> sncosmo.register(band, 'tophatg') Or if ``band.name`` has been set: >>> sncosmo.register(band) # registers band under band.name After doing this, we can get the bandpass object by doing >>> band = sncosmo.get_bandpass('tophatg') Also, **we can pass the string** ``'tophatg'`` **to any function that takes a** `~sncosmo.Bandpass` **object**. This means that you can create and register bandpasses at the top of a script, then just keep track of string identifiers throughout the rest of the script. sncosmo-2.12.1/docs/conf.py000066400000000000000000000172071476435666400155410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Licensed under a 3-clause BSD style license - see LICENSE.rst # # Astropy documentation build configuration file. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this file. # # All configuration values have a default. Some values are defined in # the global Astropy configuration which is loaded here before anything else. # See astropy.sphinx.conf for which values are set there. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('..')) # IMPORTANT: the above commented section was generated by sphinx-quickstart, but # is *NOT* appropriate for astropy or Astropy affiliated packages. It is left # commented out with this explanation to make it clear why this should not be # done. If the sys.path entry above is added, when the astropy.sphinx.conf # import occurs, it will import the *source* version of astropy instead of the # version installed (if invoked as "make html" or directly with sphinx), or the # version in the build directory (if "python setup.py build_sphinx" is used). # Thus, any C-extensions that are needed to build the documentation will *not* # be accessible, and the documentation will not build correctly. import sys import os import datetime import sphinx_rtd_theme import sphinx_gallery import matplotlib.sphinxext.plot_directive sys.path.insert(0, os.path.abspath("_helpers")) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.3' intersphinx_mapping = { 'python': ('https://docs.python.org/3/', None), 'numpy': ('https://docs.scipy.org/doc/numpy/', None), 'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None), 'astropy': ('http://docs.astropy.org/en/stable/', None), 'emcee': ('https://emcee.readthedocs.io/en/stable/', None) } # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.mathjax', 'sphinx.ext.linkcode', 'sphinx_gallery.gen_gallery', 'numpydoc', matplotlib.sphinxext.plot_directive.__name__ ] numpydoc_show_class_members = False autosummary_generate = True autoclass_content = "class" autodoc_default_flags = ["members", "inherited-members"] autodoc_docstring_signature = False sphinx_gallery_conf = { 'examples_dirs': '_examples', # path to examples scripts 'gallery_dirs': 'examples', # path to gallery generated examples 'backreferences_dir': 'modules/generated', # path to store the module # using example template 'doc_module': ('sncosmo',), # documented module(s) 'download_all_examples': False, # don't package up examples. 'default_thumb_file': os.path.join(os.path.dirname(__file__), '_logo', 'spectral_white_bkg.png') } # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # -- Project information ------------------------------------------------------ # This does not *have* to match the package name, but typically does project = 'sncosmo' author = 'Kyle Barbary and contributors' current_year = datetime.datetime.now().year copyright = '2013-{:d}, {}'.format(current_year, author) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. import sncosmo # The short X.Y version. version = sncosmo.__version__.split('-', 1)[0] # The full version, including alpha/beta/rc tags. release = sncosmo.__version__ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. default_role = 'obj' # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output --------------------------------------------------- html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [sphinx_gallery.glr_path_static()] # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. from os.path import join html_favicon = join('_static','sncosmo_logo_32.ico') # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = '{0} v{1}'.format(project, release) # Output file base name for HTML help builder. htmlhelp_basename = project + 'doc' # Add local templates path to modify autosummary templates #templates_path = ['_templates'] # Static files to copy after template files html_static_path = ['_static'] #html_style = 'sncosmo.css' # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [('index', project + '.tex', project + u' Documentation', author, 'manual')] # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [('index', project.lower(), project + u' Documentation', [author], 1)] # ----------------------------------------------------------------------------- # Source code links # # Lifted from numpy docs conf.py # ----------------------------------------------------------------------------- import inspect from os.path import relpath, dirname def linkcode_resolve(domain, info): """ Determine the URL corresponding to Python object """ if domain != 'py': return None modname = info['module'] fullname = info['fullname'] submod = sys.modules.get(modname) if submod is None: return None obj = submod for part in fullname.split('.'): try: obj = getattr(obj, part) except: return None try: fn = inspect.getsourcefile(obj) except: fn = None if not fn: return None try: source, lineno = inspect.findsource(obj) except: lineno = None if lineno: linespec = "#L%d" % (lineno + 1) else: linespec = "" fn = relpath(fn, start=dirname(sncosmo.__file__)) if 'dev' in sncosmo.__version__: return "http://github.com/sncosmo/sncosmo/blob/master/sncosmo/%s%s" % ( fn, linespec) else: return "http://github.com/sncosmo/sncosmo/blob/v%s/sncosmo/%s%s" % ( sncosmo.__version__, fn, linespec) sncosmo-2.12.1/docs/configuration.rst000066400000000000000000000056571476435666400176510ustar00rootroot00000000000000*********************** Directory Configuration *********************** The "built-in" Sources and Spectra in SNCosmo depend on some sizable data files. These files are hosted remotely, downloaded as needed, and cached locally. This all happens automatically, but it is helpful to know where the files are stored if you want to inspect them or share a common download directory between multiple users. By default, SNCosmo will create and use an ``sncosmo`` subdirectory in the AstroPy cache directory for this purpose. For example, ``$HOME/.astropy/cache/sncosmo``. After using a few models and spectra for the first time, here is what that directory might look like:: $ tree ~/.astropy/cache/sncosmo /home/kyle/.astropy/cache/sncosmo ├── models │ ├── hsiao │ │ └── Hsiao_SED_V3.fits │ └── sako │ ├── S11_SDSS-000018.SED │ ├── S11_SDSS-001472.SED │ └── S11_SDSS-002000.SED └── spectra ├── alpha_lyr_stis_007.fits └── bd_17d4708_stisnic_005.fits You can see that within the top-level ``$HOME/.astropy/cache/sncosmo`` directory, a particular directory structure is created. This directory structure is fixed in the code, so it's best not to move things around within the top-level directory. If you do, sncosmo will think the data have not been downloaded and will re-download them. Configuring the Directories =========================== What if you would rather use a different directory to store downloaded data? Perhaps you'd rather the data not be in a hidden directory, or perhaps there are multiple users who wish to use a shared data directory. There are two options: 1. Set the environment variable ``SNCOSMO_DATA_DIR`` to the directory you wish to use. For example, in bash:: export SNCOSMO_DATA_DIR=/home/user/data/sncosmo If this environment variable is set, it takes precedence over the second option (below). 2. Set the ``data_dir`` variable in the sncosmo configuartion file. This file is found in the astropy configuration directory, e.g., ``$HOME/.astropy/config/sncosmo.cfg``. When you ``import sncosmo`` it checks for this file and creates a default one if it doesn't exist. The default one looks like this:: $ cat ~/.astropy/config/sncosmo.cfg ## Directory containing SFD (1998) dust maps, with names: ## 'SFD_dust_4096_ngp.fits' and 'SFD_dust_4096_sgp.fits' ## Example: sfd98_dir = /home/user/data/sfd98 # sfd98_dir = None ## Directory where sncosmo will store and read downloaded data resources. ## If None, ASTROPY_CACHE_DIR/sncosmo will be used. ## Example: data_dir = /home/user/data/sncosmo # data_dir = None To change the data directory, simply uncomment the last line and set it to the desired directory. You can even move the data directory around, as long as you update this configuration parameter accordingly. sncosmo-2.12.1/docs/contributing.rst000066400000000000000000000237461476435666400175100ustar00rootroot00000000000000************ Contributing ************ Overview ======== SNCosmo follows the same general development workflow as astropy and many other open-source software projects. The `astropy development workflow`_ page documents the process in some detail. While you should indeed read that page, it can seem a bit overwhelming at first. So, we present here a rough outline of the process, and try to explain the reasoning behind it. .. _`astropy development workflow`: http://astropy.readthedocs.org/en/v0.4.1/development/workflow/development_workflow.html The process is centered around git and GitHub, so you need to know how to use basic git commands and also have a GitHub account. There is a "blessed" copy of the repository at https://github.com/sncosmo/sncosmo. Individual contributors make changes to their own copy (or "fork" or "clone" in git parlance) of the repository, e.g., https://github.com/kbarbary/sncosmo, then ask that their changes be merged into the "blessed" copy via a Pull Request (PR) on GitHub. A maintainer (currently Kyle) will review the changes in the PR, possibly ask for alterations, and then eventually merge the change. This seems overly complex at first glance, but there are two main benefits to this process: (1) Anyone is free to try out any crazy change they want and share it with the world on their own GitHub account, without affecting the "blessed" repository, and (2) Any proposed changes are reviewed and discussed by at least one person (the maintainer) before being merged in. Detailed steps ============== Do once: -------- 1. Hit the "fork" button in the upper right hand corner of the https://github.com/sncosmo/sncosmo page. This creates a clone of the repository on your personal github account. 2. Get it onto your computer (replace username with your GitHub username):: git clone git@github.com:username/sncosmo.git 3. Add the "blessed" version as a remote:: git remote add upstream git@github.com:sncosmo/sncosmo.git This will allow you to update your version to reflect new changes to the blessed repository that others have made). 4. Check that everything is OK:: $ git remote -v origin git@github.com:username/sncosmo.git (fetch) origin git@github.com:username/sncosmo.git (push) upstream git@github.com:sncosmo/sncosmo.git (fetch) upstream git@github.com:sncosmo/sncosmo.git (push) You can call the remotes anything you want. "origin" and "upstream" have no intrinsic meaning for git; they're just nicknames. The astropy documentation calls them "your-github-username" and "astropy" respectively. 5. Install the SNCosmo package in development mode. From the git directory:: pip install -e . If you are only editing Python code, the latest code will be used when you import sncosmo in a Python interpreter for the first time. If you are editing any of the Cython code in SNCosmo (files with .c or .pyx extensions), then you will need to run this command again to compile that code for your changes to be picked up. Every time you want to make a contribution: ------------------------------------------- 1. Ensure that the clone of the repository on your local machine is up-to-date with the latest upstream changes by doing ``git fetch upstream``. This updates your local "remote tracking branch", called ``upstream/master``. 2. Create a "topic branch" for the change you want to make. If you plan to make enhancements to the simulation code, name the branch something like "simulation-enhancements":: git branch simulation-enhancements upstream/master (``upstream/master`` is where the branch branches off from.) 3. Move to the branch you just created:: git checkout simulation-enhancements 4. *Make changes, ensure that they work, etc. Make commits as you go.* 5. Once you're happy with the state of your branch, push it to your GitHub account for the world to see:: git push origin simulation-enhancements 6. Create a PR: Go to your copy on github (https://github.com/username/sncosmo) select the branch you just pushed in the upper left-ish of the page, and hit the green button next to it. (It has a mouse-over "compare, review, create a pull request") What happens when the upstream branch is updated? ------------------------------------------------- Suppose that you are following the above workflow: you created a topic branch ``simulation-enhancements`` and made a few commits on that branch. You now want to create a pull request, but there's a problem: while you were working, more commmits were added to the ``upstream/master`` branch on GitHub. The history of your branch has now diverged from the main development branch! What to do? 1. Fetch the changes made to the upstream branch on so that you can deal with the changes locally:: git fetch upstream This will update your local branch ``upstream/master`` (and any other ``upstream`` branches) to the match the state of the upstream branch on GitHub. It doesn't do any merging or resolving, it just makes the new changes to ``upstream/master`` visible locally. 2. There are two options for this next step: ``merge`` or ``rebase`` with the latter being preferred for this purpose. Assuming you are on your branch ``simulation-enhancements``, you *could* do ``git merge upstream/master``. This would create a merge commit that merges the diverged histories back together. This works, but it can end up creating a confusing commit history, particularly if you repeat this process several times while working on your new branch. Instead, you can do:: git rebase upstream/master This actually *rewrites* your commits to make it look like they started from where ``upstream/master`` now is, rather than where it was when you started work on your ``simulation-enhancements`` branch. Your branch will have the exact same contents as if you had used ``git merge``, but the history will be different than it would have been if you had merged. In particular, there is no merge commit created, because the history has been rewritten so that your branch starts where ``upstream/master`` ends, and there is no divergent history to resolve. This means you can rebase again and again without creating a convoluted history full of merges back and forth between the branches. Trying out new ideas -------------------- git branches are the best way to try out new ideas for code modifications or additions. You don't even have to tell anyone about your bad ideas, since branches are local! They only become world visible when you push them to GitHub. If, after making several commits, you decide that your new branch ``simulation-enhancements`` sucks, you can just create a new branch starting from upstream/master again. If it is a really terrible idea you never want to see again, you can delete it by doing ``git branch -D simulation-enhancements``. Obviously this isn't a complete guide to git, but hopefully it jump-starts the git learning process. Testing ======= SNCosmo uses pytest to check that all of the code is running as expected. When you add new functionality to SNCosmo, you should write a test for that functionality. All of the tests can be found in the ``sncosmo/tests`` directory. When a new PR is created, the testsuite will be run automatically on a range of different machines and conditions using `tox`. You can run these same tests locally using ``tox``. First, install `tox`:: pip install tox From within the SNCosmo directory, run the test suite:: tox -e py3 The previous command will run the core test suite with the currently installed version of Python. You can run the full test suite with all of the optional dependencies by adding the ``-alldeps`` tag:: tox -e py3-alldeps Running the tests with the ``-cov`` tag will generate a coverage report:: tox -e py3-cov SNCosmo includes hundreds of builtin bandpasses and sources that are downloaded from external sites when they are loaded. ``tox`` can be used to check that all of these builtins are accessible with the following command:: tox -e builtins ``tox`` can also be used to check the code style:: tox -e codestyle or to build the documentation:: tox -e build_docs ``tox`` uses virtual environments for testing which can be somewhat slow. You can alternatively run the test in your own Python environment. First, install all of the testing dependencies from the ``test`` section of ``setup.cfg``. This can be done automatically when installing SNCosmo with the following command:: pip install -e .[test] The tests can then be run with the following command:: pytest --pyargs sncosmo Developer's documentation: release procedure ============================================ The release procedure is automated through GitHub Actions. To create a new release: - Update ``docs/history.rst`` with a summary of the new version's changes. - Bump version in ``sncosmo/__init__.py``. - Ensure that the tests have all completed successfully and that the docs are looking good. - Create a new release through the releases tab on GitHub, and tag it with the latest version. - Copy the change list into the release description. - Publish the release. - A conda build should start (with some delay) via a bot pull request at https://github.com/conda-forge/sncosmo-feedstock. Merge the PR once it passes all tests. **Packaging and Docs** - GitHub Actions will trigger after each release and build compiled wheels and source distributions. These will then be pushed to PyPI. - The docs for the release will show up on readthedocs.org as the new ``stable`` version. **Bumping Minimum Supported Python Version** Versions are hardcoded in - tox.ini - update the ``envlist = py{...}`` line. - setup.cfg - update ``python_requires``, ``install_requires``, and ``oldestdeps`` as needed. - .github/workflows/run_tests.yml - update the ``python`` and ``toxenv`` lines - docs/install.rst - Ensure that the first line "SNCosmo works on Python 3.x+" is correct sncosmo-2.12.1/docs/cuts.rst000066400000000000000000000016411476435666400157450ustar00rootroot00000000000000************* Applying Cuts ************* It is useful to be able to apply "cuts" to data before trying to fit a model to the data. This is particularly important when using some of the "guessing" algorithms in `~sncosmo.fit_lc` and `~sncosmo.nest_lc` that use a minimum signal-to-noise ratio to pick "good" data points. These algorithms will raise an exception if there are no data points meeting the requirements, so it is advisable to check if the data meets the requirements beforehand. Signal-to-noise ratio cuts ========================== Require at least one datapoint with signal-to-noise ratio (S/N) greater than 5 (in any band): >>> passes = np.max(data['flux'] / data['fluxerr']) > 5. >>> passes True Require two bands each with at least one datapoint having S/N > 5: >>> mask = data['flux'] / data['fluxerr'] > 5. >>> passes = len(np.unique(data['band'][mask])) >= 2 >>> passes True sncosmo-2.12.1/docs/history.rst000066400000000000000000000455211476435666400164750ustar00rootroot00000000000000=============== Version History =============== *Note:* SNCosmo uses `Semantic Versioning `_ for its version numbers. Specifically, this means that code written for sncosmo v1.0 will continue to work with any v1.x version. However, exact results may differ between versions in the 1.x series. (For example, due to changes in integration method.) v2.12.1 (2025-03-12) ==================== - Add Galax fileters (#408) - Fix bug in fitting v2.12.0 (2025-01-21) ==================== Major Changes & Additions: - Add support for Numpy 2.0 (#398) - Add non-radially varability to bandpasses (#400) - Adds hdf5 dependancy (#400) Minor Changes: - Improve testing (#402) v2.11.2 (2024-11-01) ==================== - Add SkyMapper filters v2.11.1 (2024-07-11) ==================== - Add Nugent Type Ia Super-Chandra model from Fitz Axen and Nugent, (2023) ApJ, v. 953, p. 13 v2.11.0 (2024-07-11) ==================== - Drop support for Python 3.8 - New `add_effect` for G10 and C11 SNIa intrinsic scatter models (#378) - Salt2 ColorLaw function correction (#389) v2.10.4 (2024-02-20) ==================== - Fix issue with Python 3.12. Added dependency, `looseversion`, to replace removed `distutil` module (#387). v2.10.3 (unreleased) ==================== v2.10.2 (2023-09-29) ==================== - Add ultrasat_ bandpasses (#381). .._ultrasat: https://www.weizmann.ac.il/ultrasat/ v2.10.1 (2023-06-28) ==================== - Add GOTO_ (#374) and `DES u`_ (#371) filters. .. _GOTO: https://goto-observatory.org/ .. _DES u: https://noirlab.edu/science/programs/ctio/filters/Dark-Energy-Camera v2.10.0 (2023-04-27) ==================== Major Changes: - Dropped support for Python <3.7 (#361) - Add phase dependence option to PropagationEffect (#359) Additions & Improvements: - Add retrained SALT2 models from the DES and Pantheon+ teams (#355) - Add TESS filter (#357) v2.9.0 (2022-09-27) =================== - Add P22 SALT3-NIR model (#350) - Updated SALT3 input calibration (#339) - Add Gaia filters (#351) - Improvements to code management v2.8.0 (2022-03-16) =================== - Add 2MASS filters (#338) - Add ATLAS filters (#336) v2.7.0 (2021-10-29) =================== - New zeropoint alias for improved SNANA compatibility (#318) - Add Roman filters (#218, #322) - Add the ability to a use SNANA co-add simlibs (#247) - Fix file paths for Windows users (#324) - Fix the builtins with poor spline options (#203, #328, #329) - Various improvements to code and repo management v2.6.0 (2021-09-09) =================== This release mostly involves modernizing the SNCosmo build system. - Add Pan-STARRS1 bandpasses (#212) - Added support for wheels which should now be automatically uploaded to PyPI for any new release (#304). - Fixed the Zenodo integration. New SNCosmo releases should now automatically be archived to Zenodo. - Moved citations to the new CITATION.cff format. GitHub and Zenodo will now provide BibTeX entries that can be used to cite SNCosmo (#302). - Update to the emcee version 3 API. Now only compatible with emcee>=3. - Migrate from Travis CI to GitHub Actions for continuous integration/tests (#293). - Bugfixes: - Fixed the implementation of the SALT3 error model (#300). - Fixed numpy type deprecations. - Fixed the documentation for models (#299). v2.5.0 (2021-04-20) =================== - Add SALT3 model from Kenworthy et al. (2021) - Various documentation fixes v2.4.0 (2021-03-05) =================== - Add SUGAR model from Leget et al. (2020) - Add support for the iminuit 2.0 API (#291). - Update tox to work with any version of Python 3. - Bugfixes: - Fix flatten_result bug due to API change (#285). v2.3.0 (2020-11-16) =================== - Add Swift UVOT bandpasses. - Bugfixes: - Fix segfaults in bicubic interpolation. v2.2.0 (2020-10-23) =================== - Add core-collapse models from Vincenzi et al. (2019) - New Spectrum class to handle processing and fitting spectral observations. - Removed appveyor builds. - Bugfixes: - Prevent segfaults in light curve fitting when the minuit fit fails. v2.1.0 (2020-02-25) =================== - Add ZTF transmission functions - Bugfixes - Corrected pyproject.toml; should help cases where the wrong numpy version was being installed. - Remove all references to the six package - Fix accidental mutation of ``bounds`` inputs in ``fit_lc`` and ``mcmc_lc``. v2.0.0 (2019-06-08) =================== This version is the same as v1.8, with the exception that Python 2 support has been removed, and deprecated functions and attributes have been removed. These were deprecated in v1.5.0 (released April 2017) or before. On Python 2, pip should automatically install sncosmo v1.8 still. If not, specify ``sncosmo<2.0.0``. Minor changes: - The ``salt2-h17`` source model has been renamed to ``salt2-extended-h17`` to make clearer its relation to ``salt2-extended``. It is still available under the old name for backwards compatibility. v1.8.2 (2019-06-08) =================== - Fix bug in download location of ``snana-*`` models latest versions, introduced in v1.8.0. v1.8.1 (2019-06-08) =================== - Fix bug in download location of ``salt2-extended`` model latest version, introduced in v1.8.0. v1.8.0 (2019-05-25) =================== - Add version 2.0 of many ``snana-...`` built-in core-collapse models, based on Pierel et al. 2018 (pull request 229). - Bugfixes: - Fix compatibility with scipy 1.3+ by removing outdated import statements (pull request 238). - Fix issue affecting optimization of models with free propagation effects (pull request 236). v1.7.0 (2019-02-02) =================== - Add SNEMO2, SNEMO7, SNEMO15 source models from Saunders et al. (2018) to built-ins. v1.6.0 (2018-04-27) =================== - Add Hounsell et al. (2017) SALT2 model to built-ins. - Add ``remote_timeout`` configuration option. - Build system: remove build-time dependency on astropy helpers. - Bugfixes: - Correctly delete empty files created when a download fails. - Use pseudo-inverse when inverting covariance matrix for increased stability. - Fix an issue with pickling on Cython 0.26+. - Fixed problem where ``data['fluxcov']`` was unintentionally being modified in-place when passed to ``fit_lc``. - Fixed problem where ``'fluxcov'`` not recognized as a valid name for covariance column in data in ``fit_lc``. v1.5.0 (2017-04-20) =================== This is a major new release. The highlight is really close compatibility of the SALT2 model and fitting procedure with ``snfit``, the "official" SALT2 fitter. - ``SALT2Source``: Internal interpolation scheme of ``SALT2Source`` updated to match ``snfit`` implementation exactly. Test suite now tests against ``snfit`` implementation. - ``fit_lc()``: - Handling of model covariance updated to match that of ``snfit``: model covariance is fixed for each fit and fit is repeated until convergence. - New arguments ``phase_range`` and ``wave_range``. If given, data outside this range will be discarded after an initial fit and additional fits will be performed until convergence. With ``phase_range=(-15., 45.)`` and ``wave_range=(3000., 7000.)``, behavior approximates that of snfit with default arguments. - Added support for covariance in photometric data measurements, and this covariance is used in ``fit_lc()`` if present. Covariance is stored as a ``'fluxcov'`` column in the table of measurements. - Result includes two new attributes: ``data_mask``, a boolean array indicating which rows in the input data were used in the final fit (since multiple fits might be performed), and ``nfit``, the number of fits performed. - New argument ``warn`` can be set to False to turn off warnings about dropping bands outside model wavelength range. - ``read_lc()``: - Added support for reading snfit-format "covmat" files into a table of photometry:: >>> data = read_lc('filename', format='salt2', read_covmat=True) >>> data['Fluxcov'].shape == (len(data), len(data)) True - New keyword argument ``expand_bands``. When True, the returned band column will contain ``Bandpass`` objects instead of strings. (Strings converted to bandpass objects using ``sncosmo.get_bandpass()``.) This is particularly useful for position-dependent bandpasses in the salt2 file format, such as ``megacampsf``: ``read_lc()`` reads the position from the header and feeds the position to ``get_bandpass()`` to get a Bandpass object for the correct position. - Built-in bandpasses and magnitude systems: Many new built-in bandpasses and magnitude systems. - Configuration: The environment variable ``SNCOSMO_DATA_DIR`` can be used to set the path to the data directory. If set, it takes precedence over the ``data_dir`` variable in the configuration file (``$HOME/.astropy/config/sncosmo.cfg``). v1.4.0 (2016-11-16) =================== - ``SFD98Map`` and ``get_ebv_from_map`` deprecated in favor of separate package `sfdmap `_ which has vastly improved performance (200x faster) for the typical case of scalar coordinates in ICRS frame. - ``animate_source()`` deprecated. This is a "fun extra" that is difficult to test and no longer seems to work. - Cython implementation of extinction functions has been factored out into a separate Python module called ``extinction``, which is now a dependency. - ``Model.bandflux()`` and ``Source.bandflux()`` now integrate on a fixed wavelength grid of 5 angstroms regardless of the wavelength grid of the bandpass. This will result in small differences in results from previous sncosmo versions. - The internal (publicly undocumented) ``Spectrum`` class now acts more like ``Model``; in particular, its ``bandflux()`` method now behaves the same way. As ``Spectrum`` backs ``SpectralMagSystem``, this makes the integration of models and zeropoint spectra more consistent. - Experimental (non-public) support for aliases for bandpasses, such as ``'SDSS::g'`` for ``'sdssg'``. - Sources now use cubic rather than quadratic spline interpolation internally. - ``Model.source_peakmag()`` and ``Model.set_source_peakmag()`` added as convenience functions for ``Model.source.peakmag()`` and ``Model.source.set_peakmag()`` respectively. - **[Bugfix]** Fixed missing import of ``math`` module in ``mcmc_lc()`` when using the ``priors`` keyword. [Backported to v1.3.1] [`#143 `_] v1.3.0 (2016-06-30) =================== This is mostly a bugfix release, but it also **drops support for Python 2.6.** Python 2.7 is now the minimum supported Python version. - Updates for compatibility with AstroPy 1.2. - The registry now handles subclasses more robustly. For example, if ``magsys`` is an instance of ``SpectralMagSystem``, the following used to fail:: sncosmo.register(magsys, 'name') sncosmo.get_magsystem('name') Now this works. [`#132 `_] - **[Bugfix]** ``SALT2Source`` had a bug under Python 3 (only) yielding drastically wrong fluxes. Python 2 was not affected. [`#138 `_] v1.2.0 (2015-12-01) =================== - **[API change]** Registry functions moved to the top-level namespace, as follows: - ``sncosmo.registry.register()`` -> ``sncosmo.register()`` - ``sncosmo.registry.register_loader()`` -> ``sncosmo.register_loader()`` - ``sncosmo.registry.retrieve()`` -> deprecated, use class-specific functions such as ``sncosmo.get_bandpass()``. The old import paths will still work for backwards compatibility. - ``nest_lc()`` now uses the ``nestle`` module under the hood. A new keyword ``method`` is available which selects different sampling methods implemented by ``nestle``. The new methods provide potential efficiency gains. - The MLCS2k2 model is now available as a built-in Source, with the name ``'mlcs2k2'``. - Bandpasses from the Carnegie Supernova Project added to built-ins. - In ``realize_lcs()``, a new ``scatter`` keyword makes adding noise optional. - **[Bugfix]** Fix built-in Bessell bandpass definitions, which were wrong by a term proportional to inverse wavelength. This was due to misinterpretation of the trasmission units. [backported to v1.1.1] [`#111 `_] v1.1.0 (2015-08-12) =================== This is a mostly bugfix release with more solid support for Python 3. - Added ``Model.color()`` method. - Remove ``loglmax`` from result of ``nest_lc()``, which was not officially documented or supported. Use ``np.max(res.logl)`` instead. - Fixed bug that caused non-reproducible behavior in ``nest_lc()`` even when ``numpy.random.seed()`` was called directly beforehand. [`#102 `_] - Fixed file I/O problems on Python 3 related to string encoding. [`#83 `_, `#85 `_] - Fixed problem with SDSS bandpasses being stored as integers internally, preventing them from being used with models with dust. [`#100 `_, `#101 `_] - Fixed problem where built-in source name and version strings were being dropped. [`#82 `_] - Minor doc fixes. v1.0.0 (2015-02-23) =================== - **[API change]** The API of ``mcmc_lc`` has changed significantly (the function was marked experimental in previous release). - **[Deprecation]** In result of ``fit_lc``, ``res.cov_names`` changed to ``res.vparam_names``. - **[Deprecation]** In result of ``nest_lc``, ``res.param_names`` changed to ``res.vparam_names``. This is for compatibility between the results of ``fit_lc`` and ``nest_lc``. [`#30 `_] - **[Deprecation]** Deprecate ``flatten`` keyword argument in ``fit_lc()`` in favor of explicit use of ``flatten_result()`` function. - Many new built-in models. - Many new built-in bandpasses. - New remote data fetching system. - SALT2 model covariance available via ``Model.bandfluxcov()`` method and ``modelcov=True`` keyword argument passed to ``fit_lc``. - New simulation function, ``zdist``, generates a distribution of redshifts given a volumetric rate function and cosmology. - New simulation function, ``realize_lcs``, simulates light curve data given a model, parameters, and observations. - Add color-related keyword arguments to ``plot_lc()``. - Add ``tighten_ylim`` keyword argument to ``plot_lc()``. - Add ``chisq()`` function and use internally in ``fit_lc()``. - Add ``SFD98Map`` class for dealing with SFD (1998) dust maps persistently so that the underlying FITS files are opened only once. - Update ``get_ebv_from_map()`` to work with new SkyCoord class in ``astropy.coordinates`` available in astropy v0.3 onward. Previously, this function did not work with astropy v0.4.x (where older coordinates classes had been removed). - Update to new configuration system available in astropy v0.4 onward. This makes this release incompatible with astropy versions less than 0.4. - Now compatible with Python 3. - Increased test coverage. - Numerous minor bugfixes. v0.4.0 (2014-03-26) =================== This is non-backwards-compatible release, due to changes in the way models are defined. These changes were made after feedback on the initial design. The most major change is a new central class ``Model`` used throughout the pacakge. A ``Model`` instance encompasses a ``Source`` and zero or more ``PropagationEffect`` instances. This is so that different source models (e.g., SALT2 or spectral time series models) can be combined with arbitrary dust models. The best way to think about this is ``Source`` and ``PropagationEffect`` define the rest-frame behavior of a SN and dust, and a ``Model`` puts these together to determine the observer-frame behavior. - New classes: - ``sncosmo.Model``: new main container class - ``sncosmo.Source``: replaces existing ``Model`` - ``sncosmo.TimeSeriesSource``: replaces existing ``TimeSeriesModel`` - ``sncosmo.StretchSource``: replaces existing ``StretchModel`` - ``sncosmo.SALT2Source``: replaces existing ``SALT2Model`` - ``sncosmo.PropagationEffect`` - ``sncosmo.CCM89Dust`` - ``sncosmo.OD94Dust`` - ``sncosmo.F99Dust`` - New public functions: - ``sncosmo.read_griddata_ascii``: Read file with ``phase wave flux`` rows - ``sncosmo.read_griddata_fits`` - ``sncosmo.write_griddata_fits`` - ``sncosmo.nest_lc``: Nested sampling parameter estimation of SN model - ``sncosmo.simulate_vol`` (EXPERIMENTAL): simulation convenience function. - Built-ins: - updated SALT2 model URLs - added SALT2 version 2.4 (Betoule et al 2014) - Improvements to ``sncosmo.plot_lc``: flexibility and layout - Many bugfixes v0.3.0 (2013-11-07) =================== This is a release with mostly bugfixes but a few new features, designed to be backwards compatible with v0.2.0 ahead of API changes coming in the next version. - New Functions: - ``sncosmo.get_ebv_from_map``: E(B-V) at given coordinates from SFD map. - ``sncosmo.read_snana_ascii``: Read SNANA ascii format files. - ``sncosmo.read_snana_fits``: Read SNANA FITS format files. - ``sncosmo.read_snana_simlib``: Read SNANA ascii "SIMLIB" files. - registry is now case-independent. All of the following now work:: sncosmo.get_magsystem('AB') sncosmo.get_magsystem('Ab') sncsomo.get_magsystem('ab') - Photometric data can be unordered in time. Internally, the data are sorted before being used in fitting and typing. - Numerous bugfixes. v0.2.0 (2013-08-20) =================== - Added SN 2011fe Nearby Supernova Factory data to built-in models as ``'2011fe'`` - Previously "experimental" functions now included: - ``sncosmo.fit_lc`` (previously ``sncosmo.fit_model``) - ``sncosmo.read_lc`` (previously ``sncosmo.readlc``) - ``sncosmo.write_lc`` (previously ``sncosmo.writelc``) - ``sncosmo.plot_lc`` (previously ``sncosmo.plotlc``) - New functions: - ``sncosmo.load_example_data``: Example photometric data. - ``sncosmo.mcmc_lc``: Markov Chain Monte Carlo parameter estimation. - ``sncosmo.animate_model``: Model animation using matplotlib.animation. - Fitting: ``sncosmo.fit_lc`` now uses the iminuit package for minimization by default. This requires the iminuit package to be installed, but the old minimizer (from scipy) can still be used by setting the keyword ``method='l-bfgs-b'``. - Plotting: Ability to plot model synthetic photometry without observed data, using the syntax:: >>> sncosmo.plot_lc(model=model, bands=['band1', 'band2']) - Photometric data format: Photometric data format is now more flexible, allowing various names for table columns. v0.1.0 (2013-07-15) =================== Initial release. sncosmo-2.12.1/docs/index.rst000066400000000000000000000021031476435666400160700ustar00rootroot00000000000000 .. raw:: html ******* SNCosmo ******* .. image:: _static/sncosmo_banner_96.png :width: 409px :height: 96px SNCosmo is a Python library for supernova cosmology analysis. It aims to make such analysis both as flexible and clear as possible. .. toctree:: :maxdepth: 1 :titlesonly: install models bandpasses magsystems photdata spectrum cuts simulation registry configuration .. include:: examples/index.rst :start-after: autogenerated_examples: .. toctree:: :hidden: :titlesonly: examples/index reference source-list bandpass-list magsystem-list :doc:`reference` ================ More... ======= .. toctree:: :maxdepth: 1 :titlesonly: history about contributing * Check out the source code: https://github.com/sncosmo/sncosmo * Report bugs, request features: https://github.com/sncosmo/sncosmo/issues * User & developer mailing list: https://groups.google.com/forum/#!forum/sncosmo sncosmo-2.12.1/docs/install.rst000066400000000000000000000056651476435666400164470ustar00rootroot00000000000000************ Installation ************ SNCosmo works on Python 3.8+ and depends on the following Python packages: - `numpy `_ - `scipy `_ - `astropy `_ - `extinction `_ Install using conda (recommended) ================================= If you are using Anaconda or the conda package manager, you can install SNCosmo from the conda-forge channel:: conda install -c conda-forge sncosmo Install using pip ================= First ensure that numpy and cython are installed. Then:: pip install sncosmo .. note:: The ``--no-deps`` flag is optional, but highly recommended if you already have numpy, scipy and astropy installed, since otherwise pip will sometimes try to "help" you by upgrading your Numpy installation, which may not always be desired. .. note:: If you get a ``PermissionError`` this means that you do not have the required administrative access to install new packages to your Python installation. In this case you may consider using the ``--user`` option to install the package into your home directory. You can read more about how to do this in the `pip documentation `_. Do **not** install sncosmo or other third-party packages using ``sudo`` unless you are fully aware of the risks. .. note:: You will need a C compiler (e.g. ``gcc`` or ``clang``) to be installed for the installation to succeed. Install latest development version ================================== SNCosmo is being developed `on github `_. To get the latest development version using ``git``:: git clone git://github.com/sncosmo/sncosmo.git cd sncosmo then:: pip install -e . This will install a development version of the SNCosmo package that automatically picks up any changes that you made when you import sncosmo for the first time in a Python interpreter. If you make any edits to the Cython code in SNCosmo (files with .c or .pyx extensions), then you will need to run this command again to compile that code for your changes to be picked up. Optional dependencies ===================== Several additional packages are recommended for enabling optional functionality in SNCosmo. - `matplotlib `_ for plotting functions. - `iminuit `_ for light curve fitting using the Minuit minimizer in `sncosmo.fit_lc`. - `emcee `_ for MCMC light curve parameter estimation in `sncosmo.mcmc_lc`. - `nestle `_ for nested sampling light curve parameter estimation in `sncosmo.nest_lc`. The `corner `_ package is also recommended for plotting results from the samplers `sncosmo.mcmc_lc` and `sncosmo.nest_lc`, but is not used by any part of sncosmo. sncosmo-2.12.1/docs/magsystem-list.rst000066400000000000000000000001471476435666400177510ustar00rootroot00000000000000 List of Built-in Magnitude Systems ---------------------------------- .. automodule:: magsystem_page sncosmo-2.12.1/docs/magsystems.rst000066400000000000000000000061701476435666400171650ustar00rootroot00000000000000***************** Magnitude Systems ***************** SNCosmo has facilities for converting synthetic model photometry to magnitudes in a variety of magnitude systems (or equivalently, scaling fluxes to a given zeropoint in a given magnitude system). For example, in the following code snippet, the string `'ab'` specifies that we want magnitudes on the AB magnitude system:: >>> model.bandmag('desr', 'ab', [54990., 55000., 55020.]) The string ``'ab'`` here refers to a built-in magnitude system (``'vega'`` is another option). Behind the scenes magnitude systems are represented with `~sncosmo.MagSystem` objects. As with `~sncosmo.Bandpass` objects, most places in SNCosmo that require a magnitude system can take either the name of a magnitude system in the registry or an actual `~sncosmo.MagSystem` instance. You can access these objects directly or create your own. `~sncosmo.MagSystem` objects represent the spectral flux density corresponding to magnitude zero in the given system and can be used to convert physical fluxes (in photons/s/cm^2) to magnitudes. Here's an example:: >>> ab = sncosmo.get_magsystem('ab') >>> ab.zpbandflux('sdssg') 546600.83408598113 This example gives the number of counts (in photons) when integrating the AB spectrum (which happens to be F_nu = 3631 Jansky at all wavelengths) through the SDSS g band. This works similarly for other magnitude systems:: >>> vega = sncosmo.get_magsystem('vega') >>> vega.zpbandflux('sdssg') 597541.25707788975 You can see that the Vega spectrum is a bit brighter than the AB spectrum in this particular bandpass. Therefore, SDSS *g* magnitudes given in Vega will be larger than if given in AB. There are convenience methods for converting an observed flux in a bandpass to a magnitude:: >>> ab.band_flux_to_mag(1., 'sdssg') 14.344175725172901 >>> ab.band_mag_to_flux(14.344175725172901, 'sdssg') 0.99999999999999833 So, one count per second in this band is equivalent to an AB magnitude of about 14.34. "Composite" magnitude systems ----------------------------- Sometimes, photometric data is reported in "magnitude systems" that don't correspond directly to any spectrophotometric standard. One example is "SDSS magnitudes" which are like AB magnitudes but with an offset in each band. These are represented in SNCosmo with the `~sncosmo.CompositeMagSystem` class. For example:: >>> magsys = sncosmo.CompositeMagSystem(bands={'sdssg': ('ab', 0.01), ... 'sdssr': ('ab', 0.02)}) This defines a new magnitude system that knows about only two bandpasses. In this magnitude system, an object with magnitude zero in AB would have a magntide of 0.01 in SDSS *g* and 0.02 in SDSS *r*. Indeed, you can see that the flux corresponding to magnitude zero is slightly higher in this magnitude system than in AB:: >>> magsys.zpbandflux('sdssr') 502660.28545283229 >>> ab.zpbandflux('sdssr') 493485.70128115633 Since we've only defined the offsets for this magnitude system in a couple bands, using other bandpasses results in an error:: >>> magsys.zpbandflux('bessellb') ValueError: band not defined in composite magnitude system sncosmo-2.12.1/docs/make.bat000066400000000000000000000106411476435666400156420ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end sncosmo-2.12.1/docs/models.rst000066400000000000000000000371311476435666400162550ustar00rootroot00000000000000**************** Supernova Models **************** Getting Started =============== Create a model using the built-in "source" named ``'hsiao'``: >>> import sncosmo >>> model = sncosmo.Model(source='hsiao') Set the redshift, time-of-zero-phase and the amplitude: >>> model.set(z=0.5, t0=55000., amplitude=1.e-10) Generate synthetic photometry through an observer-frame bandpass: >>> model.bandmag('desr', 'ab', [54990., 55000., 55020.]) array([ 24.82381795, 24.41496701, 25.2950865 ]) Equivalent values in photons / s / cm^2: >>> model.bandflux('desr', [54990., 55000., 55020.]) array([ 7.22413301e-05, 1.05275209e-04, 4.68034980e-05]) Equivalent values scaled so that 1 is equivalent to an AB magnitude of 25: >>> model.bandflux('desr', [54990., 55000., 55020.], zp=25., zpsys='ab') array([ 1.17617737, 1.71400939, 0.7620183 ]) Generate an observer-frame spectrum at a given time and wavelengths (in ergs/s/cm^2/Angstrom): >>> model.flux(54990., [4000., 4100., 4200.]) array([ 4.31210900e-20, 7.46619962e-20, 1.42182787e-19]) Creating a model using a built-in source ======================================== A Model in sncosmo consists of * **One "source"** A model of the spectral evolution of the source (e.g., a supernova). * **Zero or more "propagation effects"** Models of how intervening structures (e.g., host galaxy dust, milky way dust) affect the spectrum. In the above example, we created a model with no propagation effects, using one of the built-in ``Source`` instances that sncosmo knows about: ``'hsiao'``. See the full :ref:`list-of-built-in-sources` that sncosmo knows about. .. note:: In fact, the data for "built-in" sources are hosted remotely, downloaded as needed, and cached locally. So the first time you load a given model, you need to be connected to the internet. You will see a progress bar as the data are downloaded. By default, SNCosmo will use a subdirectory of the AstroPy cache directory for this purpose, e.g., ``$HOME/.astropy/cache/sncosmo``, but this can be changed by setting the ``data_dir`` configuration parameter in ``$HOME/.astropy/config/sncosmo.cfg``. See :doc:`configuration` for more information. Some built-in source models have multiple versions, which can be explicitly retrieved using the `~sncosmo.get_source` function: >>> source = sncosmo.get_source('hsiao', version='2.0') >>> model = sncosmo.Model(source=source) Model parameters ================ Each model has a set of parameter names and values: >>> model.param_names ['z', 't0', 'amplitude'] >>> model.parameters array([ 0., 0., 1.]) These can also be retrieved as: >>> model.get('z') 0.0 >>> model['z'] 0.0 Parameter values can be set by any of the following methods: >>> model.parameters[0] = 0.5 >>> model.parameters = [0.5, 0., 1.] # set the entire array >>> model['z'] = 0.5 >>> model.set(z=0.5) >>> model.set(z=0.5, amplitude=2.0) # Can specify multiple parameters >>> model.update({'z': 0.5, 'amplitude': 2.0}) What do these parameters mean? The first two, ``z`` and ``t0`` are common to all `~sncosmo.Model` instances: * ``z`` is the redshift of the source. * ``t0`` is the observer-frame time corresponding to the source's phase=0. Note that in some sources phase=0 might be at explosion while others might be at max: the definition of phase is arbitrary. However, observed time is always related to phase via ``time = t0 + phase * (1 + z)`` The next, ``amplitude``, is specific to the particular type of source. In this case, the source is a simple spectral timeseries that can only be scaled up and down. Other sources could have other parameters that affect the shape of the spectrum at each phase. For a given model, you can set the ``amplitude`` (or ``x0`` in case you are using a SALT model) according to a desired absolute magnitude in a specific band by using the method `~sncosmo.Model.set_source_peakabsmag()`. Note that the redshift ``z`` affects your result. Therefore, you could specify: >>> model.set(z=1.6) >>> model.set_source_peakabsmag(-19.0, 'bessellb', 'ab') Specifically, for SALT models, it is recommended to call `~sncosmo.Model.set_source_peakabsmag()` after setting the other model parameters, such as ``x1`` and ``c``. It probably won't make a difference if you are using the ``'bessellb'`` bandpass, but if you were setting the absolute magnitude in another band, it would make a small difference. The reason for this peculiarity is that "absolute magnitude" is not a parameter in the SALT2 model, per se. The parameters are ``x0``, ``x1``, ``c``, ``t0``, and ``z``. ``x0`` is a simple multiplicative scaling factor on the whole spectral timeseries. The ``set_source_peakabsmag()`` method is a convenience for setting ``x0`` such that the integrated flux through a given bandpass is as desired. Since the integrated flux depends on the spectral shape, it will depend on ``x1`` and ``c``. Creating a model with a source and effect(s) ============================================ Let's create a slightly more complex model. Again we will use the Hsiao spectral time series as a source, but this time we will add host galaxy dust. >>> dust = sncosmo.CCM89Dust() >>> model = sncosmo.Model(source='hsiao', ... effects=[dust], ... effect_names=['host'], ... effect_frames=['rest']) The model now has additional parameters that describe the dust, ``hostebv`` and ``hostr_v``: >>> model.param_names ['z', 't0', 'amplitude', 'hostebv', 'hostr_v'] >>> model.parameters array([ 0. , 0. , 1. , 0. , 3.1]) These are the parameters of the ``CCM89Dust`` instance we created: >>> dust.param_names ['ebv', 'r_v'] In the model, the parameter names are prefixed with the name of the effect (``host``). At any time you can print the model to get a nicely formatted string representation of its components and current parameter values: >>> print(model) source: class : TimeSeriesSource name : hsiao version : 3.0 phases : [-20, .., 85] days (22 points) wavelengths: [1000, .., 25000] Angstroms (481 points) effect (name='host' frame='rest'): class : CCM89Dust wavelength range: [1250, 33333] Angstroms parameters: z = 0.0 t0 = 0.0 amplitude = 1.0 hostebv = 0.0 hostr_v = 3.1000000000000001 Also, ``str(model)`` will return this string rather than printing it. Adding Milky Way dust ===================== Dust in the Milky Way will affect the shape of an observed supernova spectrum. It is important to take this into account in our model when fitting the model to observed data. As with host galaxy dust treated above, we can model Milky Way dust as a "propagation effect". The only difference is that Milky Way dust is in the observer frame rather than the supernova rest frame. Here, we create a model with dust in *both* the SN rest frame and the observer frame:: >>> dust = sncosmo.CCM89Dust() >>> model = sncosmo.Model(source='hsiao', ... effects=[dust, dust], ... effect_names=['host', 'mw'], ... effect_frames=['rest', 'obs']) We can see that the model includes four extra parameters (two describing the host galaxy dust and two describing the milky way dust):: >>> model.param_names ['z', 't0', 'amplitude', 'hostebv', 'hostr_v', 'mwebv', 'mwr_v'] >>> model.parameters # default values array([ 0. , 0. , 1. , 0. , 3.1, 0. , 3.1]) The host galaxy dust parameters are prefixed with ``'host'`` and the Milky Way dust parameters are prefixed with ``'mw'``. These are just the names we supplied when constructing the model. The effect names have no significance beyond this. The effect frames, on the other hand, *are* significant. The only allowed values are ``'rest'`` (rest frame) and ``'obs'`` (observer frame). A typical use pattern is to get an estimate of the amount of Milky Way dust at the location of the supernova from a dust map, and then to fix that amount of dust in the model. The following example illustrates how to do this using the Schlegel, Finkbeiner and Davis (1998) dust map with the `sfdmap `_ package. First, load the dust map (do this only once):: >>> import sfdmap >>> dustmap = sfdmap.SFDMap("/path/to/dust/maps") Now, for each SN you wish to fit, get the amount of dust at the SN location and set the ``mwebv`` model parameter appropriately. For example, if the SN is located at RA=42.8 degrees, Dec=0 degrees:: >>> ebv = dustmap.ebv(42.8, 0.0) >>> model.set(mwebv=ebv) >>> # proceed with fitting the other model parameters to the data. Note that we wish to *fix* the ``mwebv`` model parameter rather than fitting it to the data like the other parameters: We're supposing that this value is perfectly known from the dust map. Therefore, when using a function such as `~sncosmo.fit_lc` to fit the parameters, be sure *not* to include ``'mwebv'`` in the list of parameters to vary. Adding color dependant scatter model ==================================== The intrinsic scattering of SNe Ia is color dependant it can be modelled for simulation purpose by G10 or C11 models. The implemention is based on Kessler et al. 2012. They both act as random variation in the spectra model of a `~sncosmo.SALT2Source` or `~sncosmo.SALT3Source`. The G10 model relies on SALT2 residuals, thus it needs to take a `SALTSource` as an argument. It can be added to your `~sncosmo.Model` as: .. code:: python >>> source = 'salt2' >>> SALTSource = sncosmo.models.get_source(name=source) >>> G10 = sncosmo.models.G10(SALTsource=SALTSource) >>> SALTwithG10 = sncosmo.Model(source='salt2', effects=[G10], effect_names=['G10'], effect_frames=['rest']) The G10 model parameters are: * ``L0``, ``F0`` and ``F1`` are used in the multiplicative factor introduced in Kessler et al. 2012. Their default values are ``L0=2157.3``, ``F0=0`` and ``F1=1.08e-4``. * ``dL`` the wavelength range between each scatter node. Following Kessler et al. 2012 it is set by default to 800A. Since the C11 model does not relies on SALT2 residuals, it does not need a `SALTSource`. It can be added to your `~sncosmo.Model` as: .. code:: python >>> C11 = sncosmo.models.C11() >>> SALTwithC11 = sncosmo.Model(source='salt2', effects=[C11], effect_names=['C11'], effect_frames=['rest']) The C11 model parameters are: * ``CvU`` the correlation coefficient between the v and U band that could be -1, 0 or 1. * ``Sf`` a scale factor fixed by default to ``S_f=1.3`` according to Kessler et al. 2012. Phase Dependant effects ======================= Primarily to simulate lensed transients, phase dependant effects can also be applied. Derived classes of `~sncosmo.PropagationEffect` can add the ```self._minphase`` and ``self._maxphase`` parameters. Once your phase dependant effect is defined, it can be added to a model in the same way as chromatic effects, by specifying the appropriate ``effects``, ``effect_frames``, and ``effect_names`` when defining your `~sncosmo.Model`. Model spectrum ============== To retrieve a spectrum (in ergs / s / cm^2 / Angstrom) at a given observer-frame time and set of wavelengths: >>> wave = np.array([3000., 3500., 4000., 4500., 5000., 5500.]) >>> model.flux(-5., wave) array([ 5.29779465e-09, 7.77702880e-09, 7.13309678e-09, 5.68369041e-09, 3.06860759e-09, 2.59024291e-09]) We can supply a list or array of times and get a 2-d array back, representing the spectrum at each time: >>> model.flux([-5., 2.], wave) array([[ 5.29779465e-09, 7.77702880e-09, 7.13309678e-09, 5.68369041e-09, 3.06860759e-09, 2.59024291e-09], [ 2.88166481e-09, 6.15186858e-09, 7.87880448e-09, 6.93919846e-09, 3.59077596e-09, 3.27623932e-09]]) Changing the model parameters changes the results: >>> model.parameters array([0., 0., 1., 0., 3.1]) >>> model.flux(-5., [4000., 4500.]) array([ 7.13309678e-09, 5.68369041e-09]) >>> model.set(amplitude=2., hostebv=0.1) >>> model.flux(-5., [4000., 4500.]) array([ 9.39081327e-09, 7.86972003e-09]) Synthetic photometry ==================== To integrate the spectrum through a bandpass, use the bandflux method: >>> model.bandflux('sdssi', -5.) 180213.72886169454 Here we are using the SDSS I band, at time -5. days. The return value is in photons / s / cm^2. It is also possible to supply multiple times or bands: >>> model.bandflux('sdssi', [-5., 2.]) array([ 180213.72886169, 176662.68287381]) >>> model.bandflux(['sdssi', 'sdssz'], [-5., -5.]) array([ 180213.72886169, 27697.76705621]) Instead of returning flux in photons / s / cm^2, the flux can be normalized to a desired zeropoint by specifying the ``zp`` and ``zpsys`` keywords, which can also be scalars, lists, or arrays. >>> model.bandflux(['sdssi', 'sdssz'], [-5., -5.], zp=25., zpsys='ab') array([ 5.01036850e+09, 4.74414435e+09]) Instead of flux, magnitude can be returned. It works very similarly to flux: >>> model.bandmag('sdssi', 'ab', [0., 1.]) array([ 22.6255077 , 22.62566363]) >>> model.bandmag('sdssi', 'vega', [0., 1.]) array([ 22.26843273, 22.26858865]) We have been specifying the bandpasses as strings (``'sdssi'`` and ``'sdssz'``). This works because these bandpasses are in the sncosmo "registry". However, this is merely a convenience. In place of strings, we could have specified the actual `~sncosmo.Bandpass` objects to which the strings correspond. See :doc:`bandpasses` for more on how to directly create `~sncosmo.Bandpass` objects. The magnitude systems work similarly to bandpasses: ``'ab'`` and ``'vega'`` refer to built-in `~sncosmo.MagSystem` objects, but you can also directly supply custom `~sncosmo.MagSystem` objects. See :doc:`magsystems` for details. Initializing Sources directly ============================= You can initialize a source directly from your own template rather than using the built-in source templates. Initializing a ``TimeSeriesSource`` ----------------------------------- These sources are created directly from numpy arrays. Below, we build a very simple model, of a source with a flat spectrum at all times, rising from phase -50 to 0, then declining from phase 0 to +50. >>> import numpy as np >>> phase = np.linspace(-50., 50., 11) >>> disp = np.linspace(3000., 8000., 6) >>> flux = np.repeat(np.array([[0.], [1.], [2.], [3.], [4.], [5.], ... [4.], [3.], [2.], [1.], [0.]]), ... 6, axis=1) >>> source = sncosmo.TimeSeriesSource(phase, disp, flux) Typically, you would then include this source in a ``Model``: >>> model = sncosmo.Model(source) Initializing a ``SALT2Source`` ------------------------------ The SALT2 model is initialized directly from data files representing the model. You can initialize it by giving it a path to a directory containing the files. >>> source = sncosmo.SALT2Source(modeldir='/path/to/dir') By default, the initializer looks for files with names like ``'salt2_template_0.dat'``, but this behavior can be altered with keyword parameters: >>> source = sncosmo.SALT2Source(modeldir='/path/to/dir', ... m0file='mytemplate0file.dat') See `~sncosmo.SALT2Source` for more details. sncosmo-2.12.1/docs/photdata.rst000066400000000000000000000153361476435666400166010ustar00rootroot00000000000000**************** Photometric Data **************** Photometric data stored in AstroPy Table ======================================== In sncosmo, photometric data for a supernova is stored in an astropy `~astropy.table.Table`: each row in the table is a photometric observation. The table must contain certain columns. To see what such a table looks like, you can load an example with the following function:: >>> data = sncosmo.load_example_data() >>> print data time band flux fluxerr zp zpsys ------------- ----- ----------------- -------------- ---- ----- 55070.0 sdssg 0.813499900062 0.651728140824 25.0 ab 55072.0512821 sdssr -0.0852238865812 0.651728140824 25.0 ab 55074.1025641 sdssi -0.00681659003089 0.651728140824 25.0 ab 55076.1538462 sdssz 2.23929135407 0.651728140824 25.0 ab 55078.2051282 sdssg -0.0308977349373 0.651728140824 25.0 ab 55080.2564103 sdssr 2.35450321853 0.651728140824 25.0 ab ... etc ... This example data table above has the minimum six columns necessary for sncosmo's light curve fitting and plotting functions to interpret the data. (There's no harm in having more columns for other supplementary data.) Additionally, metadata about the photometric data can be stored with the table: ``data.meta`` is an `OrderedDict` of the metadata. Including Covariance ==================== If your table contains a column ``'fluxcov'`` (or any similar name; see below) it will be interpreted as covariance between the data points and will be used *instead* of the ``'fluxerr'`` column when calculating a :math:`\chi^2` value in fitting functions. For each row, the ``'fluxcov'`` column should be a length *N* array, where *N* is the number of rows in the table. In other, words, ``table['fluxcov']`` should have shape ``(N, N)``, where other columns like ``table['time']`` have shape ``(N,)``. As an example, let's add a ``'fluxcov'`` column to the example data table above. :: >>> data['fluxcov'] = np.diag(data['fluxerr']**2) >>> len(data) 40 >>> data['fluxcov'].shape (40, 40) # diagonal elements are error squared: >>> data['fluxcov'][0, 0] 0.45271884317377648 >>> data['fluxerr'][0] 0.67284384754100002 # off diagonal elements are zero: >>> data['fluxcov'][0, 1] 0.0 As is, this would be completely equivalent to just having the ``'fluxerr'`` column. But now we have the flexibility to represent non-zero off-diagonal covariance. .. note:: When sub-selecting data from a table with covariance, be sure to use `sncosmo.select_data`. For example, rather than ``table[mask]``, use ``sncosmo.select_data(table, mask)``. This ensures that the covariance column is sliced appropriately! See the documentation for `~sncosmo.select_data` for details. Flexible column names ===================== What if you'd rather call the time column ``'date'``, or perhaps ``'mjd'``? Good news! SNCosmo is flexible about the column names. For each column, it accepts a variety of alias names: .. automodule:: photdata_aliases_table Note that each column must be present in some form or another, with no repeats. For example, you can have either a ``'flux'`` column or a ``'f'`` column, but not both. The units of the flux and flux uncertainty are effectively given by the zeropoint system, with the zeropoint itself serving as a scaling factor: For example, if the zeropoint is ``25.0`` and the zeropoint system is ``'vega'``, a flux of 1.0 corresponds to ``10**(-25/2.5)`` times the integrated flux of Vega in the given bandpass. Reading and Writing photometric data from files =============================================== SNCosmo strives to be agnostic with respect to file format. In practice there are a plethora of different file formats, both standard and non-standard, used to represent tables. Rather than picking a single supported file format, or worse, creating yet another new "standard", we choose to leave the file format mostly up to the user: A user can use any file format as long as they can read their data into an astropy `~astropy.table.Table`. That said, SNCosmo does include a couple convenience functions for reading and writing tables of photometric data: `sncosmo.read_lc` and `sncosmo.write_lc`:: >>> data = sncosmo.load_example_data() >>> sncosmo.write_lc(data, 'test.txt') This creates an output file `test.txt` that looks like:: @x1 0.5 @c 0.2 @z 0.5 @x0 1.20482820761e-05 @t0 55100.0 time band flux fluxerr zp zpsys 55070.0 sdssg 0.36351153597 0.672843847541 25.0 ab 55072.0512821 sdssr -0.200801295864 0.672843847541 25.0 ab 55074.1025641 sdssi 0.307494232981 0.672843847541 25.0 ab 55076.1538462 sdssz 1.08776103656 0.672843847541 25.0 ab 55078.2051282 sdssg -0.43667895645 0.672843847541 25.0 ab 55080.2564103 sdssr 1.09780966779 0.672843847541 25.0 ab ... etc ... Read the file back in:: >>> data2 = sncosmo.read_lc('test.txt') There are a few other available formats, which can be specified using the ``format`` keyword:: >>> data = sncosmo.read_lc('test.json', format='json') The supported formats are listed below. If your preferred format is not included, use a standard reader/writer from astropy or the Python universe. +-------------+-----------------------------+-------------------------------+ | Format name | Description | Notes | +=============+=============================+===============================+ | ascii | ASCII with metadata | Not readable by | | (default) | lines marked by '@' | standard ASCII table parsers | | | | due to metadata lines. | +-------------+-----------------------------+-------------------------------+ | json | JavaScript Object Notation | Good performance, but not as | | | | human-readable as ascii | +-------------+-----------------------------+-------------------------------+ | salt2 | SALT2 new-style data files | | +-------------+-----------------------------+-------------------------------+ | salt2-old | SALT2 old-style data files | | +-------------+-----------------------------+-------------------------------+ Manipulating data tables ======================== Because photometric data tables are astropy Tables, they can be manipulated any way that Tables can. Here's a few things you might want to do. Rename a column:: >>> data.rename_column('oldname', 'newname') Add a column:: >>> data['zp'] = 26. Add a constant value to all the entries in a given column:: >>> data['zp'] += 0.03 See the documentation on astropy tables for more information. sncosmo-2.12.1/docs/pyplots/000077500000000000000000000000001476435666400157455ustar00rootroot00000000000000sncosmo-2.12.1/docs/pyplots/plotlc_example.png000066400000000000000000002114141476435666400214660ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATxy|LdH,El!{Q[jIQkF (K*]Imi˭"ԒZ~$v[ȜSl33<y\sMO3hEQB!BQ;B!D!Ba2R!B!LF !B!H"B!0)@B!&#B!dB!B B!BD!Ba2R!B!Lj _~^zMٲeqwwM6^}lB! IXXݺu"""rmjYh]tjժ8;;ӠANJFFJɅB>bMcMu=t-r/zT^=zѣ8޴mۖH*VqB _K:Y_\qZ ɚǚzeׯӵkW^ʮ]YfY۽~zZǚs[]-[駟TLJyغ~&IaY_N.]8<;wOHB!G֖:ukDK֭L#~|v !xT|;wƍI!c ȸqpuue˖TTTbcc 1cưb sjuڕN:ѰaC\\\8y$ ,Ύ9syYB!۷u ]^z>} /&+++W3Z///j׮mB!rM6DEE_s \\\hҤ V^Nբjs#իx"wˋ.]NݺuոU`Zǚz+3F!66X4 IIIhZ9F7ȳȑ#ꫯLۤњǚzZ,τn,ǎy=z:.)~~X$BXzO+-NrO> B!B!B!dB!B B!BD!Ba2R!B!Lj _~^zMٲeqwwM6^Z9r$8;;ӦMveB!B.V3aFF5j`С/&s>e \#Kx=|X]( >$%%?m۶1yB9u5ʳdB7EQԎ _|߿?@$~Wy!|WooߞTSC*}8KҜq N3$VC.X1Zp -ߢ(9S޽ksY YYYdeϷ&LJ*opB&LP>鸹YhYZZZҧO\] RRR7n5j///ڵkΝ; 퍓͛7'...qZ-sggg*VHƍYdzd޼y9kٲ%۷o'00 0|8Ǐg޼ypu=Jzz:̞=csƍGvv69΂ <<:Μ9CFFxw_f\p^zcGHH!!!;v͛܆Pз>dӦM95j (( 6i1 ,.єS9\ 8<} ÓaxLM9IUN5(sk4^Ù2e h=eff*)͛7WFo5Ji֬bgg8;;+aaaJff7 &(JrrrTRE!!!TX1nܸALL 1WҥK)T˖-OIJJ3m9qD'OϨ3;wyp߿СCsӺuks-kժ3gdĉӇ֭[S|ypA4hPc׬YӀW]44 *Zߓ"Q|QxD&RfP^TgiCqԧ.FH,̝9qss3ieӷRRZޕMvv$EA >\SRSS O>Q4?,{ҰaCuc)' RSSPf͚FQʕ+ʵkה+W*FٿZj۲*|ҬY3FW:w9rDs)R% zFQ<̛7Oh49?888tM sFor~W*س_)~(%+,tdJ<}8|0vvv9ÐaȑyDEEgц&PUFF:u^LJrqamFpp0*T`̝;^z/Aʕs}YsO,^5kOy.е~7?>+V_~\tٳgSJ9xX~= , … [oC)Tͳ*"DE#649ó~OH)`nG7xm5u\}@RSS+&kĈJvvۊKicg.]Qʾb_IQl=e+TEI:v2ai߿L0Aiܸꪔ-[VU"""wlԨQC)SҤIeJ```|Ҷm[S)SKʅ u.EQ{O^zl٢4i$-b -[(W_}h4eРAJllܹsGQHXʕS:v)ׯWjժh2DMqU){n}@d)>qrL/zaPKiE-88mo0l0ZhA׮]qvv&33?|||0ZdQ# %A6IqۯI0$|}}5ko Zj4h498j4Q)Spܹ3 .VZyi miVp$ G}K*C[BFœøgEޞӧ3sLYhkצCRb۲>>|B<[ǸK Đ!Z*cO(t&R)BbҤIdgg<Vׯ_ˋ%Ky6OnZeeй~mO; mwBQ9\[ޠG0<)Psܧ3&"П ])lmm)[lC=;wb 8˗X"-Z`ƌEv‰.ʵk2Fd񷏾?`׺rqe8~\ )"s(viE:=[4n 0~gJ!JfeeqFUO|OP^(M>#%%I&ѰaCRRRXhl۶"Ms-sss3Vd$Q[ N`4XBn&T6?0&/쉡.]80Lr^!~.@/:uę3grb ZlIZaYl.ҥK<ѣue޼yz ~~~f=d5>lmav Fm gSa0hpB!Ϩc]'\y]p%.jGBM;EXXիWg۶m9s8ڵ+vۛ 2d~ͤҥKzCܥ|%D%|h]wf&Bs:a7,ф 21B]$%%G׮]{DEE{nNʞ={dddp16l{wwwIHH0rmLGXvv!zK~ʗ/ω'$ oĥǙ`f: -tOAm׍%x>/<#!BʳiXBMƒȳT Q]qKn߾Mddd:VX{O?dEHxx8k֬aҥ4mڴmwNs޷k׎^zό3ذaCRB\BBB )X+uicj4L~BXbbbr-qJi ȑ#̙3ÇA5xalSA4)jT+< EnN9sAxx8%:Vbb"{4K+tC3|a ƽ7As2nBʺ! $8Yok]]]A.n+jG)>1OB9O\-x1W-b̙у={r\3f +V 11իеkW:uDÆ qqqɓ,X;;;̙ckftTE/3yޝ+q#r &CNfBިQXt)&LÃݻw3qDb KNtQm1$1$hdt!LȐ!Co6mڄFa֭lݺ5:F31VEߟիWsE޽]t!<O;~1r-8[Duٓ~yʍ7QǏgԩɐxO, hy :|DMB釛ڑzr'FtAioB8p0 ]`>ns҃`kNR, kӾ}{Ԏa58)jRystƕrf^k&0du_=ү'4Z`dkVy3yXҸK/Ia9E7%ARjрESSHBX5sNFAzpvvZj׏cǎ.C IDATrr2#Ggggڴiî]tnкeƬ>CfHNuOFdvpɎ!QUpV<ɛ2DPQ2Վ#U+GG|mj})))L4 ¢E`۶mܹ37odɒ%xyytRz;3^oVUސY tEBӀ Tf% wT`g&8BwuoS[t)^^^уu2o޼B /8@V qƄqAfftp̚v]05]!EKWy@yi4Q {lZ!P*:U$WHu*# aJ|76*N5x쌯/.fÆ ԯ_?eذa:tW?V;CLLLe7nP)0gGM <繯vƛ!AwB-+@G}ܹ3sS g͚5,]M</YfF;%ۺ9͏ȪX<]GDBV~_;v͛HR! E&Zp(N4)LQ:kȬY8vu͍~q]CF/{̛7W^yIOOϳ2wwwg, 6o&PIWK7/BB{ ē4ϰ6cZ=p VP)RDDDwkN8gɓ'3h@?Aj'ɟFS'鞂H_!(9}Qz#n,6UN#0Xbkk GATPs`jΜ9DDDNxxߟ_C,{!V" ʕ+#U)2U~*`j'BҾ}ٳ'nnn-[z1w\cYH;:R^(zAYd#C( aw^uF)SLuކ:M-Z̙3ѣ={3wG@@cƌaŊ$&&RzuFͲe8p xzz￳cgT1 f}H2et͕n^ק7^] !Jj͚5 <+W!éB,])*?U)C"Y<@c5KI!J`ӧ Ԯ]7nЦMWmڴ F֭[ٺukulZ-ZA888sNx׸sM6%..=z|p.̞ C횺"ĜÜE!Jˌ7dztҜ;vT1e:w󫁥#;G+S@_*R3&3X`ucڵk8::/Tk(,"::Jmwv9:»wtOA|V;I-_;w0eXXW ÃSeq B:;;3tPcFF5|p*{Y203 ƾA:Xay._۷4h.[СC̜9SNwIe09_G*Ho*2wp{#R̒'5L2TcYƒ XE*qXƷB==z4\vk׮q-\\\[.iiVHM}A$|]w `M4.p)Fdt%JL #gE Ig$ЈiP"0~f͚ř3g> ,ѣ,\Pf.:l nNR|664{@׌LaYuoٲ%͛7ЪU+g41RZvA-\^i9C¸b:!, ___֮](Yu?NSL֭XJNN6rr(nͯlhJ#ebAb֩PF]tw̞={;vɴixh۶Ē<)ͯU2,Hfj¢aHHiii\vЇ/Tjj*_|M4,_؏ܣ_~ennni߀-h`~8t=tډPx: i,;"""ω੧7d̙jG3{ Wz{/%$rF8cv$!,B 3gΰuV<<<lٲnNDD{eܺu_ \5kruX|y'#}SNYnoM_y@f-܎pTi4Q$$92 $ jGxb>3v!̖M?NFPmۖm }˖-ӠAՐ޽{s5ym >OC9yK4zBj'N1 f}C$('nuԎb5|q"jEFki%Dz ;vd֬Yٳ{ ?H`` ;v}DEEVqmn}NHH`ڵ+3+HLLztڕN:ѰaC\\\8y$ ,Ύ9sr-@u@>]P;L akpd')v"@ M$!2h@EXz3?F8DRAdȐ!tؑ;ٷo{c266+py@t*66X4 IIIԨQVV5燿?Wŋܽ{///tBxx8uUrW2K>W;CRhYk4Y@ Eo'DiC*1>|UPXI }q|%ECC4uh sQF1zq^~\mmmiѢ-Z 44gϲg2))mʵ>0V$Vp[Zw'b͛HQ˗3n8s[Y%=jiYj{eٲeR|#\T;ի#j,]Ю];lْgΝ;p@^W|/A}aN]`3 ^y[Ha>/BCCjժj1K+IiiQJר̳T`rQ]ߟ3fп/^k?ׯӧO*˰ {*d.] >uQp[a6&NH ?~QL.ĒimYO:@C&biy*'Jbu~뭷x:t(gϞeٲe믿/̰a ;[:[Yu?_yVjrMGprدy'XL4Bkײi&kP*T=sHH!!!gtW&8RX|u224yxa Н_<%CLLLe7nXF3? G3Ӈ={svZʗ -WL/_ΦM̞=_~Ǐ̙39s^'''͛s7fܹtT:8Q+׿GΙ t]/Dɫ믿NJr?y-^f͚41TŁk<(tЎrD#ݨ[Ta* f]Ɏ߷'ؒB5֑v!LJ@4 < 5ZI_Jc=]SV1 f}CPo\uQ `C7bH%Mxw҇+$T+( lƇ$Г_MKa7]_'NY~IL,GGݰh.BzC`pX>+-"c^]}RU@yp;B ~CXa+iGf|pпGFyfe7nDL:t(gÇYjT\Yt~迏o_O(_x٬Ms\+B['O,{^ ^x^$Bd΋xW!>yu ny;|{hbYQz?0` jժ >vk5msݓ&6n)PǟcXw$y 4OudtaUdٲe޽;ݻw7ta~>H$y]sѱ~\\N%VH0yO0?Bau7AGUJZ UA(|-[]?&j'.t=:Vi aV@(qhxW#t{@~lǨ& q=_iH7,&YiES\ w½{j'rHa8-6wQCdҖq 0.)@D1k@X~8WVߡRj(뤛wFعs'#F^z8;;SZ5DZcԎfr+Hś2GN77WB $[;V (I" [v 7Bu0m+jvai>3.\I7vNu)CG&x02" xvX$֭~=سICo]Zj2-]//\zAݺu7oAAA*%3A6/JgaO*pT*~سL$QnGX'*@Z-66?OK.9<4mڴDTff&'waP n\a|'ps-hS"**xdذaLNNNjjk60sHqް^ӣ!l|2joeΜ9L6hMRdMo!đsqUOуTqfq(yV)/=o J@<==YnI;} 7h˫^ѭ[7x}/bHč 7zBm=h R^EQXn֭3yLQ9 z>耇,J'nH+r;)tcN X)WHǕ좔JT{kv2g3k Ҡdr!K/DTCNgHbAA`!4úx#ʂ>gBl޼!X*p+2\ܔL #$/SL EboH0R2;BWkhڒjX$&&RVXaR_B[\o / 3Iնh}o6MQ d }@w!y܄h>$xЧEc A&)-v?k!7gS B~7pK…Ь$~%@"w_.C ٜFNmm܏'2H4Pdb֭[^4`FZZZ,^ŋ}MM;{06OkG#=;pV* 4l3\ XiXY=:p۾]cqʊ X%?h/P <<9Jz z[θp -ևݸs7Е\Kα,38 n#\V%A k,|2>(; 8{ >ڑ,2^ݩ[X| oXWhGbE(>d~%<Yvm3^±7<<7**(#|2)bxjOJ_iY]믿o-Ԣ-~oaI؎Np!1l7R{=LӡGwx1xza?TKlNDAEk&Q.Rqoj/+qznO NQi}xZƱc,nd0N u7X Zh?_xyva U s`.xw0<= V,B˫Ðŀ((tŕ!Urr""\eH9`v\ѣ҃VP:ed¶Y49u>wy%VX@M̔B!)xsuѺ5<m>.ZiC`CH`m툅[9QzCGp#7gJPfRZr \.HG ]q|pg:L^8'd~f`UTTp)׿eEy(*s "5l HxjbnCB2daE֭-y*IgēHX;!=N<'0N _O [[ p$E1POisu GWE T*Sq5Q)GO ۥn/uNu'b*Ϸ~]BaL\ =iŻ[޶@Ss5SQQ#<裏ر#s%::<~fEoڿl„õ)~ʊNÞسϰ-'\(v\6Хa:xAٽfXd6OpV\vµٹTՐ\Um1*asr489]+ڶvZt/ #G4i#G6qŸS4pz8#P !^8;;ӺuklAcN LPPPSV/9񔔔͡pIO}ڶMW>s4 ^ <д Er2-;ƾ'i mk_\\'=\: ϕ\a68qcDҦ͐#5NN3);{:' Z7YPOZ`tV0COGduם.g9sp}{7 XXBWX{>i݌W_MZZ}m؜Qx5!h#p-Nlj.)׉ !ꠦ7wM~Q9ۙ3g'N`=z1'///~=OZ`a7 8e؎5_G aܨlY mJaE|Һ>}&bpDa:]m@`^mBƨsѣYjӟjs e޽fpCoGg*/7Z h>ZW#88Ӝ*44qy)**M6 8wy޽{7IB!,O>7|޽{STTĜ9sg 4Ȫс8:X5!6$ W(..񾾾L:pn6̙3 g֭DF!;w.;v4)&003fX=Ba H~~>Ӿ{O>~ͨ(|A Fhh(x\BBm۶5)w!hn:쒃gZ)˺1pww'88GZ!"!7$((Ei_j())1>^_ݺu`f+l%7BS_|YNTV}TyL>ݤ|הY'&t9v͐!Cn8a5 {|?ތ꣥꣥Te6ߖ1n8S2)t(mѪYyVV &lijjP[i˞{9Eݽ{cҖ&lZl-WTU>[CtRFa,f/(R,,,O>DEE+>}E R^^ÇC$mqxw;w.&L0{\r!b[^$ `Qat,Zȸ acǒIaa!]v 22ӻwo<<<(((`\x[ҫW/kUK!Z'Nv:CvLRRRHIIaƌL<)BB69V\ɔ)SHNN`^^7Y#44l~Wر#C !))ٵB4-tlu򑒒"ɇBM!L6$RRRBhf%Ʈ~{)^|EqwwgӦMMBKz뭷HNNՕYfѦMCJJJ3 !h~1bEEE1d]vrۊ Ο?̙3ر#se͚57cMB4TXX{1EQBi&9uT/^$00Z7oضm L!\DDǎȑ#E!Z<\՘osrr 2&5;wrq+xyy䤩yWni5۷o 0+ `<&s !环+UU̙3,_G'mBK-f^E?~Y֭[UEQԥK=&&lZl}ܺzWTEQTEQT'''5---M6ٴz[$,Yܹs ~deel緆ҬEh.`,ˁ/g`Dkئ0j(cfL¸q8uV⭷ޢ~d?-Oq CZ]p_Y9 UV}T{i52c &Lp())1+.2{UVӷoFFlڶm+uQO"X' ]ė,`=ƶUmk׮thT||<ԖO3_TN81.a5>_Z h>ZK5[o53ZCV e޽fX4F!!m!m|#Ds{Lć+Ita'BTJJJ III$%%<Ν;edee4% ~p_b8::ҽ{wkҤpa|!lf.3gӦM#::Cرc̤=Ozz:̜9ooo͛ǡCk/JoH>qMmܸqiӆ{wN:qi/_βeHLLZ!K3 իQ\rss劢( UUUzz=u//oϚ<8qDhM\\C-,W/Y?񐿍hJm۶%33TUU( ^^^2ek~R}TV}T{~g¸ѣ-b$''، (t֐}-`{nǷ~KBG+mڈ#(**bĈݛ"̙î]X~= 2_+B6M3= 9sb-ZT ?B͝;׬:::@f̘a!h~~vhCH!Z.uww'88GZ!"!7lP?8NNNxyy3ϰ~k$Ν;ݻݻCB.j ___NJxx8v{e̙uVBCCB8q"eee-bBI@"**xdذaLNNN%$$жm[8QAat::ΤٳVi%%%dΝKXXXI[.7ܖKrݺu0[Sziii2h]aWjǺzj IDAT-IIIweƌL0J[.7ܖ:P!!6ydk#:Daa!_5EzcBRRIIIG! 4w Vnn./^… ߿+V0l0;v,ҵkW"##|z=zd͏P_)++cǎ 2$U!lZ|C۴Zz5n:֭[g(TUUY)2!O:'H8 2k#4D3`H= 99٘lTUUŋML<{^d!KIx:y~m/5m޼s?!CӦMCzHG*H(霰vHBC4>} rbbbGa|GZN:͖-[2fW}̏ Ƙ$o7J9! 8I*8I%irDBK4s V@@gΜE~ٶm`u]$&&t^{#Kmi@a/$t‰|-rY \uCPP1pttdԨQܹǵ%3B!D|H D$ rYG>}CCCLZZ!{ 㔄&!,S!uj%%%oެCjR_C$I) !6%KW[.a4EQԵih۶IY\\qqqVH&㔚NCә={Ji˅hz=)22-oP\}a///JJJʫ˪qIZZ}m؄hi^Sj5c{ngOr!D|${nL>aXWhh({5+/(( $$CŪHoH>C!pAvi,$++p||$S98%!BR~ᆼLrE.\fZbÆ ͍cǒIaa!]v >>tbcc9s&̛7C, !BXGy>a9b|˗|rE&]\\ظq#k\t0rssy衇%v!SBhPi&KaL:˗SRRBPP'Ofȑ}/^ŋM >>ޤl۶m 2'Nбcf_اE2^A[QZZԩS|25BXW4Çk.f͚EϞ=&..^_322 2)i}!UYqp aM=!hz J@lڵkC0`GaҤI9 iEȚBKtNȺ Bjt2f̘ZPUEQؗ<==5geǎDDD9결ד5+8S TH"hF' 7o6I@TURڴiclSٷofnPnuy)**M6 8wy޽{7YɚB؎ƜP\lL~$FVy-4ٜ9:0G2e ?\cM0a˖-kKIqq1fc8k=חSm޽{9s&lݺxIBBYUʔB&$/:NgRvY+Ec99J0-G-;Q`0bǙqk!5 Ҵlub&2x`&NHBBΚU-**(|aÆJrr2999&Fv/IvM~e%&8Qwt#hFqؽQR@uפ{ɓ%jvx}t֍x۷[$>!M1`{fLY; q L'OEVt:-[СC|[ҧO8^7)/(0t4yk`/¶4y@\!.J]d+**Knn.&MjKILL .dŊ1X_\%vY< I<,$ 7ruu% fK@d?ݻذa$iرdffRXXH׮]d ={6NNN6KB!!Ʈnk$::W^y^`K^^7Y#44L~'\(tЁoUBQOsAyLJK!.D1n8yƍǒ%Kѣ9r'??QFGL7xb7{899˒%K/y;v,[li:!hӧO;cǢ( ǏC!lL{@N>MTT}NNNoߞb-Z| e ڵkC1rH #G4i#G4[ڧ~ٶmr]w(3a !̙3B7|~l.]ĉ'(++#++C`8o)''OOObccMnjñcرcM 2ȨQعs'ǏCBaOV!mitTpr2t899ϒʿFY#88جz>}X!BO:'wsڡ!ї`Z{no 4+o߾ڔ~e`֎Fm;#-E:'r@it#ǐ!CcРA} 6jt(mM ]5DȰ !,BueYR|\}Sc/v}$$$жi[g24s$5'$I@9AIW"t:Ξ6N@{?Nvv6999\54XW=կuc>f}Fpp+nQpص]:ÿwu݌gpjTAk:5FZZ}6o[RaN 'msJJ}?d5}I{n6N@??E/Y>}&@ jL c=kV^c{B߻7 8Y ,Dj|||vM%\]OKT*%}HH]5*111,\+V W5;av}6"<<7/nC« !'c6E=i'aL>a4^tt4?ӽ{wt:6l ;;ۘ;L 񤧧̙3f޼y:t#4h07Ky? {u@lIuR(i/^LUUIhڵdffRRRByy9۶mcȐ!888pԩ橀BFYhG&00VZѹsg|II>MćC?"$쉦z@ή]5k={$;;8z}R ((ȤfE2B!O>7|޽{STTĜ9sg^!fkגNcȑ 0#G0i$FiJzMBBB}*ڤ/6T2IBǞ:{U[1w\:vhRM`` 3f̐D!lf.ӓX1cp1vQi"m!m|cnĀUSBؒwww9z"Bq#$ #88ج#440̄R?8NNNxyy3a 0&jP4:{U[w9vM޽aݎ|G:'VƱe5~=___Nʧ~J~~>|7$ln_?B[[ '}ǶΞbM82LbP0.#rT"|\}C>}QQQDEE? 6Pɩ񸄄ڶmkRWAbkaX{ZОbGӡLΞ=khjgsARRK,aܹz|SBTEK[^EAW8qkiÇӶm["""fc=Ohh( ,ॗ_c=ƞ={8~Iݻׯ~ Z*T ^炘ڵkgRBJJ 3f`5gVRh`]$c/mM_cNCכ|*$$T)"j\Ha) 9TN>RRRjM>Y4P +VgddG|DDDX*LM*BKMM%%%$fd@dHCDGG9<ݻwGӱaMz1ƎKff&tHL޽𠠠ٳgDjje7aB̙ôiӈfСl߾p+E&fz@V\ѣINNoaҥfz=zd͏P=z4Ѽ 2]vѫW殊U8ɞi.h.ػիW( ֭#""߸=ih.h.B Hii)Đ͉'ӟw}Lj#_x1UUU&O=܃ /؜U*-}TV}T^}ٞ={hݺ5]tUVk׎p>3z=UUUih.h.B ӧYp!W\!&& y6oG}ĪUԩlٲBaAΝߟ{\233 `ѼO!U3gÂo5O?m6`r]whv B3` `R6l0>̂ d!B!^C5!((d,GGGFΝ;Ba?prmB "3 c8pl&gϞe"TV}TN}۲2+GxRUUř3gX|9ׯ磏>q_Zy?VR}TV}TiU)**REQSRR|:~x[K.5{,++KdM64eee5 ((䤦պ&7[om$??i={ЧO&fQQQdeeUbBK)//DEEY;q)S0n8N:ŪUx뭷(//6;Vr!Z[^K@>ooo}-Bh-嵱LJf}PkV^PP@HHH#-%{/f B槹Y"&&sNcYee%YYYcB4͛qtt{E!6P\x .VX息wرdffRXXh좏'==XfΜ7СCYBB!eܸqiӆ{wN:qi/_βeHLLrZ!V`Q`#G{TUUO< ƍo .TEQ=<䓪ںuk5((H}wK.guŋՇ~XVvک RoySimZiUUmfk8zڦMO4{:`577W]rEEEV\ll:p@u޼yW_}XBPM6kT۵k.X@W_~eUQuɒ%7Mndgg>^ZW-ZdrⲇݻWuuuU˗7oVO:99O=q?{I[n[{[v\UӖK?O/bƪ;vT/\`,;r⢾jN3M6o f+Wnnn5.pٳBmAAAA^^zINN:NUEݺu"k>#RӧKK]yUQ&剉z%%I[~-Gr{?{[v\UӖ,X pI9s&;wq~J Mj;wݻwӻwocg߾}`  ؓѭ>#R-[?nԩǏ7Nxa/u3f ތ?ÇsV^͂ 8q"nnnvSKܔ-Grz?i-R;i%i'ҫW/^}Z)..}foUU9sL'N)Sl>ŵV=o`3b/߸x"#F ..72i$233:t(`?uҥ |wtޝ6mO/O],ErSPim\K8h-4 ={ӧ+V`m!QRRK,aܹY:DQ7H}z˙>}:<øMhժ!Cн{wfϞ7۷o/ .\Hrim l3RZjA;myN@?tԉgΝ;KJJ̞EQh׮jbPTr}]f̘ LF}˫oƪ㵗kZ]?#R///~GLʣx'ۯ˟gz=ׯ7ҡCyԩ`u妤->im>ZjACmuاÇmQU0uֵ;;;gܸqgmff[Wg.ꫯ{1)UEQ9sM]Nuf(y.%mGrx?i-R;i%r5??_ꫯ[~~_}~#GT;uThӟQUϨ|l>(nRvEVn7|F>_~(:c ?PUE}eȐ!jǎR VRU>XҖn}-r-㪪\ iFUUՃָL.]̦O>@UE}۷۶m3ٮgyG۫ .T7md7Wo`3b/y'VZ_/R}T777'4cuYfFDD˖-S7nܨ^rEUUKS6#m׶\+j-^|WUUUo!Cj6mÇa:88ؽ`-קT}7T___U;EEfjC}ɓ'N2E|~P-[jΝ֭[AAAIԒCH IDAT.MArۨ^r-㪪\QUU(!B!DK!!B!$ B!Bf# B!H"B!h6!B!$ B!Bf# y[n%%%sΙ=6p@ d,c888mC5q?-[f0aW_}Uk K. ???|M.^h_ii) FXXyz)^|EqwwgӦM5gۛ1cPTTTBiǥv\B$B￯*9rBT1`500PݱcߛwaUQ70۷djΝ\}'UEQԯREQǍ竟|ڶm[5**lH]v zN\^^%KԼ<駟VbWԘ5//OVt领|-!v\qi$6?[;0`zO\s̹zk=qUVVjttI%KTEQ\cٚ5kTEQԥK裏~~~jUUMcJOOWEQon{Voޫ<֭[UEQԏ?#MҎK;.\%lILLo7vWoٲ0ﺯ5kݺuu 8 ]v<3>}u?s"""ӓh$uTUբ*Rھ};'N`̘1& ''X'&3cǎcǎVNNAAAXȨQعs'Ǐ~c׮]=kSDD={4IaҎ Dش_~^{ 04~۷og턅F:==m۶1|-ZϓO>ѣ9s /f֬Y|嗼&Θ1g}/_g}ƅ x衇8pO?关3L:9s&Co>cRLPP}MN&z[֍ST1 !샴㦤v\fڵ+aaa/GoO=-">>ޯ]\\ @k׮G1700lc6%%%5΍,uGqiǯWqq Vc/Bpp0ׯѣGSYYiՕ~89#jr?::?j*z)|||Oҿ~m^xoZUK-$hҎ7-h~r ФyqqqiyYY'O{d[l-z>j([^Ĉ#(..槟~xyyonTRRb|zߚ^}Z'&!I;~kҎ {%= B\C|t֭ڵ~@@@>ܽ{A÷3WӡM.(( $$䦯޽{oXCii)+V07##???YQjٹs, ׊w}deen߾~$&!vfҎJz@ͫx獳{xxx[n;L2Bh׮'NoÃӧ[䵾k{=~OYY,\Gy'x¸[oDDDбcG~Ww^/^lrMmYYk֬ >@~~>Nݝ{ fϞѣyWϡCxyGyGMdd$ǏtNdž 6ycǒIaaqi||<2sL7oٻ︪?{AY"[q+n9Qwf4_{d4Qr&V***](EB" HpϽ{`ǎ]f͚/*C ֭[?ڵkgjRa:.qLԙ~D3qD^PV1!S6moor)""Bj3-_bj#GdZ?*m۶UKKKCիk׮;+;wVʖ-XZZ*VVVJݺu3f(III]|ҤII)VtQ /V} lԭ[WPܔ%111v Õ2e(Jz.vWZmo޼OqrrR_|Qٹsg6Jf+++I߿rg{ ! \:.qN(zj(B!(B!BoB!B B!BD!B7&_$$$0vXڷo ZSf&--sホ666xzz2aUJ.v(j V￯B! .]Jrr2ݻw45&F B ,X0Ȓ%Kh޼9>T#BvڢExbB_&?ܹse˖eښh2j www^}U֯_O޽Y!ĿrҥK8q"V(XB򤜦̹sԎ""Rի?FѥK,cbbضmXYYP! ΃t~~~8;;'_^ѣ3gyGBSb,r)@q]:uFvm۶ѧO='Bµzj6x`ׯ;3}Z.0E~-)ruvڅGUPQՎQ L\ǔL|Ξ=K>}2m&44m۶o>2{`nϞ]M1)) ҹ˵\ '˗ٹs'jq[KKKjԨ"*{{{9eJcJw>6csiRRRhڴiuK.eҥlܸnݺeZgjrS}41s:S:t~-ҥKSn]# !ȣi(x{{ӽ{w.!(HXXܻw) sqqIJJ*U+?B x)_| jJoYB<[(@{=._ f]n֭CEZZF0|,ߟ˗;BrPA-zwKaD6iiizHbMbnJu>t.`zcrNQh_CPy̔ǔXD)7g9 A2xPG)1s;aL1|ބyNgmL)D9N$ol'lAC%7^x_,DŽB!I a 2pĕF㨀něM$`qşѼ`)Y!BQuH5HK30s[pv`vZ!DI"Y%q-(= ı/>VN:˜c-sYDV1A/FAH>A3,)%CZ879[-+8mtIJ"$Ÿsuj/Tzl]+)@Q|xqa*kiK/>x/sݿ<R35|7Lc++JfSv}Ic3yy:B!Rpn$^b;>n]pk'D ΃rB`/sz ajy((,f3N [юРa3ˉs+`!_;80,e2$!VeM'PAyϪ4}/A/!^O7z= !DF#GчLg(sXaS N5>f*ɻ4* 佅B"1hY̭s3K]BUp0h*0R?='g!v 9cKcyHbC? ;VeԩIKKcܹ憍 L0x\E} d hw0k)pe:=P..BDdWZǫKB="w111,]dwfD B ,X0Ȓ%Kh޼9>T#x3(ZKH7qN  w)۩Cs$Gr3ǣaM6h4""" }Ϟ={=) 66;wlٲ,X[[CƲVZΫݻ2\\NNVh v3KhBdH(f X`u[?ctG[[[ѣ|Pb&oJ:Ǜ$v;@! 7I!_/F{A2e 7n,55JwZm#]FrJnݓ[|h9NMXEPa]E|ΝM6n:/_ΟI˖-wl"ec.xp PHy;9)A|nBHwH,UbE֕aC ڮ]YI_s+h8#:D7U+<܌萬d]s-?0.uzDRR y>""3fݔ)SbӦMtЁݻyfn߾͜9s - ~aӋ^gd;h2,$Tƛ#b4Ͻ;P۷4hXZZRT)ZhΝ;ٳgS|yhРaaaY'--iӦQZ5lllpppnݺ,X ] *!W^e4jԈ.]]@@ݺu \ #X׃Ys ztVK ?B}t޽{7Z6ۗ>!!!Y[~܂> Vb8p8iڴ);vo߾t9U)))lڴSDx{{afΜYyD'MJފD+^eXj:džvgfԩL{xm1x1v=Mi\/DCn7hЀdoSVB?s1~Q4hP=IHHPƏ(QB177W4h 0@3f2febnn(cǎU 4۷FL:5s)F/3zhE*=ʴȑ# 9r@3 EYQ %J9\y+J@V>>$c HժUqpp`֬YgQ ..>s[tsY9::( fY}eU"oNk >ƃjVu0^f?cڱD>4nܘ+VHvhРŊ`SKjJbŔv)ӱy'y:c:o(*ʖl`O%(nF䁡]EQ:(ʉ' Xiz޿j۬jׯ_!&@4MOlD_LdF0EM}Z3"x4Ԏ$#**hf̘?@qvvYqF1bG!66oxAyFhh(?3O&44Py'-;FQΞ=OP;|2kZ]HFhL_|Yʲ׀1Z[Q}簢|UAfM:]j'u>P4jժvXif޷oܜ6mڨR9ӵkW~fϞ1Vtt45J g`L_kDQs߮̋tg ;2<O۶my7V:tm۶ѣG=z4ӦMcٓSRtLuڕڵkӠA\\\|2xxxPJ\$E(/æɓH=) Gmu>ATV'\G[W(n>B 3}tzI*U2eaaAod,VL8?8֝;w޽;˴]߾}Y|9Zm #11{ts7?ܹ3VVVL:5cLBR)ᄰ]TRe)%< cf8:ǰBG0\f#p,mCpXl5G#;+++4iªUtɔ/_3vX?gժUԨQŋ'djRڶm[֯_ϲe(]4۷gɘX 3 s V;ªPiD6 }YOfϠQtG4oޜ#uӇ 닍 \pUԩS *Tˀ|)k4pwwtc'7cnnNv3gNɤ|;9pyOL3:`/!eԣIFd#ͳn,]Ѣe&?99+״zޅPq\^ ՠٙpj29b,4ݒpww/(@VeZjK/[oˋ=xb'kF]|h2=y TT;Bp2Y$ϧZtOn䉤Bo`ݿWfv666J(4 4X1?P{хsI!LE7< 7AwSDB77p}i0Yz+@:ٳbŊ1i$;w.6lΝ;RWnӟ)q NfwzBڑB?^!k\SjrN#I[#FlٲnRJ`~m&MXBEYM9ǛjG)pTcHA !L\6F=k IDATsfw gu<-:Dʉ@`^漏"_V?d:u 333? 0x3Ťр f1Ԡ'>ޜwBlׅj'*Hǧtju[;OU9( ))PfϞͺu2_w믿XBT~+:#Y@N xH"I&= 1伾fG}ġCݝ7xѣGce%B*Yd?{Dq}Վ$ɸs{4GGGE!666~gZiQ 56F$y{zsa[-c6p G!!!d~R磷d̘1|G|g8;;g*@@יpҤITR+V'OҢE <==Y`ٳ 9ƍ\{|"FG5> ܩ'Վ$D1OZ*1ʽ%*&,tv嵿mU:7J"IrQ4hR[bcc̙3 $555^{5|||+ڵk3 X"mڴ,YxTR&a9 ȗN;9 X?Z>ҝ!D899p,޽FA߱Lp2T]v+ys ,-s~4>zȰL Gm|ѓat;e-l|*N 4ivVӅ) _TT +++N8eɓ'R\ څ8N:zr<$'?B* j|g 2Yfe4Zd C> ˆ#NũA##CNҊd$=~4S~$DNڵ+?g +::FrBpnlFN?e:BWO>K+z0 :k׮򽾕-[ݻwӭ[7*U|̛7OL$ovR9 &MO{6Vs3Š*HLL޽{>}PШ;wʊSҨQ#tySLTRR GQR?ªÉ KaT/@^|||TgŅ0m4ݻDze˲OFN]_4<\*<&ss)+7na7Y&y^'ƶm2[hgggz-KV'#n!((Q  8$zCEoP/4E npct(RW z z | B<'̈́nN>g 6X/'ohF'pQ;ĚYO<1|L?dt!!jXY8КAxNM-ᭂ(rq)3-Olٲj2zQH?dVX\!DbÝP=du)0(9O5 Б#F/֭c׮]L>QFQfM:vvDo(#v"x3S;Bdu ]?nj'Q_Ց W(3S aySN޽;;;ڵ+VbDFFbn.eW*lc5>S wZȇN,s} Bo)u+ Z ?n*Rj -[$,,L&0;4zNHGaahq k$' ! ߵ!xu=CP+N}N#Q' lcԠ: Վbt\xC*uqQLOE֥otΖEIQkAADȍ P<>{_@F8Fg2_!ٞv #p a>K/666ԨQ>͸=7.vfdn^XP aToeii¸g HŨg g8H |v$! ɓ'iѢ,XgggCPPGaƍjG4^k@S R;xzrb7yP"Fd~/BNCRY(F 3>[ޡ! _ ȄWBdڵ$%%JŊhӦ ׯ_gɒ%cggrJ#(W/e Ow ^ &S V{al$Lg9' FA 'OvvvQ ֐/q>TKgsj &97%H$EFxD[1}AL]ƲG5`\\\2dQQQܻwM6dgV 6X/C=@-O,#e~!3Lt$.'L6M۷N:舵5UVeڴiz;1U{P^ 1!22+WSNi]\jo2 NF`0޽{ȴ|zɰfڴiV",,qئ uaN+HKKG0bbbbeݺuڵӧ3j(j֬IǎՎh\.`&P{ N# ș3gѣTX8^|Eʕ+F}^ʠA7nҒw}Pl2߿/O<)?9a`Qތ%k|Pp<E\˖- S;ZUus pZ +Dd0Mڴi-;vd̙믜|H`` cjՊŋ]h۶mdx8apQ|v"8'FӑQt`>;NҥK߿_/:99iy8vXg0V'JEM6|f*P'L'0kLW(fvӥ-p8><(cC} (7_B [oŋq7n޽{(Qʕ+sΝB?~zr\Q )d->cq`-s#F|vQ:jB3C(\-ԛ%*f(qjRM -p#D Ș1cpvv&00g#fϞ͑#G3gNGlٲ%͛7ФI!_ 1"M|yC( %S;<'Ԏ$0k9SP=w"@7&` 5j(Y={}||ҥ AAA|ر3gеkW7򲳝o)Gk $WN[)BSt3vfo/ N|&}Ʉ(pS̚5G=,YN:xbFIhh^sG<`?Ko4H5CTGNiK"}ѩS'jժzKhnGh(W$E24AK/BFp2T3yo8MRfkƪU(QϟjG3VME OmU3{ + e: ={ Y砷d׮]t)i|mۨQFlْ&M뫯9lg54ƝjG!;!r5 dKW2h g}u*2"):6Z7kP_s W6՘ 2uuj:5xwGYk,Z3g?ҪU+^zL}=SxqRY1g vG%qh@;.p2mqiZ'ʲe˸>ƍS;qRj'ٱw- O LI},^ 4=ztڕyrJyg9DPhkjG!N;ӖDPZY[HXW!gΜk׮>}GGG^yfϞiLVs (QQ$"76PyP7kp 85}j;n=Lp(IoHv0hР\ ax@)TGp1,$ T;իWILLW^L8f͚qA>CN:޽{s7 {{L˲1dݿ v@/N"%^ p  Ù ݺ5Ep-c?Y`$IPi} !$$sR磷$4 vv@%TuW>dQD>ᔩY@T;IZZ>$00cЪU+/N@@vm۶>5)e{DWJd.HEί+E7 !ގPnfOANRIv7I=J TJwz+@Μ9éSU+ssbŊ+( |%hKjG=g"[a8<~~~wcǎXO6Q}h;"Mft~yE786khxx嫛&Z7;uj/Jz`9vuޞMҼys7om'-[ЩS'}EAAai+X0x[}}caq&Mޢ>xklN)$1n" X=Xx1[lnټy3M4Q+a y%ikQSyS3\ZBp [֬3 _u㱧r1޽{YhZM.j(Eq8B>DqՎ%sK.F&M8|0AAAtڕerl:{y-أp{,@ŁP]rw__}=1^\ロg3336lH@@ׯ͛={ŋ쬯X9ZlZVm|B _ ajpapy~W;' %KЩS'/^ȑ# U;JM N! F ,t^?¦u]v7t_W QZ5UFxx9^ѣqssQ5!9⸍/Վ" Ng'#h0R_XB䙥%3f`ƌjG1|RYAfO&Wp3Jy~;S 1c 9ͳ <ooo<+#g~ Ûl-^ᄰ;>&-ZW"GjCvPeT\ N?ud5zkUbENd~A$cJ@0;B}6}|g?ez23x1BUYe(X4mڔT}F#44M67O@@o:MKbav0/Վ"T`9X#|X.q|Iq,Ԏ&)$$LTJ#TJTN"ٓ>}=^=O >vdEoHݺu#22]rBCC)Y$,[M<ǰapuuC9>>ssslll^Nb9mv 1,!?pBڎn=z HUR\]')j SCP, pʽeY۟uLa>ܽ{MrŌuvvvԯQbbb$ IDATus1vZqpp7k&CռHJv2?=\i9y}C!D^i.}D2Y:ᰵ\Z^Ć?KaV>| .dFdd$իWiӦ۷/c'",S ޽;޽iӦ5 pQ;05i2D"14r/kF:# Iery .p|8X尯;B x  !=$\x~ -E/ 3ݺuᆪVZX[[~zƍK,o߾wm Zne+033UVzcH[iF' |>3|8#u7(ET6U!h:FE.d#a[- UGIST ȵkXb6lO0{l-Z f FMQiozIcE1?2-0~n!a.}RY___K(A$+:é)^n}?aVh47nɓ:ujo6[n/v5XH5;~sIԔ| *S1t&7ڱD'f#9A[oK!enug&24[?xpM>_ ) ^͍7{jG) a68Iq,Yq 3|٫ENf[η, `M4S'O#EL*+CJnD"! }]h D-S6\ѭOW7(pUTZӧO}-d^ƍt'pwO0 CcXяX0NDq+,d^)?9JPV Tzʽg>Ӂy1ռ 1dp0 A14ėgii0t? ` H?1Pw{Dʫ IeM~NSpv!=ԛ qpGH6AκѴ`s:H:uؼys߲e u-DYY5(Ke#0 b;kxrf8Iq+ TveT611Q*,JAj'"oJVAS ̭a_Wng?0*z+@^yzE*U "22IPPUVgϞD%O$Bi/?M^c[G~@ɤ9H}WG?T*V[AI\"[@ ܹs3gs~`ccC~=z4' ?D|Վ#L- z2Xgc9Ԏ(L@O( 3gdϞ=lݺU+!)*v!G~nnA38xA1P'hW1˳`fΜ޽{9x 7nt4nܘ-Z`ccX) B<W1xQ,Cf1UL/RD&%F7 BV zNOP]ayd7I#ozF@Qh*SirYt+kecMw'=# #b VXv u\\ ŝS)DzWuJ=.|{w~!@!P}2@z.C8k-Δa!"wDL]o9G3:xr|Xnq:P {`~!%\kXǤB Tv!v$[=aGk!{PdGXKR:GjS2O?pi ^` -YHgEFMxYÖu ~7E4zjyY:j"<슞|u...f0$'9k8y2ҡQ=$'(66kCdVD{8!DRh25\KFOzE%X}5_MQn޼ѣGiݺCr;xho\5L-&KL $DjN/e^c`xCg(,8p󹽓z952,{? 1~f|>…rH% ȩSh߾=׸żkX"&OLff&3f(Mhh(77|p/rMӟg\(dΘwVJ%?.L=vz4`c`;DLL 11'nܸah*g}/tO""";Z:}:/2?<1/o%2ƍYti32/}lwfL\qY *!֏ ?*EBOBBypOAɏQvuZ{Wh ;4,M\\̛77|ZjuVlL5|&MdpNҡQ!vю;SYJ `7,ǑZt]x?їx WO"BDpjMG6Kps۵pq\ۃ#RYYYo5mڴi] |lt(BTԠ-hK7^b ,a*9>tq:чtʀUE8`KG"~& @~  i_坆E; ^0K ֳ%֭[?>'2n8Fͣ>JTTc̙Cdd$ᄇ[$s\p1Y:!FSh;; q|C{;tiA{j"m]"sK@6hhrߓfc |߆'wp8L;9}Ȱi [&5cǎ\z?g}o_|cDz~}ƴh"f͚Epp0O< &5sYMqlp&`1%uN'b9M8ЂvfXש,束9ÿ3.&׏tE ԬYΝ;9"7kZR[e4t8BX~tb0(99q~&s$5\Wq1-hȃxoES<*QY /n%KG"(;{a+%[^"C 00K"MI-~dmnY 8\%٤(#h@C+E=.u3puGm‾ jA\h:r!rj,ޗð>O="ʽBar3#B2, VVV)))ԨQ?gplc)ل=om3hLK#SBaR7ot4BQfٳZj˟g|}}̙3ٲe iii1m4 fx7.mO`GI-'oBŸ.B @֮]k-ox7x衇ذaÇ'//@{[qcc,BR +dA}``鈄\Kڹs'qqqgkܸq1#GХKT^ w˼CM BKiM\1lY!085 цp&6lKTMLL߿,G@SRR@ҹ7ışyALe)^<`|tu)mp :oL!*lwyj׮]`,kJ- wss3>n) nm2M:7H?Gv`1{}-BX./ K۠,$"}fL:}Qz3WPׯoozyDMyà iF1˜=WB|111ĘqㆅN4tmڴ1k>>t٬!0*V FVpYC}pt%p}.GVkQaSly6laÆѧO~wNTTzӯ_?&Nȭ[h޼9111|lذM,BbRZ! < A&ffxxx0fh֬9_D}3f ""4ٴiɌBd͗P>>m W^J! FC/重BrKi6ĆBKiD!n_JxbK"2B!uZ@ ddaB#!CSYK!BQ=D!Ba62B!B @B!f#aSr#\@_)_>¶QO)W>zV}==4nܘgyGZ:4/r}壧\@غޗQO)W>zV+VŋL:]vB`` O!D)H_.Oaҥxzz E̟?޽{[(2!%}BX?XlBBad7oѣ۷cwܸqC7I)W>zO~_iH*_uOO)W>zfr%4rH討=Zh&lj@o[/M6٪f}.g@O>}Jm۶p6nҥKСCǃ'' ,t]Ν;GPPC1\!hJ)e *oΝ;K6$$WWW}DFF2|MV! !(BOTDVdd$G!D9H_.KVǜ9sV%Ir!nTٳޞ Ҷm[.]JBB UBt߿?Y/B맛KիW:t([ի,Zo={[|*++ wwwܹSvvvVUB!py:t@iٲ%qPQ˅6fT]z5$$$йsgrssi׮uԑfBa&\~T{R+}B\U귱?jԨQꫯ|r+xe\!lnf@ S\{%&&ҳgHJJaÆ&]v={B]ѠAKS.җ !;[u=`J~K eŖR)(>^6ЏwwժU :vXC%+UT>ۈ k YK3`-aUVm̙3ٴiJ)/_?̰aÊ=v,__&RhV-rg@[ W _>-Mbɥrg }yqts:>uٳqrrbZ?> Caggg=(6CBfs+\ Q <i0|pbbb=q]X#!!x"M6U.,C_LB"j2k,>LӦM׽iVq!!!\|?YfWա Q.C /\b(Hٹs'qqq,_1c0h ǫJ^^^{GGcǎ8p@9$NPi苩eZ- _ @-Z;À2dJ)~/b7~x5j@rr2n⭷"99(uyЁ!Lf,Ђ[WB$66'''4MO> WjՊK.qA{kСC9{,oG*0|mi}(854|Ae}v4Mc޽۸ռyyy噬/HXXSLa…dggӡCve@n*rdxTF]bb" [l1>~zUh_͛10|jӗѦi9؁URإb,Y^!  IDATE>DO ̀ǓkPJIIA4fϞm|Y\4ibr Dvv6gΜ!**$sb1zS.`|0Z?;&(Njj*}1KL.-/oذ!lݺC3SLɓH|"JT*$zVf6l̙3 Ņ'N`9|z(2\a-sljET_AAA8p DDD[䱡ԯ_d/gy٨y^QsܸqBє @P.zSZ2krww/``qˢiӦt^,^Xwp Q.+{K濎RyQ:udJO (|h JqDV'kj۶-111L2[=zJ^J7%%0vAFFڵ#''T+" !ʩ-ՃnJ%YYKXRHHof̞=ݻw#d\]]ܹsfeeϒ%K㏩S_5-[4cBT3U ,[FãLǧ0vX<<4MC4J)W-۾}{$%%agg͛Hu%0VOU ,k\vUVoѮ];$n֭[ ~peeeѬY3sIzz:GF4̙cnݺ? _/̍78rPԬY9s̛oI߾}yWHLL˖Jz?ታwT5 @|}}~:of߾}[laС 6W?V^͝;wpwwgժUL>4իGiժErBOri_jՊw}_wr1&L`\exygy&$''n:zer"c3JT]^nB?w׮]ӓٳgQ͚5kXfX9uI 0}t._LÆ ^4c˭PݮҺ.n&#Њ?ynVW7312~V4c3ڠ<ƭӖTt7)DzY`~폤$QvFQ }aLUVW:^/ ۽ndX&ǓO>իWWz_uU^nxJ6R)JӦM֭ e>V!D͒ /=ȴ(=.?~|ۧIbb" 4 --Yf1k֬RJBXX;v ##v1w\|3B!W>*=ȴ(=.N_v\ڷoϾ}kSl,qnݺŒ%Kdҥ̕BbkRuzeO˔lrô%== kR;w &駟aY/_xիWĉqvvF4zfΜ9N!Dr @WqTX۶m!//>){Qi!JG+rJiӦ 6mbС&_066777<Ȇ ӓmrQ\]]-NS3 ªUx'HKKTP~ӧOO)!̛7UV^ʋjH~شiI;vSODÆ 4*_ <+ Datszpp0nnn޽QFѱcGVX3gx饗3O<$%%_W_ݻwy-.RBf\x^YĪ) @]{7˔Rh_ї)Ν;IMMeűyfi۶-Zʕ+8;;pB<==yg/Ylcƌ YV5@*ªi..(RǛ @RNz Q*o^h!#GХKB ϩSL/XӧseۜBCYjFa)_ f2~-;~mzP'jԨQ TBL ϟ/t}4i7oKJE &&&ҳg=yN\ Vq4FO=G-yNWHk׮j*"] $ Dl֭c9!:ReӇɓ'jU*R" }< (ml K"Dr5ư+J.\v OO'$${M6tؑƏO`` 7ȅBVބުU+MV/aqkGӺBviE¹׮]so6KJJb֭ 8'''EƏܹstw&--e˖qY,BtrA4hP/aTBve=7.RBc0i$.\.RJenʹshҤ P#aaaL23gޞBgBXF c׮]}R/D8enjңGB:t.J +K !ܹs_5{@ [P&<<@\\\8q , 00ÇBa92_͚5eΜ9frJV^;C=Ć Xd nnn"uGNNGx#* Q1X7(( =$ bcc =Ϊ!D!uʈ4 MLRJktprr25bٲe$''ukJq/"kW* Qq]5mڔnݺPd"u@rrrزe 񤦦N^:txnݺ7_HI /\l:t]޽Y⯈?l}Y%!*K~g_/K.R˗/9RRR cǎdddЮ];Ν ~9_!Uˮ&Żv;wfȑ]/5k0j(}Q]Vq(11VZ믿r];Ƅ **`n877x#c>4MɉlΜ9CTT8k7|Y# %WdVI3Yb/^dԩڵrJK<>++x,YAAA8pZPB!̫S?a222$::gj?RSS]aÆ̜9իW~̙_M`` 'Od-K 0K/)))aѸHff__Xz5 8ggg4M3.D͜9s̟B*|}'|œ9sLAgĈ0k֬2?ܸ(&C蒵޸ \ݻ&M*󸻻WKqrZ!iS)3 UU2ruܹwVXAtt4mڴaӦM :Ԥ7.^;wЬY3VZeq^zҪU*[!<҂w;Xt) DDD8@СC&ƾۻ!|̞=¨̝;+W^cccԩS&,Xӹ|ܸ(f0ex'x׸z*-"00={{_aHH{E)źu#''ѣG쌟3BQ уB?bBa-ZĩS0`8::üy[0!0~x5j@rr2n⭷"99(+|M!D%Y9E!4ݻw{nǔR$''RZGGG˜2e .$;;:k.wn<B (BXrX)ϯҺuk/₦i899Ι3gx!cuRr:SR aÆ̜9@\\\8q , 00Ç/LBk=TV7))' 쯌"<f3P0!L- M=$ bcc =NN& !lL @+==_ f2~-IHH&MB4R^W|ºl#:WXf B|8=Gf޼y%?N||>>駟V~m9۷o)SFZj){`E)wV9t0`ruuUNNNe˖jΜ9&ml!Z 4H5lPծ][_]eddd.k֬Q=zPA޽{ 6h[X>uTպuk3f:{l\eNIDAT+}#}u\/RїDKINNVS>>>nݺ?}[ٳڵk裏T6mz".gUzR˖-SP[nU]tQj߾}&m=~)WWWrJ~ /(Mƍ-ZJXa|6lؠjԨFo߮߯{=.[ĉf͚Cj˖-*>>^͞=[۫~rlҗ[W>җ[[ҏ+\ OgyF;3>To6prttT9C-ҕ+W KOOWުo߾&9;vZGZ(=;b $''+ggg5ybB.ӧOW~d~7n(l#&}#}u<ϖr=J/H9}^zҥK+ +''B z\K޽{kW...>bbbi}0J>gV/Vryוiڵk&”Ȱ\*e H_n}?OޗWJ?}nV2+Wʂ hԨQm~'޽[hĀ~G:ry&Gu}֞Obb"ٙHdWsA9u۷///&N۷eܸqxxx0qDΝ;۷پ};+Wd899L.ErS֐󤇾\O8/H9L|8}*ׯ'l'ƍ~;FͩW bر,^\*妬!˭I}qO_nuiӧTm?N۶mٺu+۷oᆱʮjժe(K￧o߾4oޜ ABBsۥXn/{`Ϳ#e~ӗW_ߨ&MK//7n0Ncݼy{{{qww --siAY_3^̛73i$,OYzf,?]Q+Ώ?HPP`;SO=X.ӧO'//={c=F ?~nL0Aխ[K EVzŽ#9996ϋ/4MSǏ7+MԢEl&~Xӧ'O*MԲel&z:~ԗWJ?} @ݻjm*88X999$caÆ)//B@?a BDDD9]v)Mԇ~h?((H5nXY(){`;b ݻWiϟoV?\)eWyzzt+WT?X)eT֛󤧾\OReR [Q)Μ9ShƍX>Rz-i0`JHHP_~v/kϧMZJ۷fW=;b+35oh>x Pp>7ޠiӦԮ]^zߓEXX>>>2d]Vu?CtB:u[.?~JrTJUj[MJ\ o7dPNbccbcc[.>Iq믿rȑb_+66???:wlWF FW_}˗t| GS.]x衇LbBVqw2V^o`HHHC./_= b\~5kow^^xcϟψ#hӦ [l>tޝӧOWz?888ТE fΜݻw m`j֬3ݻwO>)&&&жm[ooaW.Ž/*$ ҏ~\q@СC4ibR.WWWvjՊUV9sŋs_5kXx]~hٲ%lڴ޽;ÇϏLv… ω7VV-^xOÆ p?yy?~|_;5577r-Z(.*JZZZsV!ҏVq2:u#~~~ 8/^ߟ={ѣ1Y&=z`ž+6gyfŊv]vgsμk߶󏽷mQU{|YbB%~\* 4`۶m4mڴ7&9r3cE_{ D0;s #G4iC^^'OM6žV@@'N(8qmKz!~\qa;mj8{W%  cD+Q"ujq4]EEuiDBD0 .ILҥMBAk_<.ß{3ws9_$Ν;Ǐoyl[=o=z$!i4V(ґ#G~: -333j:|s,LZjff6+d2?jaa9B۷OgO8Bf>}>[O? mw._\#Hw:Qݾ}[rYgΜݻEݹs\ݻwfUK=x@Oֹs皵ׯ_W{߿>(˵NcnݺeHva57df?m<Ϟ={2<{}ԩSammmi.\ׯ_ߴdgϞX[[~Ffh>|к- ڮ]lϞ=H$X,n:l5?>|xSԔEQ|~ZZa|v1{馺t:mmv ɓ677E5o`}ek𗙣x3@8C 3@8C 3@8C 3@8C 3@8C 3@8 #IENDB`sncosmo-2.12.1/docs/pyplots/plotlc_example.py000066400000000000000000000003251476435666400213270ustar00rootroot00000000000000import numpy as np import sncosmo data = sncosmo.readlc('simulated_salt2_data.dat') model = sncosmo.ObsModel(source='salt2') model.set(**data.meta) sncosmo.plotlc(data, fname='plotlc_example.png', model=model) sncosmo-2.12.1/docs/reference.rst000066400000000000000000000037571476435666400167370ustar00rootroot00000000000000 *************** Reference / API *************** .. currentmodule:: sncosmo Model & Components ================== .. autosummary:: :toctree: api Model *Source component of Model* .. autosummary:: :toctree: api Source TimeSeriesSource StretchSource MLCS2k2Source SALT2Source SALT3Source SNEMOSource SUGARSource *Effect components of Model: interstellar dust extinction* .. autosummary:: :toctree: api PropagationEffect CCM89Dust OD94Dust F99Dust Bandpass & Magnitude Systems ============================ .. autosummary:: :toctree: api Bandpass AggregateBandpass BandpassInterpolator MagSystem ABMagSystem SpectralMagSystem CompositeMagSystem I/O === *Functions for reading and writing photometric data, gridded data, extinction maps, and more.* .. autosummary:: :toctree: api read_lc write_lc read_bandpass load_example_data load_example_spectrum_data read_snana_ascii read_snana_fits read_snana_simlib read_griddata_ascii read_griddata_fits write_griddata_ascii write_griddata_fits Spectra ======= .. autosummary:: :toctree: api Spectrum .. _fitting-api: Fitting Photometric Data ======================== *Estimate model parameters from photometric data* .. autosummary:: :toctree: api fit_lc mcmc_lc nest_lc *Convenience functions* .. autosummary:: :toctree: api select_data chisq flatten_result Plotting ======== *Convenience functions for quick standard plots (requires matplotlib)* .. autosummary:: :toctree: api plot_lc Simulation ========== .. autosummary:: :toctree: api zdist realize_lcs Registry ======== *Register and retrieve custom built-in sources, bandpasses, and magnitude systems* .. autosummary:: :toctree: api register register_loader get_source get_bandpass get_magsystem Class Inheritance Diagrams ========================== .. inheritance-diagram:: models magsystems bandpasses :parts: 1 sncosmo-2.12.1/docs/registry.rst000066400000000000000000000067301476435666400166430ustar00rootroot00000000000000******** Registry ******** What is it? ----------- The registry (`sncosmo.registry`) is responsible for translating string identifiers to objects, for user convenience. For example, it is used in `sncosmo.get_bandpass` and `sncosmo.get_source` to return a `~sncosmo.Bandpass` or `sncosmo.Model` object based on the name of the bandpass or model: >>> sncosmo.get_bandpass('sdssi') It is also used in methods like `~sncosmo.Model.bandflux` to give it the ability to accept either a `~sncosmo.Bandpass` object or the name of a bandpass: >>> model = sncosmo.Model(source='hsiao') >>> model.bandflux('sdssg', 0.) # works, thanks to registry. Under the covers, the ``bandflux`` method retrieves the `~sncosmo.Bandpass` corresponding to ``'sdssg'`` by calling the `sncosmo.get_bandpass` function. The registry is actually quite simple: it basically amounts to a dictionary and a few functions for accessing the dictionary. Most of the time, a user doesn't need to know anything about the registry. However, it is useful if you want to add your own "built-ins" or change the name of existing ones. Using the registry to achieve custom "built-ins" ------------------------------------------------ There are a small set of "built-in" models, bandpasses, and magnitude systems. But what if you want additional ones? Create a file ``mydefs.py`` that registers all your custom definitions:: # contents of mydefs.py import numpy as np import sncosmo wave = np.array([4000., 4200., 4400., 4600., 4800., 5000.]) trans = np.array([0., 1., 1., 1., 1., 0.]) band = sncosmo.Bandpass(wave, trans, name='tophatg') sncosmo.registry.register(band) Make sure ``mydefs.py`` is somewhere in your ``$PYTHONPATH`` or the directory you are running your main script from. Now in your script import your definitions at the beginning:: >>> import sncosmo >>> import mydefs >>> # ... proceed as normal >>> # you can now use 'tophatg' as a built-in Changing the name of built-ins ------------------------------ To change the name of the ``'sdssg'`` band to ``'SDSS_G'``:: # contents of mydefs.py import sncosmo band = sncosmo.get_bandpass('sdssg') band.name = 'SDSS_G' sncosmo.register(band) Large built-ins --------------- What if your built-ins are really big or you have a lot of them? You might only want to load them as they are needed, rather than having to load everything into memory when you do ``import mydefs``. You can use the `sncosmo.registry.register_loader` function. Suppose we have a bandpass that requires a huge data file (In reality it is unlikely that loading bandpasses would take a noticeable amount of time, but it might for models or spectra.):: # contents of mydefs.py import sncosmo def load_bandpass(filename, name=None, version=None): # ... # read data from filename, create a Bandpass object, "band" # ... return band filename = 'path/to/datafile/for/huge_tophatg' sncosmo.register_loader( sncosmo.Bandpass, # class of object returned. 'huge_tophatg', # name load_bandpass, # function that does the loading [filename] # arguments to pass to function ) Now when you ``import mydefs`` the registry will know how to load the `~sncosmo.Bandpass` named ``'huge_tophatg'`` when it is needed. When loaded, it will be saved in memory so that subsequent operations don't need to load it again. sncosmo-2.12.1/docs/simulation.rst000066400000000000000000000152321476435666400171540ustar00rootroot00000000000000 Simulation ========== First, define a set of "observations". These are the properties of our observations: the time, bandpass and depth. .. code:: python import sncosmo from astropy.table import Table obs = Table({'time': [56176.19, 56188.254, 56207.172], 'band': ['desg', 'desr', 'desi'], 'gain': [1., 1., 1.], 'skynoise': [191.27, 147.62, 160.40], 'zp': [30., 30., 30.], 'zpsys':['ab', 'ab', 'ab']}) print obs .. parsed-literal:: skynoise zpsys band gain time zp -------- ----- ---- ---- --------- ---- 191.27 ab desg 1.0 56176.19 30.0 147.62 ab desr 1.0 56188.254 30.0 160.4 ab desi 1.0 56207.172 30.0 Suppose we want to simulate a SN with the SALT2 model and the following parameters: .. code:: python model = sncosmo.Model(source='salt2') params = {'z': 0.4, 't0': 56200.0, 'x0':1.e-5, 'x1': 0.1, 'c': -0.1} To get the light curve for this single SN, we'd do: .. code:: python lcs = sncosmo.realize_lcs(obs, model, [params]) print lcs[0] .. parsed-literal:: time band flux fluxerr zp zpsys --------- ---- ------------- ------------- ---- ----- 56176.19 desg 96.0531272705 191.27537908 30.0 ab 56188.254 desr 456.360196623 149.22627064 30.0 ab 56207.172 desi 655.40885611 162.579572369 30.0 ab Note that we've passed the function a one-element list, ``[params]``, and gotten back a one-element list in return. (The ``realize_lcs`` function is designed to operate on lists of SNe for convenience.) Generating SN parameters ------------------------ We see above that it is straightforward to simulate SNe once we already know the parameters of each one. But what if we want to pick SN parameters from some defined distribution? Suppose we want to generate SN parameters for all the SNe we would find in a given search area over a defined period of time. We start by defining an area and time period, as well as a maximum redshift to consider: .. code:: python area = 1. # area in square degrees tmin = 56175. # minimum time tmax = 56225. # maximum time zmax = 0.7 First, we'd like to get the number and redshifts of all SNe that occur over our 1 square degree and 50 day time period: .. code:: python redshifts = list(sncosmo.zdist(0., zmax, time=(tmax-tmin), area=area)) print len(redshifts), "SNe" print "redshifts:", redshifts .. parsed-literal:: 9 SNe redshifts: [0.4199710008856507, 0.3500118339133868, 0.5915676316485601, 0.5857452631151785, 0.49024466410556855, 0.5732679644841575, 0.6224436826380927, 0.5853477892182203, 0.5522300320124105] Generate a list of SN parameters using these redshifts, drawing ``x1`` and ``c`` from normal distributions: .. code:: python from numpy.random import uniform, normal params = [{'x0':1.e-5, 'x1':normal(0., 1.), 'c':normal(0., 0.1), 't0':uniform(tmin, tmax), 'z': z} for z in redshifts] for p in params: print p .. parsed-literal:: {'z': 0.4199710008856507, 'x0': 1e-05, 'x1': -0.9739877070754421, 'c': -0.1465835504611458, 't0': 56191.57686616353} {'z': 0.3500118339133868, 'x0': 1e-05, 'x1': 0.04454878604727126, 'c': -0.04920811869083081, 't0': 56222.76963606611} {'z': 0.5915676316485601, 'x0': 1e-05, 'x1': -0.26765265677262423, 'c': -0.06456008680932701, 't0': 56211.706219411404} {'z': 0.5857452631151785, 'x0': 1e-05, 'x1': 0.8255953341731204, 'c': 0.08520083775049729, 't0': 56209.33583211229} {'z': 0.49024466410556855, 'x0': 1e-05, 'x1': -0.12051827966517584, 'c': -0.09490756669333822, 't0': 56189.37571007927} {'z': 0.5732679644841575, 'x0': 1e-05, 'x1': 0.3051310078192594, 'c': -0.10967604820261241, 't0': 56198.04368422346} {'z': 0.6224436826380927, 'x0': 1e-05, 'x1': -0.6329407028587257, 'c': -0.009789183239376284, 't0': 56179.88133113836} {'z': 0.5853477892182203, 'x0': 1e-05, 'x1': 0.6373371286596669, 'c': 0.05151693090038232, 't0': 56212.04579735962} {'z': 0.5522300320124105, 'x0': 1e-05, 'x1': 0.04762095339856289, 'c': -0.005018877828783951, 't0': 56182.14827040906} So far so good. The only problem is that ``x0`` doesn't vary. We'd like it to be randomly distributed with some scatter around the Hubble line, so it should depend on the redshift. Here's an alternative: .. code:: python params = [] for z in redshifts: mabs = normal(-19.3, 0.3) model.set(z=z) model.set_source_peakabsmag(mabs, 'bessellb', 'ab') x0 = model.get('x0') p = {'z':z, 't0':uniform(tmin, tmax), 'x0':x0, 'x1': normal(0., 1.), 'c': normal(0., 0.1)} params.append(p) for p in params: print p .. parsed-literal:: {'c': -0.060104568346581566, 'x0': 2.9920355958896461e-05, 'z': 0.4199710008856507, 'x1': -0.677121283126299, 't0': 56217.93979718883} {'c': 0.10405991801014292, 'x0': 2.134500759148091e-05, 'z': 0.3500118339133868, 'x1': 1.6034252041294512, 't0': 56218.008314206476} {'c': -0.14777109151711296, 'x0': 7.9108889725043354e-06, 'z': 0.5915676316485601, 'x1': -2.2082282760850993, 't0': 56218.013686428785} {'c': 0.056034777154805086, 'x0': 6.6457371815973038e-06, 'z': 0.5857452631151785, 'x1': 0.675413080007434, 't0': 56189.03517395757} {'c': -0.0709158052635228, 'x0': 1.2228145655148946e-05, 'z': 0.49024466410556855, 'x1': 0.5449847454420981, 't0': 56198.02895700289} {'c': -0.22101146234021096, 'x0': 7.4299221264917702e-06, 'z': 0.5732679644841575, 'x1': -1.543245858395605, 't0': 56189.04585414441} {'c': 0.06964843664572477, 'x0': 9.7121906557832662e-06, 'z': 0.6224436826380927, 'x1': 1.7419604610283943, 't0': 56212.827270197355} {'c': 0.07320513053870191, 'x0': 3.22205341646521e-06, 'z': 0.5853477892182203, 'x1': -0.39658066375434153, 't0': 56200.421464066916} {'c': 0.18555773972769227, 'x0': 7.5955258508017471e-06, 'z': 0.5522300320124105, 'x1': -0.24463691193386283, 't0': 56190.492271332616} Now we can generate the lightcurves for these parameters: .. code:: python lcs = sncosmo.realize_lcs(obs, model, params) print lcs[0] .. parsed-literal:: time band flux fluxerr zp zpsys --------- ---- ------------- ------------ ---- ----- 56176.19 desg 6.70520005464 191.27 30.0 ab 56188.254 desr 106.739113709 147.62 30.0 ab 56207.172 desi 1489.7521011 164.62420476 30.0 ab Note that the "true" parameters are saved in the metadata of each SN: .. code:: python lcs[0].meta .. parsed-literal:: {'c': -0.060104568346581566, 't0': 56217.93979718883, 'x0': 2.9920355958896461e-05, 'x1': -0.677121283126299, 'z': 0.4199710008856507} sncosmo-2.12.1/docs/source-list.rst000066400000000000000000000001561476435666400172400ustar00rootroot00000000000000.. _list-of-built-in-sources: List of Built-in Sources ------------------------ .. automodule:: source_page sncosmo-2.12.1/docs/spectrum.rst000066400000000000000000000111321476435666400166250ustar00rootroot00000000000000******* Spectra ******* Spectroscopic observations are supported in `sncosmo` with the `~sncosmo.Spectrum` class. A spectrum object can be created from a list of wavelengths and flux values: .. code:: python >>> wave, flux, fluxerr = sncosmo.load_example_spectrum_data() >>> spectrum = sncosmo.Spectrum(wave, flux) By default, the wavelengths are assumed to be in angstroms and the flux values as a spectral flux density (erg / s / cm^2 / A). Uncertainties ------------- A spectrum can have associated uncertainties. This can be either uncorrelated uncertainties for each spectral element: .. code:: python >>> spectrum = sncosmo.Spectrum(wave, flux, fluxerr) or a full covariance matrix: .. code:: python >>> fluxcov = np.diag(fluxerr**2) + 1e-5 * np.max(flux)**2 >>> spectrum = sncosmo.Spectrum(wave, flux, fluxcov=fluxcov) All operations will take these uncertanties into account. Synthetic photometry -------------------- Synthetic photometry can be calculated on a spectrum using any of the bandpasses available in sncosmo: .. code:: python >>> spectrum.bandflux('sdssg') 6.417843339246818 Synthetic photometry can be calculated on multiple bands simultaneously: .. code:: python >>> spectrum.bandflux(['sdssg', 'sdssr', 'sdssi']) array([6.41784334, 5.37683496, 2.8626649 ]) If a zeropoint and magnitude system are specified, then the bandflux is returned in that system (otherwise it is in photons / s / cm^2 by default). .. code:: python >>> spectrum.bandflux(['sdssg', 'sdssr', 'sdssi'], zp=25., zpsys='ab') array([117413.72315598, 108956.25581652, 79592.47424074]) Optionally, the full covariance matrix between the bandfluxes can also be calculated: .. code:: python >>> spectrum.bandfluxcov(['sdssg', 'sdssr', 'sdssi'], zp=25., zpsys='ab') (array([117413.72315598, 108956.25581652, 79592.47424074]), array([[1546972.78077853, 874550.34470817, 1287550.14818921], [ 874550.34470817, 2479812.26652077, 2226272.0481113 ], [1287550.14818921, 2226272.0481113 , 3805168.84485814]])) A band magnitude can be evaluated in a specific magnitude system: .. code:: python >>> spectrum.bandmag(['sdssg', 'sdssr', 'sdssi'], magsys='ab') array([12.32570285, 12.40686957, 12.74781999]) Rebinning a spectrum -------------------- A spectrum can be rebinned with arbitrary wavelength bins. This returns a new `~sncosmo.Spectrum` object. .. code:: python >>> binned_spectrum = spectrum.rebin(np.arange(3500, 6000, 100)) Rebinning introduces covariance between adjacent spectral elements if the bin edges in the original spectrum don't line up with the bin edges in the rebinned spectrum. This covariance is properly propagated. Fitting with spectra -------------------- Spectra can be used in fits. Any combination of spectra and photometry is allowed. To fit spectra, the times at which the spectra were taken must be specified. For example, to fit a single spectrum: .. code:: python # Create the spectrum object, and specify the time at which it was taken. >>> spectrum = sncosmo.Spectrum(wave, flux, fluxerr, time=20.) # Fit a model to the spectrum. >>> model = sncosmo.Model(source='hsiao-subsampled') >>> sncosmo.fit_lc(model=model, spectra=spectrum, ... vparam_names=['amplitude', 't0', 'z'], ... bounds={'z': (0., 0.3)}) ( success: True message: 'Minimization exited successfully.' ncall: 108 chisq: 576.7111360163605 ndof: 597 param_names: ['z', 't0', 'amplitude'] parameters: array([9.96571945e-02, 1.80278503e+01, 1.00650322e-05]) vparam_names: ['z', 't0', 'amplitude'] covariance: array([[ 1.17946556e-07, 1.64336679e-05, -5.21279026e-12], [ 1.64336679e-05, 1.70047614e-02, -4.60755668e-09], [-5.21279026e-12, -4.60755668e-09, 2.91915780e-15]]) errors: OrderedDict([('z', 0.00034343314287464677), ('t0', 0.13040215158608248), ('amplitude', 5.4029230945864686e-08)]) nfit: 1 data_mask: None, ) Other valid signatures are: .. code:: python # photometry only >>> sncosmo.fit_lc(photometry, model, ...) # a single spectrum >>> sncosmo.fit_lc(model=model, spectra=spectrum, ...) # multiple spectra >>> sncosmo.fit_lc(model=model, spectra=[spec_1, spec_2], ...) # spectra and photometry simultaneously >>> sncosmo.fit_lc(photometry, model, spectra=[spec_1, spec_2], ...) sncosmo-2.12.1/misc/000077500000000000000000000000001476435666400142365ustar00rootroot00000000000000sncosmo-2.12.1/misc/Makefile000066400000000000000000000011411476435666400156730ustar00rootroot00000000000000INC=. LIB=. # Get INC and LIB from Make.user ifeq (exists, $(shell [ -e ./Make.user ] && echo exists )) include ./Make.user endif test-salt2model : test_salt2model.o g++ -DUSELAPACK -DgFortran -g -Wall -O3 -Wl,-rpath,$(LIB) -L$(LIB) -o test-salt2model test_salt2model.o -lsnfit -llapack -lblas -llapack -lgfortran test_salt2model.o : test_salt2model.cc g++ -DHAVE_CONFIG_H -I$(INC) -g -Wall -O3 -DUSELAPACK -DgFortran -g -Wall -O3 -MT test_salt2model.o -MD -MP -std=c++11 -c -o test_salt2model.o test_salt2model.cc .PHONY: clean clean : rm test-salt2model rm test_salt2model.o rm test_salt2model.d sncosmo-2.12.1/misc/README.md000066400000000000000000000020021476435666400155070ustar00rootroot00000000000000# Miscellaneous Development Scripts ## SALT2 test helper `test_salt2model.cc` is a C++ program that outputs some test data from the snfit implementation of the SALT2 model (for testing against as a reference implementation). It links to a built snfit library. To build (on Linux anyway), create a file `Make.user` containing two variables: `LIB` and `INC`. These should point to the directories containing the built `snfit` library and the snfit header files. Example: ``` INC=/path/to/snfit/install/dir/include LIB=/path/to/snfit/install/dir/lib ``` Then run `make`. Set the `SALTPATH` environment variable to the location of the SALT data files before running the program. In bash: ``` export SALTPATH=/path/to/snfit_data ``` ## `gen_interp_test_data.py` Generates files in `sncosmo/tests/data` that the above `test_salt2model` C++ program reads. ## `gen_example_data.py` This is used, as the name implies, to generate the example photometric data distributed with sncosmo and loaded with `load_example_data.py`. sncosmo-2.12.1/misc/gen_example_data.py000077500000000000000000000015141476435666400200710ustar00rootroot00000000000000#!/usr/bin/env python import numpy as np import sncosmo from collections import OrderedDict as odict from astropy.table import Table model = sncosmo.Model(source='salt2') model.set(z=0.5, c=0.2, t0=55100., x1=0.5) model.set_source_peakabsmag(-19.5, 'bessellb', 'ab') times = np.linspace(55070., 55150., 40) bands = np.array(10 * ['sdssg', 'sdssr', 'sdssi', 'sdssz']) zp = 25. * np.ones(40) zpsys = np.array(40 * ['ab']) flux = model.bandflux(bands, times, zp=zp, zpsys=zpsys) fluxerr = (0.05 * np.max(flux)) * np.ones(40, dtype=float) flux += fluxerr * np.random.randn(40) data = Table(odict([('time', times), ('band', bands), ('flux', flux), ('fluxerr', fluxerr), ('zp', zp), ('zpsys', zpsys)]), meta=dict(zip(model.param_names, model.parameters))) sncosmo.write_lc(data, 'example_photometric_data.dat') sncosmo-2.12.1/misc/gen_example_spectrum.py000077500000000000000000000006621476435666400210250ustar00rootroot00000000000000#!/usr/bin/env python import numpy as np import sncosmo wave = np.arange(3000, 9000, 10) model = sncosmo.Model(source='hsiao-subsampled') model.set(z=0.1, amplitude=1e-5, t0=0.) sample_time = model['t0'] + 2. flux = model.flux(time=sample_time, wave=wave) fluxerr = 0.1 * flux flux = flux + np.random.normal(scale=fluxerr) np.savetxt('example_spectrum.dat', np.array([wave, flux, fluxerr]).T, header="wave flux fluxerr") sncosmo-2.12.1/misc/gen_interp_test_data.py000077500000000000000000000015231476435666400207760ustar00rootroot00000000000000#!/usr/bin/env python import os import numpy as np import sncosmo # generate arrays to input to interpolator x = np.array([-1., 0., 2., 4., 5., 6., 6.5, 7.]) y = np.array([1., 2., 3., 4., 5.]) z = np.sin(x)[:, None] * np.cos(0.25 * y) fname = '../sncosmo/tests/data/interpolation_test_input.dat' if os.path.exists(fname): print(fname, "already exists; skipping.") else: sncosmo.write_griddata_ascii(x, y, z, fname) print("wrote", fname) # generate test x and y arrays xs = np.array([-2., -1., 0.5, 2.4, 3.0, 4.0, 4.5, 6.5, 8.0]) ys = np.array([0., 0.5, 1.0, 1.5, 2.8, 3.5, 4.0, 4.5, 5.0, 6.0]) for arr, name in ((xs, 'x'), (ys, 'y')): fname = '../sncosmo/tests/data/interpolation_test_eval{}.dat'.format(name) if os.path.exists(fname): print(fname, "already exists; skipping.") else: np.savetxt(fname, arr) sncosmo-2.12.1/misc/subsample_hsiao_model.py000077500000000000000000000007401476435666400211520ustar00rootroot00000000000000#!/usr/bin/env python """ Usage: %prog INFILE OUTFILE Subsample the Hsiao spectral time series, for use as a small demonstration model that can be included with source code.""" from optparse import OptionParser from sncosmo.io import read_griddata_fits, write_griddata_fits parser = OptionParser() options, args = parser.parse_args() phase, wave, flux = read_griddata_fits(args[0]) write_griddata_fits(phase[::5], wave[::5], flux[::5, ::5], args[1]) print "wrote to", args[1] sncosmo-2.12.1/misc/test_salt2model.cc000066400000000000000000000212541476435666400176560ustar00rootroot00000000000000/* evalute some things about salt2 model for testing purposes. */ #include #include #include #include "fileutils.h" #include "fullfit.h" #include "measureddata.h" #include "pathutils.h" #include "instrument.h" #include "saltmodel.h" #include "salt2model.h" #include "saltnirmodel.h" #include "snfitexception.h" using namespace std; double phasemin=-15; double phasemax=+45; double salt1_default_wmin = 3460; double salt1_default_wmax = 6600; double salt2_default_wmin = 3000; double salt2_default_wmax = 7000; //double uband_calibration_error = 0.1; bool apply_zp_error=true; //map< string,vector > fixableparameters; #define WITHEXCEPTIONS //#define WITH_SPECTRA #define NBANDS_FOR_SINGLE_FIT 1 #define _GNU_SOURCE 1 #ifndef __USE_GNU #define __USE_GNU #endif #include /* copied from LcForFit::ComputeWeightMatForFit and modified to just compute * model covariance for specified dates and filters */ static double sqr(const double &x) { return x*x;} Mat model_rcov(Salt2Model& model, const vector& times, Filter *filter, bool use_model_errors, bool use_kcorr_errors) { double *w; size_t npoints = times.size(); //initialize result Mat result(npoints, npoints); result.Zero(); // model errors: added on diagonal only if (use_model_errors) { w = result.NonConstData(); for(size_t i=0; i read_1d_vector(const string &fname) { vector result; string line; ifstream in(fname.c_str()); while (getline(in, line)) result.push_back(atof(line.c_str())); return result; } // write model parameters to "header" of file void write_model_params(ofstream& out, const Salt2Model *model) { out << "@Redshift " << model->params.LocateParam("Redshift")->val << endl; out << "@DayMax " << model->params.LocateParam("DayMax")->val << endl; out << "@X0 " << model->params.LocateParam("X0")->val << endl; out << "@X1 " << model->params.LocateParam("X1")->val << endl; out << "@Color " << model->params.LocateParam("Color")->val << endl; } // Evaluate the model at various times and wavelengths and write results to a // file. void evaluate_and_write_timeseries(const Salt2Model *model, const vector& time, const vector& wave, const string& fname) { ofstream out(fname.c_str()); out.setf(ios::showpoint); out.precision(7); // write header out << "# output from snfit Salt2Model.SpectrumFlux with following parameters\n"; write_model_params(out, model); for (auto const& t: time) { for (auto const& w: wave) { out.precision(7); out << t << " " << w << " "; out.precision(20); // extra precision on flux out << model->SpectrumFlux(w, t) << endl; } } out.close(); } void test_bandpass_interpolation() { Instrument *megacampsf = new Instrument("MEGACAMPSF"); double area = megacampsf->MirrorArea(); // evaluate two filters at two different radial positions vector bands = {"g", "z"}; vector x_coords = {2.0, 8.0}; vector y_coords = {3.5, 7.5}; vector> wave = { {3600., 3800., 4000., 4200., 4400., 4600., 4800., 5000., 5200., 5400., 5600., 5800., 6000., 6200.}, {7600., 8000., 8400., 8800., 9200., 9600., 10000., 10400., 10800., 11200.}}; for (auto const& band: bands) { for (size_t i=0; iEffectiveFilterByBand(band, &P); // evaluate filter and write out ofstream out("../sncosmo/tests/data/snfit_filter_" + band + "_" + to_string(i) + ".dat"); out << "@name MEGACAMPSF::" << band << endl; out << "@radius " << r << endl; for (auto const& w: wave[i]) { out.precision(7); out << w << " "; out.precision(12); out << filter.Value(w) / area << endl; } out.close(); } } } int main(int nargs, char **args) { // ---------------------------------------------------------------------- // Grid2DFunction testing Grid2DFunction f("../sncosmo/tests/data/interpolation_test_input.dat"); vector x = read_1d_vector( "../sncosmo/tests/data/interpolation_test_evalx.dat"); vector y = read_1d_vector( "../sncosmo/tests/data/interpolation_test_evaly.dat"); ofstream interp_out("../sncosmo/tests/data/interpolation_test_result.dat"); for (size_t i=0; i < x.size(); i++) { for (size_t j=0; j < y.size(); j++) { interp_out << f.Value(x[i], y[j]); if (j != y.size() - 1) interp_out << " "; } interp_out << endl; } interp_out.close(); // Load model for model tests below. Salt2Model *model=new Salt2Model(); model->SetWavelengthRange(salt2_default_wmin,salt2_default_wmax); //----------------------------------------------------------------------- // test spectral surface // Observer-frame times and wavelengths vector time = {-30., -10., 0., 10., 25., 50., 80.}; vector wave = {3000., 3500., 4000., 4500., 5000., 5500., 6000., 6500., 7000., 7500., 8000., 8500., 9000.}; // parameters in order: Redshift, daymax, x0, x1, color vector> params = {{0.0, 0.0, 1.0, 0.0, 0.0}, {0.15, 0.0, 1.e-5, 0.0, 0.0}, {0.15, 5.0, 1.e-5, 1.0, 0.0}, {0.15, 5.0, 1.e-5, 0.0, 0.1}}; vector fnames = {"salt2_timeseries_1.dat", "salt2_timeseries_2.dat", "salt2_timeseries_3.dat", "salt2_timeseries_4.dat"}; for (size_t i = 0; i < params.size(); i++) { model->params.LocateParam("Redshift")->val = params[i][0]; model->params.LocateParam("DayMax")->val = params[i][1]; model->params.LocateParam("X0")->val = params[i][2]; model->params.LocateParam("X1")->val = params[i][3]; model->params.LocateParam("Color")->val = params[i][4]; evaluate_and_write_timeseries(model, time, wave, "../sncosmo/tests/data/" + fnames[i]); } // ----------------------------------------------------------------------- // Test model relative covariance model->params.LocateParam("Redshift")->val = 0.15; model->params.LocateParam("DayMax")->val = 6.5; model->params.LocateParam("X0")->val = 1.e-5; model->params.LocateParam("X1")->val = -1.0; model->params.LocateParam("Color")->val = 0.1; // get some filters Instrument *instrument = new Instrument("SDSS"); vector filters = {instrument->EffectiveFilterByBand("g"), instrument->EffectiveFilterByBand("r"), instrument->EffectiveFilterByBand("i")}; // get some times vector times = {-30.0, -20.0, -10.0, 0.0, 10., 20., 30., 40., 50., 80.}; // write params and times ofstream out("../sncosmo/tests/data/salt2_rcov_params_times.dat"); out.setf(ios::showpoint); out.precision(7); out << "# parameters and rest-frame times to evaluate model relative covariance\n"; write_model_params(out, model); for (auto const& t: times) out << t << " "; out << endl; out.close(); // separate results for each filter for (size_t i=0; i=0.29.2", "oldest-supported-numpy", "numpy<2; python_version<'3.9'", "numpy>=2; python_version>='3.9'", ] build-backend = 'setuptools.build_meta' [tool.pytest.ini_options] markers = [ "might_download: marks tests that might download a file", ] filterwarnings = [ "error", "always:numpy.ndarray size changed:RuntimeWarning", "always::astropy.wcs.FITSFixedWarning", ] sncosmo-2.12.1/setup.cfg000066400000000000000000000046421476435666400151320ustar00rootroot00000000000000[metadata] name = sncosmo author = The SNCosmo Developers author_email = kylebarbary@gmail.com license = BSD 3-Clause License license_file = LICENSE.rst url = https://sncosmo.readthedocs.org description = Package for supernova cosmology based on astropy long_description = file: README.md long_description_content_type = text/markdown keywords = supernova, cosmology classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Science/Research License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python :: 3 Topic :: Scientific/Engineering :: Astronomy Topic :: Scientific/Engineering :: Physics [options] # We set packages to find: to automatically find all sub-packages packages = sncosmo sncosmo.tests zip_safe = False install_requires = astropy>=5.0 extinction>=0.4.6 h5py>=3.11 looseversion>=1.0.0 numpy>=1.20.3 pyyaml>=6.0.1 scipy>=1.10.0 python_requires = >=3.8 [options.extras_require] oldestdeps = # Oldest versions supported, used for tests. astropy==5.0 extinction==0.4.6 h5py==3.11 looseversion==1.0.0 numpy==1.20.3 pyyaml==6.0.1 scipy==1.10.0 test = # Required to run the test suite. pytest pytest-astropy cython test_coverage = # Required for coverage tests. coveralls coverage pytest-cov all = # Used by some parts of sncosmo, but not required. iminuit matplotlib emcee>=3 nestle docs = # Required to build the docs. cython numpy matplotlib scipy extinction astropy sphinx sphinx_gallery sphinx_rtd_theme pillow iminuit emcee>=3 numpydoc pyyaml [options.package_data] * = data/*, data/*/*, data/*/*/*, data/*/*/*/*, data/*/*/*/*/*, data/*/*/*/*/*/* sncosmo = sncosmo.cfg [coverage:run] source = sncosmo omit = sncosmo/tests/* sncosmo/conftest.py sncosmo/__init__.py sncosmo/version.py sncosmo/cython_version.py sncosmo/_deprecated.py [coverage:report] exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about packages we have installed except ImportError # Don't complain if tests don't hit assertions raise AssertionError raise NotImplementedError # Don't complain about script hooks def main\(.*\): # Ignore branches that don't pertain to this version of Python pragma: py{ignore_python_version} sncosmo-2.12.1/setup.py000077500000000000000000000011351476435666400150200ustar00rootroot00000000000000#!/usr/bin/env python # Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy import os import re from setuptools import setup from setuptools.extension import Extension # Synchronize version from code. VERSION = re.findall(r"__version__ = \"(.*?)\"", open(os.path.join("sncosmo", "__init__.py")).read())[0] # Cython extensions extensions = [ Extension( "sncosmo.salt2utils", sources=[os.path.join("sncosmo", "salt2utils.pyx")], include_dirs=[numpy.get_include()], ) ] setup( version=VERSION, ext_modules=extensions, ) sncosmo-2.12.1/sncosmo/000077500000000000000000000000001476435666400147645ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/__init__.py000066400000000000000000000112551476435666400171010ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ sncosmo: A Python package for supernova cosmology """ import os from astropy.config import ConfigItem, ConfigNamespace __version__ = "2.12.1" def test( package=None, test_path=None, args=None, plugins=None, verbose=False, pastebin=None, pep8=False, pdb=False, coverage=False, open_files=False, **kwargs ): """ Run the tests using py.test. A proper set of arguments is constructed and passed to `pytest.main`. Parameters ---------- package : str, optional The name of a specific package to test, e.g. 'io.fits' or 'utils'. If nothing is specified all default tests are run. test_path : str, optional Specify location to test by path. May be a single file or directory. Must be specified absolutely or relative to the calling directory. args : str, optional Additional arguments to be passed to `pytest.main` in the `args` keyword argument. plugins : list, optional Plugins to be passed to `pytest.main` in the `plugins` keyword argument. verbose : bool, optional Convenience option to turn on verbose output from py.test. Passing True is the same as specifying `-v` in `args`. pastebin : {'failed','all',None}, optional Convenience option for turning on py.test pastebin output. Set to 'failed' to upload info for failed tests, or 'all' to upload info for all tests. pep8 : bool, optional Turn on PEP8 checking via the pytest-pep8 plugin and disable normal tests. Same as specifying `--pep8 -k pep8` in `args`. pdb : bool, optional Turn on PDB post-mortem analysis for failing tests. Same as specifying `--pdb` in `args`. coverage : bool, optional Generate a test coverage report. The result will be placed in the directory htmlcov. open_files : bool, optional Fail when any tests leave files open. Off by default, because this adds extra run time to the test suite. Works only on platforms with a working ``lsof`` command. parallel : int, optional When provided, run the tests in parallel on the specified number of CPUs. If parallel is negative, it will use the all the cores on the machine. Requires the `pytest-xdist `_ plugin installed. Only available when using Astropy 0.3 or later. kwargs Any additional keywords passed into this function will be passed on to the astropy test runner. This allows use of test-related functionality implemented in later versions of astropy without explicitly updating the package template. See Also -------- pytest.main : py.test function wrapped by `run_tests`. """ import os from astropy.tests.helper import TestRunner runner = TestRunner(os.path.dirname(__file__)) return runner.run_tests( package=package, test_path=test_path, args=args, plugins=plugins, verbose=verbose, pastebin=pastebin, pep8=pep8, pdb=pdb, coverage=coverage, open_files=open_files, **kwargs ) # Create default configurations. The file sncosmo.cfg should be # kept in sync with the ConfigItems here. class _Conf(ConfigNamespace): """Configuration parameters for sncosmo.""" data_dir = ConfigItem( None, "Directory where sncosmo will store and read downloaded data " "resources. If None, ASTROPY_CACHE_DIR/sncosmo is created and " "used. Example: data_dir = /home/user/data/sncosmo", cfgtype="string(default=None)", ) sfd98_dir = ConfigItem( None, "Directory containing SFD (1998) dust maps, with names: " "'SFD_dust_4096_ngp.fits' and 'SFD_dust_4096_sgp.fits'. " "Example: sfd98_dir = /home/user/data/sfd98", cfgtype="string(default=None)", ) remote_timeout = ConfigItem(10.0, "Remote timeout in seconds.") # Create an instance of the class we just defined. # This needs to be done before the imports below because `conf` is used # in some parts of the library. conf = _Conf() # clean up namespace del os, ConfigItem, ConfigNamespace # import all the things into the top-level namespace from .bandpasses import * from .magsystems import * from .spectrum import * from .models import * from .io import * from .snanaio import * from .fitting import * from .simulation import * from .plotting import * from .photdata import * from .registry import * from .specmodel import * # Register all the built-ins. from .builtins import * sncosmo-2.12.1/sncosmo/_registry.py000066400000000000000000000201031476435666400173410ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst from collections import OrderedDict class Registry(object): """Connect strings to instances and loaders.""" def __init__(self): self._loaders = OrderedDict() self._instances = OrderedDict() self._primary_loaders = [] # keys of _loaders not including aliases def register_loader(self, name, func, args=None, version=None, meta=None, force=False): """Register a data reading function. Parameters ---------- name : str The data identifier. func : callable The function to read in the data. Must accept a name and version keyword argument. args : list, optional Arguments to pass to the function. Default is an empty list. version : str, optional Sub-version of name, if desired. Use formats such as ``'1'``, ``'1.0'``, ``'1.0.0'``, etc. Default is `None`. force : bool, optional Whether to override any existing function if already present. meta : dict, optional Metadata describing this loader. Default is an empty dictionary. """ if args is None: args = [] if meta is None: meta = {} name = name.lower() # names are stored in all-lowercase. key = (name, version) # check if key already exists in the registry if key in self._loaders and not force: versionstr = \ "" if version is None else " (version={!r})".format(version) raise Exception("Loader named {!r}{:s} is already " "defined. Use force=True to override." .format(name, versionstr)) self._loaders[key] = func, args, meta self._primary_loaders.append(key) def register(self, instance, name=None, force=False): """Register a class instance. Parameters ---------- instance : object The object to be registered. name : str, optional Identifier. If `None`, the name is taken from the `name` attribute of the instance, if it exists and is a string. force : bool, optional Whether to override any existing instance of the same name. Note: this may not play well with versioned instances. """ if name is None: try: name = instance.name except AttributeError: raise ValueError("name not given and instance has no 'name' " "attribute") if not isinstance(name, str): raise ValueError("name attribute of {0!r:s} is not a string.") name = name.lower() key = (name, None) # check if key is already in instances or loaders: if (key in self._instances or key in self._loaders) and not force: raise Exception("{0:s} already in registry. Use force=True" " to override.".format(name)) self._instances[key] = instance def alias(self, new_name, existing_name, new_version=None, existing_version=None): """Alias a new name to an existing name.""" found = False new_key = (new_name, new_version) existing_key = (existing_name, existing_version) if existing_key in self._loaders: found = True self._loaders[new_key] = self._loaders[existing_key] if existing_key in self._instances: found = True self._instances[new_key] = self._instances[existing_key] if not found: raise Exception("{0!r} not found in registry" .format(existing_name)) def retrieve(self, name, version=None): """Retrieve an instance from a registered identifier. Parameters ---------- name : str Identifier of the specific instance. `name` is case-independent (internally names are stored as lowercase). version : str Sub-identifier. If `None`, default to highest or only version. Returns ------- instance Notes ----- **Precedence** The following are tried in this order: 1. If ``name`` is already loaded in the registry, that instance is returned. 2. If there is a loader defined for ``name``, it is used to create an instance, save it to the registry and return it. 3. An Exception is raised listing the available registered names. **Versioning** There is support for multiple versions of data for the same `name`. 1. If ``version`` is specified, the registry and its loaders are searched for ``(name, version)``. 2. If ``version`` is not specified but there are registered loaders for ``(name, version)``, the latest version is used, and both ``(name, None)`` and ``(name, version)`` are saved to the registry. "Latest" is defined by string comparision. """ name = name.lower() key = (name, version) # Try to retrieve from instances try: return self._instances[key] except KeyError: pass # Try to retrieve from the loaders. if key in self._loaders: func, args, meta = self._loaders[key] if version is None: self._instances[key] = func(*args, name=name) else: self._instances[key] = func(*args, name=name, version=version) return self._instances[key] # If we got this far and the version is not specified, # find the latest version and try to load it. if version is None: latest_version = None for regkey in self._loaders.keys(): if (name == regkey[0] and (latest_version is None or regkey[1] > latest_version)): latest_version = regkey[1] if latest_version is not None: regkey = (name, latest_version) func, args, meta = self._loaders[regkey] self._instances[regkey] = func(*args, name=name, version=latest_version) self._instances[key] = self._instances[regkey] return self._instances[key] # At this point we will raise an exception and all the following # is to make it more informative. # all names regardless of version: registered_names = set(k[0] for k in self._loaders) registered_names.update(set(k[0] for k in self._instances)) # If we don't have the name regardless of version: if version is None or name not in registered_names: raise Exception( "{0!r} not in registry. Registered names: '{1}'" .format(name, "', '".join(registered_names))) # If version was specified but we don't have that specific version: registered_versions = [regkey[1] for regkey in self._loaders if name == regkey[0]] raise Exception( "No {0!r} with version='{1:s}' in registry. Registered" " versions: '{2:s}'".format(name, version, "', '".join(registered_versions))) def get_loaders_metadata(self): """Return the metadata of all registered loaders. Returns ------- loadermeta : list of dict Each item in the list is a dictionary containing a 'name' keyword, a 'version' keyword (if applicable), and the metadata keywords for the given loader. """ result = [] for key in self._primary_loaders: loader = self._loaders[key] name, version = key m = {'name': name} if version is not None: m['version'] = version m.update(loader[2]) result.append(m) return result sncosmo-2.12.1/sncosmo/bandpasses.py000066400000000000000000001012021476435666400174550ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import copy import astropy.units as u import numpy as np from astropy.io import ascii from astropy.utils import lazyproperty from scipy.interpolate import splev, splrep, interp1d, RegularGridInterpolator from numpy.polynomial.polynomial import polyval2d from ._registry import Registry from .constants import HC_ERG_AA, SPECTRUM_BANDFLUX_SPACING from .utils import integration_grid __all__ = ['get_bandpass', 'read_bandpass', 'Bandpass', 'AggregateBandpass', 'BandpassInterpolator', 'GeneralBandpassInterpolator', 'Transforms'] _BANDPASSES = Registry() _BANDPASS_INTERPOLATORS = Registry() def get_bandpass(name, *args, **kwargs): """Get a Bandpass from the registry by name. This function can return several types of Bandpass objects depending on the bandpass's variability on the focal plane: - **Static Bandpass:** If the bandpass does not vary spatially on the focal plane, `get_bandpass` returns a `Bandpass` instance directly. - **Radially Variable Bandpass:** For bandpasses with radial variability (e.g., `megacampsf`), an `AggregateBandpass` instance is returned, which adjusts the bandpass shape based on the specified radius. - **Position-Dependent Bandpass:** For bandpasses that vary with position on the sensor plane (e.g., `ztf`, `megacam6`, `hsc`), the returned object depends on the specified coordinates and sensor ID. If a single location is requested, an interpolated `Bandpass` instance is returned; if multiple positions are specified, an array of bandpass objects is generated to represent the spatial variations. Refer to the Examples section for more detailed usage scenarios. Parameters ---------- name : str or `Bandpass` The name of the Bandpass to retrieve from the registry, or an existing Bandpass object. If a Bandpass object is passed, it is returned directly. *args : tuple, optional Additional positional arguments, used primarily for radially variable bandpasses (e.g., radius for radial interpolations). **kwargs : dict, optional Named arguments to customize the Bandpass retrieval: - 'radius' : float, optional Radius for radial bandpasses (default: 0). - 'wave' : array-like, optional Wavelength grid for an interpolated Bandpass. - 'x', 'y' : float, optional Spatial coordinates for 2D interpolation. - 'sensor_id' : int, optional Sensor ID for multi-sensor configurations (default: 1). - 'filter_frame' : bool, optional If True, applies a filter frame (default: False). Returns ------- Bandpass or ndarray - If the name corresponds to a static Bandpass: returns the Bandpass object. - If the Bandpass is radially variable: returns the interpolated Bandpass object at the specified radius. - For general interpolated Bandpasses (2D or multi-sensor): returns an array of transmission values, or a Bandpass object if only one spatial filter is evaluated. Raises ------ TypeError If the parameters provided in `*args` and `**kwargs` are invalid Notes ----- - Uses _BANDPASSES and _BANDPASS_INTERPOLATORS to retrieve static and interpolated Bandpasses, respectively. - The general case applies to configurations like ZTF, MegaCam, and HSC, where multidimensional interpolation grids are used. Examples -------- Basic usage with a static Bandpass: >>> bandpass = get_bandpass('ztfg') Using a radial Bandpass with a specific radius: >>> radial_bandpass = get_bandpass('megacampsf::r', radius=1.2) Specifying spatial coordinates and a sensor ID for the Bandpasses managed by a `GeneralBandpassInterpolator` >>> interpolated_bandpass = get_bandpass( ... 'megacam6::g', x=2355.22, y=1222.4, sensor_id=12) Specifying a vector of spatial coordinates and sensor ids >>> x = numpy.random.uniform(0., 2048., size=100) >>> y = numpy.random.uniform(0., 4600., size=100) >>> sensor_id = numpy.random.randint(0, 36, size=100) >>> interpolated_bandpasses = get_bandpass( ... 'megacam6::z', x=x, y=y, sensor_id=sensor_id) Using a custom wavelength grid >>> x = numpy.random.uniform(0., 3000., size=100) >>> y = numpy.random.uniform(0., 3000., size=100) >>> sensor_id = numpy.random.randint(0, 65, size=100) >>> wave_grid = numpy.random.linspace(3000., 11000., 8000) >>> custom_wave_bandpass = get_bandpass('ztf::r', wave=wave_grid) """ if isinstance(name, Bandpass): return name # static bandpass if len(kwargs) + len(args) == 0: return _BANDPASSES.retrieve(name) # radially variable bandpass (snfit-like version) interp = _BANDPASS_INTERPOLATORS.retrieve(name) if isinstance(interp, BandpassInterpolator): # arguments check: the `pos` argument to BandpassInterpolator.at can be # passed as a positional argument or by the 'radius' keyword argument if args and kwargs: raise TypeError( 'keyword and positional arguments cannot be mixed') if not args and list(kwargs.keys()) != ['radius']: raise TypeError( 'unexpected keyword arguments {}'.format( ', '.join(set(kwargs.keys()) - {'radius'}))) return interp.at(*args or (kwargs.get('radius', 0),)) # general case (e.g. ZTF, MegaCam, HSC), at this point `interp` is an # instance of GeneralBandpassInterpolator unexpected_kwargs = set( kwargs.keys() - {'x', 'y', 'sensor_id', 'wave'}) if unexpected_kwargs: raise TypeError( 'unexpected keyword arguments {}'.format( ', '.join(unexpected_kwargs))) wavegrid = kwargs.get('wave', interp.wavegrid) trans = interp.eval_at( kwargs.get('x', 0.), kwargs.get('y', 0.), kwargs.get('sensor_id', 1), wavegrid, filter_frame=kwargs.get('filter_frame', False)) if trans.shape[0] == 1: return Bandpass(wavegrid, trans.squeeze(), name=name) return trans def read_bandpass(fname, fmt='ascii', wave_unit=u.AA, trans_unit=u.dimensionless_unscaled, normalize=False, trim_level=None, name=None): """Read bandpass from two-column ASCII file containing wavelength and transmission in each line. Parameters ---------- fname : str File name. fmt : {'ascii'} File format of file. Currently only ASCII file supported. wave_unit : `~astropy.units.Unit` or str, optional Wavelength unit. Default is Angstroms. trans_unit : `~astropy.units.Unit`, optional Transmission unit. Can be `~astropy.units.dimensionless_unscaled`, indicating a ratio of transmitted to incident photons, or units proportional to inverse energy, indicating a ratio of transmitted photons to incident energy. Default is ratio of transmitted to incident photons. normalize : bool, optional If True, normalize fractional transmission to be 1.0 at peak. It is recommended to set to True if transmission is in units of inverse energy. (When transmission is given in these units, the absolute value is usually not significant; normalizing gives more reasonable transmission values.) Default is False. name : str, optional Identifier. Default is `None`. Returns ------- band : `~sncosmo.Bandpass` """ if fmt != 'ascii': raise ValueError("format {0} not supported. Supported formats: 'ascii'" .format(fmt)) t = ascii.read(fname, names=['wave', 'trans']) return Bandpass(t['wave'], t['trans'], wave_unit=wave_unit, trans_unit=trans_unit, normalize=normalize, trim_level=trim_level, name=name) def slice_exclude_below(a, minvalue, grow=1): """Contiguous range in 1-d array `a` that excludes values less than `minvalue`. Range is expanded by `grow` in each direction.""" idx = np.flatnonzero(a >= minvalue) i0 = max(idx[0] - grow, 0) i1 = min(idx[-1] + 1 + grow, len(a)) # exclusive return slice(i0, i1) class Bandpass(object): """Transmission as a function of spectral wavelength. Parameters ---------- wave : list_like Wavelength. Monotonically increasing values. trans : list_like Transmission fraction. wave_unit : `~astropy.units.Unit` or str, optional Wavelength unit. Default is Angstroms. trans_unit : `~astropy.units.Unit`, optional Transmission unit. Can be `~astropy.units.dimensionless_unscaled`, indicating a ratio of transmitted to incident photons, or units proportional to inverse energy, indicating a ratio of transmitted photons to incident energy. Default is ratio of transmitted to incident photons. normalize : bool, optional If True, normalize fractional transmission to be 1.0 at peak. It is recommended to set normalize=True if transmission is in units of inverse energy. (When transmission is given in these units, the absolute value is usually not significant; normalizing gives more reasonable transmission values.) Default is False. trim_level : float, optional If given, crop bandpass to region where transmission is above this fraction of the maximum transmission. For example, if maximum transmission is 0.5, ``trim_level=0.001`` will remove regions where transmission is below 0.0005. Only contiguous regions on the sides of the bandpass are removed. name : str, optional Identifier. Default is `None`. Examples -------- Construct a Bandpass and access the input arrays: >>> b = Bandpass([4000., 4200., 4400.], [0.5, 1.0, 0.5]) >>> b.wave array([ 4000., 4200., 4400.]) >>> b.trans array([ 0.5, 1. , 0.5]) Bandpasses act like continuous 1-d functions (linear interpolation is used): >>> b([4100., 4300.]) array([ 0.75, 0.75]) The effective (transmission-weighted) wavelength is a property: >>> b.wave_eff 4200.0 The ``trim_level`` keyword can be used to remove "out-of-band" transmission upon construction. The following example removes regions of the bandpass with tranmission less than 1 percent of peak: >>> band = Bandpass([4000., 4100., 4200., 4300., 4400., 4500.], ... [0.001, 0.002, 0.5, 0.6, 0.003, 0.001], ... trim_level=0.01) >>> band.wave array([ 4100., 4200., 4300., 4400.]) >>> band.trans array([ 0.002, 0.5 , 0.6 , 0.003]) While less strictly correct than including the "out-of-band" transmission, only considering the region of the bandpass where transmission is significant can improve model-bandpass overlap as well as performance. """ def __init__(self, wave, trans, wave_unit=u.AA, trans_unit=u.dimensionless_unscaled, normalize=False, name=None, trim_level=None): wave = np.asarray(wave, dtype=np.float64) trans = np.asarray(trans, dtype=np.float64) if wave.shape != trans.shape: raise ValueError('shape of wave and trans must match') if wave.ndim != 1: raise ValueError('only 1-d arrays supported') # Ensure that units are actually units and not quantities, so that # `to` method returns a float and not a Quantity. wave_unit = u.Unit(wave_unit) trans_unit = u.Unit(trans_unit) if wave_unit != u.AA: wave = wave_unit.to(u.AA, wave, u.spectral()) # If transmission is in units of inverse energy, convert to # unitless transmission: # # (transmitted photons / incident photons) = # (photon energy) * (transmitted photons / incident energy) # # where photon energy = h * c / lambda if trans_unit != u.dimensionless_unscaled: trans = (HC_ERG_AA / wave) * trans_unit.to(u.erg**-1, trans) # Check that values are monotonically increasing. # We could sort them, but if this happens, it is more likely a user # error or faulty bandpass definition. So we leave it to the user to # sort them. if not np.all(np.ediff1d(wave) > 0.): raise ValueError('bandpass wavelength values must be monotonically' ' increasing when supplied in wavelength or ' 'decreasing when supplied in energy/frequency.') if normalize: trans /= np.max(trans) # Trim "out-of-band" transmission if trim_level is not None: s = slice_exclude_below(trans, np.max(trans) * trim_level, grow=1) wave = wave[s] trans = trans[s] # if more than one leading or trailing transmissions are zero, we # can remove them. if ((trans[0] == 0.0 and trans[1] == 0.0) or (trans[-1] == 0.0 and trans[-2] == 0.0)): i = 0 while i < len(trans) and trans[i] == 0.0: i += 1 if i == len(trans): raise ValueError('all zero transmission') j = len(trans) - 1 while j >= 0 and trans[j] == 0.0: j -= 1 # back out to include a single zero if i > 0: i -= 1 if j < len(trans) - 1: j += 1 wave = wave[i:j+1] trans = trans[i:j+1] self.wave = wave self.trans = trans # Set up interpolation. # This appears to be the fastest-evaluating interpolant in # scipy.interpolate. self._tck = splrep(self.wave, self.trans, k=1) self.name = name def minwave(self): return self.wave[0] def maxwave(self): return self.wave[-1] @lazyproperty def wave_eff(self): """Effective wavelength of bandpass in Angstroms.""" wave, _ = integration_grid(self.minwave(), self.maxwave(), SPECTRUM_BANDFLUX_SPACING) weights = self(wave) return np.sum(wave * weights) / np.sum(weights) def __call__(self, wave): return splev(wave, self._tck, ext=1) def __repr__(self): name = '' if self.name is not None: name = ' {!r}'.format(self.name) return "<{:s}{:s} at 0x{:x}>".format(self.__class__.__name__, name, id(self)) def shifted(self, factor, name=None): """Return a new Bandpass instance with all wavelengths multiplied by a factor.""" return Bandpass(factor * self.wave, self.trans, name=name) class _SampledFunction(object): """Represents a 1-d continuous function, used in AggregateBandpass.""" def __init__(self, x, y): self.x = np.asarray(x, dtype=np.float64) self.y = np.asarray(y, dtype=np.float64) self.xmin = x[0] self.xmax = x[-1] self._tck = splrep(self.x, self.y, k=1) def __call__(self, x): return splev(x, self._tck, ext=1) class AggregateBandpass(Bandpass): """Bandpass defined by multiple transmissions in series. Parameters ---------- transmissions : list of (wave, trans) pairs. Functions defining component transmissions. prefactor : float, optional Scalar factor to multiply transmissions by. Default is 1.0. name : str, optional Name of bandpass. family : str, optional Name of "family" this bandpass belongs to. Such an identifier can be useful for identifying bandpasses belonging to the same instrument/filter combination but different focal plane positions. """ def __init__(self, transmissions, prefactor=1.0, name=None, family=None): if len(transmissions) < 1: raise ValueError("empty list of transmissions") # Set up transmissions as `_SampledFunction`s. # # We allow passing `_SampledFunction`s directly to allow # RadialBandpassGenerator to generate AggregateBandpasses a # bit more efficiently, even though _SampledFunction isn't # part of the public API. self.transmissions = [t if isinstance(t, _SampledFunction) else _SampledFunction(t[0], t[1]) for t in transmissions] self.prefactor = prefactor self.name = name self.family = family # Determine min/max wave: since sampled functions are zero outside # their domain, minwave is the *largest* minimum x value, and # vice-versa for maxwave. self._minwave = max(t.xmin for t in self.transmissions) self._maxwave = min(t.xmax for t in self.transmissions) def minwave(self): return self._minwave def maxwave(self): return self._maxwave def __str__(self): return ("AggregateBandpass: {:d} components, prefactor={!r}, " "range=({!r}, {!r}), name={!r}" .format(len(self.transmissions), self.prefactor, self.minwave(), self.maxwave(), self.name)) def __call__(self, wave): t = self.transmissions[0](wave) for trans in self.transmissions[1:]: t *= trans(wave) t *= self.prefactor return t def shifted(self, factor, name=None, family=None): """Return a new AggregateBandpass instance with all wavelengths multiplied by a factor.""" transmissions = [(factor * t.x, t.y) for t in self.transmissions] return AggregateBandpass(transmissions, prefactor=self.prefactor, name=name, family=family) class BandpassInterpolator(object): """Bandpass generator defined as a function of focal plane position. Instances of this class are not Bandpasses themselves, but generate Bandpasses at a given focal plane position. This class stores the transmission as a function of focal plane position and interpolates between the defined positions to return the bandpass at an arbitrary position. Parameters ---------- transmissions : list of (wave, trans) pairs Transmissions that apply everywhere in the focal plane. dependent_transmissions : list of (value, wave, trans) Transmissions that depend on some parameter. Each `value` is the scalar parameter value, `wave` and `trans` are 1-d arrays. prefactor : float, optional Scalar multiplying factor. name : str Examples -------- Transmission uniform across focal plane: >>> uniform_trans = ([4000., 5000.], [1., 0.5]) # wave, trans Transmissions as a function of radius: >>> trans0 = (0., [4000., 5000.], [0.5, 0.5]) # radius=0 >>> trans1 = (1., [4000., 5000.], [0.75, 0.75]) # radius=1 >>> trans2 = (2., [4000., 5000.], [0.1, 0.1]) # radius=2 >>> band_interp = BandpassInterpolator([uniform_trans], ... [trans0, trans1, trans2], ... name='my_band') Min and max radius: >>> band_interp.minpos(), band_interp.maxpos() (0.0, 2.0) Get bandpass at a given radius: >>> band = band_interp.at(1.5) >>> band The band is aggregate of uniform transmission part, and interpolated radial-dependent part. >>> band([4500., 4600.]) array([ 0.65625, 0.6125 ]) """ def __init__(self, transmissions, dependent_transmissions, prefactor=1.0, name=None): # create sampled functions for normal transmissions self.transmissions = [_SampledFunction(t[0], t[1]) for t in transmissions] # ensure dependent transmissions are sorted sorted_trans = sorted(dependent_transmissions, key=lambda x: x[0]) self.dependent_transmissions = [(t[0], _SampledFunction(t[1], t[2])) for t in sorted_trans] self.prefactor = prefactor self.name = name def minpos(self): """Minimum positional parameter value.""" return self.dependent_transmissions[0][0] def maxpos(self): """Maximum positional parameter value.""" return self.dependent_transmissions[-1][0] def at(self, pos): """Return the bandpass at the given position""" if pos < self.minpos() or pos >= self.maxpos(): raise ValueError("Position outside bounds") # find index such that t[i-1] <= pos < t[i] i = 1 while (i < len(self.dependent_transmissions) and pos > self.dependent_transmissions[i][0]): i += 1 # linearly interpolate second transmission onto first v0, f0 = self.dependent_transmissions[i-1] v1, f1 = self.dependent_transmissions[i] w1 = (pos - v0) / (v1 - v0) w0 = 1.0 - w1 x = f0.x y = w0 * f0.y + w1 * f1(x) f = _SampledFunction(x, y) transmissions = copy.copy(self.transmissions) # shallow copy the list transmissions.append(f) name = "" if self.name is None else (self.name + " ") name += "at {:f}".format(pos) return AggregateBandpass(transmissions, prefactor=self.prefactor, name=name, family=self.name) class Transforms(object): """Provides transformations from pixel coordinates to focal plane and filter coordinates. The `Transforms` class is designed to map x, y pixel coordinates to corresponding focal plane and filter coordinates based on simple polynomial transformations. These transformations are accurate within a few millimeters and do not account for astrometric distortion, focusing instead on efficient and approximate coordinate mappings. Bandpasses managed with the `GeneralBandpassInterpolator` class are distributed with `Transforms` to map (x, y, sensor) coordinates (typically, what we measure) to filter-frame coordinates. .. note:: These transformations are not intended for astrometric precision. Parameters ---------- to_focalplane : dict Dictionary mapping each sensor ID to polynomial coefficients for conversion from pixel coordinates to focal plane coordinates. The coefficients are used for 2D polynomial evaluation. to_filter : dict Dictionary mapping each sensor ID to polynomial coefficients for conversion from pixel coordinates to filter frame coordinates. The coefficients are used for 2D polynomial evaluation. """ def __init__(self, to_focalplane, to_filter): self._to_focalplane = to_focalplane self._to_filter = to_filter @staticmethod def _to_coords(x, y, sensor_id, coords): x_out = np.zeros_like(x) y_out = np.zeros_like(y) for s_id in np.unique(sensor_id): idx = s_id == sensor_id x_out[idx] = polyval2d(x[idx], y[idx], coords[s_id][0]) y_out[idx] = polyval2d(x[idx], y[idx], coords[s_id][1]) return x_out, y_out def to_focalplane(self, x, y, sensor_id): """ Maps (x, y, sensor) coordinates to focal plane coordinates. Parameters ---------- x : array-like x-coordinates in the sensor frame (in pixels) y : array-like y-coordinates in the sensor frame (in pixels) sensor_id : array-like Sensor IDs for each x, y coordinate, used to select the appropriate focal plane transformation. Returns ------- tuple of arrays Transformed X, Y coordinates in the focal plane frame. """ return self._to_coords(x, y, sensor_id, self._to_focalplane) def to_filter(self, x, y, sensor_id): """ Maps (x, y sensor_id) coordinates to filter frame coordinates. Parameters ---------- x : array-like x-coordinates in the sensor frame (pixels) y : array-like Y-coordinates in the sensor frame (pixels) sensor_id : array-like Sensor IDs for each x, y coordinate, used to select the appropriate filter transformation. Returns ------- tuple of arrays Transformed X, Y coordinates in the filter frame. """ return self._to_coords(x, y, sensor_id, self._to_filter) class GeneralBandpassInterpolator(object): """A general-purpose interpolator for bandpasses with spatial or sensor-specific variability. This class provides a flexible interpolation for bandpass transmissions that may vary across the focal plane due to spatial position, sensor-specific quantum efficiency (QE), and/or radial dependence. Instances of this class are not Bandpasses themselves, but generate Bandpasses at a given focal plane position. This class stores the transmission as a function of focal plane position and interpolates between the defined positions to return the bandpass at an arbitrary position. Parameters ---------- static_transmissions : list of arrays Each element is an array representing a static bandpass transmission profile over wavelength. The static transmissions are defined independently of sensor position. specific_sensor_qe : dict, optional Dictionary of sensor-specific quantum efficiency (QE) profiles. Keys are sensor IDs, and values are arrays defining the wavelength and QE values. variable_transmission : tuple, optional Defines position-dependent transmission. Can be either a radial transmission or a 2D spatial transmission, specified as: - Radial: (radius, wavelength, transmission) - Spatial: (x, y, wavelength, transmission) transforms : object, optional Transformation object for converting between coordinate frames (e.g., CCD and filter frames). prefactor : float, default=1.0 Scaling factor applied to the transmission values. name : str, optional Name identifier for the bandpass interpolator instance. """ def __init__(self, static_transmissions, specific_sensor_qe=None, variable_transmission=None, transforms=None, prefactor=1.0, name=None): # static transmissions self.static_transmissions = [ interp1d(*tr.T, bounds_error=False, fill_value=0.) for tr in static_transmissions] # we also need to track the wavelength range on which all the static # transmissions are defined wl = np.array( [(tr[:, 0].min(), tr[:, 0].max()) for tr in static_transmissions]) static_wl_range = wl[:, 0].max(), wl[:, 1].min() # specific sensor quantum efficiencies if specific_sensor_qe is not None: self.specific_sensor_qe = { key: interp1d( *specific_sensor_qe[key].T, bounds_error=False, fill_value=0.) for key in specific_sensor_qe} else: self.specific_sensor_qe = None # and finally, the transmissions which vary smoothly as a function of a # location parameters, which may be either a radius or a self.variable_transmission = None if variable_transmission is not None: if len(variable_transmission) == 3: rad, wl, tr = variable_transmission idx = (wl >= static_wl_range[0]) & (wl <= static_wl_range[1]) wl, tr = wl[idx], tr[:, idx] self.wavegrid = wl self.wave = (wl.min(), wl.max()) self.pos = (rad.min(), rad.max()) self.variable_transmission = \ RegularGridInterpolator( [rad, wl], tr, bounds_error=False, fill_value=0.) self.radial = True elif len(variable_transmission) == 4: x, y, wl, tr = variable_transmission idx = (wl >= static_wl_range[0]) & (wl <= static_wl_range[1]) wl, tr = wl[idx], tr[:, :, idx] self.wavegrid = wl self.wave = (wl.min(), wl.max()) self.pos = (x.min(), y.min()), (x.max(), y.max()) self.variable_transmission = \ RegularGridInterpolator( [x, y, wl], tr, bounds_error=False, fill_value=0.) self.radial = False else: raise ValueError('unable to handle the transmission data') self.transforms = transforms if transforms is not None else None self.prefactor = prefactor self.name = name def minwave(self): """Returns the minimum wavelength over which the bandpass is defined""" return self.wave[0] def maxwave(self): """Returns the maximum wavelength over which the bandpass is defined""" return self.wave[1] def minpos(self): """Returns the minimum position in the spatial grid for variable bandpasses""" return self.pos[0] def maxpos(self): """Returns the maximim position in the spatial grid for variable bandpasses""" return self.pos[1] def at(self, x, y, sensor_id): """Return the bandpass at the specified position Parameters ---------- x : float or ndarray X-coordinate in pixels or mm, depending on `filter_frame`. y : float or ndarray Y-coordinate in pixels or mm, depending on `filter_frame`. sensor_id : int or ndarray Identifier for sensor-specific configurations. Returns ------- Bandpass The interpolated Bandpass at the specified position. """ trans = self.eval_at(x, y, sensor_id, self.wavegrid).squeeze() return Bandpass(self.wavegrid, trans) def eval_at(self, x, y, sensor_id, wl, filter_frame=False): """Evaluates transmission values at given positions and wavelengths. Parameters ---------- x : float or ndarray X-coordinates in pixels or mm (depending on `filter_frame`). y : float or ndarray Y-coordinates in pixels or mm (depending on `filter_frame`). sensor_id : int or ndarray Sensor ID(s) to specify the quantum efficiency profile. wl : ndarray Wavelength grid for transmission evaluation. filter_frame : bool, default=False If True, interprets x and y as coordinates in the filter frame (mm), otherwise assumes CCD coordinates (pixels). Returns ------- ndarray Transmission values array of shape `(len(wl), len(x))`. """ trans = None x = np.atleast_1d(x).astype(float) y = np.atleast_1d(y).astype(float) sensor_id = np.atleast_1d(sensor_id).astype(int) if not filter_frame: X, Y = self.transforms.to_filter(x, y, sensor_id) else: X, Y = x, y if self.variable_transmission: if not self.radial: XY = np.vstack((X, Y)) v = np.array([ self.variable_transmission(np.array([ np.full(len(wl), x), np.full(len(wl), y), wl]).T) for x, y in XY.T]) trans = trans * v if trans is not None else v else: rad = np.sqrt(X**2 + Y**2) v = np.array( [self.variable_transmission(np.array([ np.full(len(wl), r), wl]).T) for r in rad]) trans = trans * v if trans is not None else v # if not defined, we assume that the qe is in the static transmissions if self.specific_sensor_qe: # many requested positions may be on the same senors. it is better # then to pre-compute the QE's once for all qe = {} for s_id in np.unique(sensor_id, axis=0): qe[s_id] = self.specific_sensor_qe[s_id](wl) # and then, we combine them together v = np.array([qe[s_id] for s_id in sensor_id]) trans = trans * v if trans is not None else v for tr in self.static_transmissions: v = tr(wl) trans = trans * v if trans is not None else v return trans sncosmo-2.12.1/sncosmo/builtins.py000066400000000000000000002065431476435666400172010ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Importing this module registers loaders for built-in data structures: - Sources - Bandpasses - MagSystems """ import os import warnings from os.path import join import h5py import numpy as np from astropy import units as u, wcs from astropy.config import get_cache_dir from astropy.io import fits from astropy.utils.data import get_pkg_data_filename from . import conf from . import io from . import snfitio from .bandpasses import ( Bandpass, _BANDPASSES, _BANDPASS_INTERPOLATORS, read_bandpass, BandpassInterpolator, GeneralBandpassInterpolator, Transforms) from .constants import BANDPASS_TRIM_LEVEL from .magsystems import ( ABMagSystem, CompositeMagSystem, SpectralMagSystem, _MAGSYSTEMS) from .models import ( MLCS2k2Source, SALT2Source, SALT3Source, SNEMOSource, SUGARSource, TimeSeriesSource, _SOURCES) from .specmodel import SpectrumModel from .utils import DataMirror # This module is only imported for its side effects. __all__ = [] def get_rootdir(): # use the environment variable if set data_dir = os.environ.get('SNCOSMO_DATA_DIR') # otherwise, use config file value if set. if data_dir is None: data_dir = conf.data_dir # if still None, use astropy cache dir (and create if necessary!) if data_dir is None: data_dir = join(get_cache_dir(), "sncosmo") if not os.path.isdir(data_dir): if os.path.exists(data_dir): raise RuntimeError("{0} not a directory".format(data_dir)) os.mkdir(data_dir) return data_dir DATADIR = DataMirror(get_rootdir, "https://sncosmo.github.io/data") # ============================================================================= # Bandpasses def load_bandpass_bessell(pkg_data_name, name=None): """Bessell bandpasses have (1/energy) transmission units.""" fname = get_pkg_data_filename(pkg_data_name) return read_bandpass(fname, wave_unit=u.AA, trans_unit=u.erg**-1, normalize=True, name=name) def load_bandpass_remote_aa(relpath, name=None): abspath = DATADIR.abspath(relpath) return read_bandpass(abspath, wave_unit=u.AA, trim_level=BANDPASS_TRIM_LEVEL, name=name) def load_bandpass_remote_nm(relpath, name=None): abspath = DATADIR.abspath(relpath) return read_bandpass(abspath, wave_unit=u.nm, trim_level=BANDPASS_TRIM_LEVEL, name=name) def load_bandpass_remote_um(relpath, name=None): abspath = DATADIR.abspath(relpath) return read_bandpass(abspath, wave_unit=u.micron, trim_level=BANDPASS_TRIM_LEVEL, name=name) def load_bandpass_remote_wfc3(relpath, name=None): abspath = DATADIR.abspath(relpath) _, wave, trans = np.loadtxt(abspath, unpack=True) return Bandpass(wave, trans, wave_unit=u.AA, trim_level=BANDPASS_TRIM_LEVEL, name=name) def tophat_bandpass_um(ctr, width, name=None): """Create a tophat Bandpass centered at `ctr` with width `width` (both in microns).""" wave = np.array([ctr - width / 2.0, ctr + width / 2.0]) trans = np.array([1.0, 1.0]) return Bandpass(wave, trans, wave_unit=u.micron, name=name) # Bessell bandpasses (transmission is in units of (photons / erg)) bessell_meta = { 'filterset': 'bessell', 'reference': ('B90', '`Bessell 1990 `__, Table 2'), 'description': 'Representation of Johnson-Cousins UBVRI system'} for name, fname in [('bessellux', 'bessell/bessell_ux.dat'), ('bessellb', 'bessell/bessell_b.dat'), ('bessellv', 'bessell/bessell_v.dat'), ('bessellr', 'bessell/bessell_r.dat'), ('besselli', 'bessell/bessell_i.dat')]: _BANDPASSES.register_loader(name, load_bandpass_bessell, args=('data/bandpasses/' + fname,), meta=bessell_meta) # Shifted bessell filters used in SNLS3 (in units of photon / photon) snls3_landolt_meta = { 'filterset': 'snls3-landolt', 'dataurl': 'http://supernovae.in2p3.fr/sdss_snls_jla/ReadMe.html', 'retrieved': '13 February 2017', 'description': 'Bessell bandpasses shifted as in JLA analysis', 'reference': ('B14a', '`Betoule et al. (2014) `__, Footnote 21')} for name, fname in [ ('standard::u', 'bandpasses/snls3-landolt/sux-shifted.dat'), ('standard::b', 'bandpasses/snls3-landolt/sb-shifted.dat'), ('standard::v', 'bandpasses/snls3-landolt/sv-shifted.dat'), ('standard::r', 'bandpasses/snls3-landolt/sr-shifted.dat'), ('standard::i', 'bandpasses/snls3-landolt/si-shifted.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=snls3_landolt_meta) des_meta = { 'filterset': 'des', 'retrieved': '22 March 2013', 'description': 'Dark Energy Camera grizy filter set at airmass 1.3'} for name, fname in [('desu', 'bandpasses/des/des_u.dat'), ('desg', 'bandpasses/des/des_g.dat'), ('desr', 'bandpasses/des/des_r.dat'), ('desi', 'bandpasses/des/des_i.dat'), ('desz', 'bandpasses/des/des_z.dat'), ('desy', 'bandpasses/des/des_y.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=des_meta) sdss_meta = { 'filterset': 'sdss', 'reference': ('D10', '`Doi et al. 2010 `__, Table 4'), 'description': ('SDSS 2.5m imager at airmass 1.3 (including ' 'atmosphere), normalized')} for name, fname in [('sdssu', 'bandpasses/sdss/sdss_u.dat'), ('sdssg', 'bandpasses/sdss/sdss_g.dat'), ('sdssr', 'bandpasses/sdss/sdss_r.dat'), ('sdssi', 'bandpasses/sdss/sdss_i.dat'), ('sdssz', 'bandpasses/sdss/sdss_z.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=sdss_meta) _BANDPASSES.alias('sdss::u', 'sdssu') _BANDPASSES.alias('sdss::g', 'sdssg') _BANDPASSES.alias('sdss::r', 'sdssr') _BANDPASSES.alias('sdss::i', 'sdssi') _BANDPASSES.alias('sdss::z', 'sdssz') # HST ACS WFC bandpasses: remote acs_meta = {'filterset': 'acs', 'dataurl': 'http://www.stsci.edu/hst/acs/analysis/throughputs', 'retrieved': 'direct download', 'description': 'Hubble Space Telescope ACS WFC filters'} for name, fname in [('f435w', 'bandpasses/acs-wfc/wfc_F435W.dat'), ('f475w', 'bandpasses/acs-wfc/wfc_F475W.dat'), ('f555w', 'bandpasses/acs-wfc/wfc_F555W.dat'), ('f606w', 'bandpasses/acs-wfc/wfc_F606W.dat'), ('f625w', 'bandpasses/acs-wfc/wfc_F625W.dat'), ('f775w', 'bandpasses/acs-wfc/wfc_F775W.dat'), # TODO: 814 filter from STScI has multiple identical # wavelength values. # ('f814w', 'bandpasses/acs-wfc/wfc_F814W.dat'), ('f850lp', 'bandpasses/acs-wfc/wfc_F850LP.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=acs_meta) _BANDPASSES.alias('acswf::f606w', 'f606w') _BANDPASSES.alias('acswf::f775w', 'f775w') _BANDPASSES.alias('acswf::f850lp', 'f850lp') # HST NICMOS NIC2 bandpasses: remote nicmos_meta = {'filterset': 'nicmos-nic2', 'dataurl': 'http://www.stsci.edu/hst/', 'retrieved': '05 Aug 2014', 'description': 'Hubble Space Telescope NICMOS2 filters'} for name, fname in [ ('nicf110w', 'bandpasses/nicmos-nic2/hst_nicmos_nic2_f110w.dat'), ('nicf160w', 'bandpasses/nicmos-nic2/hst_nicmos_nic2_f160w.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=nicmos_meta) _BANDPASSES.alias('nicmos2::f110w', 'nicf110w') _BANDPASSES.alias('nicmos2::f160w', 'nicf160w') # WFC3 IR bandpasses: remote wfc3ir_meta = {'filterset': 'wfc3-ir', 'dataurl': 'http://www.stsci.edu/hst/wfc3/ins_performance/' 'throughputs/Throughput_Tables', 'retrieved': 'direct download', 'description': 'Hubble Space Telescope WFC3 IR filters'} for name, fname in [('f098m', 'bandpasses/wfc3-ir/f098m.IR.tab'), ('f105w', 'bandpasses/wfc3-ir/f105w.IR.tab'), ('f110w', 'bandpasses/wfc3-ir/f110w.IR.tab'), ('f125w', 'bandpasses/wfc3-ir/f125w.IR.tab'), ('f127m', 'bandpasses/wfc3-ir/f127m.IR.tab'), ('f139m', 'bandpasses/wfc3-ir/f139m.IR.tab'), ('f140w', 'bandpasses/wfc3-ir/f140w.IR.tab'), ('f153m', 'bandpasses/wfc3-ir/f153m.IR.tab'), ('f160w', 'bandpasses/wfc3-ir/f160w.IR.tab')]: _BANDPASSES.register_loader(name, load_bandpass_remote_wfc3, args=(fname,), meta=wfc3ir_meta) wfc3uvis_meta = {'filterset': 'wfc3-uvis', 'dataurl': 'http://www.stsci.edu/hst/wfc3/ins_performance/' 'throughputs/Throughput_Tables', 'retrieved': 'direct download', 'description': ('Hubble Space Telescope WFC3 UVIS filters ' '(CCD 2)')} for name, fname in [('f218w', "bandpasses/wfc3-uvis/f218w.UVIS2.tab"), ('f225w', "bandpasses/wfc3-uvis/f225w.UVIS2.tab"), ('f275w', "bandpasses/wfc3-uvis/f275w.UVIS2.tab"), ('f300x', "bandpasses/wfc3-uvis/f300x.UVIS2.tab"), ('f336w', "bandpasses/wfc3-uvis/f336w.UVIS2.tab"), ('f350lp', "bandpasses/wfc3-uvis/f350lp.UVIS2.tab"), ('f390w', "bandpasses/wfc3-uvis/f390w.UVIS2.tab"), ('f689m', "bandpasses/wfc3-uvis/f689m.UVIS2.tab"), ('f763m', "bandpasses/wfc3-uvis/f763m.UVIS2.tab"), ('f845m', "bandpasses/wfc3-uvis/f845m.UVIS2.tab"), ('f438w', "bandpasses/wfc3-uvis/f438w.UVIS2.tab"), ('uvf475w', "bandpasses/wfc3-uvis/f475w.UVIS2.tab"), ('uvf555w', "bandpasses/wfc3-uvis/f555w.UVIS2.tab"), ('uvf606w', "bandpasses/wfc3-uvis/f606w.UVIS2.tab"), ('uvf625w', "bandpasses/wfc3-uvis/f625w.UVIS2.tab"), ('uvf775w', "bandpasses/wfc3-uvis/f775w.UVIS2.tab"), ('uvf814w', "bandpasses/wfc3-uvis/f814w.UVIS2.tab"), ('uvf850lp', "bandpasses/wfc3-uvis/f850lp.UVIS2.tab")]: _BANDPASSES.register_loader(name, load_bandpass_remote_wfc3, args=(fname,), meta=wfc3uvis_meta) # Kepler kepler_meta = { 'filterset': 'kepler', 'retrieved': 'direct download', 'description': 'Bandpass for the Kepler spacecraft', 'dataurl': 'http://keplergo.arc.nasa.gov/CalibrationResponse.shtml'} _BANDPASSES.register_loader( 'kepler', load_bandpass_remote_nm, args=("bandpasses/kepler/kepler_response_hires1.txt",), meta=kepler_meta) csp_meta = { 'filterset': 'csp', 'retrieved': '8 Feb 2017', 'description': ('Carnegie Supernova Project filters (Swope+DuPont ' 'Telescopes) updated 6 Oct 2016'), 'dataurl': 'http://csp.obs.carnegiescience.edu/data/filters'} for name, fname in [ ('cspb', 'bandpasses/csp/B_tel_ccd_atm_ext_1.2.dat'), ('csphs', 'bandpasses/csp/H_SWO_TAM_scan_atm.dat'), ('csphd', 'bandpasses/csp/H_DUP_TAM_scan_atm.dat'), ('cspjs', 'bandpasses/csp/J_SWO_TAM_scan_atm.dat'), ('cspjd', 'bandpasses/csp/J_DUP_TAM_scan_atm.dat'), ('cspv3009', 'bandpasses/csp/V_LC3009_tel_ccd_atm_ext_1.2.dat'), ('cspv3014', 'bandpasses/csp/V_LC3014_tel_ccd_atm_ext_1.2.dat'), ('cspv9844', 'bandpasses/csp/V_LC9844_tel_ccd_atm_ext_1.2.dat'), ('cspys', 'bandpasses/csp/Y_SWO_TAM_scan_atm.dat'), ('cspyd', 'bandpasses/csp/Y_DUP_TAM_scan_atm.dat'), ('cspg', 'bandpasses/csp/g_tel_ccd_atm_ext_1.2.dat'), ('cspi', 'bandpasses/csp/i_tel_ccd_atm_ext_1.2.dat'), ('cspk', 'bandpasses/csp/kfilter'), ('cspr', 'bandpasses/csp/r_tel_ccd_atm_ext_1.2.dat'), ('cspu', 'bandpasses/csp/u_tel_ccd_atm_ext_1.2.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=csp_meta) _BANDPASSES.alias('swope2::u', 'cspu') _BANDPASSES.alias('swope2::b', 'cspb') _BANDPASSES.alias('swope2::g', 'cspg') _BANDPASSES.alias('swope2::v', 'cspv3014') _BANDPASSES.alias('swope2::v1', 'cspv3009') _BANDPASSES.alias('swope2::v2', 'cspv9844') _BANDPASSES.alias('swope2::r', 'cspr') _BANDPASSES.alias('swope2::i', 'cspi') _BANDPASSES.alias('swope2::y', 'cspys') _BANDPASSES.alias('swope2::j', 'cspjs') _BANDPASSES.alias('swope2::h', 'csphs') jwst_nircam_meta = {'filterset': 'jwst-nircam', 'dataurl': 'http://www.stsci.edu/jwst/instruments/nircam' '/instrumentdesign/filters', 'retrieved': '09 Sep 2014', 'description': 'James Webb Space Telescope NIRCAM ' 'Wide+Medium filters'} for name, fname in [('f070w', 'bandpasses/nircam/jwst_nircam_f070w.dat'), ('f090w', 'bandpasses/nircam/jwst_nircam_f090w.dat'), ('f115w', 'bandpasses/nircam/jwst_nircam_f115w.dat'), ('f150w', 'bandpasses/nircam/jwst_nircam_f150w.dat'), ('f200w', 'bandpasses/nircam/jwst_nircam_f200w.dat'), ('f277w', 'bandpasses/nircam/jwst_nircam_f277w.dat'), ('f356w', 'bandpasses/nircam/jwst_nircam_f356w.dat'), ('f444w', 'bandpasses/nircam/jwst_nircam_f444w.dat'), ('f140m', 'bandpasses/nircam/jwst_nircam_f140m.dat'), ('f162m', 'bandpasses/nircam/jwst_nircam_f162m.dat'), ('f182m', 'bandpasses/nircam/jwst_nircam_f182m.dat'), ('f210m', 'bandpasses/nircam/jwst_nircam_f210m.dat'), ('f250m', 'bandpasses/nircam/jwst_nircam_f250m.dat'), ('f300m', 'bandpasses/nircam/jwst_nircam_f300m.dat'), ('f335m', 'bandpasses/nircam/jwst_nircam_f335m.dat'), ('f360m', 'bandpasses/nircam/jwst_nircam_f360m.dat'), ('f410m', 'bandpasses/nircam/jwst_nircam_f410m.dat'), ('f430m', 'bandpasses/nircam/jwst_nircam_f430m.dat'), ('f460m', 'bandpasses/nircam/jwst_nircam_f460m.dat'), ('f480m', 'bandpasses/nircam/jwst_nircam_f480m.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_um, args=(fname,), meta=jwst_nircam_meta) jwst_miri_meta = { 'filterset': 'jwst-miri', 'dataurl': ('http://ircamera.as.arizona.edu/MIRI/' 'ImPCE_TN-00072-ATC-Iss2.xlsx'), 'retrieved': '16 Feb 2017', 'description': 'James Webb Space Telescope MIRI filters'} for name in ['f560w', 'f770w', 'f1000w', 'f1130w', 'f1280w', 'f1500w', 'f1800w', 'f2100w', 'f2550w']: fname = "bandpasses/miri/jwst_miri_{}.dat".format(name) _BANDPASSES.register_loader(name, load_bandpass_remote_um, args=(fname,), meta=jwst_miri_meta) jwst_miri_meta2 = {'filterset': 'jwst-miri-tophat', 'dataurl': ('http://www.stsci.edu/jwst/instruments/miri/' 'instrumentdesign/filters'), 'retrieved': '09 Sep 2014', 'description': ('James Webb Space Telescope MIRI ' 'filters (idealized tophat)')} for name, ctr, width in [('f1065c', 10.65, 0.53), ('f1140c', 11.4, 0.57), ('f1550c', 15.5, 0.78), ('f2300c', 23., 4.6)]: _BANDPASSES.register_loader(name, tophat_bandpass_um, args=(ctr, width), meta=jwst_miri_meta2) # LSST bandpasses lsst_meta = {'filterset': 'lsst', 'dataurl': ('https://github.com/lsst/throughputs/tree/' '7632edaa9e93d06576e34a065ea4622de8cc48d0/baseline'), 'retrieved': '16 Nov 2016', 'description': 'LSST baseline total throughputs, v1.1.'} for letter in ['u', 'g', 'r', 'i', 'z', 'y']: name = 'lsst' + letter relpath = 'bandpasses/lsst/total_{}.dat'.format(letter) _BANDPASSES.register_loader(name, load_bandpass_remote_nm, args=(relpath,), meta=lsst_meta) # Keplercam keplercam_meta = { 'filterset': 'keplercam', 'dataurl': 'http://supernovae.in2p3.fr/sdss_snls_jla/ReadMe.html', 'retrieved': '13 Feb 2017', 'description': 'Keplercam transmissions as used in JLA'} for name, fname in [('keplercam::us', 'bandpasses/keplercam/Us_Keplercam.txt'), ('keplercam::b', 'bandpasses/keplercam/B_Keplercam.txt'), ('keplercam::v', 'bandpasses/keplercam/V_Keplercam.txt'), ('keplercam::r', 'bandpasses/keplercam/r_Keplercam.txt'), ('keplercam::i', 'bandpasses/keplercam/i_Keplercam.txt')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=keplercam_meta) # 4shooter fourshooter_meta = { 'filterset': '4shooter2', 'dataurl': 'http://supernovae.in2p3.fr/sdss_snls_jla/ReadMe.html', 'retrieved': '13 Feb 2017', 'description': '4Shooter filters as used in JLA'} for name, fname in [('4shooter2::us', 'bandpasses/4shooter2/Us_4Shooter2.txt'), ('4shooter2::b', 'bandpasses/4shooter2/B_4Shooter2.txt'), ('4shooter2::v', 'bandpasses/4shooter2/V_4Shooter2.txt'), ('4shooter2::r', 'bandpasses/4shooter2/R_4Shooter2.txt'), ('4shooter2::i', 'bandpasses/4shooter2/I_4Shooter2.txt')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=fourshooter_meta) roman_meta = { 'filterset': 'roman-wfi', 'dataurl': 'https://roman.gsfc.nasa.gov/science/WFI_technical.html', 'retrieved': '30 Nov 2020', 'description': 'Roman filters from Jeff Kruk: ' 'Roman_effarea_20201130.txt '} for name, fname in [('f062', 'roman_f062.dat'), # R ('f087', 'roman_f087.dat'), # Z ('f106', 'roman_f106.dat'), # Y ('f129', 'roman_f129.dat'), # J ('f158', 'roman_f158.dat'), # H ('f184', 'roman_f184.dat'), # F ('f213', 'roman_f213.dat'), # K ('f146', 'roman_f146.dat')]: # Wide _BANDPASSES.register_loader(name, load_bandpass_remote_um, args=('bandpasses/roman-wfi/' + fname,), meta=roman_meta) # ZTF ztf_meta = { 'filterset': 'ztf', 'retrieved': '7 Jun 2018', 'description': 'ZTF filters from Uli Feindt. No atmospheric correction.'} for name, fname in [('ztfg', 'bandpasses/ztf/P48_g.dat'), ('ztfr', 'bandpasses/ztf/P48_R.dat'), ('ztfi', 'bandpasses/ztf/P48_I.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=ztf_meta) # Swift UVOT swift_meta = { 'filterset': 'swift-uvot', 'retrieved': '19 May 2020', 'description': "Swift UVOT filters retreieved from the Spanish Virtual " "Observatory filter profile service." } for name, fname in [('uvot::b', 'bandpasses/swift/Swift_UVOT.B.dat'), ('uvot::u', 'bandpasses/swift/Swift_UVOT.U.dat'), ('uvot::uvm2', 'bandpasses/swift/Swift_UVOT.UVM2.dat'), ('uvot::uvw1', 'bandpasses/swift/Swift_UVOT.UVW1.dat'), ('uvot::uvw2', 'bandpasses/swift/Swift_UVOT.UVW2.dat'), ('uvot::v', 'bandpasses/swift/Swift_UVOT.V.dat'), ('uvot::white', 'bandpasses/swift/Swift_UVOT.white.dat')]: _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(fname,), meta=swift_meta) # Pan-STARRS1 ps1_meta = { 'filterset': 'ps1', 'dataurl': 'https://ipp.ifa.hawaii.edu/ps1.filters/', 'reference': ('T12', '`Tonry 2012 `__, Table 3'), 'retrieved': '17 Aug 2021', 'description': 'Pan-STARRS1 filters at airmass 1.2' } for filter_id in ['open', 'g', 'r', 'i', 'z', 'y', 'w']: name = 'ps1::{}'.format(filter_id) fname = "bandpasses/ps1/ps1_{}.dat".format(filter_id) _BANDPASSES.register_loader(name, load_bandpass_remote_nm, args=(fname,), meta=ps1_meta) # ATLAS atlas_meta = { 'filterset': 'atlas', 'retrieved': '28 Dec 2021', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': ('ATLAS filters from SVO (includes filter, optics,' 'detector and atmosphere.)')} for filt in ['Cyan', 'Orange']: name = 'atlas' + filt[0].lower() relpath = 'bandpasses/atlas/Atlas.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=atlas_meta) # 2MASS twomass_meta = { 'filterset': '2MASS', 'retrieved': '1 Feb 2022', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': ('2MASS filters from SVO (includes filter, instrument,' 'and atmosphere.)')} for filt in ['J', 'H', 'Ks']: name = '2mass' + filt.lower() relpath = 'bandpasses/2mass/2mass.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=twomass_meta) # Gaia gaia_meta = { 'filterset': 'gaia', 'retrieved': '27 Sep 2022', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': ('eDR3 Gaia filters from SVO (includes filter, instrument,' 'and optics.)')} for filt in ['Gbp', 'G', 'Grp', 'Grvs']: name = 'gaia::' + filt.lower() relpath = 'bandpasses/gaia/gaia.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=gaia_meta) # TESS tess_meta = { 'filterset': 'tess', 'retrieved': '7 March 2023', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': 'TESS filter from SVO (includes filter and instrument)' } for filt in ['Red']: name = 'tess' relpath = 'bandpasses/tess/tess.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=tess_meta) # GALEX galex_meta = { 'filterset': 'galex', 'retrieved': '10 March 2025', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': 'GALEX filters from SVO (includes filter and instrument)' } for filt in ['fuv', 'nuv']: name = 'galex::' + filt relpath = 'bandpasses/galex/galex.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=galex_meta) # GOTO goto_meta = { 'filterset': 'goto', 'retrieved': '16 June 2023', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': ('GOTO filters from SVO (includes filter, optics,' 'detector and atmosphere.)')} for filt in ['B', 'G', 'L', 'R']: name = 'goto' + filt[0].lower() relpath = 'bandpasses/goto/goto.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=goto_meta) # SkyMapper skymapper_meta = { 'filterset': 'skymapper', 'retrieved': '1 Novermber 2024', 'dataurl': ('http://svo2.cab.inta-csic.es/svo/theory/fps/getdata.php?' 'format=ascii&id=Misc'), 'description': ('Normalized SkyMapper filters from SVO (includes filter,' 'optics, detector and atmosphere.)')} for filt in ['u', 'g', 'r', 'i', 'z']: name = 'skymapper' + filt[0].lower() relpath = 'bandpasses/skymapper/skymapper.{}'.format(filt) _BANDPASSES.register_loader(name, load_bandpass_remote_aa, args=(relpath,), meta=skymapper_meta) # ============================================================================= # interpolators megacam_meta = {'filterset': 'megacampsf'} def load_megacampsf(letter, name=None): abspath = DATADIR.abspath('bandpasses/megacampsf', isdir=True) return snfitio.read_snfit_bandpass_interpolator(abspath, letter, name=name) for letter in ('u', 'g', 'r', 'i', 'z', 'y'): _BANDPASS_INTERPOLATORS.register_loader('megacampsf::' + letter, load_megacampsf, args=(letter,), meta=megacam_meta) def load_general_bandpass_interpolator(relpath, band, name=None, version=None): """Extract variable bandpass information from an HDF5 file. Return an instance of GeneralBandpassInterpolator created from the HDF5 file located at `DATADIR / relpath` for a given `band`. """ filename = DATADIR.abspath(relpath) with h5py.File(filename, 'r') as f: static = f['static'] static_transmissions = [static[k][...] for k in static] if 'qe' in f: specific_sensor_qe = { int(k): v[...] for k, v in f['/qe/map'].items()} else: specific_sensor_qe = None to_focalplane = { int(k): v[...] for k, v in f['/transforms/to_focalplane'].items()} to_filter = { int(k): v[...] for k, v in f['/transforms/to_filter'].items()} transforms = Transforms(to_focalplane, to_filter) g = f['bandpasses'][band] if 'radii' in g: variable_transmission = ( g['radii'][...], g['wave'][...], g['trans'][...]) elif 'X' in g and 'Y' in g: variable_transmission = ( g['X'][...], g['Y'][...], g['wave'][...], g['trans'][...]) else: # pragma: no cover raise ValueError( 'failed to load interpolator from {} for band {}'.format( relpath, band)) return GeneralBandpassInterpolator( static_transmissions=static_transmissions, specific_sensor_qe=specific_sensor_qe, variable_transmission=variable_transmission, transforms=transforms, name=name) def load_default_bandpasses(relpath, band, name=None, version=None): """Extract the static (averaged) bandpass information from an HDF5 file. Return an instance of Bandpass created from the HDF5 file located at `DATADIR / relpath` for a given `band`. """ filename = DATADIR.abspath(relpath) with h5py.File(filename, 'r') as f: bandpass = f['averaged_bandpasses'][band] return Bandpass( bandpass['wave'][...], bandpass['trans'][...], name=name) # ZTF variable bandpasses ztfv_meta = { 'filterset': 'ztf', 'retrieved': '22 December 2023', 'description': ( 'A re-determination of the ZTF filters by P. Rosnet et al ' '(ZTF-II IN2P3 participation group)')} for version in ('0',): for band in ('g', 'r', 'I'): name = 'ztf::' + band relpath = 'bandpasses/ztf/ztf_v{}.hdf5'.format(version) _BANDPASS_INTERPOLATORS.register_loader( name, load_general_bandpass_interpolator, args=(relpath, band), version=version, meta=ztfv_meta) # ZTF default bandpasses ztfd_meta = { 'filterset': 'ztf', 'retrieved': '22 December 2023', 'description': ( 'A re-determination of the ZTF filters by P. Rosnet et al ' '(ZTF-II IN2P3 participation group) - focal plane average')} for version in ('0',): for band in ('g', 'r', 'I'): name = 'ztf::' + band relpath = 'bandpasses/ztf/ztf_v{}.hdf5'.format(version) _BANDPASSES.register_loader( name, load_default_bandpasses, args=(relpath, band), version=version, meta=ztfd_meta) # megacam6 (re-measurements of the decommissioned MegaCam filters @ LMA) megacamv_meta = { 'filterset': 'megacam6', 'retrieved': '22 December 2023', 'description': ( 'A re-determination of the decommissioned MegaCam ' 'filters by M. Betoule and LMA')} for version in ('0',): for band in ('g', 'r', 'i', 'i2', 'z'): name = 'megacam6::' + band relpath = 'bandpasses/megacam/megacam6_v{}.hdf5'.format(version) _BANDPASS_INTERPOLATORS.register_loader( name, load_general_bandpass_interpolator, args=(relpath, band), version=version, meta=megacamv_meta) # megacam6 default bandpasses megacamd_meta = { 'filterset': 'megacam6', 'retrieved': '22 December 2023', 'description': ( 'A re-determination of the decommissioned MegaCam ' 'filters by M. Betoule and LMA -- focal plane average')} for version in ('0',): for band in ('g', 'r', 'i', 'i2', 'z'): name = 'megacam6::' + band relpath = 'bandpasses/megacam/megacam6_v{}.hdf5'.format(version) _BANDPASSES.register_loader( name, load_default_bandpasses, args=(relpath, band), version=version, meta=megacamd_meta) # HSC - Tanaki version hscv_meta = { 'filterset': 'hsc', 'retrieved': '22 December 2023', 'description': ( 'A model of the HSC filters - ' 'built on a series of measurements by et al.')} for version in ('0',): for band in ('g', 'r', 'r2', 'i', 'i2', 'z', 'Y'): name = 'hsc::' + band relpath = 'bandpasses/hsc/hsc_v{}.hdf5'.format(version) _BANDPASS_INTERPOLATORS.register_loader( name, load_general_bandpass_interpolator, args=(relpath, band), version=version, meta=hscv_meta) hscd_meta = { 'filterset': 'hsc', 'retrieved': '22 December 2023', 'description': ( 'A model of the HSC filters - ' 'built on a series of measurements by et al. -- ' 'focal plane average')} for version in ('0',): for band in ('g', 'r', 'r2', 'i', 'i2', 'z', 'Y'): name = 'hsc::' + band relpath = 'bandpasses/hsc/hsc_v{}.hdf5'.format(version) _BANDPASSES.register_loader( name, load_default_bandpasses, args=(relpath, band), version=version, meta=hscd_meta) ultrasat_meta = {'filterset': 'ultrasat'} def load_ultrasat(name=None): wavelengths = DATADIR.abspath('bandpasses/ultrasat/Wavelength.dat') Rdeg = DATADIR.abspath('bandpasses/ultrasat/Rdeg.dat') transmission = DATADIR.abspath('bandpasses/ultrasat/ULTRASAT_TR.dat') wavelengths = np.loadtxt(wavelengths) Rdeg = np.loadtxt(Rdeg) transmission = np.loadtxt(transmission, delimiter=',').T # transmission functions at each radius radial_transmissions = [] for r, tr in zip(Rdeg, transmission): radial_transmissions.append((r, wavelengths*u.AA, tr)) return BandpassInterpolator([], radial_transmissions, name=name) _BANDPASS_INTERPOLATORS.register_loader('ultrasat', load_ultrasat, meta=ultrasat_meta) # ============================================================================= # Sources def load_timeseries_ascii(relpath, zero_before=False, time_spline_degree=3, name=None, version=None): abspath = DATADIR.abspath(relpath) phase, wave, flux = io.read_griddata_ascii(abspath) return TimeSeriesSource(phase, wave, flux, name=name, version=version, zero_before=zero_before, time_spline_degree=time_spline_degree) def load_timeseries_fits(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath) phase, wave, flux = io.read_griddata_fits(abspath) return TimeSeriesSource(phase, wave, flux, name=name, version=version) def load_timeseries_fits_local(pkg_data_name, name=None, version=None): fname = get_pkg_data_filename(pkg_data_name) phase, wave, flux = io.read_griddata_fits(fname) return TimeSeriesSource(phase, wave, flux, name=name, version=version) def load_salt2model(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath, isdir=True) return SALT2Source(modeldir=abspath, name=name, version=version) def load_salt3model(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath, isdir=True) return SALT3Source(modeldir=abspath, name=name, version=version) def load_2011fe(relpath, name=None, version=None): # filter warnings about RADESYS keyword in files warnings.filterwarnings('ignore', category=wcs.FITSFixedWarning, append=True) abspath = DATADIR.abspath(relpath, isdir=True) phasestrs = [] spectra = [] disp = None for fname in os.listdir(abspath): if fname[-4:] == '.fit': hdulist = fits.open(join(abspath, fname)) flux_density = hdulist[0].data phasestrs.append(fname[-8:-4]) # like 'P167' or 'M167' spectra.append(flux_density) # Get dispersion values if we haven't yet # (dispersion should be the same for all) if disp is None: w = wcs.WCS(hdulist[0].header) nflux = len(flux_density) idx = np.arange(nflux) # pixel coords idx.shape = (nflux, 1) # make it 2-d disp = w.wcs_pix2world(idx, 0)[:, 0] hdulist.close() # get phases in floats phases = [] for phasestr in phasestrs: phase = 0.1 * float(phasestr[1:]) if phasestr[0] == 'M': phase = -phase phases.append(phase) # Add a point at explosion. # The phase of explosion is given in the paper as # t_expl - t_bmax = 55796.696 - 55814.51 = -17.814 # where t_expl is from Nugent et al (2012) phases.append(-17.814) spectra.append(np.zeros_like(spectra[0])) # order spectra and put them all together spectra = sorted(zip(phases, spectra), key=lambda x: x[0]) flux = np.array([s[1] for s in spectra]) phases = np.array(phases) phases.sort() return TimeSeriesSource(phases, disp, flux, time_spline_degree=1, name=name, version=version) # Nugent models website = 'https://c3.lbl.gov/nugent/nugent_templates.html' subclass = '`~sncosmo.TimeSeriesSource`' n02ref = ('N02', 'Nugent, Kim & Permutter 2002 ' '') s04ref = ('S04', 'Stern, et al. 2004 ' '') l05ref = ('L05', 'Levan et al. 2005 ' '') g99ref = ('G99', 'Gilliland, Nugent & Phillips 1999 ' '') fa23ref = ('F23', 'Fitz Axen and Nugent 2023 ' '') nugent_models = [('sn1a', '1.2', 'SN Ia', n02ref, 3), ('sn91t', '1.1', 'SN Ia', s04ref, 3), ('sn91bg', '1.1', 'SN Ia', n02ref, 3), ('sn1superc', '1.0', 'SN Ia', fa23ref, 1), ('sn1bc', '1.1', 'SN Ib/c', l05ref, 3), ('hyper', '1.2', 'SN Ib/c', l05ref, 1), ('sn2p', '1.2', 'SN IIP', g99ref, 1), ('sn2l', '1.2', 'SN IIL', g99ref, 1), ('sn2n', '2.1', 'SN IIn', g99ref, 1)] for suffix, ver, sntype, ref, time_spline_degree in nugent_models: name = "nugent-" + suffix relpath = "models/nugent/{0}_flux.v{1}.dat".format(suffix, ver) _SOURCES.register_loader(name, load_timeseries_ascii, args=(relpath, False, time_spline_degree), version=ver, meta={'url': website, 'type': sntype, 'subclass': subclass, 'reference': ref}) # Sako et al 2011 models ref = ('S11', 'Sako et al. 2011 ' '') website = 'http://sdssdp62.fnal.gov/sdsssn/SNANA-PUBLIC/' subclass = '`~sncosmo.TimeSeriesSource`' note = "extracted from SNANA's SNDATA_ROOT on 29 March 2013." for name, sntype, fn in [('s11-2004hx', 'SN IIL/P', 'S11_SDSS-000018.SED'), ('s11-2005lc', 'SN IIP', 'S11_SDSS-001472.SED'), ('s11-2005hl', 'SN Ib', 'S11_SDSS-002000.SED'), ('s11-2005hm', 'SN Ib', 'S11_SDSS-002744.SED'), ('s11-2005gi', 'SN IIP', 'S11_SDSS-003818.SED'), ('s11-2006fo', 'SN Ic', 'S11_SDSS-013195.SED'), ('s11-2006jo', 'SN Ib', 'S11_SDSS-014492.SED'), ('s11-2006jl', 'SN IIP', 'S11_SDSS-014599.SED')]: meta = {'url': website, 'type': sntype, 'subclass': subclass, 'reference': ref, 'note': note} _SOURCES.register_loader(name, load_timeseries_ascii, args=('models/sako/' + fn,), version='1.0', meta=meta) # Hsiao models meta = {'url': 'http://csp.obs.carnegiescience.edu/data/snpy', 'type': 'SN Ia', 'subclass': '`~sncosmo.TimeSeriesSource`', 'reference': ('H07', 'Hsiao et al. 2007 ' ''), 'note': 'extracted from the SNooPy package on 21 Dec 2012.'} for version, fn in [('1.0', 'Hsiao_SED.fits'), ('2.0', 'Hsiao_SED_V2.fits'), ('3.0', 'Hsiao_SED_V3.fits')]: _SOURCES.register_loader('hsiao', load_timeseries_fits, args=('models/hsiao/' + fn,), version=version, meta=meta) # subsampled version of Hsiao v3.0, for testing purposes. _SOURCES.register_loader('hsiao-subsampled', load_timeseries_fits_local, args=('data/models/Hsiao_SED_V3_subsampled.fits',), version='3.0', meta=meta) # SALT2 models website = 'http://supernovae.in2p3.fr/salt/doku.php?id=salt_templates' g10ref = ('G10', 'Guy et al. 2010 ' '') b14ref = ('B14b', 'Betoule et al. 2014 ' '') t21ref = ('T21', 'Taylor et al. 2021 ' '') b22ref = ('B22', 'Brout et al. 2022 ' '') t23ref = ('T23', 'Taylor et al. 2023 ' '') for topdir, ver, ref in [('salt2-2-0', '2.0', g10ref), ('salt2-4', '2.4', b14ref), ('salt2-T21', 'T21', t21ref), ('salt2-Pan+', 'B22', b22ref), ('salt2-k21-frag', 'T23', t23ref), ]: meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT2Source`', 'url': website, 'reference': ref} _SOURCES.register_loader('salt2', load_salt2model, args=('models/salt2/'+topdir,), version=ver, meta=meta) # SALT2 extended meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT2Source`', 'url': 'http://sdssdp62.fnal.gov/sdsssn/SNANA-PUBLIC/', 'note': "extracted from SNANA's SNDATA_ROOT on 15 August 2013."} _SOURCES.register_loader('salt2-extended', load_salt2model, args=('models/snana/salt2_extended',), version='1.0', meta=meta) ref = ('SNSEDExtend', 'Pierel et al. 2018' '') meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT2Source`', 'ref': ref} _SOURCES.register_loader('salt2-extended', load_salt2model, args=('models/pierel/salt2-extended',), version='2.0', meta=meta) # SALT3-NIR meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT3Source`', 'url': 'https://doi.org/10.5281/zenodo.7068818', 'note': """See Pierel et al. 2022, ApJ."""} _SOURCES.register_loader('salt3-nir', load_salt3model, args=('models/salt3-nir/salt3nir-p22',), version='1.0', meta=meta) # SALT3 meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT3Source`', 'url': 'https://salt3.readthedocs.io/en/latest/', 'note': """See Kenworthy et al. 2021, ApJ, 923, 265K. Revised with Fragilistic calibration (Brout et al., 2022)"""} _SOURCES.register_loader('salt3', load_salt3model, args=('models/salt3/salt3-f22',), version='2.0', meta=meta) meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SALT2Source`', 'url': 'http://snana.uchicago.edu/', 'note': "extracted from SNANA's SNDATA_ROOT on 24 April 2018. SALT2" " model with wide wavelength range, Hounsell et al. 2017", 'reference': ('H17', 'Hounsell et al. 2017 ' '')} _SOURCES.register_loader('salt2-extended-h17', load_salt2model, args=('models/snana/salt2-h17',), version='1.0', meta=meta) # Alias to 'salt2-h17' for backwards-compatibility _SOURCES.alias('salt2-h17', 'salt2-extended-h17', new_version='1.0', existing_version='1.0') # 2011fe meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.TimeSeriesSource`', 'url': 'http://snfactory.lbl.gov/snf/data', 'reference': ('P13', 'Pereira et al. 2013 ' '')} _SOURCES.register_loader('snf-2011fe', load_2011fe, version='1.0', args=('models/snf/SN2011fe',), meta=meta) # SNANA CC SN models url = 'http://das.sdss2.org/ge/sample/sdsssn/SNANA-PUBLIC/' subclass = '`~sncosmo.TimeSeriesSource`' ref = ('SNANA', 'Kessler et al. 2009 ' '') note = "extracted from SNANA's SNDATA_ROOT on 5 August 2014." # 'PSNID' denotes that model is used in PSNID. models = [('snana-2004fe', 'SN Ic', 'CSP-2004fe.SED'), ('snana-2004gq', 'SN Ic', 'CSP-2004gq.SED'), ('snana-sdss004012', 'SN Ic', 'SDSS-004012.SED'), # no IAU name ('snana-2006fo', 'SN Ic', 'SDSS-013195.SED'), # PSNID ('snana-sdss014475', 'SN Ic', 'SDSS-014475.SED'), # no IAU name ('snana-2006lc', 'SN Ic', 'SDSS-015475.SED'), ('snana-2007ms', 'SN II-pec', 'SDSS-017548.SED'), # type Ic in SNANA ('snana-04d1la', 'SN Ic', 'SNLS-04D1la.SED'), ('snana-04d4jv', 'SN Ic', 'SNLS-04D4jv.SED'), ('snana-2004gv', 'SN Ib', 'CSP-2004gv.SED'), ('snana-2006ep', 'SN Ib', 'CSP-2006ep.SED'), ('snana-2007Y', 'SN Ib', 'CSP-2007Y.SED'), ('snana-2004ib', 'SN Ib', 'SDSS-000020.SED'), ('snana-2005hm', 'SN Ib', 'SDSS-002744.SED'), # PSNID ('snana-2006jo', 'SN Ib', 'SDSS-014492.SED'), # PSNID ('snana-2007nc', 'SN Ib', 'SDSS-019323.SED'), ('snana-2004hx', 'SN IIP', 'SDSS-000018.SED'), # PSNID ('snana-2005gi', 'SN IIP', 'SDSS-003818.SED'), # PSNID ('snana-2006gq', 'SN IIP', 'SDSS-013376.SED'), ('snana-2006kn', 'SN IIP', 'SDSS-014450.SED'), ('snana-2006jl', 'SN IIP', 'SDSS-014599.SED'), # PSNID ('snana-2006iw', 'SN IIP', 'SDSS-015031.SED'), ('snana-2006kv', 'SN IIP', 'SDSS-015320.SED'), ('snana-2006ns', 'SN IIP', 'SDSS-015339.SED'), ('snana-2007iz', 'SN IIP', 'SDSS-017564.SED'), ('snana-2007nr', 'SN IIP', 'SDSS-017862.SED'), ('snana-2007kw', 'SN IIP', 'SDSS-018109.SED'), ('snana-2007ky', 'SN IIP', 'SDSS-018297.SED'), ('snana-2007lj', 'SN IIP', 'SDSS-018408.SED'), ('snana-2007lb', 'SN IIP', 'SDSS-018441.SED'), ('snana-2007ll', 'SN IIP', 'SDSS-018457.SED'), ('snana-2007nw', 'SN IIP', 'SDSS-018590.SED'), ('snana-2007ld', 'SN IIP', 'SDSS-018596.SED'), ('snana-2007md', 'SN IIP', 'SDSS-018700.SED'), ('snana-2007lz', 'SN IIP', 'SDSS-018713.SED'), ('snana-2007lx', 'SN IIP', 'SDSS-018734.SED'), ('snana-2007og', 'SN IIP', 'SDSS-018793.SED'), ('snana-2007ny', 'SN IIP', 'SDSS-018834.SED'), ('snana-2007nv', 'SN IIP', 'SDSS-018892.SED'), ('snana-2007pg', 'SN IIP', 'SDSS-020038.SED'), ('snana-2006ez', 'SN IIn', 'SDSS-012842.SED'), ('snana-2006ix', 'SN IIn', 'SDSS-013449.SED')] for name, sntype, fn in models: relpath = 'models/snana/' + fn meta = {'url': url, 'subclass': subclass, 'type': sntype, 'ref': ref, 'note': note} _SOURCES.register_loader(name, load_timeseries_ascii, args=(relpath,), version='1.0', meta=meta) # P18 p18Models_CC = [('snana-2004fe', 'SN Ic', 'CSP-2004fe.SED'), ('snana-2004gq', 'SN Ic', 'CSP-2004gq.SED'), ('snana-sdss004012', 'SN Ic', 'SDSS-004012.SED'), # no IAU ('snana-2006fo', 'SN Ic', 'SDSS-013195.SED'), # PSNID ('snana-sdss014475', 'SN Ic', 'SDSS-014475.SED'), # no IAU ('snana-2006lc', 'SN Ic', 'SDSS-015475.SED'), ('snana-2007ms', 'SN II-pec', 'SDSS-017548.SED'), ('snana-04d1la', 'SN Ic', 'SNLS-04D1la.SED'), ('snana-04d4jv', 'SN Ic', 'SNLS-04D4jv.SED'), ('snana-2004gv', 'SN Ib', 'CSP-2004gv.SED'), ('snana-2006ep', 'SN Ib', 'CSP-2006ep.SED'), ('snana-2007Y', 'SN Ib', 'CSP-2007Y.SED'), ('snana-2004ib', 'SN Ib', 'SDSS-000020.SED'), ('snana-2005hm', 'SN Ib', 'SDSS-002744.SED'), # PSNID ('snana-2006jo', 'SN Ib', 'SDSS-014492.SED'), # PSNID ('snana-2007nc', 'SN Ib', 'SDSS-019323.SED'), ('snana-2004hx', 'SN IIP', 'SDSS-000018.SED'), # PSNID ('snana-2005gi', 'SN IIP', 'SDSS-003818.SED'), # PSNID ('snana-2006gq', 'SN IIP', 'SDSS-013376.SED'), ('snana-2006kn', 'SN IIP', 'SDSS-014450.SED'), ('snana-2006jl', 'SN IIP', 'SDSS-014599.SED'), # PSNID ('snana-2006iw', 'SN IIP', 'SDSS-015031.SED'), ('snana-2006kv', 'SN IIP', 'SDSS-015320.SED'), ('snana-2006ns', 'SN IIP', 'SDSS-015339.SED'), ('snana-2007iz', 'SN IIP', 'SDSS-017564.SED'), ('snana-2007nr', 'SN IIP', 'SDSS-017862.SED'), ('snana-2007kw', 'SN IIP', 'SDSS-018109.SED'), ('snana-2007ky', 'SN IIP', 'SDSS-018297.SED'), ('snana-2007lj', 'SN IIP', 'SDSS-018408.SED'), ('snana-2007lb', 'SN IIP', 'SDSS-018441.SED'), ('snana-2007ll', 'SN IIP', 'SDSS-018457.SED'), ('snana-2007nw', 'SN IIP', 'SDSS-018590.SED'), ('snana-2007ld', 'SN IIP', 'SDSS-018596.SED'), ('snana-2007md', 'SN IIP', 'SDSS-018700.SED'), ('snana-2007lz', 'SN IIP', 'SDSS-018713.SED'), ('snana-2007lx', 'SN IIP', 'SDSS-018734.SED'), ('snana-2007og', 'SN IIP', 'SDSS-018793.SED'), ('snana-2007ny', 'SN IIP', 'SDSS-018834.SED'), ('snana-2007nv', 'SN IIP', 'SDSS-018892.SED'), ('snana-2007pg', 'SN IIP', 'SDSS-020038.SED')] ref = ('SNSEDExtend', 'Pierel et al. 2018' '') for name, sntype, fn in p18Models_CC: relpath = 'models/pierel/' + fn meta = {'subclass': '`~sncosmo.TimeSeriesSource`', 'type': sntype, 'ref': ref} _SOURCES.register_loader(name, load_timeseries_ascii, args=(relpath,), version='2.0', meta=meta) # V19 V19_CC_models = [ ('v19-asassn14jb-corr', '1.0', 'SN II', 'V19_ASASSN14jb_HostExtCorr.SED'), ('v19-asassn14jb', '1.0', 'SN II', 'V19_ASASSN14jb_noHostExtCorr.SED'), ('v19-asassn15oz-corr', '1.0', 'SN II', 'V19_ASASSN15oz_HostExtCorr.SED'), ('v19-asassn15oz', '1.0', 'SN II', 'V19_ASASSN15oz_noHostExtCorr.SED'), ('v19-1987A-corr', '1.0', 'SN II', 'V19_SN1987A_HostExtCorr.SED'), ('v19-1987A', '1.0', 'SN II', 'V19_SN1987A_noHostExtCorr.SED'), ('v19-1993J-corr', '1.0', 'SN IIb', 'V19_SN1993J_HostExtCorr.SED'), ('v19-1993J', '1.0', 'SN IIb', 'V19_SN1993J_noHostExtCorr.SED'), ('v19-1994I-corr', '1.0', 'SN Ic', 'V19_SN1994I_HostExtCorr.SED'), ('v19-1994I', '1.0', 'SN Ic', 'V19_SN1994I_noHostExtCorr.SED'), ('v19-1998bw-corr', '1.0', 'SN Ic-BL', 'V19_SN1998bw_HostExtCorr.SED'), ('v19-1998bw', '1.0', 'SN Ic-BL', 'V19_SN1998bw_noHostExtCorr.SED'), ('v19-1999dn-corr', '1.0', 'SN IIb', 'V19_SN1999dn_HostExtCorr.SED'), ('v19-1999dn', '1.0', 'SN IIb', 'V19_SN1999dn_noHostExtCorr.SED'), ('v19-1999em-corr', '1.0', 'SN II', 'V19_SN1999em_HostExtCorr.SED'), ('v19-1999em', '1.0', 'SN II', 'V19_SN1999em_noHostExtCorr.SED'), ('v19-2002ap-corr', '1.0', 'SN Ic-BL', 'V19_SN2002ap_HostExtCorr.SED'), ('v19-2002ap', '1.0', 'SN Ic-BL', 'V19_SN2002ap_noHostExtCorr.SED'), ('v19-2004aw-corr', '1.0', 'SN Ic', 'V19_SN2004aw_HostExtCorr.SED'), ('v19-2004aw', '1.0', 'SN Ic', 'V19_SN2004aw_noHostExtCorr.SED'), ('v19-2004et-corr', '1.0', 'SN II', 'V19_SN2004et_HostExtCorr.SED'), ('v19-2004et', '1.0', 'SN II', 'V19_SN2004et_noHostExtCorr.SED'), ('v19-2004fe-corr', '1.0', 'SN Ic', 'V19_SN2004fe_HostExtCorr.SED'), ('v19-2004fe', '1.0', 'SN Ic', 'V19_SN2004fe_noHostExtCorr.SED'), ('v19-2004gq-corr', '1.0', 'SN Ib', 'V19_SN2004gq_HostExtCorr.SED'), ('v19-2004gq', '1.0', 'SN Ib', 'V19_SN2004gq_noHostExtCorr.SED'), ('v19-2004gt-corr', '1.0', 'SN Ic', 'V19_SN2004gt_HostExtCorr.SED'), ('v19-2004gt', '1.0', 'SN Ic', 'V19_SN2004gt_noHostExtCorr.SED'), ('v19-2004gv-corr', '1.0', 'SN Ib', 'V19_SN2004gv_HostExtCorr.SED'), ('v19-2004gv', '1.0', 'SN Ib', 'V19_SN2004gv_noHostExtCorr.SED'), ('v19-2005bf-corr', '1.0', 'SN Ib', 'V19_SN2005bf_HostExtCorr.SED'), ('v19-2005bf', '1.0', 'SN Ib', 'V19_SN2005bf_noHostExtCorr.SED'), ('v19-2005hg-corr', '1.0', 'SN Ib', 'V19_SN2005hg_HostExtCorr.SED'), ('v19-2005hg', '1.0', 'SN Ib', 'V19_SN2005hg_noHostExtCorr.SED'), ('v19-2006T-corr', '1.0', 'SN IIb', 'V19_SN2006T_HostExtCorr.SED'), ('v19-2006T', '1.0', 'SN IIb', 'V19_SN2006T_noHostExtCorr.SED'), ('v19-2006aa-corr', '1.0', 'SN IIn', 'V19_SN2006aa_HostExtCorr.SED'), ('v19-2006aa', '1.0', 'SN IIn', 'V19_SN2006aa_noHostExtCorr.SED'), ('v19-2006aj-corr', '1.0', 'SN Ic-BL', 'V19_SN2006aj_HostExtCorr.SED'), ('v19-2006aj', '1.0', 'SN Ic-BL', 'V19_SN2006aj_noHostExtCorr.SED'), ('v19-2006ep-corr', '1.0', 'SN Ib', 'V19_SN2006ep_HostExtCorr.SED'), ('v19-2006ep', '1.0', 'SN Ib', 'V19_SN2006ep_noHostExtCorr.SED'), ('v19-2007Y-corr', '1.0', 'SN Ib', 'V19_SN2007Y_HostExtCorr.SED'), ('v19-2007Y', '1.0', 'SN Ib', 'V19_SN2007Y_noHostExtCorr.SED'), ('v19-2007gr-corr', '1.0', 'SN Ic', 'V19_SN2007gr_HostExtCorr.SED'), ('v19-2007gr', '1.0', 'SN Ic', 'V19_SN2007gr_noHostExtCorr.SED'), ('v19-2007od-corr', '1.0', 'SN II', 'V19_SN2007od_HostExtCorr.SED'), ('v19-2007od', '1.0', 'SN II', 'V19_SN2007od_noHostExtCorr.SED'), ('v19-2007pk-corr', '1.0', 'SN IIn', 'V19_SN2007pk_HostExtCorr.SED'), ('v19-2007pk', '1.0', 'SN IIn', 'V19_SN2007pk_noHostExtCorr.SED'), ('v19-2007ru-corr', '1.0', 'SN Ic-BL', 'V19_SN2007ru_HostExtCorr.SED'), ('v19-2007ru', '1.0', 'SN Ic-BL', 'V19_SN2007ru_noHostExtCorr.SED'), ('v19-2007uy-corr', '1.0', 'SN Ib', 'V19_SN2007uy_HostExtCorr.SED'), ('v19-2007uy', '1.0', 'SN Ib', 'V19_SN2007uy_noHostExtCorr.SED'), ('v19-2008D-corr', '1.0', 'SN Ib', 'V19_SN2008D_HostExtCorr.SED'), ('v19-2008D', '1.0', 'SN Ib', 'V19_SN2008D_noHostExtCorr.SED'), ('v19-2008aq-corr', '1.0', 'SN IIb', 'V19_SN2008aq_HostExtCorr.SED'), ('v19-2008aq', '1.0', 'SN IIb', 'V19_SN2008aq_noHostExtCorr.SED'), ('v19-2008ax-corr', '1.0', 'SN IIb', 'V19_SN2008ax_HostExtCorr.SED'), ('v19-2008ax', '1.0', 'SN IIb', 'V19_SN2008ax_noHostExtCorr.SED'), ('v19-2008bj-corr', '1.0', 'SN II', 'V19_SN2008bj_HostExtCorr.SED'), ('v19-2008bj', '1.0', 'SN II', 'V19_SN2008bj_noHostExtCorr.SED'), ('v19-2008bo-corr', '1.0', 'SN IIb', 'V19_SN2008bo_HostExtCorr.SED'), ('v19-2008bo', '1.0', 'SN IIb', 'V19_SN2008bo_noHostExtCorr.SED'), ('v19-2008fq-corr', '1.0', 'SN IIn', 'V19_SN2008fq_HostExtCorr.SED'), ('v19-2008fq', '1.0', 'SN IIn', 'V19_SN2008fq_noHostExtCorr.SED'), ('v19-2008in-corr', '1.0', 'SN II', 'V19_SN2008in_HostExtCorr.SED'), ('v19-2008in', '1.0', 'SN II', 'V19_SN2008in_noHostExtCorr.SED'), ('v19-2009N-corr', '1.0', 'SN II', 'V19_SN2009N_HostExtCorr.SED'), ('v19-2009N', '1.0', 'SN II', 'V19_SN2009N_noHostExtCorr.SED'), ('v19-2009bb-corr', '1.0', 'SN Ic-BL', 'V19_SN2009bb_HostExtCorr.SED'), ('v19-2009bb', '1.0', 'SN Ic-BL', 'V19_SN2009bb_noHostExtCorr.SED'), ('v19-2009bw-corr', '1.0', 'SN II', 'V19_SN2009bw_HostExtCorr.SED'), ('v19-2009bw', '1.0', 'SN II', 'V19_SN2009bw_noHostExtCorr.SED'), ('v19-2009dd-corr', '1.0', 'SN II', 'V19_SN2009dd_HostExtCorr.SED'), ('v19-2009dd', '1.0', 'SN II', 'V19_SN2009dd_noHostExtCorr.SED'), ('v19-2009ib-corr', '1.0', 'SN II', 'V19_SN2009ib_HostExtCorr.SED'), ('v19-2009ib', '1.0', 'SN II', 'V19_SN2009ib_noHostExtCorr.SED'), ('v19-2009ip-corr', '1.0', 'SN IIn', 'V19_SN2009ip_HostExtCorr.SED'), ('v19-2009ip', '1.0', 'SN IIn', 'V19_SN2009ip_noHostExtCorr.SED'), ('v19-2009iz-corr', '1.0', 'SN Ib', 'V19_SN2009iz_HostExtCorr.SED'), ('v19-2009iz', '1.0', 'SN Ib', 'V19_SN2009iz_noHostExtCorr.SED'), ('v19-2009jf-corr', '1.0', 'SN Ib', 'V19_SN2009jf_HostExtCorr.SED'), ('v19-2009jf', '1.0', 'SN Ib', 'V19_SN2009jf_noHostExtCorr.SED'), ('v19-2009kr-corr', '1.0', 'SN II', 'V19_SN2009kr_HostExtCorr.SED'), ('v19-2009kr', '1.0', 'SN II', 'V19_SN2009kr_noHostExtCorr.SED'), ('v19-2010al-corr', '1.0', 'SN IIn', 'V19_SN2010al_HostExtCorr.SED'), ('v19-2010al', '1.0', 'SN IIn', 'V19_SN2010al_noHostExtCorr.SED'), ('v19-2011bm-corr', '1.0', 'SN Ic', 'V19_SN2011bm_HostExtCorr.SED'), ('v19-2011bm', '1.0', 'SN Ic', 'V19_SN2011bm_noHostExtCorr.SED'), ('v19-2011dh-corr', '1.0', 'SN IIb', 'V19_SN2011dh_HostExtCorr.SED'), ('v19-2011dh', '1.0', 'SN IIb', 'V19_SN2011dh_noHostExtCorr.SED'), ('v19-2011ei-corr', '1.0', 'SN IIb', 'V19_SN2011ei_HostExtCorr.SED'), ('v19-2011ei', '1.0', 'SN IIb', 'V19_SN2011ei_noHostExtCorr.SED'), ('v19-2011fu-corr', '1.0', 'SN IIb', 'V19_SN2011fu_HostExtCorr.SED'), ('v19-2011fu', '1.0', 'SN IIb', 'V19_SN2011fu_noHostExtCorr.SED'), ('v19-2011hs-corr', '1.0', 'SN IIb', 'V19_SN2011hs_HostExtCorr.SED'), ('v19-2011hs', '1.0', 'SN IIb', 'V19_SN2011hs_noHostExtCorr.SED'), ('v19-2011ht-corr', '1.0', 'SN IIn', 'V19_SN2011ht_HostExtCorr.SED'), ('v19-2011ht', '1.0', 'SN IIn', 'V19_SN2011ht_noHostExtCorr.SED'), ('v19-2012A-corr', '1.0', 'SN II', 'V19_SN2012A_HostExtCorr.SED'), ('v19-2012A', '1.0', 'SN II', 'V19_SN2012A_noHostExtCorr.SED'), ('v19-2012ap-corr', '1.0', 'SN Ic-BL', 'V19_SN2012ap_HostExtCorr.SED'), ('v19-2012ap', '1.0', 'SN Ic-BL', 'V19_SN2012ap_noHostExtCorr.SED'), ('v19-2012au-corr', '1.0', 'SN Ib', 'V19_SN2012au_HostExtCorr.SED'), ('v19-2012au', '1.0', 'SN Ib', 'V19_SN2012au_noHostExtCorr.SED'), ('v19-2012aw-corr', '1.0', 'SN II', 'V19_SN2012aw_HostExtCorr.SED'), ('v19-2012aw', '1.0', 'SN II', 'V19_SN2012aw_noHostExtCorr.SED'), ('v19-2013ab-corr', '1.0', 'SN II', 'V19_SN2013ab_HostExtCorr.SED'), ('v19-2013ab', '1.0', 'SN II', 'V19_SN2013ab_noHostExtCorr.SED'), ('v19-2013am-corr', '1.0', 'SN II', 'V19_SN2013am_HostExtCorr.SED'), ('v19-2013am', '1.0', 'SN II', 'V19_SN2013am_noHostExtCorr.SED'), ('v19-2013by-corr', '1.0', 'SN II', 'V19_SN2013by_HostExtCorr.SED'), ('v19-2013by', '1.0', 'SN II', 'V19_SN2013by_noHostExtCorr.SED'), ('v19-2013df-corr', '1.0', 'SN IIb', 'V19_SN2013df_HostExtCorr.SED'), ('v19-2013df', '1.0', 'SN IIb', 'V19_SN2013df_noHostExtCorr.SED'), ('v19-2013ej-corr', '1.0', 'SN II', 'V19_SN2013ej_HostExtCorr.SED'), ('v19-2013ej', '1.0', 'SN II', 'V19_SN2013ej_noHostExtCorr.SED'), ('v19-2013fs-corr', '1.0', 'SN II', 'V19_SN2013fs_HostExtCorr.SED'), ('v19-2013fs', '1.0', 'SN II', 'V19_SN2013fs_noHostExtCorr.SED'), ('v19-2013ge-corr', '1.0', 'SN Ic', 'V19_SN2013ge_HostExtCorr.SED'), ('v19-2013ge', '1.0', 'SN Ic', 'V19_SN2013ge_noHostExtCorr.SED'), ('v19-2014G-corr', '1.0', 'SN II', 'V19_SN2014G_HostExtCorr.SED'), ('v19-2014G', '1.0', 'SN II', 'V19_SN2014G_noHostExtCorr.SED'), ('v19-2016X-corr', '1.0', 'SN II', 'V19_SN2016X_HostExtCorr.SED'), ('v19-2016X', '1.0', 'SN II', 'V19_SN2016X_noHostExtCorr.SED'), ('v19-2016bkv-corr', '1.0', 'SN II', 'V19_SN2016bkv_HostExtCorr.SED'), ('v19-2016bkv', '1.0', 'SN II', 'V19_SN2016bkv_noHostExtCorr.SED'), ('v19-2016gkg-corr', '1.0', 'SN IIb', 'V19_SN2016gkg_HostExtCorr.SED'), ('v19-2016gkg', '1.0', 'SN IIb', 'V19_SN2016gkg_noHostExtCorr.SED'), ('v19-iptf13bvn-corr', '1.0', 'SN Ib', 'V19_iPTF13bvn_HostExtCorr.SED'), ('v19-iptf13bvn', '1.0', 'SN Ib', 'V19_iPTF13bvn_noHostExtCorr.SED') ] note = "Templates from Vincenzi et al. 19. Each template is extended in the " \ "ultraviolet (1600AA) and in the near infrared (10000AA). Each template " \ "can be used in its original version (v19-sn-name) or in its host dust " \ "extinction corrected version (v19-sn-name-corr)." for name, vrs, sntype, fn in V19_CC_models: relpath = 'models/vincenzi/' + fn meta = {'subclass': '`~sncosmo.TimeSeriesSource`', 'type': sntype, 'ref': ('Vincenzi2019', 'Vincenzi et al. 2019 ' ''), 'note': note, 'url': 'https://github.com/maria-vincenzi/PyCoCo_templates'} _SOURCES.register_loader(name, load_timeseries_ascii, args=(relpath,), version=vrs, meta=meta) # Pop III CC SN models from D.Whalen et al. 2013. meta = {'type': 'PopIII', 'subclass': '`~sncosmo.TimeSeriesSource`', 'reference': ('Whalen13', 'Whalen et al. 2013 ' ''), 'note': "private communication (D.Whalen, May 2014)."} for name, fn in [('whalen-z15b', 'popIII-z15B.sed.restframe10pc.dat'), ('whalen-z15d', 'popIII-z15D.sed.restframe10pc.dat'), ('whalen-z15g', 'popIII-z15G.sed.restframe10pc.dat'), ('whalen-z25b', 'popIII-z25B.sed.restframe10pc.dat'), ('whalen-z25d', 'popIII-z25D.sed.restframe10pc.dat'), ('whalen-z25g', 'popIII-z25G.sed.restframe10pc.dat'), ('whalen-z40b', 'popIII-z40B.sed.restframe10pc.dat'), ('whalen-z40g', 'popIII-z40G.sed.restframe10pc.dat')]: relpath = 'models/whalen/' + fn _SOURCES.register_loader(name, load_timeseries_ascii, args=(relpath, True), version='1.0', meta=meta) # MLCS2k2 def load_mlcs2k2(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath) return MLCS2k2Source(abspath, name=name, version=version) meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.MLCS2k2Source`', 'reference': ('Jha07', 'Jha, Riess and Kirshner 2007 ' ''), 'note': 'In MLCS2k2 language, this version corresponds to ' '"MLCS2k2 v0.07 rv19-early-smix vectors"'} _SOURCES.register_loader('mlcs2k2', load_mlcs2k2, args=('models/mlcs2k2/mlcs2k2.modelflux.v1.0.fits',), version='1.0', meta=meta) # SNEMO def load_snemo(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath) return SNEMOSource(abspath, name=name, version=version) for name, file, ver in [('snemo2', 'snemo2_ev.dat', '1.0'), ('snemo7', 'snemo7_ev.dat', '1.0'), ('snemo15', 'snemo15_ev.dat', '1.0')]: meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SNEMOSource`', 'url': 'https://snfactory.lbl.gov/snemo/', 'reference': ('Saunders18', 'Saunders et al. 2018 ' '')} _SOURCES.register_loader(name, load_snemo, args=['models/snemo/'+file], version=ver, meta=meta) # SUGAR models def load_sugarmodel(relpath, name=None, version=None): abspath = DATADIR.abspath(relpath, isdir=True) return SUGARSource(abspath, name=name, version=version) for name, files, ver in [('sugar', 'sugar', '1.0')]: meta = {'type': 'SN Ia', 'subclass': '`~sncosmo.SUGARSource`', 'url': 'http://supernovae.in2p3.fr/sugar_template/', 'reference': ('Leget20', 'Leget et al. 2020 ' '')} _SOURCES.register_loader(name, load_sugarmodel, args=['models/sugar/'+files], version=ver, meta=meta) # ============================================================================= # MagSystems def load_ab(name=None): return ABMagSystem(name=name) def load_spectral_magsys_fits(relpath, name=None): abspath = DATADIR.abspath(relpath) hdulist = fits.open(abspath) dispersion = hdulist[1].data['WAVELENGTH'] flux_density = hdulist[1].data['FLUX'] hdulist.close() refspectrum = SpectrumModel(dispersion, flux_density, unit=(u.erg / u.s / u.cm**2 / u.AA), wave_unit=u.AA) return SpectralMagSystem(refspectrum, name=name) def load_csp(name=None): # Values transcribed from # http://csp.obs.carnegiescience.edu/data/filters # on 13 April 2017 return CompositeMagSystem(bands={'cspu': ('bd17', 10.519), 'cspg': ('bd17', 9.644), 'cspr': ('bd17', 9.352), 'cspi': ('bd17', 9.250), 'cspb': ('vega', 0.030), 'cspv3014': ('vega', 0.0096), 'cspv3009': ('vega', 0.0096), 'cspv9844': ('vega', 0.0096), 'cspys': ('vega', 0.), 'cspjs': ('vega', 0.), 'csphs': ('vega', 0.), 'cspk': ('vega', 0.), 'cspyd': ('vega', 0.), 'cspjd': ('vega', 0.), 'csphd': ('vega', 0.)}, name=name) def load_ab_b12(name=None): # offsets are in the sense (mag_SDSS - mag_AB) = offset # -> for example: a source with AB mag = 0. will have SDSS mag = 0.06791 bands = {'sdssu': ('ab', 0.06791), 'sdssg': ('ab', -0.02028), 'sdssr': ('ab', -0.00493), 'sdssi': ('ab', -0.01780), 'sdssz': ('ab', -0.01015)} # add aliases for above for letter in 'ugriz': bands['sdss::' + letter] = bands['sdss' + letter] families = {'megacampsf::u': ('ab', 0.0), 'megacampsf::g': ('ab', 0.0), 'megacampsf::r': ('ab', 0.0), 'megacampsf::i': ('ab', 0.0), 'megacampsf::z': ('ab', 0.0), 'megacampsf::y': ('ab', 0.0)} return CompositeMagSystem(bands=bands, families=families, name=name) def load_jla1(name=None): """JLA1 magnitude system based on BD+17 STIS v003 spectrum""" base = load_spectral_magsys_fits("spectra/bd_17d4708_stisnic_003.fits") bands = {'standard::u': (base, 9.724), 'standard::b': (base, 9.907), 'standard::v': (base, 9.464), 'standard::r': (base, 9.166), 'standard::i': (base, 8.846), 'keplercam::us': (base, 9.724), 'keplercam::b': (base, 9.8803), 'keplercam::v': (base, 9.4722), 'keplercam::r': (base, 9.3523), 'keplercam::i': (base, 9.2542), '4shooter2::us': (base, 9.724), '4shooter2::b': (base, 9.8744), '4shooter2::v': (base, 9.4789), '4shooter2::r': (base, 9.1554), '4shooter2::i': (base, 8.8506), 'swope2::u': (base, 10.514), 'swope2::g': (base, 9.64406), 'swope2::r': (base, 9.3516), 'swope2::i': (base, 9.25), 'swope2::b': (base, 9.876433), 'swope2::v': (base, 9.476626), 'swope2::v1': (base, 9.471276), 'swope2::v2': (base, 9.477482)} return CompositeMagSystem(bands=bands, name=name) _MAGSYSTEMS.register_loader( 'jla1', load_jla1, meta={'subclass': '`~sncosmo.CompositeMagSystem`', 'url': 'http://supernovae.in2p3.fr/sdss_snls_jla/ReadMe.html', 'description': ('JLA1 magnitude system based on BD+17 ' 'STIS v003 spectrum')}) _MAGSYSTEMS.alias('vega2', 'jla1') # AB _MAGSYSTEMS.register_loader( 'ab', load_ab, meta={'subclass': '`~sncosmo.ABMagSystem`', 'description': 'Source of 3631 Jy has magnitude 0 in all bands'}) # Vega, BD17 website = 'ftp://ftp.stsci.edu/cdbs/calspec/' subclass = '`~sncosmo.SpectralMagSystem`' vega_desc = 'Vega (alpha lyrae) has magnitude 0 in all bands.' bd17_desc = 'BD+17d4708 has magnitude 0 in all bands.' for name, fn, desc in [('vega', 'alpha_lyr_stis_007.fits', vega_desc), ('bd17', 'bd_17d4708_stisnic_005.fits', bd17_desc)]: _MAGSYSTEMS.register_loader(name, load_spectral_magsys_fits, args=('spectra/' + fn,), meta={'subclass': subclass, 'url': website, 'description': desc}) # CSP _MAGSYSTEMS.register_loader( 'csp', load_csp, meta={'subclass': '`~sncosmo.CompositeMagSystem`', 'url': 'http://csp.obs.carnegiescience.edu/data/filters', 'description': 'Carnegie Supernova Project magnitude system.'}) # ab_b12 _MAGSYSTEMS.register_loader( 'ab-b12', load_ab_b12, meta={'subclass': '`~sncosmo.CompositeMagSystem`', 'url': 'http://supernovae.in2p3.fr/sdss_snls_jla/ReadMe.html', 'description': 'Betoule et al (2012) calibration of SDSS system.'}) _MAGSYSTEMS.alias('ab_b12', 'ab-b12') _MAGSYSTEMS.alias('vegahst', 'vega') sncosmo-2.12.1/sncosmo/conftest.py000066400000000000000000000010661476435666400171660ustar00rootroot00000000000000import pytest collect_ignore = ["tests/test_download_builtins.py"] def pytest_addoption(parser): parser.addoption("--no-downloads", action="store_true", default=False, help="skip tests that might download data.") def pytest_collection_modifyitems(config, items): if config.getoption("--no-downloads"): skip_test = pytest.mark.skip( reason="--no-downloads set" ) for item in items: if "might_download" in item.keywords: item.add_marker(skip_test) sncosmo-2.12.1/sncosmo/constants.py000066400000000000000000000006261476435666400173560ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Constants used elsewhere in sncosmo.""" import astropy.constants as const import astropy.units as u BANDPASS_TRIM_LEVEL = 0.001 SPECTRUM_BANDFLUX_SPACING = 1.0 MODEL_BANDFLUX_SPACING = 5.0 H_ERG_S = const.h.cgs.value C_AA_PER_S = const.c.to(u.AA / u.s).value HC_ERG_AA = H_ERG_S * C_AA_PER_S FLAMBDA_UNIT = u.erg / u.s / u.cm**2 / u.AA sncosmo-2.12.1/sncosmo/data/000077500000000000000000000000001476435666400156755ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/data/bandpasses/000077500000000000000000000000001476435666400200205ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/000077500000000000000000000000001476435666400214515ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/bessell_b.dat000066400000000000000000000004211476435666400240720ustar00rootroot000000000000003600.0 0.000 3700.0 0.030 3800.0 0.134 3900.0 0.567 4000.0 0.920 4100.0 0.978 4200.0 1.000 4300.0 0.978 4400.0 0.935 4500.0 0.853 4600.0 0.740 4700.0 0.640 4800.0 0.536 4900.0 0.424 5000.0 0.325 5100.0 0.235 5200.0 0.150 5300.0 0.095 5400.0 0.043 5500.0 0.009 5600.0 0.000 sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/bessell_i.dat000066400000000000000000000004531476435666400241060ustar00rootroot000000000000007000.0 0.000 7100.0 0.024 7200.0 0.232 7300.0 0.555 7400.0 0.785 7500.0 0.910 7600.0 0.965 7700.0 0.985 7800.0 0.990 7900.0 0.995 8000.0 1.000 8100.0 1.000 8200.0 0.990 8300.0 0.980 8400.0 0.950 8500.0 0.910 8600.0 0.860 8700.0 0.750 8800.0 0.560 8900.0 0.330 9000.0 0.150 9100.0 0.030 9200.0 0.000 sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/bessell_r.dat000066400000000000000000000004401476435666400241130ustar00rootroot000000000000005500.0 0.00 5600.0 0.23 5700.0 0.74 5800.0 0.91 5900.0 0.98 6000.0 1.00 6100.0 0.98 6200.0 0.96 6300.0 0.93 6400.0 0.90 6500.0 0.86 6600.0 0.81 6700.0 0.78 6800.0 0.72 6900.0 0.67 7000.0 0.61 7100.0 0.56 7200.0 0.51 7300.0 0.46 7400.0 0.40 7500.0 0.35 8000.0 0.14 8500.0 0.03 9000.0 0.00 sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/bessell_ux.dat000066400000000000000000000005051476435666400243100ustar00rootroot000000000000003000.0 0.000 3050.0 0.016 3100.0 0.068 3150.0 0.167 3200.0 0.287 3250.0 0.423 3300.0 0.560 3350.0 0.673 3400.0 0.772 3450.0 0.841 3500.0 0.905 3550.0 0.943 3600.0 0.981 3650.0 0.993 3700.0 1.000 3750.0 0.989 3800.0 0.916 3850.0 0.804 3900.0 0.625 3950.0 0.423 4000.0 0.238 4050.0 0.114 4100.0 0.051 4150.0 0.019 4200.0 0.000 sncosmo-2.12.1/sncosmo/data/bandpasses/bessell/bessell_v.dat000066400000000000000000000004701476435666400241220ustar00rootroot000000000000004700.0 0.000 4800.0 0.030 4900.0 0.163 5000.0 0.458 5100.0 0.780 5200.0 0.967 5300.0 1.000 5400.0 0.973 5500.0 0.898 5600.0 0.792 5700.0 0.684 5800.0 0.574 5900.0 0.461 6000.0 0.359 6100.0 0.270 6200.0 0.197 6300.0 0.135 6400.0 0.081 6500.0 0.045 6600.0 0.025 6700.0 0.017 6800.0 0.013 6900.0 0.009 7000.0 0.000 sncosmo-2.12.1/sncosmo/data/examples/000077500000000000000000000000001476435666400175135ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/data/examples/example_photometric_data.dat000066400000000000000000000044701476435666400252530ustar00rootroot00000000000000@x1 0.5 @c 0.2 @z 0.5 @x0 1.20482820761e-05 @t0 55100.0 time band flux fluxerr zp zpsys 55070.0 sdssg 0.36351153597 0.672843847541 25.0 ab 55072.0512821 sdssr -0.200801295864 0.672843847541 25.0 ab 55074.1025641 sdssi 0.307494232981 0.672843847541 25.0 ab 55076.1538462 sdssz 1.08776103656 0.672843847541 25.0 ab 55078.2051282 sdssg -0.43667895645 0.672843847541 25.0 ab 55080.2564103 sdssr 1.09780966779 0.672843847541 25.0 ab 55082.3076923 sdssi 3.7562685627 0.672843847541 25.0 ab 55084.3589744 sdssz 5.34858894966 0.672843847541 25.0 ab 55086.4102564 sdssg 2.82614187269 0.672843847541 25.0 ab 55088.4615385 sdssr 7.56547045054 0.672843847541 25.0 ab 55090.5128205 sdssi 7.43316961013 0.672843847541 25.0 ab 55092.5641026 sdssz 10.7032994531 0.672843847541 25.0 ab 55094.6153846 sdssg 1.57823662148 0.672843847541 25.0 ab 55096.6666667 sdssr 9.4201672834 0.672843847541 25.0 ab 55098.7179487 sdssi 12.1116206376 0.672843847541 25.0 ab 55100.7692308 sdssz 13.6445335233 0.672843847541 25.0 ab 55102.8205128 sdssg 2.20875359735 0.672843847541 25.0 ab 55104.8717949 sdssr 9.63442013167 0.672843847541 25.0 ab 55106.9230769 sdssi 11.3724767104 0.672843847541 25.0 ab 55108.974359 sdssz 12.5739036768 0.672843847541 25.0 ab 55111.025641 sdssg 0.608391162103 0.672843847541 25.0 ab 55113.0769231 sdssr 5.91642855037 0.672843847541 25.0 ab 55115.1282051 sdssi 7.99428156096 0.672843847541 25.0 ab 55117.1794872 sdssz 7.90862755929 0.672843847541 25.0 ab 55119.2307692 sdssg 0.679196211518 0.672843847541 25.0 ab 55121.2820513 sdssr 4.03447329476 0.672843847541 25.0 ab 55123.3333333 sdssi 7.03584281564 0.672843847541 25.0 ab 55125.3846154 sdssz 6.84086374285 0.672843847541 25.0 ab 55127.4358974 sdssg 0.325442801496 0.672843847541 25.0 ab 55129.4871795 sdssr 2.6597485586 0.672843847541 25.0 ab 55131.5384615 sdssi 3.99520404021 0.672843847541 25.0 ab 55133.5897436 sdssz 5.73989458094 0.672843847541 25.0 ab 55135.6410256 sdssg 0.330702283107 0.672843847541 25.0 ab 55137.6923077 sdssr 0.565286726579 0.672843847541 25.0 ab 55139.7435897 sdssi 3.04318346795 0.672843847541 25.0 ab 55141.7948718 sdssz 5.62692686384 0.672843847541 25.0 ab 55143.8461538 sdssg -0.722654789013 0.672843847541 25.0 ab 55145.8974359 sdssr 1.12091764262 0.672843847541 25.0 ab 55147.9487179 sdssi 2.1246695264 0.672843847541 25.0 ab 55150.0 sdssz 5.3482175645 0.672843847541 25.0 ab sncosmo-2.12.1/sncosmo/data/examples/example_spectrum.dat000066400000000000000000001277341476435666400236000ustar00rootroot00000000000000# wave flux fluxerr 3.000000000000000000e+03 1.093260655088280773e-14 1.250426308430417815e-15 3.010000000000000000e+03 1.342797060155329734e-14 1.306661694472589989e-15 3.020000000000000000e+03 1.112443105885883520e-14 1.348923470511236498e-15 3.030000000000000000e+03 1.454348662326869624e-14 1.374466980476627766e-15 3.040000000000000000e+03 1.236287623155605666e-14 1.385181152610841019e-15 3.050000000000000000e+03 1.597829803420650659e-14 1.387588499467763244e-15 3.060000000000000000e+03 1.337778313844877331e-14 1.388412993788750097e-15 3.070000000000000000e+03 1.363784797016784445e-14 1.394378608315160782e-15 3.080000000000000000e+03 1.400372203722568104e-14 1.412209315788350168e-15 3.090000000000000000e+03 1.455960085498613814e-14 1.447919241643274778e-15 3.100000000000000000e+03 1.269060238214616347e-14 1.504683122089276668e-15 3.110000000000000000e+03 1.678802893570370432e-14 1.584965846029299551e-15 3.120000000000000000e+03 1.794351789137730038e-14 1.691232302366290099e-15 3.130000000000000000e+03 2.087296399998729612e-14 1.825947380003180389e-15 3.140000000000000000e+03 1.904453334582060826e-14 1.991284414884854194e-15 3.150000000000000000e+03 2.359953215704485834e-14 2.182711024920776410e-15 3.160000000000000000e+03 2.567724796431202814e-14 2.388989109984997006e-15 3.170000000000000000e+03 2.552066189205818276e-14 2.598589016993502061e-15 3.180000000000000000e+03 2.680828080564182127e-14 2.799981092862289886e-15 3.190000000000000000e+03 2.695361443889183085e-14 2.981635684507326050e-15 3.200000000000000000e+03 3.190588496273256603e-14 3.133168926489801473e-15 3.210000000000000000e+03 3.005682330061234634e-14 3.248780103951666462e-15 3.220000000000000000e+03 3.391761847227029454e-14 3.323814289680067484e-15 3.230000000000000000e+03 3.658643814457581539e-14 3.353616556462148245e-15 3.240000000000000000e+03 3.756982972292109217e-14 3.333531977085050874e-15 3.250000000000000000e+03 3.619971816633762015e-14 3.259453371137000927e-15 3.260000000000000000e+03 2.826001320949835712e-14 3.139871734631054056e-15 3.270000000000000000e+03 2.638681254300432751e-14 2.995876240005093248e-15 3.280000000000000000e+03 2.669489041364573577e-14 2.849103806498082944e-15 3.290000000000000000e+03 2.882030745874173726e-14 2.721191353348982855e-15 3.300000000000000000e+03 2.702401603081178441e-14 2.633775799796768467e-15 3.310000000000000000e+03 2.595071852731433965e-14 2.602776837212758626e-15 3.320000000000000000e+03 2.713947462688838545e-14 2.621245245497713460e-15 3.330000000000000000e+03 2.925576403112187419e-14 2.676514576684752628e-15 3.340000000000000000e+03 3.064323025144185464e-14 2.755918382807004073e-15 3.350000000000000000e+03 2.956332236673025566e-14 2.846790215897579170e-15 3.360000000000000000e+03 3.135741293303623287e-14 2.936855134849039483e-15 3.370000000000000000e+03 2.460720098009815737e-14 3.022842856320956566e-15 3.380000000000000000e+03 3.149141659142896697e-14 3.110487754739928527e-15 3.390000000000000000e+03 3.243909881049375269e-14 3.205915711391979995e-15 3.400000000000000000e+03 3.433318708221660863e-14 3.315252607563147433e-15 3.410000000000000000e+03 3.320441899351789785e-14 3.444624324539446794e-15 3.420000000000000000e+03 3.244482921751628410e-14 3.596008583799671702e-15 3.430000000000000000e+03 3.870952792617011944e-14 3.754790467593629048e-15 3.440000000000000000e+03 4.024089323752848381e-14 3.902206898363888011e-15 3.450000000000000000e+03 3.956690409822158658e-14 4.019494798553011459e-15 3.460000000000000000e+03 5.026382320014109701e-14 4.087891090603570150e-15 3.470000000000000000e+03 3.812992445718583824e-14 4.089381658695132453e-15 3.480000000000000000e+03 4.506348873901885183e-14 4.023178506958551810e-15 3.490000000000000000e+03 4.324265169645959185e-14 3.905719759475941491e-15 3.500000000000000000e+03 3.501596432438188837e-14 3.754192502066422634e-15 3.510000000000000000e+03 4.219873537424047279e-14 3.585783820549120321e-15 3.520000000000000000e+03 3.917668380416698490e-14 3.417680800743170678e-15 3.530000000000000000e+03 2.867716367236208155e-14 3.264326686614127879e-15 3.540000000000000000e+03 3.532675336308905642e-14 3.129189354713285732e-15 3.550000000000000000e+03 3.088149303019524728e-14 3.012992839738377789e-15 3.560000000000000000e+03 2.329243027103104166e-14 2.916461176387130500e-15 3.570000000000000000e+03 2.884927320072741467e-14 2.840318399357269528e-15 3.580000000000000000e+03 2.793117477247279077e-14 2.785295985166163345e-15 3.590000000000000000e+03 2.794413444023401000e-14 2.752296572182636525e-15 3.600000000000000000e+03 2.691878933073082190e-14 2.742393960626988279e-15 3.610000000000000000e+03 2.528066267525698760e-14 2.756669392539145246e-15 3.620000000000000000e+03 2.731779897936721608e-14 2.796204109959040772e-15 3.630000000000000000e+03 2.863305363563268246e-14 2.862079354926595579e-15 3.640000000000000000e+03 3.077056338771732584e-14 2.953942894473055810e-15 3.650000000000000000e+03 2.690803426043529301e-14 3.065708595594897492e-15 3.660000000000000000e+03 3.334977838624617859e-14 3.189856850279911815e-15 3.670000000000000000e+03 3.242673944847878482e-14 3.318868050515890365e-15 3.680000000000000000e+03 3.482667779674707855e-14 3.445222588290627094e-15 3.690000000000000000e+03 3.532400262442902171e-14 3.561592661341347402e-15 3.700000000000000000e+03 3.687010808297600403e-14 3.665061999642463068e-15 3.710000000000000000e+03 3.831584203486440057e-14 3.757125865405565550e-15 3.720000000000000000e+03 4.643153566382258834e-14 3.839471326591681302e-15 3.730000000000000000e+03 3.554226697588292679e-14 3.913785451161847822e-15 3.740000000000000000e+03 4.417127875591812404e-14 3.981755307077086044e-15 3.750000000000000000e+03 3.889771601158805554e-14 4.046025756132187342e-15 3.760000000000000000e+03 4.090638469281548110e-14 4.113072835456960182e-15 3.770000000000000000e+03 4.425691866442550490e-14 4.190330376014976371e-15 3.780000000000000000e+03 4.856236052483270826e-14 4.285232208769804564e-15 3.790000000000000000e+03 5.309623609558732810e-14 4.405212164685014202e-15 3.800000000000000000e+03 4.884088282403400633e-14 4.557388186894656163e-15 3.810000000000000000e+03 4.998609585636369459e-14 4.741612798454106004e-15 3.820000000000000000e+03 4.828109893251733694e-14 4.950473102340033986e-15 3.830000000000000000e+03 5.353638745482417816e-14 5.176240313699607582e-15 3.840000000000000000e+03 4.504989684232296897e-14 5.411185647680007676e-15 3.850000000000000000e+03 4.938173444705611176e-14 5.647580319428370186e-15 3.860000000000000000e+03 5.657956781253537773e-14 5.877638279071094301e-15 3.870000000000000000e+03 5.470618763564577143e-14 6.093344416651405877e-15 3.880000000000000000e+03 5.645247993230121635e-14 6.286626357191742763e-15 3.890000000000000000e+03 6.277704422323698405e-14 6.449411725714546754e-15 3.900000000000000000e+03 6.675784167714972071e-14 6.573628147242262010e-15 3.910000000000000000e+03 6.301454174371029119e-14 6.651715346484368001e-15 3.920000000000000000e+03 7.517643212015023058e-14 6.687891340952419036e-15 3.930000000000000000e+03 7.326581916697798742e-14 6.698152440960040312e-15 3.940000000000000000e+03 7.906982675364309121e-14 6.699007056507882873e-15 3.950000000000000000e+03 6.809024805507754537e-14 6.706963597596623795e-15 3.960000000000000000e+03 6.401948613356454452e-14 6.738530474226922011e-15 3.970000000000000000e+03 7.373378080863319856e-14 6.801961935843686185e-15 3.980000000000000000e+03 6.652696017903499884e-14 6.872495589668796311e-15 3.990000000000000000e+03 7.172856297471442131e-14 6.917114882368377383e-15 4.000000000000000000e+03 6.122867040543222967e-14 6.902803260608548084e-15 4.010000000000000000e+03 6.412621048996471532e-14 6.796544171055430252e-15 4.020000000000000000e+03 5.445778761510131871e-14 6.566865330150889572e-15 4.030000000000000000e+03 6.001126682042752198e-14 6.217812659178679313e-15 4.040000000000000000e+03 5.838885063817656661e-14 5.788950284264461628e-15 4.050000000000000000e+03 5.362410105198226350e-14 5.321386601309628316e-15 4.060000000000000000e+03 4.654138655322174675e-14 4.856230006215553033e-15 4.070000000000000000e+03 3.443429080768945198e-14 4.434588894883674122e-15 4.080000000000000000e+03 3.960087455980115349e-14 4.089123377827442214e-15 4.090000000000000000e+03 4.164371726586521501e-14 3.818700424008633994e-15 4.100000000000000000e+03 3.360884022109927965e-14 3.613738717001103913e-15 4.110000000000000000e+03 3.036032071447209363e-14 3.464656940378707997e-15 4.120000000000000000e+03 3.486152651778569794e-14 3.361873777715297938e-15 4.130000000000000000e+03 3.120782415847699435e-14 3.296225056366372191e-15 4.140000000000000000e+03 3.933032878322091482e-14 3.268140910664987384e-15 4.150000000000000000e+03 3.745494624084400547e-14 3.287645781921781578e-15 4.160000000000000000e+03 3.525718893409744363e-14 3.365181255229023831e-15 4.170000000000000000e+03 3.555580330033018901e-14 3.511188915678998973e-15 4.180000000000000000e+03 3.488036278383284138e-14 3.736110348363960678e-15 4.190000000000000000e+03 4.466185657750724940e-14 4.046471449757065743e-15 4.200000000000000000e+03 4.552366913322664797e-14 4.433135361854968687e-15 4.210000000000000000e+03 4.672221997907329330e-14 4.883049538035207435e-15 4.220000000000000000e+03 5.838094337636791103e-14 5.383161431675319909e-15 4.230000000000000000e+03 4.906442130213505243e-14 5.920418496152869276e-15 4.240000000000000000e+03 6.047066032230401767e-14 6.481451532899241475e-15 4.250000000000000000e+03 7.482776947578398571e-14 7.045608348585698633e-15 4.260000000000000000e+03 6.892863917372104422e-14 7.584953755123274934e-15 4.270000000000000000e+03 9.276475802453220059e-14 8.071235912476902278e-15 4.280000000000000000e+03 8.348738621037448317e-14 8.476202980611540173e-15 4.290000000000000000e+03 7.531991009229507721e-14 8.771603119492076342e-15 4.300000000000000000e+03 9.430966404836808015e-14 8.936674536368171260e-15 4.310000000000000000e+03 1.108263055435418455e-13 8.980615627628215117e-15 4.320000000000000000e+03 9.310753882448541746e-14 8.920114836945306167e-15 4.330000000000000000e+03 9.028303827157646453e-14 8.771860607992536353e-15 4.340000000000000000e+03 8.205735241406009161e-14 8.552541384442981843e-15 4.350000000000000000e+03 9.270012476691005077e-14 8.279231044995914532e-15 4.360000000000000000e+03 8.096528058259923552e-14 7.977868473952317660e-15 4.370000000000000000e+03 8.872500796513460139e-14 7.683257561214900009e-15 4.380000000000000000e+03 8.086019006907965272e-14 7.430587631712525070e-15 4.390000000000000000e+03 6.461011638281760590e-14 7.255048010374075266e-15 4.400000000000000000e+03 6.975695529254285356e-14 7.191828022128440909e-15 4.410000000000000000e+03 7.031096078451917547e-14 7.263349319058201157e-15 4.420000000000000000e+03 8.364689762708149913e-14 7.440962861860856217e-15 4.430000000000000000e+03 7.705781750651472293e-14 7.683251938387626692e-15 4.440000000000000000e+03 7.476909302362937441e-14 7.948799836489737922e-15 4.450000000000000000e+03 7.011401140190238631e-14 8.196189844018418401e-15 4.460000000000000000e+03 9.759471547792812980e-14 8.385058048747165205e-15 4.470000000000000000e+03 9.197004175909095804e-14 8.499254936662588360e-15 4.480000000000000000e+03 8.805160915864362147e-14 8.546845391964333541e-15 4.490000000000000000e+03 8.577548715677304133e-14 8.536947098774348131e-15 4.500000000000000000e+03 9.174815258000223567e-14 8.478677741214593717e-15 4.510000000000000000e+03 9.891242354393484512e-14 8.381155003407023994e-15 4.520000000000000000e+03 7.889751609027970572e-14 8.253263107508044173e-15 4.530000000000000000e+03 8.601569373099087217e-14 8.102952427811863945e-15 4.540000000000000000e+03 7.315923019308659396e-14 7.937939876647142937e-15 4.550000000000000000e+03 7.465972838235669582e-14 7.765942366342537622e-15 4.560000000000000000e+03 8.728705721047850817e-14 7.594676809226709203e-15 4.570000000000000000e+03 8.000978577287503550e-14 7.431575449176451290e-15 4.580000000000000000e+03 7.581503793071255066e-14 7.277523155675601184e-15 4.590000000000000000e+03 5.521437266661467188e-14 7.126857423815054082e-15 4.600000000000000000e+03 8.264083001088148803e-14 6.973631080233865193e-15 4.610000000000000000e+03 5.370235979516233776e-14 6.811896951571054229e-15 4.620000000000000000e+03 7.162674111998225358e-14 6.635707864465645633e-15 4.630000000000000000e+03 7.051976632045681037e-14 6.440656570211172389e-15 4.640000000000000000e+03 5.687612172843070542e-14 6.228495518719112503e-15 4.650000000000000000e+03 7.478836331663516161e-14 6.002517084555446996e-15 4.660000000000000000e+03 6.307939303868032081e-14 5.766013642286149793e-15 4.670000000000000000e+03 4.664086886461760868e-14 5.522277566477200339e-15 4.680000000000000000e+03 4.564619425610579793e-14 5.274816991260416455e-15 4.690000000000000000e+03 5.473681335652128895e-14 5.032102520782018526e-15 4.700000000000000000e+03 5.357270311575238006e-14 4.807567229202605046e-15 4.710000000000000000e+03 4.874973899407062817e-14 4.614859950248681518e-15 4.720000000000000000e+03 3.892344844632861189e-14 4.467629517646684811e-15 4.730000000000000000e+03 4.579192250478777112e-14 4.379524765123079407e-15 4.740000000000000000e+03 4.246133998483106448e-14 4.359302481532920825e-15 4.750000000000000000e+03 3.947959167858794878e-14 4.396151276245644522e-15 4.760000000000000000e+03 4.213783985260185893e-14 4.474367713759278570e-15 4.770000000000000000e+03 4.959617673345010231e-14 4.578248358571854987e-15 4.780000000000000000e+03 4.869512180994764764e-14 4.692089775181404213e-15 4.790000000000000000e+03 4.997543583438287505e-14 4.800538098796930371e-15 4.800000000000000000e+03 5.138759772086644011e-14 4.896279590979997990e-15 4.810000000000000000e+03 5.301585967770457733e-14 4.980040639644717800e-15 4.820000000000000000e+03 5.361515763702328438e-14 5.052897203416160806e-15 4.830000000000000000e+03 5.804017720305422101e-14 5.115925240919423258e-15 4.840000000000000000e+03 5.944601313913993651e-14 5.170200710779590359e-15 4.850000000000000000e+03 4.874848026410132534e-14 5.217908123400246055e-15 4.860000000000000000e+03 5.402320801235215727e-14 5.265666196298939278e-15 4.870000000000000000e+03 5.456693320485246447e-14 5.321202198771712179e-15 4.880000000000000000e+03 5.946574518364274238e-14 5.392243400114610064e-15 4.890000000000000000e+03 6.518171412160179816e-14 5.486517069623676661e-15 4.900000000000000000e+03 5.846540951822662379e-14 5.611259126750058484e-15 4.910000000000000000e+03 5.728467179543052134e-14 5.762404444512221966e-15 4.920000000000000000e+03 6.615047036791112452e-14 5.924586849495977116e-15 4.930000000000000000e+03 6.727365113046485117e-14 6.081948818442183880e-15 4.940000000000000000e+03 6.576061940908939197e-14 6.218632828091756634e-15 4.950000000000000000e+03 5.728503889055098137e-14 6.318781355185578986e-15 4.960000000000000000e+03 6.381095110782787688e-14 6.370962790011492895e-15 4.970000000000000000e+03 6.100605420651458435e-14 6.381449177045079830e-15 4.980000000000000000e+03 4.379830292107614159e-14 6.360938474308866196e-15 4.990000000000000000e+03 6.199777271844868092e-14 6.320128639825378399e-15 5.000000000000000000e+03 6.762384539757091602e-14 6.269717631617146789e-15 5.010000000000000000e+03 6.230092321750152414e-14 6.220041511142068365e-15 5.020000000000000000e+03 5.985074996149382737e-14 6.173112718871740509e-15 5.030000000000000000e+03 5.710561585459887722e-14 6.122620074291438891e-15 5.040000000000000000e+03 5.358601108422838341e-14 6.061890500321826345e-15 5.050000000000000000e+03 5.976340643251489765e-14 5.984250919883558604e-15 5.060000000000000000e+03 5.779188650307934405e-14 5.883028255897294556e-15 5.070000000000000000e+03 6.539305466853396217e-14 5.754172002463288991e-15 5.080000000000000000e+03 5.198327083306155908e-14 5.604121938400172417e-15 5.090000000000000000e+03 4.906091471762980809e-14 5.441940413706179918e-15 5.100000000000000000e+03 5.993605651814619398e-14 5.276689778379544214e-15 5.110000000000000000e+03 5.114233158494718361e-14 5.117432382418498023e-15 5.120000000000000000e+03 5.148180199680357596e-14 4.972975264583792120e-15 5.130000000000000000e+03 4.263783571613806539e-14 4.846253305174243209e-15 5.140000000000000000e+03 4.480446356484533522e-14 4.734329226026711835e-15 5.150000000000000000e+03 4.885681072103371649e-14 4.634010437740619198e-15 5.160000000000000000e+03 4.257211309740140075e-14 4.542104350915343899e-15 5.170000000000000000e+03 4.134067120534713536e-14 4.455418376150281892e-15 5.180000000000000000e+03 4.099036855548151259e-14 4.371478360242834351e-15 5.190000000000000000e+03 4.043266506349060936e-14 4.290683894782417003e-15 5.200000000000000000e+03 3.243903461047789868e-14 4.214153007556444482e-15 5.210000000000000000e+03 5.205410286476815241e-14 4.143003726352343255e-15 5.220000000000000000e+03 4.151169311222747587e-14 4.078354078957525591e-15 5.230000000000000000e+03 4.228605333441780200e-14 4.021179168521815621e-15 5.240000000000000000e+03 3.911968732140512628e-14 3.969166831530279266e-15 5.250000000000000000e+03 4.167793848020823802e-14 3.916717637803215553e-15 5.260000000000000000e+03 4.301593598438049939e-14 3.858089232523350364e-15 5.270000000000000000e+03 3.884168369528266052e-14 3.787539260873387494e-15 5.280000000000000000e+03 3.926922343157469467e-14 3.699325368036035470e-15 5.290000000000000000e+03 3.517314229441358588e-14 3.590720565941170220e-15 5.300000000000000000e+03 3.833594933733612813e-14 3.471059333507314402e-15 5.310000000000000000e+03 3.217791166602621139e-14 3.352691516400158073e-15 5.320000000000000000e+03 3.885498648999068173e-14 3.247966960285384983e-15 5.330000000000000000e+03 3.573181953457536344e-14 3.169235510828683611e-15 5.340000000000000000e+03 3.323422779046275692e-14 3.128345548016196709e-15 5.350000000000000000e+03 2.604459051078769078e-14 3.125611741204541301e-15 5.360000000000000000e+03 2.644737969381834581e-14 3.149815049120818541e-15 5.370000000000000000e+03 2.983102658489775231e-14 3.189234964812564923e-15 5.380000000000000000e+03 3.273143999925777895e-14 3.232150981327334295e-15 5.390000000000000000e+03 3.234223027906471400e-14 3.266842591712670645e-15 5.400000000000000000e+03 3.250432424977117001e-14 3.284279903520421765e-15 5.410000000000000000e+03 3.747230486073859649e-14 3.286195482319617939e-15 5.420000000000000000e+03 3.012222336023607687e-14 3.277012508183587336e-15 5.430000000000000000e+03 3.280546965450675706e-14 3.261154161185658523e-15 5.440000000000000000e+03 3.167058423099512241e-14 3.243043621399161642e-15 5.450000000000000000e+03 3.507440871447588215e-14 3.227057656734223581e-15 5.460000000000000000e+03 2.944566387404492197e-14 3.216505555347446577e-15 5.470000000000000000e+03 3.164693940618815150e-14 3.213629125641899943e-15 5.480000000000000000e+03 3.063887535547714158e-14 3.220623763857454859e-15 5.490000000000000000e+03 3.376338178787067318e-14 3.239684866233982110e-15 5.500000000000000000e+03 3.016445457982863147e-14 3.273007829011350905e-15 5.510000000000000000e+03 3.858754108144404822e-14 3.322143874329315264e-15 5.520000000000000000e+03 3.367127390717914330e-14 3.386067527927137679e-15 5.530000000000000000e+03 3.833696846207927692e-14 3.463109141443969003e-15 5.540000000000000000e+03 3.961363833604532592e-14 3.551599066518952202e-15 5.550000000000000000e+03 3.353118433241567816e-14 3.649867654791234974e-15 5.560000000000000000e+03 3.846742150176691544e-14 3.756164677381690021e-15 5.570000000000000000e+03 4.492630890666462147e-14 3.866886553490983869e-15 5.580000000000000000e+03 4.305970177437176935e-14 3.976576350399581209e-15 5.590000000000000000e+03 4.031462634604716549e-14 4.079696554869638603e-15 5.600000000000000000e+03 4.424426841184533112e-14 4.170709653663348901e-15 5.610000000000000000e+03 3.907432109253004186e-14 4.244078133542895485e-15 5.620000000000000000e+03 4.420266861243388312e-14 4.295938602372468016e-15 5.630000000000000000e+03 4.535265011480273177e-14 4.329124152424292298e-15 5.640000000000000000e+03 4.062267935256841136e-14 4.348141997072606722e-15 5.650000000000000000e+03 4.177153295547832189e-14 4.357499349691644158e-15 5.660000000000000000e+03 4.941808986852090231e-14 4.361703423655646152e-15 5.670000000000000000e+03 4.912722277695613235e-14 4.365021100568793047e-15 5.680000000000000000e+03 4.003967395554919090e-14 4.366191631324150911e-15 5.690000000000000000e+03 4.437279923314373739e-14 4.358426636103657346e-15 5.700000000000000000e+03 4.469322067996347652e-14 4.334697403319199002e-15 5.710000000000000000e+03 3.841821757644505607e-14 4.287975221382669626e-15 5.720000000000000000e+03 4.707592818612280788e-14 4.211231378705952715e-15 5.730000000000000000e+03 4.416422969317799738e-14 4.100352658539155795e-15 5.740000000000000000e+03 4.745364387073866826e-14 3.962887823485234404e-15 5.750000000000000000e+03 3.808214421470906811e-14 3.809301130985368115e-15 5.760000000000000000e+03 3.828005852033786373e-14 3.650056838480729400e-15 5.770000000000000000e+03 3.321456538595958509e-14 3.495619203412493097e-15 5.780000000000000000e+03 3.146868450269313719e-14 3.356253349200906627e-15 5.790000000000000000e+03 3.435372422766371592e-14 3.237644316784901041e-15 5.800000000000000000e+03 3.084384722566636517e-14 3.140897064622084305e-15 5.810000000000000000e+03 3.128208831173923454e-14 3.066917417149162217e-15 5.820000000000000000e+03 3.039212226212052448e-14 3.016611198802811383e-15 5.830000000000000000e+03 3.149465444484953335e-14 2.990884234019717879e-15 5.840000000000000000e+03 2.959596861417451291e-14 2.989324037866294438e-15 5.850000000000000000e+03 2.913495202615686952e-14 3.006244887927842284e-15 5.860000000000000000e+03 3.716594188698705410e-14 3.034642752419385358e-15 5.870000000000000000e+03 2.766667331355278159e-14 3.067513599555951149e-15 5.880000000000000000e+03 2.784323267916578249e-14 3.097853397552561229e-15 5.890000000000000000e+03 3.471552112131783426e-14 3.118790543544344791e-15 5.900000000000000000e+03 3.198312344600509863e-14 3.126499299828748502e-15 5.910000000000000000e+03 3.543187799176470527e-14 3.120199793865549126e-15 5.920000000000000000e+03 2.914764760754055876e-14 3.099244582034623156e-15 5.930000000000000000e+03 2.920122571591463904e-14 3.062986220715846297e-15 5.940000000000000000e+03 3.160663887858093052e-14 3.010777266289093467e-15 5.950000000000000000e+03 3.043209528420020872e-14 2.943584469796086539e-15 5.960000000000000000e+03 2.960024518686823779e-14 2.868831360925916680e-15 5.970000000000000000e+03 2.707897594605787551e-14 2.795555664029522807e-15 5.980000000000000000e+03 2.950990323869241922e-14 2.732795103457838705e-15 5.990000000000000000e+03 2.909331871151585246e-14 2.689587403561804078e-15 6.000000000000000000e+03 2.514735788646263691e-14 2.674692503747791411e-15 6.010000000000000000e+03 2.426370395771186269e-14 2.690481289697284540e-15 6.020000000000000000e+03 2.699862583009653905e-14 2.732935593366876287e-15 6.030000000000000000e+03 2.908468295160733109e-14 2.797759461768582788e-15 6.040000000000000000e+03 2.900628426837208384e-14 2.880656941914435563e-15 6.050000000000000000e+03 2.827699181895621639e-14 2.977332080816457848e-15 6.060000000000000000e+03 3.110005992414686818e-14 3.083163360003056814e-15 6.070000000000000000e+03 3.195509714843736539e-14 3.192226999068148938e-15 6.080000000000000000e+03 3.341700954001040974e-14 3.298273652122033846e-15 6.090000000000000000e+03 3.644582394691519525e-14 3.395053973275008400e-15 6.100000000000000000e+03 3.428355206025894946e-14 3.476318616637371436e-15 6.110000000000000000e+03 4.130498869875532197e-14 3.536039622232326316e-15 6.120000000000000000e+03 4.169083783198984092e-14 3.573280906079851370e-15 6.130000000000000000e+03 3.627681049850071719e-14 3.592198260196722773e-15 6.140000000000000000e+03 3.438541517708433323e-14 3.597168862512612942e-15 6.150000000000000000e+03 3.785137420195500205e-14 3.592569890957194296e-15 6.160000000000000000e+03 3.323967362195750878e-14 3.582778523460142410e-15 6.170000000000000000e+03 3.409555455742879307e-14 3.571231919165158662e-15 6.180000000000000000e+03 2.619717031806390775e-14 3.557607162072053961e-15 6.190000000000000000e+03 3.232329222962494741e-14 3.540641317394667782e-15 6.200000000000000000e+03 4.096395278908184130e-14 3.519071450346840390e-15 6.210000000000000000e+03 3.234848539421517297e-14 3.491634626142409288e-15 6.220000000000000000e+03 3.265111265580029710e-14 3.457118074879576447e-15 6.230000000000000000e+03 3.177215748672518083e-14 3.415462818996933749e-15 6.240000000000000000e+03 3.266443386116332041e-14 3.367763673273450360e-15 6.250000000000000000e+03 3.628067310719434179e-14 3.315165617372476087e-15 6.260000000000000000e+03 3.619651903053763405e-14 3.258813630957348512e-15 6.270000000000000000e+03 2.765141537989206018e-14 3.199852693691402848e-15 6.280000000000000000e+03 2.957842962213984572e-14 3.139752133341610259e-15 6.290000000000000000e+03 2.842397822309824841e-14 3.081278670089443900e-15 6.300000000000000000e+03 3.646657711190053074e-14 3.027523372220004986e-15 6.310000000000000000e+03 3.527551112200350728e-14 2.981577308018395523e-15 6.320000000000000000e+03 2.845043291261716969e-14 2.946531545769717120e-15 6.330000000000000000e+03 3.029947014251265930e-14 2.925364964399319659e-15 6.340000000000000000e+03 2.937135652822076436e-14 2.918476087558307825e-15 6.350000000000000000e+03 3.134022411467598990e-14 2.923683083623541897e-15 6.360000000000000000e+03 3.128644463775847498e-14 2.938691931612124902e-15 6.370000000000000000e+03 3.228889701612950789e-14 2.961208610541164207e-15 6.380000000000000000e+03 2.734463348123720106e-14 2.988939099427765599e-15 6.390000000000000000e+03 3.166358070333364724e-14 3.019692400852319962e-15 6.400000000000000000e+03 3.013091245393788469e-14 3.051689611648330547e-15 6.410000000000000000e+03 3.220545732598734048e-14 3.083254852212587672e-15 6.420000000000000000e+03 2.836623873425564608e-14 3.112712242941881263e-15 6.430000000000000000e+03 2.914671274053657940e-14 3.138385904232996114e-15 6.440000000000000000e+03 2.818835900248514196e-14 3.158662411737924368e-15 6.450000000000000000e+03 3.243868358428499672e-14 3.173364811978276860e-15 6.460000000000000000e+03 3.336035193276594264e-14 3.183752622345291001e-15 6.470000000000000000e+03 3.605777370565974874e-14 3.191147815485398929e-15 6.480000000000000000e+03 3.283871281497726915e-14 3.196872364045041854e-15 6.490000000000000000e+03 2.906572697335900171e-14 3.202248240670651914e-15 6.500000000000000000e+03 3.054966940054935267e-14 3.208083629543919668e-15 6.510000000000000000e+03 2.927108094394543632e-14 3.213131560987529140e-15 6.520000000000000000e+03 3.749509814936450485e-14 3.215631276859422382e-15 6.530000000000000000e+03 3.358969700247748674e-14 3.213822019017535135e-15 6.540000000000000000e+03 3.372180809369561922e-14 3.205943029319809056e-15 6.550000000000000000e+03 3.189311038517494845e-14 3.190206397390655677e-15 6.560000000000000000e+03 3.125078045407578798e-14 3.164199711483444329e-15 6.570000000000000000e+03 2.836110283000593261e-14 3.124886058480498589e-15 6.580000000000000000e+03 2.924485404781482884e-14 3.069201373030628872e-15 6.590000000000000000e+03 3.453623267329513691e-14 2.994081589782633362e-15 6.600000000000000000e+03 2.828339308168973227e-14 2.896462643385313796e-15 6.610000000000000000e+03 2.469134494411601203e-14 2.774750950170273458e-15 6.620000000000000000e+03 2.276911136570181984e-14 2.633234853200314334e-15 6.630000000000000000e+03 2.873866301552497205e-14 2.477673177221040351e-15 6.640000000000000000e+03 2.132736346146965465e-14 2.313824746978056227e-15 6.650000000000000000e+03 1.688094705930095529e-14 2.147448387216963917e-15 6.660000000000000000e+03 2.104413410102517986e-14 1.984263352318754287e-15 6.670000000000000000e+03 1.980143086656317071e-14 1.829078778278326808e-15 6.680000000000000000e+03 1.317803434452527621e-14 1.685793682704473391e-15 6.690000000000000000e+03 1.606733127929596578e-14 1.558267512841417649e-15 6.700000000000000000e+03 1.227661219395114091e-14 1.450359715933338232e-15 6.710000000000000000e+03 1.347438939491884923e-14 1.365929739224428776e-15 6.720000000000000000e+03 1.381643285892839541e-14 1.308007897008590451e-15 6.730000000000000000e+03 1.252622128104193696e-14 1.276307971778549228e-15 6.740000000000000000e+03 1.246621469628858242e-14 1.269714613076739990e-15 6.750000000000000000e+03 1.390748144719965883e-14 1.287112470445597029e-15 6.760000000000000000e+03 1.303329933760573923e-14 1.327386193427553849e-15 6.770000000000000000e+03 1.483895457688039139e-14 1.389352302434230265e-15 6.780000000000000000e+03 1.694065744416575032e-14 1.470260347868530856e-15 6.790000000000000000e+03 1.245582873411397246e-14 1.565792910124655620e-15 6.800000000000000000e+03 1.717310014038979639e-14 1.671564440465960493e-15 6.810000000000000000e+03 1.549526396085030148e-14 1.783189390155829810e-15 6.820000000000000000e+03 2.317421547129164667e-14 1.896282210457637653e-15 6.830000000000000000e+03 1.758955970520517350e-14 2.006873250204172600e-15 6.840000000000000000e+03 2.407867181703508454e-14 2.112656448505866824e-15 6.850000000000000000e+03 2.114552404836308499e-14 2.211741642042563250e-15 6.860000000000000000e+03 2.283231114187110493e-14 2.302238667494109928e-15 6.870000000000000000e+03 2.490873011028542994e-14 2.382257361540348995e-15 6.880000000000000000e+03 2.590924243365989107e-14 2.449973409175411612e-15 6.890000000000000000e+03 2.127397135279053570e-14 2.505077006622018144e-15 6.900000000000000000e+03 2.682210415122318975e-14 2.548772861331483293e-15 6.910000000000000000e+03 2.535211231727132901e-14 2.582331529069405261e-15 6.920000000000000000e+03 2.315039360857564973e-14 2.607023565601377124e-15 6.930000000000000000e+03 2.394961611836017389e-14 2.624119526692999846e-15 6.940000000000000000e+03 2.378849788520266497e-14 2.634740785977248137e-15 6.950000000000000000e+03 2.755846203952777264e-14 2.639411988556589317e-15 6.960000000000000000e+03 2.810808851687995713e-14 2.638508597400870368e-15 6.970000000000000000e+03 2.581965341915617123e-14 2.632406075479934721e-15 6.980000000000000000e+03 2.518996908237122787e-14 2.621479885763625414e-15 6.990000000000000000e+03 2.375544190789464992e-14 2.606107954307025565e-15 7.000000000000000000e+03 2.866176919616563552e-14 2.586724858125619815e-15 7.010000000000000000e+03 2.736315718488437351e-14 2.563821825195309322e-15 7.020000000000000000e+03 2.345254316942088310e-14 2.537892546577222701e-15 7.030000000000000000e+03 2.291333306377008910e-14 2.509430713332498427e-15 7.040000000000000000e+03 2.062345564181311243e-14 2.478930016522265904e-15 7.050000000000000000e+03 2.501450215055743526e-14 2.446884342328428710e-15 7.060000000000000000e+03 2.452252834945960506e-14 2.413788357415964247e-15 7.070000000000000000e+03 2.049100525708903237e-14 2.380136923570620938e-15 7.080000000000000000e+03 2.364352308830126288e-14 2.346424902578146809e-15 7.090000000000000000e+03 2.176112293407614049e-14 2.313147156224290675e-15 7.100000000000000000e+03 1.802872460590557286e-14 2.280791626658768033e-15 7.110000000000000000e+03 2.384710911904083393e-14 2.249687104402557581e-15 7.120000000000000000e+03 2.644200576418025665e-14 2.220003228347908321e-15 7.130000000000000000e+03 2.096113327355554369e-14 2.191902717751026075e-15 7.140000000000000000e+03 1.781591699612193133e-14 2.165548291868130863e-15 7.150000000000000000e+03 2.301479573380125955e-14 2.141102669955432056e-15 7.160000000000000000e+03 1.903837677475914788e-14 2.118602237507220980e-15 7.170000000000000000e+03 2.058965413398300412e-14 2.097578044970091931e-15 7.180000000000000000e+03 2.122088085957017735e-14 2.077434809028721161e-15 7.190000000000000000e+03 2.310403486613058200e-14 2.057577246367778611e-15 7.200000000000000000e+03 2.297040914698739011e-14 2.037410073671942110e-15 7.210000000000000000e+03 2.266410843999297921e-14 2.016357765053840052e-15 7.220000000000000000e+03 2.289411195399230871e-14 1.994299215469132254e-15 7.230000000000000000e+03 1.740152575683083351e-14 1.971567740716507194e-15 7.240000000000000000e+03 1.902230816365833771e-14 1.948516414022607858e-15 7.250000000000000000e+03 2.050816290405210573e-14 1.925498308614083544e-15 7.260000000000000000e+03 1.785521840669158378e-14 1.902866497717576056e-15 7.270000000000000000e+03 2.184027677955829682e-14 1.880843784955734371e-15 7.280000000000000000e+03 1.976509626948697370e-14 1.859131895535202634e-15 7.290000000000000000e+03 1.946284377480746415e-14 1.837302285058629798e-15 7.300000000000000000e+03 2.079568507230604493e-14 1.814926409128661661e-15 7.310000000000000000e+03 1.788845064525928960e-14 1.791575723347947175e-15 7.320000000000000000e+03 1.773726422641147237e-14 1.766842574426525004e-15 7.330000000000000000e+03 1.810513505891981896e-14 1.740799804544493808e-15 7.340000000000000000e+03 1.624946209269417219e-14 1.714000751352008696e-15 7.350000000000000000e+03 1.663971578154262352e-14 1.687019643606612513e-15 7.360000000000000000e+03 1.697852155766507442e-14 1.660430710065859940e-15 7.370000000000000000e+03 1.692152585622949911e-14 1.634808179487295598e-15 7.380000000000000000e+03 1.598108747700040529e-14 1.610621421274564211e-15 7.390000000000000000e+03 1.392442064005433343e-14 1.587920367415697894e-15 7.400000000000000000e+03 1.708926241670596919e-14 1.566650090544827090e-15 7.410000000000000000e+03 1.542563969806977686e-14 1.546755663296080663e-15 7.420000000000000000e+03 1.611768491080513070e-14 1.528182158303589057e-15 7.430000000000000000e+03 1.527846337445698795e-14 1.510869083887053541e-15 7.440000000000000000e+03 1.385364999767684704e-14 1.494627969134353309e-15 7.450000000000000000e+03 1.480993209812126312e-14 1.479142363901543512e-15 7.460000000000000000e+03 1.471094103328418630e-14 1.464090253730249729e-15 7.470000000000000000e+03 1.180879049759479214e-14 1.449149624162102277e-15 7.480000000000000000e+03 1.354324099908125805e-14 1.433998460738727524e-15 7.490000000000000000e+03 1.567115526578342771e-14 1.418408716549806924e-15 7.500000000000000000e+03 1.514118205052204534e-14 1.402528214877237131e-15 7.510000000000000000e+03 1.194827103356745404e-14 1.386598746550968301e-15 7.520000000000000000e+03 1.563691307852147010e-14 1.370862102400951187e-15 7.530000000000000000e+03 1.207859121718074709e-14 1.355560073257136735e-15 7.540000000000000000e+03 1.215662491840630436e-14 1.340929241884392294e-15 7.550000000000000000e+03 1.404909128530752425e-14 1.327086405550703296e-15 7.560000000000000000e+03 1.430230714002340787e-14 1.314028576027168917e-15 7.570000000000000000e+03 1.307377134165147793e-14 1.301747557019805524e-15 7.580000000000000000e+03 1.358417764540030452e-14 1.290235152234634018e-15 7.590000000000000000e+03 1.331595824518531275e-14 1.279483165377670174e-15 7.600000000000000000e+03 1.533268836248844414e-14 1.269463122798702727e-15 7.610000000000000000e+03 1.551706846709785547e-14 1.260065441422595484e-15 7.620000000000000000e+03 1.335127786750169948e-14 1.251160260817984421e-15 7.630000000000000000e+03 9.918917621614381256e-15 1.242617720553503743e-15 7.640000000000000000e+03 1.206720214106602693e-14 1.234307960197788636e-15 7.650000000000000000e+03 1.077017514096487895e-14 1.226104117207225312e-15 7.660000000000000000e+03 1.379053934559320010e-14 1.217948280456492028e-15 7.670000000000000000e+03 1.311048592648306690e-14 1.209851490238557311e-15 7.680000000000000000e+03 1.093315244781215515e-14 1.201827784734140909e-15 7.690000000000000000e+03 1.124942398764861287e-14 1.193891202123965330e-15 7.700000000000000000e+03 1.078082071952786023e-14 1.186055780588749928e-15 7.710000000000000000e+03 1.343781778823163803e-14 1.178340171392393044e-15 7.720000000000000000e+03 1.312251632082091860e-14 1.170781478131500308e-15 7.730000000000000000e+03 9.269993184495908418e-15 1.163421417485856928e-15 7.740000000000000000e+03 1.043671653153696013e-14 1.156301706135245156e-15 7.750000000000000000e+03 1.098182201994113941e-14 1.149464060759449414e-15 7.760000000000000000e+03 1.389805391208096259e-14 1.142944567888312990e-15 7.770000000000000000e+03 1.216038254210787039e-14 1.136649820603051102e-15 7.780000000000000000e+03 1.039280837948383534e-14 1.130356918536251682e-15 7.790000000000000000e+03 1.013787328904749877e-14 1.123837331170558968e-15 7.800000000000000000e+03 1.258581865980148011e-14 1.116862527988622917e-15 7.810000000000000000e+03 1.101451178952270647e-14 1.109203978473088752e-15 7.820000000000000000e+03 1.228413601333217449e-14 1.100726008282610533e-15 7.830000000000000000e+03 1.167174879417888159e-14 1.091664367779867611e-15 7.840000000000000000e+03 8.780635470976081912e-15 1.082347663503546987e-15 7.850000000000000000e+03 1.058230817999979027e-14 1.073104501992335074e-15 7.860000000000000000e+03 1.174212422911343395e-14 1.064263489784918873e-15 7.870000000000000000e+03 1.023814703388652822e-14 1.056143281423632664e-15 7.880000000000000000e+03 1.125642013577855915e-14 1.048833635534701224e-15 7.890000000000000000e+03 9.214077582513129870e-15 1.042195414828241602e-15 7.900000000000000000e+03 9.951692434352362703e-15 1.036079530018016746e-15 7.910000000000000000e+03 1.002011556571380870e-14 1.030336891817793348e-15 7.920000000000000000e+03 9.684773239508097894e-15 1.024818410941333959e-15 7.930000000000000000e+03 9.122866120338856563e-15 1.019375735902901979e-15 7.940000000000000000e+03 9.004063827254823925e-15 1.013863466418756910e-15 7.950000000000000000e+03 9.168707162300231193e-15 1.008136940005657915e-15 7.960000000000000000e+03 1.177476114862651689e-14 1.002051494180364161e-15 7.970000000000000000e+03 9.700939653109175627e-15 9.954624664596348135e-16 7.980000000000000000e+03 9.038834074169803816e-15 9.882277501793073912e-16 7.990000000000000000e+03 9.959670800618741705e-15 9.802640225140448234e-16 8.000000000000000000e+03 9.435273229239916139e-15 9.715467444773344627e-16 8.010000000000000000e+03 9.613428551564480745e-15 9.620539329017418180e-16 8.020000000000000000e+03 1.063383407167537050e-14 9.517636046198351597e-16 8.030000000000000000e+03 1.056323490104950782e-14 9.406537764641803913e-16 8.040000000000000000e+03 8.954228503194638797e-15 9.287115299173044791e-16 8.050000000000000000e+03 9.373557447795710106e-15 9.159602050615697658e-16 8.060000000000000000e+03 7.880422269828820293e-15 9.024322066293010374e-16 8.070000000000000000e+03 8.195982587960265864e-15 8.881599393528218964e-16 8.080000000000000000e+03 1.077349288190392855e-14 8.731758079644553538e-16 8.090000000000000000e+03 7.950374509432533526e-15 8.575159932809191571e-16 8.100000000000000000e+03 8.328640772530636834e-15 8.413035260599937197e-16 8.110000000000000000e+03 9.089345492102327028e-15 8.247482870005219238e-16 8.120000000000000000e+03 9.068606850446690347e-15 8.080639328857380356e-16 8.130000000000000000e+03 8.315521771034587483e-15 7.914641204988828290e-16 8.140000000000000000e+03 7.539726046442419415e-15 7.751625066231901756e-16 8.150000000000000000e+03 8.715202062726209138e-15 7.593944459865418437e-16 8.160000000000000000e+03 8.660803093665607931e-15 7.444820850953929453e-16 8.170000000000000000e+03 7.027725583974538722e-15 7.307692684008445168e-16 8.180000000000000000e+03 5.322730974834539025e-15 7.185998403539963130e-16 8.190000000000000000e+03 6.434962772531472235e-15 7.083176454059486800e-16 8.200000000000000000e+03 7.508897171659795603e-15 7.002530965505247699e-16 8.210000000000000000e+03 6.887456966605741218e-15 6.944276832641886380e-16 8.220000000000000000e+03 7.730251569966393982e-15 6.905539715060434676e-16 8.230000000000000000e+03 7.315819741915697306e-15 6.883310957779163328e-16 8.240000000000000000e+03 7.411073656588786449e-15 6.874581905816348989e-16 8.250000000000000000e+03 6.978971453210496269e-15 6.876343904190254512e-16 8.260000000000000000e+03 6.958490698235001689e-15 6.885997840983734267e-16 8.270000000000000000e+03 6.416660365740985288e-15 6.902582776537966309e-16 8.280000000000000000e+03 6.782137819619898215e-15 6.925547314258706407e-16 8.290000000000000000e+03 7.202239565493107623e-15 6.954340057551714274e-16 8.300000000000000000e+03 6.663438377191397572e-15 6.988409609822753571e-16 8.310000000000000000e+03 7.045953922059824808e-15 7.027190920699768076e-16 8.320000000000000000e+03 7.626591675980956855e-15 7.069804902921161918e-16 8.330000000000000000e+03 6.888627164643447291e-15 7.115058432335778859e-16 8.340000000000000000e+03 7.048284671335211144e-15 7.161744731014654619e-16 8.350000000000000000e+03 6.557951836513030939e-15 7.208657021028831819e-16 8.360000000000000000e+03 7.175044561972627058e-15 7.254588524449344206e-16 8.370000000000000000e+03 6.719466061283710419e-15 7.298661795474541279e-16 8.380000000000000000e+03 8.014207132594956609e-15 7.341316716812010880e-16 8.390000000000000000e+03 7.748369710889627014e-15 7.383322503296655619e-16 8.400000000000000000e+03 7.646474623039373525e-15 7.425448369763371202e-16 8.410000000000000000e+03 8.089379929177615394e-15 7.468463531047058265e-16 8.420000000000000000e+03 7.440274486682733087e-15 7.513125211408314270e-16 8.430000000000000000e+03 7.538170582841336437e-15 7.559914851898844530e-16 8.440000000000000000e+03 9.062327100873444551e-15 7.609038110361469112e-16 8.450000000000000000e+03 7.521229101243836735e-15 7.660688654064701948e-16 8.460000000000000000e+03 8.060588026804271476e-15 7.715060150277052044e-16 8.470000000000000000e+03 9.234435797522918575e-15 7.772346266267030374e-16 8.480000000000000000e+03 8.260779044248005991e-15 7.832491783659487944e-16 8.490000000000000000e+03 7.980445650827735707e-15 7.894445941504579675e-16 8.500000000000000000e+03 8.777376714865786018e-15 7.956909093208798544e-16 8.510000000000000000e+03 7.160496431466528857e-15 8.018581592178633586e-16 8.520000000000000000e+03 8.759254779885973725e-15 8.078163791820575806e-16 8.530000000000000000e+03 8.351944731073455614e-15 8.134362520205613487e-16 8.540000000000000000e+03 8.448688103905941409e-15 8.186033522688473045e-16 8.550000000000000000e+03 9.642349888615637665e-15 8.232181461907579582e-16 8.560000000000000000e+03 7.083492817206758450e-15 8.271817475165873229e-16 8.570000000000000000e+03 7.490493249472429713e-15 8.303952699766286227e-16 8.580000000000000000e+03 7.653164987025714132e-15 8.327598273011756733e-16 8.590000000000000000e+03 7.711394199373580636e-15 8.341845338365661943e-16 8.600000000000000000e+03 8.116701773383629537e-15 8.346105063933112513e-16 8.610000000000000000e+03 9.353593794184092120e-15 8.339868623979654193e-16 8.620000000000000000e+03 8.973598481912560913e-15 8.322627192770831747e-16 8.630000000000000000e+03 9.221981266755328901e-15 8.293871944572200786e-16 8.640000000000000000e+03 8.350104249006054748e-15 8.253155149422383100e-16 8.650000000000000000e+03 8.000667155049483291e-15 8.201434280141040294e-16 8.660000000000000000e+03 8.523910162385899620e-15 8.141072012328844208e-16 8.670000000000000000e+03 8.797455339106017094e-15 8.074492117359546666e-16 8.680000000000000000e+03 8.355219766737116042e-15 8.004118366606924144e-16 8.690000000000000000e+03 7.783730033125774526e-15 7.932374531444727481e-16 8.700000000000000000e+03 8.318510101520451628e-15 7.861326208001776455e-16 8.710000000000000000e+03 8.817168847161786403e-15 7.791606291427084766e-16 8.720000000000000000e+03 7.763528404157108085e-15 7.723489501624749843e-16 8.730000000000000000e+03 7.664306200274959033e-15 7.657250558498844467e-16 8.740000000000000000e+03 6.135434746732140860e-15 7.593164181953451277e-16 8.750000000000000000e+03 6.698737836889642992e-15 7.531479898025381442e-16 8.760000000000000000e+03 7.176984756485109591e-15 7.471867773804358109e-16 8.770000000000000000e+03 7.235674158252774398e-15 7.413418417433005555e-16 8.780000000000000000e+03 7.855949112793247158e-15 7.355197243186673627e-16 8.790000000000000000e+03 6.453241957644627353e-15 7.296269665340737811e-16 8.800000000000000000e+03 6.582989016999412422e-15 7.235701098170544998e-16 8.810000000000000000e+03 6.667055399741633015e-15 7.172787951785340258e-16 8.820000000000000000e+03 6.345959239607949163e-15 7.107750619629884473e-16 8.830000000000000000e+03 6.700920687937627199e-15 7.041040490982830792e-16 8.840000000000000000e+03 5.131012947684380843e-15 6.973108955122826445e-16 8.850000000000000000e+03 8.069551039047083791e-15 6.904407401328519650e-16 8.860000000000000000e+03 6.821809923618237868e-15 6.835343363472204940e-16 8.870000000000000000e+03 6.637223640842967629e-15 6.765315701080155523e-16 8.880000000000000000e+03 5.813187164323505052e-15 6.692714599332609477e-16 8.890000000000000000e+03 7.931205806462736377e-15 6.615886388003444292e-16 8.900000000000000000e+03 6.535108972888019767e-15 6.533177396866567044e-16 8.910000000000000000e+03 7.331033566329981303e-15 6.442933955695854236e-16 8.920000000000000000e+03 6.857017864061186037e-15 6.344158010492402531e-16 8.930000000000000000e+03 4.803586961481385504e-15 6.238473972166105407e-16 8.940000000000000000e+03 5.187400760886239969e-15 6.128161867854061706e-16 8.950000000000000000e+03 6.635938337003664705e-15 6.015501724693376189e-16 8.960000000000000000e+03 5.800028657551126415e-15 5.902773569821150655e-16 8.970000000000000000e+03 6.735859203364710185e-15 5.792244259914434079e-16 8.980000000000000000e+03 5.085515182185991088e-15 5.685877731069372963e-16 8.990000000000000000e+03 5.500258263852649420e-15 5.585334998801179786e-16 sncosmo-2.12.1/sncosmo/data/models/000077500000000000000000000000001476435666400171605ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/data/models/Hsiao_SED_V3_subsampled.fits000066400000000000000000001320001476435666400243700ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 481 NAXIS2 = 22 WCSAXES = 2 / Number of coordinate axes CRPIX1 = 1 / Pixel coordinate of reference point CRPIX2 = 1 / Pixel coordinate of reference point CDELT1 = 50 / Coordinate increment at reference point CDELT2 = 5 / Coordinate increment at reference point CRVAL1 = 1000 / Coordinate value at reference point CRVAL2 = -20 / Coordinate value at reference point LATPOLE = 90 / [deg] Native latitude of celestial pole RESTFRQ = 0 / [Hz] Line rest frequency RESTWAV = 0 / [Hz] Line rest wavelength END ~}||||}wk^RD6( {naTI=2'  $+18?EKQW]bglotwz}}{yvtqoljgda^[WTQNJGD@=:730-)&#  'Ѐ(pD)LI* Z*­+6P+,C,T !,U,s,,=Z,6,-K,/-- -3-$-.>\.r.y.j.b.d .c.Y.N.Wr.~c.//iَ/a//j!0+0n0~ 0[\0 B///J/)0-0^0oN04d/#D/υ00*0R 0mq000}0j(0O00 0/\/,/.5/c//si/h/֦L//N/ /ݫ/-/8/u/Z/L/Ƅ/B/f//&///Ԃ///n//</.y/\Z/T/>/n/Ss4/>V/8V/M&l/g/p6/o/rF/p^/ik/b_/ZO)/P`/FC/;S/2>v/'|`/'/////+// /sH/P.s.v..)...G..j)./.;.<.D.)...w6.T./..v.ۺ-.}.K.Y.Q.d. . .].E.Y.e.F.r..K.a...py.lc.i .e}.a.]a.WC.Q.J.Dt.?Y.`.B:.C}.Df.D.G.J2.L^{.K˒.H.E9.BB.A.AG.A5.@e.?Z.=.;.9n.7.5.4.1..^M.*.&."W..Y...M.. ..p".PI-gj-5--7u--`-/-{-|--c-U-̎/-Ȕ-{[-K%-.--6- -----`-H--=q-X--f/--9b-b--T-H--{--C)-{-w-sl-pv-n6]-l8i-jP-h؉-f-dl-a-_p-]I[-[j-Y-WF-U-Rm-Oh-Lz-I-F-D -A-?C-<-:f-7֦-5D-2dz-0l-.5-,-*-( -%-#-!----0--A- -\-(-D--H---OW---k-˾--œ-e-5$---d-9P----+~-- - Ii- C-*- - -/,]I,,,),,,8,`,C,~b,,!,?+,C,{,',l,,,?,Ғ,,,Y|,I.,,,,}t,%,,,c_,Ǜ,BD,i,,",,e,/,,,f,R,,,u,NG,,I,x,,,d,#,,,y,,,(,],Q,J,<,?,,~,,L6,G,sy,,:,, y,;,3,,,},z,wL,t,qK,n;,k,i.,g,ej,c{,b,`׀,_,^,]@V,[I,ZJ,X',V,UW,S,R,Q,P,P(&,OX,O,N,M,Lj,K,JL,HQ,EZ,Cج,A8,@D,?,> ,=G,;,8K,3,,gN,#,6,,({,X,K,o\,5\,,=,t,, ,"R,#ڌ,%S,&,'",(,),)r+Hh,x-%-9.@_.M._//&=/5L8/>/Fp/Oz"/Y/f/u/M-/;/qf/// /!o/x/M//u/N/u/C+///q080M0w011 1"h1Cf1{k1]1(e1:0يf1 1;10)1Q1.1W1o11V0֭01%1Nq1|1H1{71i1z;1i1HU1"1 1 G1|1 1[1\1a1?1}0|0簯0 0U0200B0ǬV0t0ै0㒮0@200J0`0z0K0)000 0Z0)0 0v0P0Y0{}0X0=΃0R0yD0300040~0w0q0hXb0^n0Vw0M:0F,0>0:d03E0/c0.E0,V0(0 0y090#0Q00p0r0;=0ˊ0+:0//P/ /%F// /)//Ʊ /ɵk/'g/ԍ/lG/ٺ/"/$//T/^7/E=/'//'/W/$//q//}/F/0///Z//9>/v/?/eU/|~/u+/o3/h/aӼ/[r/UN/Q˥/O* /N/Q7=/Uv/Yb/[(/ZRn/X/W(/VM/U/T/Rr/Pk/M$/Jt/Gل/E$/B/?v/.-.+0.*#.(u@.&.%^?.#." 8. h...2..+.}....1..F.~'.x..d.4.. . "m. . w. .h./J..LR.e-- -T-Wb-TI- (-\- - -֋-)--̮D--Ǘ-ů-<-ˆ-----!-- -.-BM---'-R---,-9V-L,-n---L<---->-g-9O---6-v-"-y-X--e---A--IJ-q-y-#-t-8-|-0-;-}-{ !-xׯ-v-uD-s;-r5-p|-n-m/-kN-ig-g-eX-dd-c5t-bL-a-a-`-`%H-_`-_c-_--_-_ <-_)-_"-^)-^-^p3-^4-^ -^M-^-`-bHu-e -j(-n-s-v3 -xL-y -{B-}-N-!-*--- _-=-Щ--#_-x;--,-io,-d.e.+/H4/////:/:/+/>/Y00 0=0(Ǣ07W0Ff0U00aM0hd0e0`0_O0_0`*0f' 0lF0hX0`40^<00L1?11h111]1L1`h1a19K1N1ŘZ1݆222)1L1j1[[1ق1a1q2 1;21b2<2|1⩻1111!1ͳ1O1JE111cs111p`1F1B;1EIs1R߄1hG1x11x1g1Ar16}191,D1"O12 191=I10tb0y50&0%00c0z !0e0Le09050;0=B09w0203,07˯0@0M{x0W0\0\Y0YL0T0M0D"07`0+1/0%y}0%;\0$0% H0(0/h03n030-50'0$D0 0@0KX0&0 u00 [0H/l//L/l//P///}M/Ȕ//i/g/R/M//'w/\/G/x/&//Q/ /P/Z0/,/v/N/?//h/~8///B/#// /O/f//bQ/{0/ /+S///% /]/\D//F/~9/{/v/r(/l/f/a/[Y/UW/Q0w/M4S/J|/G/F?/E /D@/C/B/@/>/</9/6j/2//%/+8/'/"/C/8/N//=3/h/ / M/=/#//>.r. .}.:. n...G.j.2".q>.|.e?.;...ȹ.čf.^c./D.@... ].y.+.X.5l.1.x. 0..R.J.̿.".'.p.M..!.%.\=.!n.c. .mi.n.K...j....c..n..Q=.y. .\.%...j..<..J7.9.R.*S.'.́..J..Q.6:.K(.*..I.{.L.Ș.{.vh.ql.m.i\U.eI.a_.]-.Z.V.S.P.Mp.KIz.Hz.EE.B.@.=?W.:.8/.6$.4w.3"^.2B.1M.0&.03./../T....D.-.-.,/.,7=.,G.+.+8.+l.+Aj.*+.).(~.'(8.%x.$%.". ..9.ϣ.7.'..%.). 5. K8...h-O--9--%-Q-偲--ޱ-W- --.-/-m-y-Ǒ!-`-o]---3--8 --N-c-J-ƬZ-r-Ȑ--ɟ~-QU-[-`-s-Ѓ -=-h-]--ض-}-t&--O-{--- -..KX..T..`.c.$. 3c+,-dU. F../1f/`/ ///K/;/;/a/gE/o//g/00//]/|000 B0 K00.0Gs0Z(W0h0 J0/1!14Z1Uw121y311#11B1q=1}1q1112221,r1Q1Q1y1z2)\2!e22$ 2$2Y1ܴ1~1 11˙011U11ܫJ1z1Mc1411l<1p.1t+1{*11š1_11x1_ 1j1s1_1H1`m1y1$1Nj1nH1[xb1] 1g1kj1k1S}x1001 141EO 1E]1:1+1Eh1Yx1&100o0؜0'0Jl000u0 0*0-~00$0G0wq0f40b\<0gYi0m40tg0|0!0G0?b0wQ0j0c7L0Z)0Mm0;0+0'?040J0e3!0f0q0Ak0}000{0jI0Y:0M0I@0C30<09"007h07H04a0*A0"n0o000y0b0 q0 0O00/!/t/H/'//ڇ&/)7///+/Ǵ/LL/2n/Y//|//<"/~e/*#// +G//3/{/ ..4.@...7.ꙉ..} .Q~.រ.j.ߡL.$.Ǡ.I.X.ۨ..mD."..... ....e..:.X.".!X...-..Ĉ.k.yE.ei.9...H.....ʕ.0.D<.È.T.ū. 1...<.¥.&!.]A.:./.Q.;.,...YL...+.Z..A..4.BK.Ы.=5.y.Ѱ. .>M.c.o.Uf..". ..#.v;. .k.%..-..6&..T..@.~.| Y.z%B.x3#.uf.sEK.p.l<.h.c._O..[N.W.Sm.P,I.M[{.K.I=.H|.Gc.G@.G{.G.H*`.H^.Gt.F[.C(.A,.=@.:r.6.3=.0}.-.*.(!U.%`y."..9.k.$.`e.ԅ.f]. . .J..S.--2-3----Y-ޫ>--c-\-ղU-m-Ӡ-dT-X-k-֔N-?-'--m---PO-$-C --`--&--.#..R..., ...F.{3. M. 8. N. . 9.K].x...'...+[l,;9?,-..pv.X.u//&/H/#O/ T//щ//1k/ST/o^/~a8/0W1R1nE1~1Jw1Ϸ1ٺ171]1gW@122^61-1"25;1311^21T101R1gC1ܶ11#1;~1 1N1}=1`)1rwu1e1_1z1Fj111D1c+i1d1`1I{1U1}11w1^S[1L1U1e;1h1ci1P̎1100n11)f164[14d1-1$1lk1+1z0M0>00͟0Fd000e0g00|00N0(0s/0S08A0002 0:0B~0IA0R 0Z0\c0S0Hߨ0;̣00=0$0C00t0 U0N0.0I/0_+0mjJ0tg0tr<0ova0f0W2j0C1030)w0 0R0?000A$0Q0 vD00// /߭g///5//>/w/7U/dv/]/"/xe//]^/1/j/:/5/{/x</w/w/y/{/|$/|۔/|A/zΆ/xs/u1/q1/l5/hJ/d/`,o/\C/YY/Wm/Tx/R/N/J/F2/B%/=/9m/5v/1ƍ/.c/+`C/()/&/%/$r/% /%/'#/(5/)/*to/*Z2/)2/&/#؇/ ,v/I/o //'"/ [/ QL/+/gd/!/U:..<.r..v..Ԟ. .#.>.z-..!]. x.Ų.&..@.A.r.NF.X..E+..!..r.}.q.f>..]-.&..5.g".H.O..w.l...1.t.H..'.YT..AG.kh.-..[.|.z.y e.yU.|.q-.-.C.dw.L..N..:..@`..?%..vj.H .Y.$.c.a.S.B./..g.R.2.K4.<.l..%r.y.....f.<........].% .o..mR.BS.%;.~0K.z?s.v<.s.o.mT.j.hX.fh.dE.c.aE.`Yr._S.^.^ .].\p.[}o.Ya.V[.Ri.M.H4'.BS.-~--M-1--b --*-{0-*X-;--wF--Ȗ-A-U--f----F--|R-~---~-k-e--̒- M-r-љ*+x,H,-if-PS. .7j.Y0.swn....Ǥ.x.. ..6(.// ..9.R.h/S/;/IE/QIC/dW/w/S/^0 O0a1kc1/1e$1(1+ 1ؓ1oL1ҋ 1f1F11?J11"1v1H161[511w1(61U1k19I[11*1*Y1C1=91$s1-(1E1j$111-1k1`M^1X`1F}E1.-S.+_.+c.+.,;}.-Xh..'.0.2a.5$l.7?.:e.v.?9.>5.<.9.5.0.+<.%W..B..D. /...- -[L-r-y- -I--ް-- -֒-#-ס-%0-=-K-}:-N--ϙ-_---\-h-ݙ-3-V-@&--o-- ----Q-7-@---- ---(-L-|-}--(-tX--E-/n-gL-/p-}Z-w@-q:>-k-f-cH-aj-a;-dZ@-k-w-"-ѥ-R-T-L-}-"-9-p-Z]--%-a--&-t-݊--5|-w-QN-͡*9+DS,',-K-gJ<-Kk-ģ -[1--4-.V. }.oy."9.2z.Dh2.U2.b\R.f.d%.].Q&.YK. s.8..L.˲P.^.U.a.]/gպ/]/E/60+;0'0=//I0H 070K0'0:0e0S0^0!0|11 n0081+1`1v__1;]1$1!1)V16F1*X1#h1n0015I1pTi14:1q 1x0,1D11c10ͤ0'0 &1 17|1O1Jձ1?191,.1 31 0|w0ȩ0w0.B1@l111T0j000l0t090y0 0L0 N00¸:0E0]/0o0Rv0A0: 06020-0)=0*0) 0')0'ht0%000/_|/=/v&/)0?0g0 Q0 0 0 //ΚM/ //ow/ql/ E//Χ08g0'0@}0R0^x0b0a0Ya0I[F07(0%}0r0g0 0&///ԷL/]/ /v/8//{/^/ͤ/"/V9/////V/v//yd9/q\*/l_/i '/f#/ce/]/VJt/M$/B/7]/,/")/bR// ݫ/=.;.L.@...HM.J.˫..r._..7..s.q.{.vb.h.[.Pf.F.>..8.6#.6!.:Kp.@.H.R@.[2.dT.lW.o.o.k}Z.d%.[.R.I.BT.<+.7.3np./ .+w.(;.&_.$".#.".!+.n.z..~.C.6. . X. Ln. ...&->--A-g*--{--Ě-J-"y-F....'.+-.<=.M0._~.q b....q..>..`.g.h.=.I..~J.z6|.u0.q.m.jwz.h.f .d .a:.^~.Z.W,.S.Q^.P'}.P{.R&.V.\c~.c.l_.u.~O./%...Y....4.X.K.8Y. .G.4..|.<,......wK.ki$.`y.U=.J.?.5.+).#.d..2. .8.W.Z.e{.A .n2..`..N.L. K.#0U.%g.'.(%g.(k%.'Ԟ.&N.#. U@.R.".=. X.u--iR-]F-N-J-,E-2-@r-6"-.-6O-S-͞-H5-̨-˞-S-&--65-si--s-(-E-x--Ru-|-&-B_-d-yd-bd-[-G- --G-8-y-O-Fh--6-F-6-c--U-h5--Cs-{-T-----{i-w-s}-p*-m-j-i-i-h-i3b-id-jx-l-n-r7-x-~~--J-c---4-5-*--V-OK-K-o--|-yX--@---j*Z+S+n,Xp],2b-!-Y3-L-=---76-ƅ-X1-- --͐^-(-.7...o.-."MQ...3b}.I.R-.Xs.e L.g".WS.K .R/ /X//@#/#/k/Ek/g!//T///^/(/0\0E0V0~Ub0{*0M}0B30081~(0֡0x%0U&0u0 0J0:0?0l"0{P0Xp1710130&00 0X/ME/I/A}/d/Ж///\//d//p/m/s/$7/f]/Z /h///7//&////|8/o~@/b"/T#/GK/://*/$E/`// 9s//ro.M..횮.F.ܑ:..Ȫ.t.e.`.m2.d.q..tO.g0.\.Sz.K$.Ea.AMO.@f.Ay.E.Ln.U.`a.kj9.tf/.y.{+2.x8.r .jX.bs.[[.U<.O.J6|.D~.>.9.6S.5v.89.;{.>.=@.8.1v.)v.!*1..ߪ.p.4T.5.T..5D. 5. 1.%w..e.D^.U.Q.<..~.,. i..^..$nG.1+.@BO.Pp.b.s&..`.!.sh.!.7+....4...*.].VR..̮._.`/.Y._T.|.wԇ.rk.ll.f3q.`U.[.X_.W^4.X.\e.a>.g.nx.vM$.~U...w. E..9...E.W..|.T.g'. ..`c.+."'.be.#..O+.!.t..e.W.Gxu.7.(M=.Y........W.(..gX.\\.z[. 98. . .0<....Kn.C. S. ߨ. :.k..+.R. ..P-6--/--ױ5-(--ś-%-Tf-uV---[c-`%-H-ː-Κ-ʻ-˂-1-?--p--t-Q--S-WH---kK-*---VN-ٵ--“- -e-k-- -<--)--P--P-k-a--z--|-Mb--[E-he-J"-~r-z-w0-t-rp -p -m6-kC_-h-fR-e-fn-jn-q$3-z8---u-:--Յ-d-4----j--EL--G*-1p--}*(*ڕ+,'Q,},?-'-E -T--V-U)-V-]-m.-/-A-U->--- e.*5...n.".h. 3x.T.%..).ˣ. 7A.y.R.\.d/& /4cN/8.o/c/AB/9hQ/9/6/N V/|P//j//0ݒ0&/jq/q0(60000c`0N;0L0]G0`5^0p0M0c07e0W{0080o0v0Jl0o 0=00E00Wk`0Ge0Z*90n00H*0*G0*1W31 0O0۽p0_00XY010/Y0f0;0k00o 0T^0b0qpC0 0f0"060%O0lW0ԝ0 0o0N250;505}7020,'0#100<0(0/_040:ZA0CI0I\0@R000.10;P0H;0Ld0H@0AJ7070) 00l///[u/G`/</`p+/{n/d00-#0N_d0fcF0z01+0'0 0|p,0g10Q0?!02L0)0!0..4..Ȉ.<.̎.as.uZ.;..]q.,..@|.'.(.U].zl.t.qe.pɩ.r+u.u\.z2..c.-".E.Y..i.wq.v .p*. .ƽ.1..ѹV.U.϶.X.CI..?.CF..W.q.`.Ч.w.j._v.U.KH.C#U.;.5@l./.+".'[.#l..b. . ...T.bE..ǃ.SQ.4.ҝ...:0."3...F.. d..G--=-D -[-----`;-M-ۢ-t-o-j-e-TX-.-s-Q---ǼA-I:- ---]----`-22--.-ҘG- -a-e-g--|- --Q-&~--ޅ-ƒ-;-te--5-'- )--i-1-K,-R---?-~--'---&t-T-G--p--wD--~--ؑ-߳-H}---o\-i -ů{- -"-f- N-x[---n-͇`-q^-W-I-B)O*Z+N+w,_,O,- -y-Y-- -!-&W-7-N-i----[--H-O--Z-]--E-.V. -]-.8 ...l.;/m/!|e/I.ݫ,/ n/%33/O/" /5B-/*:/E~ /Sr/w/Ȗ//Y/c//r=/00-|0QE\0=000 00 a 0*}s0p/Nf/0.*0B000>V%0"0*80=0v00L@0%i0Ҩ0/0:0l00G0(0 i0"0ڹ0W|0V0|0@0000p)0E0086001h0-H070:+C0Ci0k10O0&00CP0w 09O0MP03~0'80 0S0000An0300 o-0#01N+0A[0?}0-M0'Cd08~0Pd0^-0\" 0M"0500"//}/EL///'p//&/R/>/0ʚ00t0B)0R0a0i$)0fMt0XZN0BiP0-7d0$"00 0E0@////͊///N/./H/->/0'000_ 0s0D/` ///'///P/>4/B /)/1/v//g/ I/\//F/o/_/Nߍ/?y/2?/'Zj///;/\/ J/.6.;.U.. .0..Q.V.x.@.y!.iN.].Xд.Z.bG.ml-.y.`.&...h^..yn.M|.] ..zw.q).gת._^.X>'.R.N^C.K.H:5.Ew.B*.?.<.9.7f.5(:.2 .0.-i.*!L.&.!.J.GS..U .{.+.!z.'...5.;a.A.I.Q .Yu.aA.h4.m.qG.t .vU1.y23.|..2;..D..R.ڎ.=Z.tK.{.D..l.p..<.e..ި.F@.&.m.X.<.Q.vV.xX.k.a.ZZ.W.U.W%(.Y.]k.b.iI=.qA .{"..n.-.|.$..j...Ya.v...}h...@.lI.ZL.K+.>Z>.3d<.*C."ah.:.R.. n. . M5. (. .ـ.q.L.z-@-P----g--Ȁ--.--ƫ-t-(--J-M-w#-)[-͉--Ν--)--;----Y-#l---g-h!- -a-m-s-؏-U-|g--ֽ-u-~--?- U-;%-r-z-r-Ə-(-I-ɝD-,?-ڬ3--v-4}--i---u--d-݋-Вt----(-$--p-F-4--P6-TW-l-(x--Ѡ- +---}m-n-Z-w-N-b-P-x-k-----L-5-?:-=W-G4-_-z-ʖL--]--k-‘-,-^--M-)ś"*++J#+x,8+,$|, I,,U,3,Fj,s-l(--f -2ɢ-N~5-m1---.-b-V--6-I-"---U?-ҙ"->-၅-詌. ..xM.:/&&E//7*.8/ %/F/+/ +// /#%0/0/T/nS/w(/H///?/l00 0]/1/߇t//o/0 L//0/p/Cp00`/@00080Z"0 //缧/0Ⱥ030M4u0TQ0fɥ04 0s0 ]00`08B0F\0j 0mA0S$0/v0X0 00_00+0 Yf0"0K 0qP00{0^ȟ020 z/5"/ Q/+//э/|//ǖ/ί//]/0s0zs0500(0 E0V0(0';0^U0̄/ל////O/R.H/:.=/x//.߼..I. 7.x.f.u.....f.o.T.}1.x.sֲ.o g.i.c;.[.R.J!.BP.R<.F48.N}.V(,.\X.`j.aH.`\.[.SZ.H..;#.-.!/.\.g.θ.w--ܖ-B-F-s--7-W-p-ζ-6--2+-J-z-?--7-o-H--S --'W-YL-}--‹-̲--;-g--{-$--0>-{-l$-a!-[-Y-Z/-]*0-bG-kog-w-ho-9-f-$'---)-(---e-0-a----T3-&-"-}j-w-.---.---a--+-d-$---J-K-d-f-B--.---tKW-cA-Vz-Me-IK-K-QI-\-j-yF-]-ϩ-d--L-u-}-:K-5-t"-iHO-bq-`-fk-r-N-[-H'- -ߴ-9-#-g-A---8----`-t@-v--A)*fR+ +,!4!,|\,B,Ē,c',P,;--#F-1K-3-D@.-j-- -ݖ-:-k}--|-G --}v-)---3-fc-g-ߊN.ۭ.P0.A.e/..̕0.,..a(/,}..n.5/4//0/Y/l/s&//x /-/0a0&0L/\/=/506U0/z//ў/ҿ//Յ0//P0000Bk0=g0$0>/˜/K//O/C/]/q/[f////!/•/Y /#P///0/Y/3/*/////://~ /i(/KU/`...z.//, /t!/ //K/|T////ʍp/e/_/T//Ž//|/"/|/2@/$P/d2/a.i.Վ6.w .9..:.i..Ĕ.Ⱥ.g.S..E5.<.6'.0a.(ʈ.0.Sj..r-ҭ.Y.pG.&.-B.<<.G=L.Mi.N.JS.C.:k .0.&.d.w.--k-2-z-S-\q--".d.56.=.DH.p....G. >.4.{-8-d -c-ɉ-ɱ- 5.D. . '.".r.Z.!'.-|v.;B.I.V-"._ZS.c>.c.`.Y.Qm&.HD.A; .;.6.5R.5S.7g.:.=m".?Ӈ.A.C.C.CS.C.C*.C.Ceq.C.DW.DE.C.Aq.?.>(.<.:L.6u.1.*s.".z.. G.ȸ.E.3.h.Q-_-wZ.q,.0.6.. P7.L..ֺ..c"...m.\.0]. .>.-<^--J-W -C-ʛ-“-\----- -S,- ---}--F-C-ir---b----=-- -4^---Z-A-u=-e-WSj-J->-5F-/C-+U@-)-*-./-3JF-9-A?$-HF-P@5-W-]-d>-k -s-{--'-Uw-E;--5-R-~u-u-k~-aq-Ys-S1-O-OT-Q-Wg -_" -hw-sS-~--e--v--[-s-]-Sv-zh-i}-X-G-7q-)B-#-V-7-%-0S-n-!--t;-9q-DX-L-R?-Sd-R-L-D-:K-/>-#_-!_-4m-So----(-3!Y->-I5l-SH-]!g-dN-h-ir-g-d6-`E-^Y-^-_X -`w-at -b |)$*_W++,[,v@F,s,R=,9p,֬,2-! -T-e-\W-iJ-Æ-<--0-t-K-Nj-'---3-}]-u-Z>-р -|k-}<-.C.1.n.p .C.._. Y...߸=.h.M.ɤ.Q/ /"/S:/`/a//*!/+/unG/٤H0 T<0/\/-Q/P /u///{/x/i+/h/u0+}j0*_/n/U///0 P9/R/2}//l//Nl0 00Dz0560U]0_-0AX0l//0>0+q06)0"u0Y/·/>//_/PZ/z-/,/NJ/0:0#600,x/Ϊ/3q/k/T/OJ/T/W@/T/U%/c8/}///u/n/,//?///*/m /A///ޣ/RІ/5/%/ؼ..Z.l..4<./-/f*@//e/z//'/8V/@/W/k~/V|/M\/Jr/L8/Qi/VA*/U/U/UJ/V1~/T]/P).z\.m.\k.GZ.2-U.A..b...z---ܢ-C-m- y-'-"P--#.F.X.fo.X.... B).-- --3L-\G-P-----i-V--Ǽ-ѹV- -Ȉ-iw-׸-jN--|->-~-S-S-B--->-O---Ȼg-.b-ٙ6-V-.aD..."3.([.+$/.+.*.&~.!..=.C.^.@.>.x..|,..!r.#.%3k.&N.'.'r.(m.).*a.+~.+ b.*.*.*.*˶.*ê.).'#e."~..`. .\-1-p--à-D-돺-f-- ----..T.O .P.TC.vR..O---|--匯- -Չ-̹=-D-UJ-z-P-v-V---;--f- --'-"q-P---}Ё-z-x%-wh-w1-xַ-z-}-t-U- -L-|R-u8-kX-_Z-Rm-F-9t-.#-#܂-S-@-1P- -,S-L"-- -#-)0/-.-4G-:-@T-F -M-Tw-[-`gF-dL-gh1-h-gʪ-ds-_e-W$-N*-C-9^-0-*-'-'ڥ-+i-0u-9-B\-L-WE-`-hN-m@-p6{-oH-k-c-Y\-Lv-=---W- ,o,},G,O!,g,w,_,-O-H-R-%r-**-+n-(Fo-!-- 0-,;, },,q,O,F-K-@9--,j-;Z-H<-R-W--Wp-R{-K-C->fz-;f-: -9(-9F-9)*Dl+/+FS, ];,Y~,,,f,c,-݀-#oV-1fo-0;)->g-e-.f- -h-6---.--O-- ----y-xx-#3-[.m[.޵.3.%E.M0.L.%.i.$.... .LZ.D/ / /6"/>/M//]/$p/Q;z//(/R/(/o//7//c//qѰ/\e/p/0"0$B/ը/rc/ҳ/$/`00W/x'/n// /P/00/փ//cbO/2>/!/ '/(2/./0F/5C/E]/_/{3/J// /{//U/L/!///;/o//[!A/6-//`/"..|x.?u.X.? .b/f/.A1/I/Ys./h/t/x/sܮ/c3/LF/4ۨ/#x/P// /(/0/3/4&/4*/4/2/./'/"J/ c/&f-/3>.]..4.1. . .n.:.5H... .]a..D. '. 6(. . .-..B.!.E.I._ .!.. =.e.C.K_.U.N.!\.T. @.-g--擉-΢-}-K-נ-ְ-/--֬- 6-0--A-촢--K--d-ȍ--RA-݊-ۻ--b-{u---2H-V\-f-U-^D--H7- ----p- -z-s_-l1-f9-a-^-]Oh-]R-^|-`N-c/-f-h-j-j(-hD-d6X-]/-U`-L#-AV-5V-*Y-[-.- [-H-,E,0,--- -\- ---#E6-(-.;-39-8-=y-AmC-Dk-F6-F-C[-?-8-/-&_-T--) - :- @- u-,<-%i-#c-,L-5=s-=-D:-I-L-M -J9-E->I-4&-(0-- `,,7~,=,a,l!,,,c,l,X,-c- Y-2--`- --,},蒛,0,{p,Bl,™,,,gz,L-I---$-+{a-/|-0--*-)w-%,-!-*-g-R-r-5w)y *&W*+w8+l,8,w,D,,,,\,݅,>-/-ж--ŕ-I-Y-_-c߶-h<-l5u-nU-o-r-u-|-/-!- --“l-.Gũ..a..Т. .rG...`~.v. .H.}..( ./ ]/(/:01//8/d/7U/k/#7/C/&/_///E/8/@/d/J~/r/\0 0 R/a/(&// ///6/l/ W/A/f5/J/J/*/"I/Xs0 0^0!0//m//Z0 0Cn/P///y9/DI/7/Pu/~/>/@/֎/8//t/zt/0+/ -.Wc.1/ ///r/,!/D/^ /pƥ/s/tu/},/{/fs/X%/\/lB//v/lu/R/0 /v.m..+.^j.Fڇ.l.,.Q%.Hg.ȟ//x/!?/,,/5a#/8/5_/),/aM/g.5.D..// /,///{///S /Xl./G/@/'//)0?/05/2,5/,/"//a/_/#X/,F///-P\/%`// [/..[7.p.ݭ..ɦK. . ...zV.Z.@..K.!..V?.-Q-ݿ-[-4-g-=E--u-c-w -h -Y7-O̻-O>-[V-q&-Ġ-v----?-y----K----g`-O-9~8-(-i--Ȱ-*-=G-Ry-f3-vQ-}---E-x-nO-b-Vi--J-?BL-7?-3#a-3}-85P-@-K~-WG-d-p-{---y- -z-F ---S-⭍-̱- -$N-Z--- -T--A-̖-ϣ-ҊR-'M-׊--F-H-ሚ-J- -h--P-I----6--f----}-σ-Z4-2-K-g----S--TI-,-a-}-5-W--/-&8---t-/--[-F- -أ--Y--g-v5,-j2-a-Y-TZ-Q'-Orj-Ny-Mck-Kp=-H$-Cl-=-7j-1U--T-+-+>-,-/}-2ܷ-6=$-9Q-:-;-;N-9C-6-2Ł--w-&r--J- -A],5W,,,4,i,",,_,,A,g,,,B,}=-p-- ----D-&--M-- 1-,o,J,,ם,0,՚,O,x,,Wc-- yY-5-T--M- ?-?Y-3&--_-%,a,,9,b,)T,W],k,v,,cU,,Q1,,IJO,ь.,o,v,$,dR,ٍ,pb,a,D',@v,3,/,m,G,e,G,>,ܳ,+,|5, ,~---j,},,=,,;",',x)Dΐ*.,*g+CH+Z,,C,g/,y1$,,,BW,,,N,P,yQ-9M- -%`-0jF-8-=-?s-?il->->d-@1-D-Q-pt-`-S-j......f|.C.k+.z.r=...?...1.q./L/ k/%`/i9/q//-9//=//ʮ/@//R////Q/7[/~7/s0 b50XK/ȕ//vW]/ //5/`/*/z//C/2/ԇO/C/J?/;T0I-0T/ڼ/-/p#/%/01/$/÷//Ax/~?//o//\/m4M/2://ǫf/(/`/Ho/ ڙ.O.g.ѧ5..XW./t/k/%P/;ݻ/J3/IP/H.e/O /O;/.f . c5.;.8ß.{p.j.H.1/_/ //{/Z*//wV/{.Q.Y.S.ع../~!/ !m/ ~-/ L/g/."D.Ȋ.D.d.DI.... |/z//^L///?/|//.6da.. j---ً-Ϋ-@V-3- -f\-o:-WZ-Iv8-A-<-7!-/8-%8----&p-9E-R-n-Ȏ- >---+--!-SX-iT-S{->P-* %-V-,,,ۊ,ϓ,- -t-0->u-G3-L-K-G(-@?-7o:--.-"|--u-۾--l- K-yp- -%m^-/-8-@-Hп-Q -]h-l--*-h>-Ep-'-`[-----j-e--1-W-:-C- w-Rp--p---S1-*- -»--q-׽-Ď-rZ-T--j-(-]$-&-M-!'---A-K-;-I-W-`----6-ġ-X-qi--7--~)-*-e-e-N-h- -t-x]b-n!-dZ-Z-Q˞-I-@7-9Eg-2)-+2-%3- -Ly-H--+-o-N- /-U-,Dy, ,dž--W<- - g-4-R-͹- -Q- ?n- -~-],D,,D{,+},~,g,,4,X,Ȍ,&,1,w,$,z,կ,?,,,ʪ,i,-,޺,,,쏅,*, ,,4,,0,P,!,T,A,1,u,,,>,k,XK,H,),,;E,C,{,,,],F,ѯ,D,,tS,,,tc,fx,`c,b5F,kNT,zf, ,,Nh, ,,l, , ,],|,g, ,p,\>,hh,N,,,, ,,8,,hV,1q,, ,w,X,',' ,,@,ر,,C)+l)䈂*+*(l+8`+[,*,I,Yy,^,`,gY,vG,>,M,3,O,--\--"GG-'-(-(-'q-&D-':E-+>-6-T-F-U-T#...wT.(.c.9p..q.i*.@.z...2l..b.B//}//U/`.$/A/`/l/́O/P/W/sN/~/ /ϕ/X/G/-/r!/}(00?/Ìw//g/(/v/[A//oL/jCm/r%/d//B//I/R//]//z#/G/t/ȏ//ғ/ /3/b8'/_1/o'/2/k/@/JY//D///m/' .@....c.۬.5.Q//{^/!#/,-_---&-Z-Y$-yIJ-r-ni-l5-k,*-jV-j-k-li%-n܍-rs-v-{a-=-- -&-|_c-v-ol-gr_-_@-WJ-O@-HE-@-81-/-%'-T-H- {J--,ʁ,p,),,b,l!,,ۛ,Ԡ^,,y,J,,w;,a,,$,s,;N-_->F, I,,<,,ޕ,5,),,,$,,,,@,V,8,m,Y,),,S,G(,,2,A,H,u,,', ,N.,+%, , ,,b\,to,],~,#,0e,,B,,!(,,#,,,A,$',i,v,.,u,Ƣ7,V,!,,,,0,x6,_,K3,;,0s,+,-3,5l(,BA,Rԅ,e`,w,W, ,U,щ,ҁ,ݯ,Rr,,k5,z2,q!,l,i@,i,m3_,s|,|#,I,,],,Ά,%,T{,,<,y,a,e4,,,SI)~)ܔ*+n++j,<,5e,C2,H ,J,P,^,t,W,,,,,-y- a-G--=-?-D-b--D-&-Aa -jC--&-ZP.m .;.t.B.J. .v.n4.,H.t...}.7..ĞV./ //I /Q.yw/23/y[/;//Y/e/a9/r/~//i/@/C/&o/i>//.D.δ...k0.QE.9\ .'a-L-D-ѯ-(.!`.`e.73..^.I.+..Cr.). . ?..xLj..B.qo.r.$.u.®.I.._.P.!-.,...L.a.gK.I.... '. ...l.4.4.{..qo.Z6.FR.9.0.*;.%O.-.6. -w --ɵ-v-;--r,-bۦ-Y>-O]-B`J-05-}]- v,X,Tz,,Ѯ,;&,lc,,8,e,.},:,--.-'--81-E-L'F-M(-H-?-4Q-&-- n,,࿚,L,1,Z, ,/,%,,9,-Wr- K,- 5- u-:-p-.-b,A,?1,Ԃ,~,S,׃,,,ӷ, 3,,i,T,y,O,b-OY- L-r"--+-;-I-V$-a2-h-mq-p-s-t'-t -r-o-kɕ-f-a -[-XZ-U-Uz-V-XrG-Z -]k-`-b-dv-f-gZ -g-hqr-j-n̴-th>-z+o-~e@-r-}*P-wJ-oO-f3,-^-W^-Sv-QF-P~-Pu-PG-Q"!-Qޓ-S0-U9-W-ZM-\ -\K2-Z.-W93-RC}-L\-F(-@A-;-6 -2K-.\-(q- -B - ,B,Ͳ,tr,l,=, ,i,/0,',5,|,q,[,Q",,,,kK,BS,#?,7,,,Q,',=,;,G,r!,ޖ,Dž,,\,.,X,,~a,p/,h ,fQ,h,mu,t۲,|,9,,Z,,,Ո,,o,d6,,,p,$,,I,~, , ,v9,d,V,MO,J@,OX,Z#,i@,|b,̛,~,,,,,p,,| ,9-,,,`,w$-,_,H,2W,,y,X+)++,C, ,,.+-,?J,O2j,ZB,bX,e,e,c),`t,],Z,W,U,Rp,P8,P&,Ps,S$D,Wl,^cW,f,p&Q,z5,,,m,e,+,JR,,,,,$) k)*qK+ +-+ , l,$A,1,5,7W,=q,I ,]ۂ,y,H,3,,݌, ,X-:- d- e- - 3}- 5- b-ͱ-{-1-W-q9-o-&;.[ .{.a%.o'...}P.t.e.>..~t.ύ. ...,/ݰ/ s/-/--80-'6--8- TR-,A4,,I,,Q,E,6,,D,(=,?,,Y,s,,7[,G,,zb,,I,Ώ,,V],r,,B,, D,,u,#,,],4,,),e,h,֣1,i-+-T2-;A-,--9[-E-O-W#-\-^]-\M-W]1-On|-EC-;VQ-1C-)-$s$-!M0- f- N-!n-#-%-&װ-( -(Y-'W -%-%,L-'D-,-5:-@r-J-R(-U}Z-T-PTY-I-BT-<˄-8-6f-5-63s-6-7-7I@-7-8 -8"-9hc-9Q-7J*-3-.-(˺-"-- -y--Px--6- a,g,,,D,N,,w,=,S,,,,A,m,y,|,z,{z,v,,h,,,~g,,,ߒz,, ,,,ۭF,M=,D,,",|,;,t",_,PE,F,,A٭,B[,H],P,Y=,bL,jp,q{Z,w/,},2K,,,,En,j:,,,҂,Q,spf,f:7,W-,H,9m^,+L,,:\,,,*,;Y,O0,b[,s,tv,|,7I,k,=,y0,~ ,sF*,dΧ,SS,?yL,*`D,j,0+Q.+a+,++++`++V+ +, ,@6,"~,(;,*B,*H,,*5,+nl,.`,2,6,:G,;,; ,:,80,736,8N,;BG,@,G,Ptz,YY,cF,mz,u]Y,{1,~,~&,|jM,y$,v6,,t)e)1*dor++w{+$,C,",(,,A,.* ,3[,>G,R R,l,>`,F, ,Ȗ:,g, ,+?-D-9--k-C--5--(>-L p-dfF-c t-.O3.nt.U.=$.-...w. .*..x{.|w.Z.e.,..V).{/)E/=l../QAs///Η/%//,p/..(.[/}/A/L/3GA/..J.^W.W.$.j.F.7..1. ./F.{.G#.Y..Э.ê..:..3...ʷ.!@.`M.5ID.:=.44-4--4d--j- -Ə.{.(.:BN.CEs.H{.RZ.V.Y9.R'.=.'.%.s.!z.<8.\[$.yJ.D0. .i.9.~.q .ch&.WX.Nhv.Im.H%.K.N.Q.Tt.V*p.V".S.L*.DQ.;.4 .,*.#.,. -U-%-ņ--y--ʞ---_-q+-X`-=j-"-  ,-,ܥ),3,κN, , ,;,{8,,q2,a,[#,[*,Zt,X,V3,Xk:,a,u!,,Q,b,J2,-$-- --E,i,l,/T,],p,*,X,,!,,,{,C,,K,(,լ!,ՙ,ϖ,9,P,Y,Š,k,Qs,,,,u,_ ,X,`|,r,,)G,0 ,>,=,k,,_,\,,,,,,--C-O-(-0z-4-6s-56-1v-*|[-"--4-- -k=-*;,(,,----Ex-p-G-U-z--K- UA--Q-$M-'U-&1-#---Y--I-ܠ-)-[---)?---t - (-- J--,n ,9,G, ,,,,Q,,LE,,,~,^e_,NR},L@,Rq,ZI,_,^\j,Xj ,Pt,Im,D,CE,DMW,G,M,W,fٖ,{\@,i,Y,(,U,D,,Zx,k},,,,,~,h ,S!,@F],0A,#,X,Ո,ц,+L,$=,,",3s,9,?,D,It,N ,S,,Y0l,]qi,_,_F,]],Xh,Q{,I ,,?p|,4,)l$,g,Y,5P++f+/+i,Z,U,#,2Y,@3,K/,R0,U,U,S_,OG,H%,>,3F,%=,[l,4++O+x+ +-+n+]eg+W+]8+r+S++C++t+)(+d,,C&,G,T9,, i_, o,aC,,~,Ъ,,,x, ,#,'[,-,4,;,C,K,Q,V,Y,[d,Zs,Y>,X,XC()D)**Ytf*=+k+Ø+F,B,,#,%̳,*,5,G,aP,f,,[,,ӻ9,@,,{z,~,i0,,m,GQ-V- - --BE-Yt,-X*2-ͬi.E9.c #.K1.t2..r.J".}:).*.U%.ek.s.r.).$.hx.G .".ҧ//6l../=/w/8/U/,9/,ݩ/A/N/Z,/X9/W/${/8W/k/t//N/xX/0Yf/7 /E/I/k/)j///CV/T/t[ /e/8/\/m/kJ0/X6/.-.:.7/X+/D//d7@/('/ ZA/ /5/x/.^.H-X--i-u-rmL-gU-_.#-V?-K>->M-/-- $,|,Ή,,2z,ja,[,-,x,r,kA,K,1,!=,3,,V,R ,",'@,1,C3,]X,~ ,&,d",Vf,,МD,Ҁ,I,(,,V,,,3,\l,sI,\=,K,C|,D: ,P,hn,,^,x,,A, Q,3,,,,E,v,,,kT,K,4,+,0;r,>,Q&`,aa,lП,qW/,qv,n,i,e,a,b ,k,,,,,, ?-i- 4------ '5-[,I,,e ,T,̷,{{,ƣ5,ơ,,pp,O,Ϝ,,+,,?,ëM,_<,K,,,y,,,,, ,,ޑ_,N,H,2,ܝ,@,7 ,PS,ݶ,_,,h,i,ְp,/,Ⱥ,]b,H,,T,c,,,\c,#,7,w,a,>z7,%\,I,ˁ,,$,($,',#T,.,,,,,,46,%G,1=T,A',V,lK,x',,G,,,,R,l%,XZ,EW,3,#,FF,'++ʳ+*+<+,*,7, ,V*,6,q,o,!f,%,),,ӓ,.,.P,+,'W,!,P,,/, ,Ysncosmo-2.12.1/sncosmo/fitting.py000066400000000000000000001530141476435666400170060ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import copy import math import time import warnings from collections import OrderedDict import numpy as np from .bandpasses import Bandpass from .photdata import photometric_data from .utils import Interp1D, Result, ppf __all__ = ['fit_lc', 'nest_lc', 'mcmc_lc', 'flatten_result', 'chisq'] class DataQualityError(Exception): pass def generate_chisq(data, model, spectra, signature='iminuit', modelcov=False): """Define and return a chisq function for use in optimization. This function pre-computes and saves the inverse covariance matrix, making subsequent evaluations faster. The model covariance (if specified) is fixed at the time the chisq function is generated.""" # precompute inverse covariance matrix if data is not None: cov = (np.diag(data.fluxerr**2) if data.fluxcov is None else data.fluxcov) if modelcov: _, mcov = model.bandfluxcov(data.band, data.time, zp=data.zp, zpsys=data.zpsys) cov = cov + mcov invcov = np.linalg.pinv(cov) # If we have spectra, build the covariance matrix for them individually. if spectra is not None: if modelcov: raise ValueError('modelcov not supported for spectra') spectra = np.atleast_1d(spectra) spectra_invcovs = [] for spectrum in spectra: spectra_invcovs.append(np.linalg.pinv(spectrum.fluxcov)) # iminuit expects each parameter to be a separate argument (including fixed # parameters) if signature == 'iminuit': def chisq(*parameters): model.parameters = parameters full_chisq = 0. if data is not None: model_flux = model.bandflux(data.band, data.time, zp=data.zp, zpsys=data.zpsys) diff = data.flux - model_flux phot_chisq = np.dot(np.dot(diff, invcov), diff) if math.isnan(phot_chisq): # Tell iminuit to keep away from this point # (in most cases this should be inf instead of nan anyway) phot_chisq = float('inf') full_chisq += phot_chisq if spectra is not None: for spectrum, spec_invcov in zip(spectra, spectra_invcovs): sample_wave, sampling_matrix = \ spectrum.get_sampling_matrix() sample_flux = model.flux(spectrum.time, sample_wave) spec_model_flux = ( sampling_matrix.dot(sample_flux) / sampling_matrix.dot(np.ones_like(sample_flux)) ) spec_diff = spectrum.flux - spec_model_flux spec_chisq = spec_invcov.dot(spec_diff).dot(spec_diff) full_chisq += spec_chisq return full_chisq else: raise ValueError("unknown signature: {!r}".format(signature)) return chisq def chisq(data, model, modelcov=False): """Calculate chisq statistic for the model, given the data. Parameters ---------- model : `~sncosmo.Model` data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. modelcov : bool Include model covariance? Calls ``model.bandfluxcov`` method instead of ``model.bandflux``. The source in the model must therefore implement covariance. Returns ------- chisq : float """ data = photometric_data(data) data.sort_by_time() if data.fluxcov is None and not modelcov: mflux = model.bandflux(data.band, data.time, zp=data.zp, zpsys=data.zpsys) return np.sum(((data.flux - mflux) / data.fluxerr)**2) else: # need to invert a covariance matrix cov = (np.diag(data.fluxerr**2) if data.fluxcov is None else data.fluxcov) if modelcov: mflux, mcov = model.bandfluxcov(data.band, data.time, zp=data.zp, zpsys=data.zpsys) cov = cov + mcov else: mflux = model.bandflux(data.band, data.time, zp=data.zp, zpsys=data.zpsys) invcov = np.linalg.pinv(cov) diff = data.flux - mflux return np.dot(np.dot(diff, invcov), diff) def flatten_result(res): """Turn a result from fit_lc into a simple dictionary of key, value pairs. Useful when saving results to a text file table, where structures like a covariance matrix cannot be easily written to a single table row. Parameters ---------- res : Result Result object from `~sncosmo.fit_lc`. Returns ------- flatres : Result Flattened result. Keys are all strings, values are one of: float, int, string), suitable for saving to a text file. """ flat = Result(success=(1 if res.success else 0), ncall=res.ncall, chisq=res.chisq, ndof=res.ndof) # Parameters and uncertainties for i, n in enumerate(res.param_names): flat[n] = res.parameters[i] if res.errors is None: flat[n + '_err'] = float('nan') else: flat[n + '_err'] = res.errors.get(n, 0.) # Covariances. for n1 in res.param_names: for n2 in res.param_names: key = n1 + '_' + n2 + '_cov' if n1 not in res.vparam_names or n2 not in res.vparam_names: flat[key] = 0. elif res.covariance is None: flat[key] = float('nan') else: i = res.vparam_names.index(n1) j = res.vparam_names.index(n2) flat[key] = res.covariance[i, j] return flat def _mask_bands(data, model, z_bounds=None): if z_bounds is None: return model.bandoverlap(data.band) else: return np.all(model.bandoverlap(data.band, z=z_bounds), axis=1) def _warn_dropped_bands(data, mask): """Warn that we are dropping some bands from the data:""" drop_bands = [(b.name if b.name is not None else repr(b)) for b in set(data.band[np.invert(mask)])] warnings.warn("Dropping following bands from data: " + ", ".join(drop_bands) + "(out of model wavelength range)", RuntimeWarning) def cut_bands(data, model, z_bounds=None, warn=True): mask = _mask_bands(data, model, z_bounds=z_bounds) if not np.all(mask): # Fail if there are no overlapping bands whatsoever. if not np.any(mask): raise RuntimeError('No bands in data overlap the model.') if warn: _warn_dropped_bands(data, mask) data = data[mask] return data, mask def t0_bounds(data, model, spectra=None): """Determine bounds on t0 parameter of the model. The lower bound is such that the latest model time is equal to the earliest data time. The upper bound is such that the earliest model time is equal to the latest data time.""" times = [] if data is not None: times.append(data.time) if spectra is not None: for spec in np.atleast_1d(spectra): times.append(spec.time) times = np.hstack(times) return (model.get('t0') + np.min(times) - model.maxtime(), model.get('t0') + np.max(times) - model.mintime()) def _guess_t0_and_amplitude_photometry(data, model, minsnr): """Guess t0 and amplitude of the model from photometry.""" # get data above the signal-to-noise ratio cut significant_data = data[(data.flux / data.fluxerr) > minsnr] if len(significant_data) == 0: raise DataQualityError('No data points with S/N > {0}. Initial ' 'guessing failed.'.format(minsnr)) # grid on which to evaluate model light curve timegrid = np.linspace(model.mintime(), model.maxtime(), int(model.maxtime() - model.mintime() + 1)) # get data flux on a consistent scale in order to compare to model # flux light curve. norm_flux = significant_data.normalized_flux(zp=25., zpsys='ab') model_lc = {} data_flux = {} data_time = {} for band in set(significant_data.band): model_lc[band] = ( model.bandflux(band, timegrid, zp=25., zpsys='ab') / model.parameters[2]) mask = significant_data.band == band data_flux[band] = norm_flux[mask] data_time[band] = significant_data.time[mask] if len(model_lc) == 0: raise DataQualityError('No data points with S/N > {0}. Initial ' 'guessing failed.'.format(minsnr)) # find band with biggest ratio of maximum data flux to maximum model flux maxratio = float("-inf") maxband = None for band in model_lc: ratio = np.max(data_flux[band]) / np.max(model_lc[band]) if ratio > maxratio: maxratio = ratio maxband = band # amplitude guess is the largest ratio amplitude = abs(maxratio) # time guess is time of max in the band with the biggest ratio data_tmax = data_time[maxband][np.argmax(data_flux[maxband])] model_tmax = timegrid[np.argmax(model_lc[maxband])] t0 = model.get('t0') + data_tmax - model_tmax return t0, amplitude def _guess_t0_and_amplitude_spectra(spectra, model, minsnr): """Guess t0 and amplitude of the model from spectra. The spectra don't necessarily have the same binning which makes this challenging. To handle this, we synthesize photometry in a range of different synthetic filters. We then call `_guess_t0_and_amplitude_photometry` on this photometry. """ # Build a set of bands to use for synthetic photometry. target_band_width = 500 # Angstroms minwave = model.minwave() maxwave = model.maxwave() band_count = int(math.ceil((maxwave - minwave) / target_band_width)) band_edges = np.linspace(minwave, maxwave, band_count+1) band_starts = band_edges[:-1] band_ends = band_edges[1:] bandpasses = np.array([Bandpass([start, end], [1., 1.]) for start, end in zip(band_starts, band_ends)]) all_bands = [] all_fluxes = [] all_fluxerrs = [] all_times = [] for spectrum in np.atleast_1d(spectra): # Find the bandpasses that overlap this spectrum. band_mask = ((spectrum.bin_edges[0] <= band_starts) & (spectrum.bin_edges[-1] >= band_ends)) spec_bands = bandpasses[band_mask] spec_flux, spec_fluxcov = spectrum.bandfluxcov(spec_bands, zp=25., zpsys='ab') spec_fluxerr = np.sqrt(np.diag(spec_fluxcov)) all_bands.extend(spec_bands) all_fluxes.extend(spec_flux) all_fluxerrs.extend(spec_fluxerr) all_times.extend(np.ones_like(spec_flux) * spectrum.time) photometry = photometric_data({ 'band': all_bands, 'flux': all_fluxes, 'fluxerr': all_fluxerrs, 'time': all_times, 'zp': [25.] * len(all_fluxes), 'zpsys': ['ab'] * len(all_fluxes), }) # Sort the photometry by time photometry = photometry[np.argsort(photometry.time)] return _guess_t0_and_amplitude_photometry(photometry, model, minsnr) def guess_t0_and_amplitude(data, model, minsnr, spectra=None): """Guess t0 and amplitude of the model based on the data. If we have photometry, we use it for the guessing. If not, we use all available spectra instead. """ if data is not None: return _guess_t0_and_amplitude_photometry(data, model, minsnr) elif spectra is not None: return _guess_t0_and_amplitude_spectra(spectra, model, minsnr) else: raise ValueError('need either photometry or spectra to guess t0 and ' 'amplitude.') def _print_iminuit_params(names, fixed_parameters, start_values, start_errors, bounds): """Verbose printing of parameters to pass to Minuit""" for name in names: if name in fixed_parameters: continue print(name, start_values[name], 'step=', start_errors[name], end=" ") if name in bounds: print("bounds=", bounds[name], end=" ") print() def _phase_and_wave_mask(data, t0, z, phase_range, wave_range): """Return a mask for the data based on an allowed rest-frame phase and/or wavelength range (given some t0 and z).""" if phase_range is not None: data_phase = (data.time - t0) / (1.0 + z) phase_mask = ((data_phase > phase_range[0]) & (data_phase < phase_range[1])) if wave_range is not None: data_obswave = np.array([b.wave_eff for b in data.band]) data_restwave = data_obswave / (1.0 + z) wave_mask = ((data_restwave > wave_range[0]) & (data_restwave < wave_range[1])) if phase_range: if wave_range: return phase_mask & wave_mask return phase_mask if wave_range: return wave_mask return None def _run_iminuit(chisq, parameter_names, start_values, start_errors, bounds, fixed_parameters, maxcall, verbose=False): try: import iminuit except ImportError: raise ValueError("Minimization method 'minuit' requires the iminuit " "package") # The iminuit API changed significantly in version 2. Handle both the new # and old APIs. from looseversion import LooseVersion iminuit_version = LooseVersion(iminuit.__version__) if verbose: print("Initial parameters:") _print_iminuit_params(parameter_names, fixed_parameters, start_values, start_errors, bounds) print() if iminuit_version >= LooseVersion("2.0.0"): # New API m = iminuit.Minuit(chisq, name=parameter_names, **start_values) m.errordef = iminuit.Minuit.LEAST_SQUARES m.print_level = 1 if verbose >= 2 else 0 m.throw_nan = True for key, value in start_errors.items(): m.errors[key] = value for key, value in bounds.items(): m.limits[key] = value for key in fixed_parameters: m.fixed[key] = True m.migrad(ncall=maxcall) fmin = m.fmin else: # Old API # Set up keyword arguments to pass to Minuit initializer. kwargs = {} # forced_parameters/name keyword also changed in v1.4.3, handle that. if iminuit_version >= LooseVersion("1.4.3"): kwargs['name'] = parameter_names else: kwargs['forced_parameters'] = parameter_names for key, value in start_values.items(): kwargs[key] = value for key, value in start_errors.items(): kwargs['error_' + key] = value for key, value in bounds.items(): kwargs['limit_' + key] = value for key in fixed_parameters: kwargs['fix_' + key] = True kwargs['error_' + key] = 0. m = iminuit.Minuit(chisq, errordef=1., print_level=(1 if verbose >= 2 else 0), throw_nan=True, **kwargs) fmin, params = m.migrad(ncall=maxcall) return m, fmin def fit_lc(data=None, model=None, vparam_names=[], bounds=None, spectra=None, method='minuit', guess_amplitude=True, guess_t0=True, guess_z=True, minsnr=5.0, modelcov=False, verbose=False, maxcall=10000, phase_range=None, wave_range=None, warn=True): """Fit model parameters to data by minimizing chi^2. Ths function defines a chi^2 to minimize, makes initial guesses for t0 and amplitude, then runs a minimizer. Parameters ---------- data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` The model to fit. vparam_names : list Model parameters to vary in the fit. bounds : `dict`, optional Bounded range for each parameter. Keys should be parameter names, values are tuples. If a bound is not given for some parameter, the parameter is unbounded. The exception is ``t0``: by default, the minimum bound is such that the latest phase of the model lines up with the earliest data point and the maximum bound is such that the earliest phase of the model lines up with the latest data point. spectra : `~sncosmo.Spectrum` or list of `~sncosmo.Spectrum` objects A list of spectra to include in the fit. guess_amplitude : bool, optional Whether or not to guess the amplitude from the data. If false, the current model amplitude is taken as the initial value. Only has an effect when fitting amplitude. Default is True. guess_t0 : bool, optional Whether or not to guess t0. Only has an effect when fitting t0. Default is True. guess_z : bool, optional Whether or not to guess z (redshift). Only has an effect when fitting redshift. Default is True. minsnr : float, optional When guessing amplitude and t0, only use data with signal-to-noise ratio (flux / fluxerr) greater than this value. Default is 5. method : {'minuit'}, optional Minimization method to use. Currently there is only one choice. modelcov : bool, optional Include model covariance when calculating chisq. Default is False. If true, the fit is performed multiple times until convergence. phase_range : (float, float), optional If given, discard data outside this range of phases. Note that **the definition of phase varies between models**: For example, phase=0 refers to explosion time in some models and time of peak B band flux in others. *New in version 1.5.0* wave_range : (float, float), optional If given, discard data with bandpass effective wavelengths outside this range. *New in version 1.5.0* verbose : bool, optional Print messages during fitting. warn : bool, optional Issue a warning when dropping bands outside the wavelength range of the model. Default is True. *New in version 1.5.0* Returns ------- res : Result The optimization result represented as a ``Result`` object, which is a `dict` subclass with attribute access. Therefore, ``res.keys()`` provides a list of the attributes. Attributes are: - ``success``: boolean describing whether fit succeeded. - ``message``: string with more information about exit status. - ``ncall``: number of function evaluations. - ``chisq``: minimum chi^2 value. - ``ndof``: number of degrees of freedom (len(data) - len(vparam_names)). - ``param_names``: same as ``model.param_names``. - ``parameters``: 1-d `~numpy.ndarray` of best-fit values (including fixed parameters) corresponding to ``param_names``. - ``vparam_names``: list of varied parameter names. - ``covariance``: 2-d `~numpy.ndarray` of parameter covariance; indicies correspond to order of ``vparam_names``. - ``errors``: OrderedDict of varied parameter uncertainties. Corresponds to square root of diagonal entries in covariance matrix. - ``nfit``: number of times the fit was performed. Can be greater than one when model covariance, phase range or wavelength range is used. *New in version 1.5.0.* - ``data_mask``: Boolean array the same length as data specifying whether each observation was used in the final fit. *New in version 1.5.0.* fitmodel : `~sncosmo.Model` A copy of the model with parameters set to best-fit values. Notes ----- **t0 guess:** If ``t0`` is being fit and ``guess_t0=True``, the function will guess the initial starting point for ``t0`` based on the data. The guess is made as follows: * Evaluate the time and value of peak flux for the model in each band given the current model parameters. * Determine the data point with maximum flux in each band, for points with signal-to-noise ratio > ``minsnr`` (default is 5). If no points meet this criteria, the band is ignored (for the purpose of guessing only). * For each band, compare model's peak flux to the peak data point. Choose the band with the highest ratio of data / model. * Set ``t0`` so that the model's time of peak in the chosen band corresponds to the peak data point in this band. **amplitude guess:** If amplitude (assumed to be the first model parameter) is being fit and ``guess_amplitude=True``, the function will guess the initial starting point for the amplitude based on the data. **redshift guess:** If redshift (``z``) is being fit and ``guess_z=True``, the function will set the initial value of ``z`` to the average of the bounds on ``z``. Examples -------- The `~sncosmo.flatten_result` function can be used to make the result a dictionary suitable for appending as rows of a table: >>> from astropy.table import Table >>> table_rows = [] >>> for sn in sne: ... res, fitmodel = sncosmo.fit_lc( ... sn, model, ['t0', 'x0', 'x1', 'c']) ... table_rows.append(flatten_result(res)) >>> t = Table(table_rows) """ if data is not None: # Standardize data data = photometric_data(data) # sort by time (some sources require this) # We keep track of indicies that sort the array because we want to be # able to report the indexes of the original data that were used in the # fit. if not np.all(np.ediff1d(data.time) >= 0.0): sortidx = np.argsort(data.time) data = data[sortidx] else: sortidx = None else: sortidx = None if spectra is not None: # Make sure that we have times for all of the spectra for spectrum in np.atleast_1d(spectra): if spectrum.time is None: raise TypeError("'time' must be set for each spectrum") if model is None: raise TypeError("missing required argument 'model'") # Make a copy of the model so we can modify it with impunity. model = copy.copy(model) # Check that vparam_names isn't empty and contains only parameters # known to the model. if len(vparam_names) == 0: raise ValueError("no parameters supplied") for s in vparam_names: if s not in model.param_names: raise ValueError("Parameter not in model: " + repr(s)) # Order vparam_names the same way it is ordered in the model: vparam_names = [s for s in model.param_names if s in vparam_names] # initialize bounds bounds = copy.deepcopy(bounds) if bounds else {} # Check that 'z' is bounded (if it is going to be fit). if 'z' in vparam_names: if 'z' not in bounds or None in bounds['z']: raise ValueError('z must be bounded if fit.') if guess_z: model.set(z=sum(bounds['z']) / 2.) if model.get('z') < bounds['z'][0] or model.get('z') > bounds['z'][1]: raise ValueError('z out of range.') if data is not None: # Cut bands that are not allowed by the wavelength range of the model. fitdata, support_mask = cut_bands(data, model, z_bounds=bounds.get('z', None), warn=warn) # Initially this is the complete mask on data. data_mask = support_mask else: fitdata = None support_mask = None data_mask = None # Find t0 bounds to use, if not explicitly given if 't0' in vparam_names and 't0' not in bounds: bounds['t0'] = t0_bounds(fitdata, model, spectra) # Note that in the parameter guessing below, we assume that the source # amplitude is the 3rd parameter of the Model (1st parameter of the Source) # Turn off guessing if we're not fitting the parameter. if model.param_names[2] not in vparam_names: guess_amplitude = False if 't0' not in vparam_names: guess_t0 = False # Make guesses for t0 and amplitude. # (For now, we assume it is the 3rd parameter of the model.) if (guess_amplitude or guess_t0): t0, amplitude = guess_t0_and_amplitude(fitdata, model, minsnr, spectra) if guess_amplitude: model.parameters[2] = amplitude if guess_t0: model.set(t0=t0) if method == 'minuit': fixed_parameters = [] start_values = {} start_errors = {} # Figure out parameters to pass to iminuit. for name in model.param_names: start_values[name] = model.get(name) # Fix parameters not being varied in the fit. if name not in vparam_names: fixed_parameters.append(name) continue # Check that the bounds are valid. if name in bounds: if None in bounds[name]: raise ValueError('one-sided bounds not allowed for ' 'minuit minimizer') # Initial step size if name in bounds: step = 0.02 * (bounds[name][1] - bounds[name][0]) elif model.get(name) != 0.: step = 0.1 * model.get(name) else: step = 1. start_errors[name] = step # count degrees of freedom ndof = 0 if data is not None: ndof += len(fitdata) elif spectra is not None: for spectrum in np.atleast_1d(spectra): ndof += len(spectrum) ndof -= len(vparam_names) # run once with no model covariance, regardless of whether # modelcov=True fitchisq = generate_chisq(fitdata, model, spectra, signature='iminuit', modelcov=False) m, fmin = _run_iminuit(fitchisq, model.param_names, start_values, start_errors, bounds, fixed_parameters, maxcall, verbose) if verbose: print("{} function calls; {} dof.".format(fmin.nfcn, ndof)) # numpy array of best-fit values (including fixed parameters). parameters = np.array([m.values[name] for name in model.param_names]) model.parameters = parameters # set model parameters to best fit. # Iterative Fitting if phase_range or wave_range: if spectra is not None: raise ValueError('phase_range and wave_range are not ' 'supported for spectra') range_mask = _phase_and_wave_mask(data, model.get('t0'), model.get('z'), phase_range, wave_range) data_mask = range_mask & support_mask # if model covariance, we need to re-run iteratively until convergence # if phase range is given, we need to rerun if there are any # masked points. refit = (modelcov or ((phase_range or wave_range) and np.any(data_mask != support_mask))) nfit = 1 while refit: # set new starting point to last point for name in vparam_names: start_values[name] = model.get(name) # re-crop data based on ranges, if necessary if (phase_range or wave_range): fitdata = data[data_mask] # count degrees of freedom ndof = 0 if data is not None: ndof += len(fitdata) elif spectra is not None: for spectrum in np.atleast_1d(spectra): ndof += len(spectrum) ndof -= len(vparam_names) # generate chisq function based on new starting point fitchisq = generate_chisq(fitdata, model, spectra, signature='iminuit', modelcov=modelcov) m, fmin = _run_iminuit(fitchisq, model.param_names, start_values, start_errors, bounds, fixed_parameters, maxcall, verbose) if verbose: print("{} function calls; {} dof.".format(fmin.nfcn, ndof)) parameters = np.array([m.values[name] for name in model.param_names]) model.parameters = parameters nfit += 1 refit = False # only consider refitting if we got a valid answer and we're under # the maximum number of iterations: if fmin.is_valid and nfit < 22: # recalculate data mask based on new t0, z if phase_range or wave_range: old_data_mask = data_mask range_mask = _phase_and_wave_mask(data, model.get('t0'), model.get('z'), phase_range, wave_range) data_mask = support_mask & range_mask # we'll refit if we changed any data refit = np.any(data_mask != old_data_mask) # refit if *any* parameter changed by more than 10% of # statistical error bar if modelcov: for name in vparam_names: frac_change = (abs(m.values[name] - start_values[name]) / m.errors[name]) refit = refit or frac_change > 0.1 # Build a message. message = [] if fmin.has_reached_call_limit: message.append('Reached call limit.') if fmin.hesse_failed: message.append('Hesse Failed.') if not fmin.has_covariance: message.append('No covariance.') elif not fmin.has_accurate_covar: # iminuit docs wrong message.append('Covariance may not be accurate.') if not fmin.has_posdef_covar: # iminuit docs wrong message.append('Covariance not positive definite.') if fmin.has_made_posdef_covar: message.append('Covariance forced positive definite.') if not fmin.has_valid_parameters: message.append('Parameter(s) value and/or error invalid.') if len(message) == 0: message.append('Minimization exited successfully.') # iminuit: m.np_matrix() doesn't work # Covariance matrix (only varied parameters) as numpy array. if m.covariance is None: covariance = None else: covariance = np.array([ [m.covariance[(n1, n2)] for n1 in vparam_names] for n2 in vparam_names]) # OrderedDict of errors if m.errors is None: errors = None else: errors = OrderedDict((name, m.errors[name]) for name in vparam_names) # If we need to, unsort the mask so mask applies to input data if sortidx is not None: unsort_idx = np.argsort(sortidx) # indicies that will unsort array data_mask = data_mask[unsort_idx] # Compile results res = Result(success=fmin.is_valid, message=' '.join(message), ncall=fmin.nfcn, chisq=fmin.fval, ndof=ndof, param_names=model.param_names, parameters=parameters, vparam_names=vparam_names, covariance=covariance, errors=errors, nfit=nfit, data_mask=data_mask) else: raise ValueError("unknown method {0:r}".format(method)) return res, model def nest_lc(data, model, vparam_names, bounds, guess_amplitude_bound=False, minsnr=5., priors=None, ppfs=None, npoints=100, method='single', maxiter=None, maxcall=None, modelcov=False, rstate=None, verbose=False, warn=True, **kwargs): """Run nested sampling algorithm to estimate model parameters and evidence. Parameters ---------- data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` The model to fit. vparam_names : list Model parameters to vary in the fit. bounds : `dict` Bounded range for each parameter. Bounds must be given for each parameter, with the exception of ``t0``: by default, the minimum bound is such that the latest phase of the model lines up with the earliest data point and the maximum bound is such that the earliest phase of the model lines up with the latest data point. guess_amplitude_bound : bool, optional If true, bounds for the model's amplitude parameter are determined automatically based on the data and do not need to be included in `bounds`. The lower limit is set to zero and the upper limit is 10 times the amplitude "guess" (which is based on the highest-flux data point in any band). Default is False. minsnr : float, optional Minimum signal-to-noise ratio of data points to use when guessing amplitude bound. Default is 5. priors : `dict`, optional Prior probability distribution function for each parameter. The keys should be parameter names and the values should be callables that accept a float. If a parameter is not in the dictionary, the prior defaults to a flat distribution between the bounds. ppfs : `dict`, optional Prior percent point function (inverse of the cumulative distribution function) for each parameter. If a parameter is in this dictionary, the ppf takes precedence over a prior pdf specified in ``priors``. npoints : int, optional Number of active samples to use. Increasing this value increases the accuracy (due to denser sampling) and also the time to solution. method : {'classic', 'single', 'multi'}, optional Method used to select new points. Choices are 'classic', single-ellipsoidal ('single'), multi-ellipsoidal ('multi'). Default is 'single'. maxiter : int, optional Maximum number of iterations. Iteration may stop earlier if termination condition is reached. Default is no limit. maxcall : int, optional Maximum number of likelihood evaluations. Iteration may stop earlier if termination condition is reached. Default is no limit. modelcov : bool, optional Include model covariance when calculating chisq. Default is False. rstate : `~numpy.random.RandomState`, optional RandomState instance. If not given, the global random state of the ``numpy.random`` module will be used. verbose : bool, optional Print running evidence sum on a single line. warn : bool, optional Issue warning when dropping bands outside the model range. Default is True. *New in version 1.5.0* Returns ------- res : Result Attributes are: * ``niter``: total number of iterations * ``ncall``: total number of likelihood function calls * ``time``: time in seconds spent in iteration loop. * ``logz``: natural log of the Bayesian evidence Z. * ``logzerr``: estimate of uncertainty in logz (due to finite sampling) * ``h``: Bayesian information. * ``vparam_names``: list of parameter names varied. * ``samples``: 2-d `~numpy.ndarray`, shape is (nsamples, nparameters). Each row is the parameter values for a single sample. For example, ``samples[0, :]`` is the parameter values for the first sample. * ``logprior``: 1-d `~numpy.ndarray` (length=nsamples); log(prior volume) for each sample. * ``logl``: 1-d `~numpy.ndarray` (length=nsamples); log(likelihood) for each sample. * ``weights``: 1-d `~numpy.ndarray` (length=nsamples); Weight corresponding to each sample. The weight is proportional to the prior * likelihood for the sample. * ``parameters``: 1-d `~numpy.ndarray` of weighted-mean parameter values from samples (including fixed parameters). Order corresponds to ``model.param_names``. * ``covariance``: 2-d `~numpy.ndarray` of parameter covariance; indicies correspond to order of ``vparam_names``. Calculated from ``samples`` and ``weights``. * ``errors``: OrderedDict of varied parameter uncertainties. Corresponds to square root of diagonal entries in covariance matrix. * ``ndof``: Number of degrees of freedom (len(data) - len(vparam_names)). * ``bounds``: Dictionary of bounds on varied parameters (including any automatically determined bounds). * ``data_mask``: Boolean array the same length as data specifying whether each observation was used. *New in version 1.5.0.* estimated_model : `~sncosmo.Model` A copy of the model with parameters set to the values in ``res.parameters``. """ try: import nestle except ImportError: raise ImportError("nest_lc() requires the nestle package.") # experimental parameters tied = kwargs.get("tied", None) data = photometric_data(data) # sort by time if not np.all(np.ediff1d(data.time) >= 0.0): sortidx = np.argsort(data.time) data = data[sortidx] else: sortidx = None model = copy.copy(model) bounds = copy.copy(bounds) # need to copy this b/c we modify it below # Order vparam_names the same way it is ordered in the model: vparam_names = [s for s in model.param_names if s in vparam_names] # Drop data that the model doesn't cover. fitdata, data_mask = cut_bands(data, model, z_bounds=bounds.get('z', None), warn=warn) if guess_amplitude_bound: if model.param_names[2] not in vparam_names: raise ValueError("Amplitude bounds guessing enabled but " "amplitude parameter {0!r} is not varied" .format(model.param_names[2])) if model.param_names[2] in bounds: raise ValueError("cannot supply bounds for parameter {0!r}" " when guess_amplitude_bound=True" .format(model.param_names[2])) # If redshift is bounded, set model redshift to midpoint of bounds # when doing the guess. if 'z' in bounds: model.set(z=sum(bounds['z']) / 2.) _, amplitude = guess_t0_and_amplitude(fitdata, model, minsnr) bounds[model.param_names[2]] = (0., 10. * amplitude) # Find t0 bounds to use, if not explicitly given if 't0' in vparam_names and 't0' not in bounds: bounds['t0'] = t0_bounds(fitdata, model) if ppfs is None: ppfs = {} if tied is None: tied = {} # Convert bounds/priors combinations into ppfs if bounds is not None: for key, val in bounds.items(): if key in ppfs: continue # ppfs take priority over bounds/priors a, b = val if priors is not None and key in priors: # solve ppf at discrete points and return interpolating # function x_samples = np.linspace(0., 1., 101) ppf_samples = ppf(priors[key], x_samples, a, b) f = Interp1D(0., 1., ppf_samples) else: f = Interp1D(0., 1., np.array([a, b])) ppfs[key] = f # NOTE: It is important that iparam_names is in the same order # every time, otherwise results will not be reproducible, even # with same random seed. This is because iparam_names[i] is # matched to u[i] below and u will be in a reproducible order, # so iparam_names must also be. iparam_names = [key for key in vparam_names if key in ppfs] ppflist = [ppfs[key] for key in iparam_names] npdim = len(iparam_names) # length of u ndim = len(vparam_names) # length of v # Check that all param_names either have a direct prior or are tied. for name in vparam_names: if name in iparam_names: continue if name in tied: continue raise ValueError("Must supply ppf or bounds or tied for parameter '{}'" .format(name)) def prior_transform(u): d = {} for i in range(npdim): d[iparam_names[i]] = ppflist[i](u[i]) v = np.empty(ndim, dtype=float) for i in range(ndim): key = vparam_names[i] if key in d: v[i] = d[key] else: v[i] = tied[key](d) return v # Indicies of the model parameters in vparam_names idx = np.array([model.param_names.index(name) for name in vparam_names]) def loglike(parameters): model.parameters[idx] = parameters return -0.5 * chisq(fitdata, model, modelcov=modelcov) t0 = time.time() res = nestle.sample(loglike, prior_transform, ndim, npdim=npdim, npoints=npoints, method=method, maxiter=maxiter, maxcall=maxcall, rstate=rstate, callback=(nestle.print_progress if verbose else None)) elapsed = time.time() - t0 # estimate parameters and covariance from samples vparameters, cov = nestle.mean_and_cov(res.samples, res.weights) # update model parameters to estimated ones. model.set(**dict(zip(vparam_names, vparameters))) # If we need to, unsort the mask so mask applies to input data if sortidx is not None: unsort_idx = np.argsort(sortidx) # indicies that will unsort array data_mask = data_mask[unsort_idx] # `res` is a nestle.Result object. Collect result into a sncosmo.Result # object for consistency, and add more fields. res = Result(niter=res.niter, ncall=res.ncall, logz=res.logz, logzerr=res.logzerr, h=res.h, samples=res.samples, weights=res.weights, logvol=res.logvol, logl=res.logl, vparam_names=copy.copy(vparam_names), ndof=len(fitdata) - len(vparam_names), bounds=bounds, time=elapsed, parameters=model.parameters.copy(), covariance=cov, errors=OrderedDict(zip(vparam_names, np.sqrt(np.diagonal(cov)))), param_dict=OrderedDict(zip(model.param_names, model.parameters)), data_mask=data_mask) return res, model def mcmc_lc(data, model, vparam_names, bounds=None, priors=None, guess_amplitude=True, guess_t0=True, guess_z=True, minsnr=5., modelcov=False, nwalkers=10, nburn=200, nsamples=1000, sampler='ensemble', thin=1, a=2.0, warn=True): """Run an MCMC chain to get model parameter samples. This is a convenience function around `emcee.EnsembleSampler`. It defines the likelihood function and makes a heuristic guess at a good set of starting points for the walkers. It then runs the sampler, starting with a burn-in run. If you're not getting good results, you might want to try increasing the burn-in, increasing the walkers, or specifying a better starting position. To get a better starting position, you could first run `~sncosmo.fit_lc`, then run this function with all ``guess_[name]`` keyword arguments set to False, so that the current model parameters are used as the starting point. Parameters ---------- data : `~astropy.table.Table` or `~numpy.ndarray` or `dict` Table of photometric data. Must include certain columns. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` The model to fit. vparam_names : iterable Model parameters to vary. bounds : `dict`, optional Bounded range for each parameter. Keys should be parameter names, values are tuples. If a bound is not given for some parameter, the parameter is unbounded. The exception is ``t0``: by default, the minimum bound is such that the latest phase of the model lines up with the earliest data point and the maximum bound is such that the earliest phase of the model lines up with the latest data point. priors : `dict`, optional Prior probability functions. Keys are parameter names, values are functions that return probability given the parameter value. The default prior is a flat distribution. guess_amplitude : bool, optional Whether or not to guess the amplitude from the data. If false, the current model amplitude is taken as the initial value. Only has an effect when fitting amplitude. Default is True. guess_t0 : bool, optional Whether or not to guess t0. Only has an effect when fitting t0. Default is True. guess_z : bool, optional Whether or not to guess z (redshift). Only has an effect when fitting redshift. Default is True. minsnr : float, optional When guessing amplitude and t0, only use data with signal-to-noise ratio (flux / fluxerr) greater than this value. Default is 5. modelcov : bool, optional Include model covariance when calculating chisq. Default is False. nwalkers : int, optional Number of walkers in the sampler. nburn : int, optional Number of samples in burn-in phase. nsamples : int, optional Number of samples in production run. sampler: str, optional The kind of sampler to use. Currently only 'ensemble' for `emcee.EnsembleSampler` is supported. thin : int, optional Factor by which to thin samples in production run. Output samples array will have (nsamples/thin) samples. a : float, optional Proposal scale parameter passed to the sampler. warn : bool, optional Issue a warning when dropping bands outside the wavelength range of the model. Default is True. *New in version 1.5.0* Returns ------- res : Result Has the following attributes: * ``param_names``: All parameter names of model, including fixed. * ``parameters``: Model parameters, with varied parameters set to mean value in samples. * ``vparam_names``: Names of parameters varied. Order of parameters matches order of samples. * ``samples``: 2-d array with shape ``(N, len(vparam_names))``. Order of parameters in each row matches order in ``res.vparam_names``. * ``covariance``: 2-d array giving covariance, measured from samples. Order corresponds to ``res.vparam_names``. * ``errors``: dictionary giving square root of diagonal of covariance matrix for varied parameters. Useful for ``plot_lc``. * ``mean_acceptance_fraction``: mean acceptance fraction for all walkers in the sampler. * ``ndof``: Number of degrees of freedom (len(data) - len(vparam_names)). *New in version 1.5.0.* * ``data_mask``: Boolean array the same length as data specifying whether each observation was used. *New in version 1.5.0.* est_model : `~sncosmo.Model` Copy of input model with varied parameters set to mean value in samples. """ try: import emcee except ImportError: raise ImportError("mcmc_lc() requires the emcee package.") # Standardize and normalize data. data = photometric_data(data) # sort by time if not np.all(np.ediff1d(data.time) >= 0.0): sortidx = np.argsort(data.time) data = data[sortidx] else: sortidx = None # Make a copy of the model so we can modify it with impunity. model = copy.copy(model) bounds = copy.deepcopy(bounds) if bounds else {} if priors is None: priors = {} # Check that vparam_names isn't empty, check for unknown parameters. if len(vparam_names) == 0: raise ValueError("no parameters supplied") for names in (vparam_names, bounds, priors): for name in names: if name not in model.param_names: raise ValueError("Parameter not in model: " + repr(name)) # Order vparam_names the same way it is ordered in the model: vparam_names = [s for s in model.param_names if s in vparam_names] ndim = len(vparam_names) # Check that 'z' is bounded (if it is going to be fit). if 'z' in vparam_names: if 'z' not in bounds or None in bounds['z']: raise ValueError('z must be bounded if allowed to vary.') if guess_z: model.set(z=sum(bounds['z']) / 2.) if model.get('z') < bounds['z'][0] or model.get('z') > bounds['z'][1]: raise ValueError('z out of range.') # Cut bands that are not allowed by the wavelength range of the model. fitdata, data_mask = cut_bands(data, model, z_bounds=bounds.get('z', None), warn=warn) # Find t0 bounds to use, if not explicitly given if 't0' in vparam_names and 't0' not in bounds: bounds['t0'] = t0_bounds(fitdata, model) # Note that in the parameter guessing below, we assume that the source # amplitude is the 3rd parameter of the Model (1st parameter of the Source) # Turn off guessing if we're not fitting the parameter. if model.param_names[2] not in vparam_names: guess_amplitude = False if 't0' not in vparam_names: guess_t0 = False # Make guesses for t0 and amplitude. # (we assume amplitude is the 3rd parameter of the model.) if guess_amplitude or guess_t0: t0, amplitude = guess_t0_and_amplitude(fitdata, model, minsnr) if guess_amplitude: model.parameters[2] = amplitude if guess_t0: model.set(t0=t0) # Indicies used in probability function. # modelidx: Indicies of model parameters corresponding to vparam_names. # idxbounds: tuples of (varied parameter index, low bound, high bound). # idxpriors: tuples of (varied parameter index, function). modelidx = np.array([model.param_names.index(k) for k in vparam_names]) idxbounds = [(vparam_names.index(k), bounds[k][0], bounds[k][1]) for k in bounds] idxpriors = [(vparam_names.index(k), priors[k]) for k in priors] # Posterior function. def lnlike(parameters): for i, low, high in idxbounds: if not low < parameters[i] < high: return -np.inf model.parameters[modelidx] = parameters logp = -0.5 * chisq(fitdata, model, modelcov=modelcov) return logp def lnprior(parameters): logp = 0 for i, func in idxpriors: logp += math.log(func(parameters[i])) return logp def lnprob(parameters): return lnprior(parameters) + lnlike(parameters) # Moves to use moves = emcee.moves.StretchMove(a=a) if sampler == 'ensemble': # Heuristic determination of walker initial positions: distribute # walkers in a symmetric gaussian ball, with heuristically # determined scale. ctr = model.parameters[modelidx] scale = np.ones(ndim) for i, name in enumerate(vparam_names): if name in bounds: scale[i] = 0.0001 * (bounds[name][1] - bounds[name][0]) elif model.get(name) != 0.: scale[i] = 0.01 * model.get(name) else: scale[i] = 0.1 pos = ctr + scale * np.random.normal(size=(nwalkers, ndim)) sampler = emcee.EnsembleSampler(nwalkers, ndim, lnprob, moves=moves) else: raise ValueError('Invalid sampler type. Currently only ' '"ensemble" is supported.') # Run the sampler. pos, prob, state = sampler.run_mcmc(pos, nburn) # burn-in sampler.reset() sampler.run_mcmc(pos, nsamples, thin_by=thin) # production run samples = sampler.get_chain(flat=True).reshape(-1, ndim) # Summary statistics. vparameters = np.mean(samples, axis=0) cov = np.cov(samples, rowvar=0) model.set(**dict(zip(vparam_names, vparameters))) errors = OrderedDict(zip(vparam_names, np.sqrt(np.diagonal(cov)))) mean_acceptance_fraction = np.mean(sampler.acceptance_fraction) # If we need to, unsort the mask so mask applies to input data if sortidx is not None: unsort_idx = np.argsort(sortidx) # indicies that will unsort array data_mask = data_mask[unsort_idx] res = Result(param_names=copy.copy(model.param_names), parameters=model.parameters.copy(), vparam_names=vparam_names, samples=samples, covariance=cov, errors=errors, ndof=len(fitdata) - len(vparam_names), mean_acceptance_fraction=mean_acceptance_fraction, data_mask=data_mask) return res, model sncosmo-2.12.1/sncosmo/io.py000066400000000000000000000633271476435666400157600ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Functions for supernova light curve I/O""" import json import math import os from collections import OrderedDict import numpy as np from astropy import wcs from astropy.io import fits from astropy.table import Table from .bandpasses import get_bandpass from .utils import dict_to_array __all__ = ['read_lc', 'write_lc', 'load_example_data', 'load_example_spectrum_data', 'read_griddata_ascii', 'read_griddata_fits', 'write_griddata_ascii', 'write_griddata_fits'] def _stripcomment(line, char='#'): pos = line.find(char) if pos == -1: return line else: return line[:pos] def _cast_str(s): try: return int(s) except ValueError: try: return float(s) except ValueError: return s.strip() def read_griddata_ascii(name_or_obj): """Read 2-d grid data from a text file. Each line has values `x0 x1 y`. Space separated. x1 values are only read for first x0 value. Others are assumed to match. Parameters ---------- name_or_obj : str or file-like object Returns ------- x0 : numpy.ndarray 1-d array. x1 : numpy.ndarray 1-d array. y : numpy.ndarray 2-d array of shape (len(x0), len(x1)). """ if isinstance(name_or_obj, str): f = open(name_or_obj, 'r') else: f = name_or_obj x0 = [] # x0 values. x1 = None # x1 values for first x0 value, assume others are the same. y = [] # 2-d array of internal values x0_current = None x1_current = [] y1_current = [] for line in f: stripped_line = _stripcomment(line) if len(stripped_line) == 0: continue x0_tmp, x1_tmp, y_tmp = map(float, stripped_line.split()) if x0_current is None: x0_current = x0_tmp # Initialize first time # If there is a new x0 value, ingest the old one and reset values if x0_tmp != x0_current: x0.append(x0_current) if x1 is None: x1 = x1_current y.append(y1_current) x0_current = x0_tmp x1_current = [] y1_current = [] x1_current.append(x1_tmp) y1_current.append(y_tmp) # Ingest the last x0 value and y1 array x0.append(x0_current) y.append(y1_current) f.close() return np.array(x0), np.array(x1), np.array(y) def read_multivector_griddata_ascii(name_or_obj): """Read 2-d grid data from a text file. Each line has values `x0 x1 y0 y1 ...`. Space separated. Assumed to be grid of values. Parameters ---------- name_or_obj : str or file-like object The name of the file or a file-like object containing the data. Returns ------- x0 : numpy.ndarray 1-d array. x1 : numpy.ndarray 1-d array. y : numpy.ndarray 3-d array of shape ``(n, len(x0), len(x1))`` where ``n`` is the number of y values on each line. """ data = np.loadtxt(name_or_obj) x0 = np.sort(np.unique(data[:, 0])) x1 = np.sort(np.unique(data[:, 1])) y = np.zeros((len(data[0]) - 2, len(x0), len(x1))) for i0, p in enumerate(x0): for i1, q in enumerate(x1): ind = (data[:, 0] == p) & (data[:, 1] == q) y[:, i0, i1] = data[ind, 2:] return x0, x1, y def read_griddata_fits(name_or_obj, ext=0): """Read a multi-dimensional grid of data from a FITS file, where the grid coordinates are encoded in the FITS-WCS header keywords. Parameters ---------- name_or_obj : str or file-like object Returns ------- x0, x1, ... : `~numpy.ndarray` 1-d arrays giving coordinates of grid. The number of these arrays will depend on the dimension of the data array. For example, if the data have two dimensions, a total of three arrays will be returned: ``x0, x1, y``, with ``x0`` giving the coordinates of the first axis of ``y``. If the data have three dimensions, a total of four arrays will be returned: ``x0, x1, x2, y``, and so on with higher dimensions. y : `~numpy.ndarray` n-d array of shape ``(len(x0), len(x1), ...)``. For three dimensions for example, the value at ``y[i, j, k]`` corresponds to coordinates ``(x0[i], x1[j], x2[k])``. """ hdulist = fits.open(name_or_obj) w = wcs.WCS(hdulist[ext].header) y = hdulist[ext].data # get abcissa values (coordinates at grid values) xs = [] for i in range(y.ndim): j = y.ndim - i # The i-th axis (in Python) corresponds to FITS AXISj coords = np.zeros((y.shape[i], y.ndim), dtype=np.float32) coords[:, j-1] = np.arange(y.shape[i]) x = w.wcs_pix2world(coords, 0)[:, j-1] xs.append(x) hdulist.close() return tuple(xs) + (y,) def write_griddata_ascii(x0, x1, y, name_or_obj): """Write 2-d grid data to a text file. Each line has values `x0 x1 y`. Space separated. Parameters ---------- x0 : numpy.ndarray 1-d array. x1 : numpy.ndarray 1-d array. y : numpy.ndarray 2-d array of shape (len(x0), len(x1)). name_or_obj : str or file-like object Filename to write to or open file. """ if isinstance(name_or_obj, str): f = open(name_or_obj, 'w') else: f = name_or_obj for j in range(len(x0)): for i in range(len(x1)): f.write("{0:.7g} {1:.7g} {2:.7g}\n".format(x0[j], x1[i], y[j, i])) if isinstance(name_or_obj, str): f.close() def write_griddata_fits(x0, x1, y, name_or_obj): """Write a 2-d grid of data to a FITS file The grid coordinates are encoded in the FITS-WCS header keywords. Parameters ---------- x0 : numpy.ndarray 1-d array. x1 : numpy.ndarray 1-d array. y : numpy.ndarray 2-d array of shape (len(x0), len(x1)). name_or_obj : str or file-like object Filename to write to or open file. """ d0, d1 = np.ediff1d(x0), np.ediff1d(x1) if not (np.allclose(d0, d0[0]) and np.allclose(d1, d1[0])): raise ValueError('grid must be regularly spaced in both x0 and x1') if not (len(x0), len(x1)) == y.shape: raise ValueError('length of x0 and x1 do not match shape of y') w = wcs.WCS(naxis=2) w.wcs.crpix = [1, 1] w.wcs.crval = [x1[0], x0[0]] w.wcs.cdelt = [d1[0], d0[0]] hdu = fits.PrimaryHDU(y, header=w.to_header()) hdu.writeto(name_or_obj) # ----------------------------------------------------------------------------- # Reader: ascii def _read_ascii(f, delim=None, metachar='@', commentchar='#'): meta = OrderedDict() colnames = [] cols = [] readingdata = False for line in f: # strip leading & trailing whitespace, newline, and comments line = line.strip() pos = line.find(commentchar) if pos > -1: line = line[:pos] if len(line) == 0: continue if not readingdata: # Read metadata if line[0] == metachar: pos = line.find(' ') # Find first space. if pos in [-1, 1]: # Space must exist and key must exist. raise ValueError('Incorrectly formatted metadata line: ' + line) meta[line[1:pos]] = _cast_str(line[pos:]) continue # Read header line for item in line.split(delim): colnames.append(item.strip()) cols.append([]) readingdata = True continue # Now we're reading data items = line.split(delim) for col, item in zip(cols, items): col.append(_cast_str(item)) data = OrderedDict(zip(colnames, cols)) return meta, data # ----------------------------------------------------------------------------- # Reader: salt2 def _expand_bands(band_list, meta): """Given a list containing band names, return a list of Bandpass objects""" # Treat dependent bandpasses based on metadata contents # TODO: need a way to figure out which bands are position dependent! # for now, we assume *all* or none are. if "X_FOCAL_PLANE" in meta and "Y_FOCAL_PLANE" in meta: r = math.sqrt(meta["X_FOCAL_PLANE"]**2 + meta["Y_FOCAL_PLANE"]**2) # map name to object for unique bands name_to_band = {name: get_bandpass(name, r) for name in set(band_list)} return [name_to_band[name] for name in band_list] else: # For other bandpasses, get_bandpass will return the same object # on each call, so just use it directly. return [get_bandpass(name) for name in band_list] def _read_salt2(name_or_obj, read_covmat=False, expand_bands=False): """Read a new-style SALT2 file. Such a file has metadata on lines starting with '@' and column names on lines starting with '#' and containing a ':' after the column name. There is optionally a line containing '#end' before the start of data. """ if isinstance(name_or_obj, str): f = open(name_or_obj, 'r') else: f = name_or_obj meta = OrderedDict() colnames = [] cols = [] readingdata = False for line in f: # strip leading & trailing whitespace & newline line = line.strip() if len(line) == 0: continue if not readingdata: # Read metadata if line[0] == '@': pos = line.find(' ') # Find first space. if pos in [-1, 1]: # Space must exist and key must exist. raise ValueError('Incorrectly formatted metadata line: ' + line) meta[line[1:pos]] = _cast_str(line[pos:]) continue # Read header line if line[0] == '#': pos = line.find(':') if pos in [-1, 1]: continue # comment line colname = line[1:pos].strip() if colname == 'end': continue colnames.append(colname) cols.append([]) continue # If the first non-whitespace character is not '@' or '#', # assume the line is the first data line. readingdata = True # strip comments pos = line.find('#') if pos > -1: line = line[:pos] if len(line) == 0: continue # Now we're reading data items = line.split() for col, item in zip(cols, items): col.append(_cast_str(item)) if isinstance(name_or_obj, str): f.close() # read covariance matrix file, if requested and present if read_covmat and 'COVMAT' in meta: fname = os.path.join(os.path.dirname(f.name), meta['COVMAT']) # use skiprows=1 because first row has array dimensions fluxcov = np.loadtxt(fname, skiprows=1) # asethetics: capitalize 'Fluxcov' to match salt2 colnames # such as 'Fluxerr' colnames.append('Fluxcov') cols.append(fluxcov) data = OrderedDict(zip(colnames, cols)) if expand_bands: data['Filter'] = _expand_bands(data['Filter'], meta) return meta, data # ----------------------------------------------------------------------------- # Reader: salt2-old def _read_salt2_old(dirname, filenames=None): """Read old-style SALT2 files from a directory. A file named 'lightfile' must exist in the directory. """ # Get list of files in directory. if not (os.path.exists(dirname) and os.path.isdir(dirname)): raise IOError("Not a directory: '{0}'".format(dirname)) dirfilenames = os.listdir(dirname) # Read metadata from lightfile. if 'lightfile' not in dirfilenames: raise IOError("no lightfile in directory: '{0}'".format(dirname)) with open(os.path.join(dirname, 'lightfile'), 'r') as lightfile: meta = OrderedDict() for line in lightfile.readlines(): line = line.strip() if len(line) == 0: continue try: key, val = line.split() except ValueError: raise ValueError('expected space-separated key value pairs in ' 'lightfile: {0}' .format(os.path.join(dirname, 'lightfile'))) meta[key] = _cast_str(val) # Get list of filenames to read. if filenames is None: filenames = dirfilenames if 'lightfile' in filenames: filenames.remove('lightfile') # We already read the lightfile. fullfilenames = [os.path.join(dirname, f) for f in filenames] # Read data from files. data = None for fname in fullfilenames: with open(fname, 'r') as f: filemeta, filedata = _read_salt2(f) # Check that all necessary file metadata was defined. if not ('INSTRUMENT' in filemeta and 'BAND' in filemeta and 'MAGSYS' in filemeta): raise ValueError('not all necessary global keys (INSTRUMENT, ' 'BAND, MAGSYS) are defined in file {0}' .format(fname)) # Add the instrument/band to the file data, in anticipation of # aggregating it with other files. firstcol = next(iter(filedata.values())) data_length = len(firstcol) filter_name = '{0}::{1}'.format(filemeta.pop('INSTRUMENT'), filemeta.pop('BAND')) filedata['Filter'] = data_length * [filter_name] filedata['MagSys'] = data_length * [filemeta.pop('MAGSYS')] # If this if the first file, initialize data lists, otherwise if keys # match, append this file's data to the main data. if data is None: data = filedata elif set(filedata.keys()) == set(data.keys()): for key in data: data[key].extend(filedata[key]) else: raise ValueError('column names do not match between files') # Append any extra metadata in this file to the master metadata. if len(filemeta) > 0: meta[filter_name] = filemeta return meta, data # ----------------------------------------------------------------------------- # Reader: json def _read_json(f): t = json.load(f) # Encode data keys as ascii rather than UTF-8 so that they can be # used as numpy structured array names later. d = {} for key, value in t['data'].items(): d[key] = value return t['meta'], d # ----------------------------------------------------------------------------- # All readers READERS = {'ascii': _read_ascii, 'json': _read_json, 'salt2': _read_salt2, 'salt2-old': _read_salt2_old} def read_lc(file_or_dir, format='ascii', **kwargs): """Read light curve data for a single supernova. Parameters ---------- file_or_dir : str Filename (formats 'ascii', 'json', 'salt2') or directory name (format 'salt2-old'). For 'salt2-old' format, directory must contain a file named 'lightfile'. All other files in the directory are assumed to be photometry files, unless the `filenames` keyword argument is set. format : {'ascii', 'json', 'salt2', 'salt2-old'}, optional Format of file. Default is 'ascii'. 'salt2' is the new format available in snfit version >= 2.3.0. read_covmat : bool, optional **[salt2 only]** If True, and if a ``COVMAT`` keyword is present in header, read the covariance matrix from the filename specified by ``COVMAT`` (assumed to be in the same directory as the lightcurve file) and include it as a column named ``Fluxcov`` in the returned table. Default is False. *New in version 1.5.0* expand_bands : bool, optional **[salt2 only]** If True, convert band names into equivalent Bandpass objects. This is particularly useful for position-dependent bandpasses in the salt2 file format: the position information is read from the header and used when creating the bandpass objects. *New in version 1.5.0* delim : str, optional **[ascii only]** Used to split entries on a line. Default is `None`. Extra whitespace is ignored. metachar : str, optional **[ascii only]** Lines whose first non-whitespace character is `metachar` are treated as metadata lines, where the key and value are split on the first whitespace. Default is ``'@'`` commentchar : str, optional **[ascii only]** One-character string indicating a comment. Default is '#'. filenames : list, optional **[salt2-old only]** Only try to read the given filenames as photometry files. Default is to try to read all files in directory. Returns ------- t : astropy `~astropy.table.Table` Table of data. Metadata (as an `OrderedDict`) can be accessed via the ``t.meta`` attribute. For example: ``t.meta['key']``. The key is case-sensitive. Examples -------- Read an ascii format file that includes metadata (``StringIO`` behaves like a file object): >>> from io import StringIO >>> f = StringIO(''' ... @id 1 ... @RA 36.0 ... @description good ... time band flux fluxerr zp zpsys ... 50000. g 1. 0.1 25. ab ... 50000.1 r 2. 0.1 25. ab ... ''') >>> t = read_lc(f, format='ascii') >>> print(t) time band flux fluxerr zp zpsys ------- ---- ---- ------- ---- ----- 50000.0 g 1.0 0.1 25.0 ab 50000.1 r 2.0 0.1 25.0 ab >>> t.meta OrderedDict([('id', 1), ('RA', 36.0), ('description', 'good')]) """ try: readfunc = READERS[format] except KeyError: raise ValueError("Reader not defined for format {0!r}. Options: " .format(format) + ", ".join(READERS.keys())) if format == 'salt2-old': meta, data = readfunc(file_or_dir, **kwargs) elif isinstance(file_or_dir, str): with open(file_or_dir, 'r') as f: meta, data = readfunc(f, **kwargs) else: meta, data = readfunc(file_or_dir, **kwargs) return Table(data, meta=meta) # =========================================================================== # # Writers # # =========================================================================== # # ----------------------------------------------------------------------------- # Writer: ascii def _write_ascii(f, data, meta, **kwargs): delim = kwargs.get('delim', ' ') metachar = kwargs.get('metachar', '@') if meta is not None: for key, val in meta.items(): f.write('{0}{1}{2}{3}\n'.format(metachar, key, delim, str(val))) keys = data.dtype.names length = len(data) f.write(delim.join(keys)) f.write('\n') for i in range(length): f.write(delim.join([str(data[key][i]) for key in keys])) f.write('\n') # ----------------------------------------------------------------------------- # Writer: salt2 KEY_TO_SALT2KEY_META = { 'Z': 'REDSHIFT', # Not sure if this is used. 'Z_HELIOCENTRIC': 'Z_HELIO', 'MAGSYS': 'MagSys', 'Z_SOURCE': 'z_source'} KEY_TO_SALT2KEY_COLUMN = { 'Mjd': 'Date', 'Time': 'Date', 'Flux': 'FluxPsf', 'Fluxpsf': 'FluxPsf', 'Fluxerr': 'FluxPsferr', 'Fluxpsferr': 'FluxPsferr', 'Airmass': 'AirMass', 'Zp': 'ZP', 'Zpsys': 'MagSys', 'Magsys': 'MagSys', 'Band': 'Filter'} def _write_salt2(f, data, meta, **kwargs): raw = kwargs.get('raw', False) pedantic = kwargs.get('pedantic', True) if meta is not None: for key, val in meta.items(): if not raw: key = key.upper() key = KEY_TO_SALT2KEY_META.get(key, key) f.write('@{0} {1}\n'.format(key, str(val))) keys = data.dtype.names length = len(data) # Write column names keys_as_written = [] for key in keys: if not raw: key = key.capitalize() key = KEY_TO_SALT2KEY_COLUMN.get(key, key) f.write('#{0} :\n'.format(key)) keys_as_written.append(key) f.write('#end :\n') # Check that necessary fields exist if pedantic: if not ('Filter' in keys_as_written and 'MagSys' in keys_as_written): raise ValueError('photometry data missing required some fields ' ': Filter, MagSys') # Write the data itself for i in range(length): f.write(' '.join([str(data[key][i]) for key in keys])) f.write('\n') # ----------------------------------------------------------------------------- # Writer: snana KEY_TO_SNANAKEY_COLUMN = { 'TIME': 'MJD', 'DATE': 'MJD', 'FILTER': 'FLT', 'BAND': 'FLT', 'FLUX': 'FLUXCAL', 'FLUXERR': 'FLUXCALERR', 'ZP': 'ZPT', 'ZEROPOINT': 'ZPT'} KEY_TO_SNANAKEY_META = { 'DEC': 'DECL'} SNANA_REQUIRED_META = ['RA', 'DECL', 'SURVEY', 'FILTERS', 'MWEBV'] SNANA_REQUIRED_COLUMN = ['MJD', 'FLT', 'FLUXCAL', 'FLUXCALERR', 'ZPT'] def _write_snana(f, data, meta, **kwargs): raw = kwargs.get('raw', False) pedantic = kwargs.get('pedantic', True) # Write metadata keys_as_written = [] if meta is not None: for key, val in meta.items(): if not raw: key = key.upper() key = KEY_TO_SNANAKEY_META.get(key, key) f.write('{0}: {1}\n'.format(key, str(val))) keys_as_written.append(key) # Check that necessary metadata was written if pedantic: for key in SNANA_REQUIRED_META: if key not in keys_as_written: raise ValueError('Missing required metadata kw: ' + key) # Get column names and data length keys = data.dtype.names length = len(data) # Convert column names keys_to_write = [] for key in keys: if not raw: key = key.upper() key = KEY_TO_SNANAKEY_COLUMN.get(key, key) keys_to_write.append(key) # Check that necessary column names are included if pedantic: for key in SNANA_REQUIRED_COLUMN: if key not in keys_to_write: raise ValueError('Missing required column name: ' + key) # Write the header f.write('\n' '# ==========================================\n' '# TERSE LIGHT CURVE OUTPUT:\n' '#\n' 'NOBS: {0:d}\n' 'NVAR: {1:d}\n' 'VARLIST: {2}\n' .format(length, len(keys), ' '.join(keys_to_write))) # Write data for i in range(length): f.write('OBS: ') f.write(' '.join([str(data[key][i]) for key in keys])) f.write('\n') # ----------------------------------------------------------------------------- # Writer: json def _write_json(f, data, meta, **kwargs): # Build a dictionary of pure-python objects output = OrderedDict([('meta', meta), ('data', OrderedDict())]) for key in data.dtype.names: output['data'][key] = data[key].tolist() json.dump(output, f) del output # ----------------------------------------------------------------------------- # All writers WRITERS = {'ascii': _write_ascii, 'salt2': _write_salt2, 'snana': _write_snana, 'json': _write_json} def write_lc(data, fname, format='ascii', **kwargs): """Write light curve data. Parameters ---------- data : `~astropy.table.Table` Light curve data. fname : str Filename. format : {'ascii', 'salt2', 'snana', 'json'}, optional Format of file. Default is 'ascii'. 'salt2' is the new format available in snfit version >= 2.3.0. delim : str, optional **[ascii only]** Character used to separate entries on a line. Default is ' '. metachar : str, optional **[ascii only]** Metadata designator. Default is '@'. raw : bool, optional **[salt2, snana]** By default, the SALT2 and SNANA writers rename some metadata keys and column names in order to comply with what snfit and SNANA expect. Set to True to override this. Default is False. pedantic : bool, optional **[salt2, snana]** If True, check that output column names and header keys comply with expected formatting, and raise a ValueError if not. It is probably a good idea to set to False when raw is True. Default is True. """ if format not in WRITERS: raise ValueError("Writer not defined for format {0!r}. Options: " .format(format) + ", ".join(WRITERS.keys())) if isinstance(data, Table): meta = data.meta data = np.asarray(data) else: meta = OrderedDict() if not isinstance(data, np.ndarray): data = dict_to_array(data) with open(fname, 'w') as f: WRITERS[format](f, data, meta, **kwargs) def load_example_data(): """ Load an example photometric data table. Returns ------- data : `~astropy.table.Table` """ from astropy.utils.data import get_pkg_data_filename filename = get_pkg_data_filename( 'data/examples/example_photometric_data.dat') return read_lc(filename, format='ascii') def load_example_spectrum_data(): """ Load example spectrum data. Returns ------- wave : `~numpy.array` Wavelengths of each spectral bin flux : `~numpy.array` Flux in each spectral bin fluxerr : `~numpy.array` Flux error in each spectral bin """ from astropy.utils.data import get_pkg_data_filename filename = get_pkg_data_filename( 'data/examples/example_spectrum.dat') return np.genfromtxt(filename).T sncosmo-2.12.1/sncosmo/magsystems.py000066400000000000000000000143721476435666400175410ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import abc import math import numpy as np from ._registry import Registry from .bandpasses import get_bandpass from .constants import H_ERG_S, SPECTRUM_BANDFLUX_SPACING from .utils import integration_grid __all__ = ['get_magsystem', 'MagSystem', 'SpectralMagSystem', 'ABMagSystem', 'CompositeMagSystem'] _MAGSYSTEMS = Registry() def get_magsystem(name): """Get a MagSystem from the registry by name.""" if isinstance(name, MagSystem): return name return _MAGSYSTEMS.retrieve(name) class MagSystem(object): """An abstract base class for magnitude systems.""" __metaclass__ = abc.ABCMeta def __init__(self, name=None): self._zpbandflux = {} self._name = name @abc.abstractmethod def _refspectrum_bandflux(self, band): """Flux of the fundamental spectrophotometric standard.""" pass @property def name(self): """Name of magnitude system.""" return self._name @name.setter def name(self, value): self._name = value def zpbandflux(self, band): """Flux of an object with magnitude zero in the given bandpass. Parameters ---------- bandpass : `~sncosmo.spectral.Bandpass` or str Returns ------- bandflux : float Flux in photons / s / cm^2. """ band = get_bandpass(band) try: return self._zpbandflux[band] except KeyError: bandflux = self._refspectrum_bandflux(band) self._zpbandflux[band] = bandflux return bandflux def band_flux_to_mag(self, flux, band): """Convert flux (photons / s / cm^2) to magnitude.""" return -2.5 * math.log10(flux / self.zpbandflux(band)) def band_mag_to_flux(self, mag, band): """Convert magnitude to flux in photons / s / cm^2""" return self.zpbandflux(band) * 10.**(-0.4 * mag) class CompositeMagSystem(MagSystem): """ CompositeMagSystem(bands=None, families=None, name=None) A magnitude system defined in a specific set of bands. In each band, there is a fundamental standard with a known (generally non-zero) magnitude. Parameters ---------- bands: dict, optional Dictionary where keys are `~sncosmo.Bandpass` instances or names, thereof and values are 2-tuples of magnitude system and offset. The offset gives the magnitude of standard in the given band. A positive offset means that the composite magsystem zeropoint flux is higher (brighter) than that of the standard. families : dict, optional Similar to the ``bands`` argument, but keys are strings that apply to any bandpass that has a matching ``family`` attribute. This is useful for generated bandpasses where the transmission differs across focal plane (and hence the bandpass at each position is unique), but all photometry has been calibrated to the same offset. name : str The ``name`` attribute of the magnitude system. Examples -------- Create a magnitude system defined in only two SDSS bands where an object with AB magnitude of 0 would have a magnitude of 0.01 and 0.02 in the two bands respectively: >>> sncosmo.CompositeMagSystem(bands={'sdssg': ('ab', 0.01), ... 'sdssr': ('ab', 0.02)}) """ def __init__(self, bands=None, families=None, name=None): super(CompositeMagSystem, self).__init__(name=name) if bands is not None: self._bands = {get_bandpass(band): (get_magsystem(magsys), offset) for band, (magsys, offset) in bands.items()} else: self._bands = {} if families is not None: self._families = {f: (get_magsystem(magsys), offset) for f, (magsys, offset) in families.items()} else: self._families = {} @property def bands(self): return self._bands def _refspectrum_bandflux(self, band): val = self._bands.get(band) if val is not None: standard, offset = val return 10.**(0.4 * offset) * standard.zpbandflux(band) if hasattr(band, 'family'): val = self._families.get(band.family) if val is not None: standard, offset = val return 10.**(0.4 * offset) * standard.zpbandflux(band) raise ValueError('band not defined in composite magnitude system') def __str__(self): s = "CompositeMagSystem {!r}:\n".format(self.name) for band, (magsys, offset) in self._bands.items(): s += " {!r}: system={!r} offset={}\n".format( band, magsys, offset) for family, (magsys, offset) in self._families.items(): s += " {!r}: system={!r} offset={}\n".format( family, magsys, offset) return s class SpectralMagSystem(MagSystem): """A magnitude system defined by a fundamental spectrophotometric standard. Parameters ---------- refspectrum : `sncosmo.SpectrumModel` The spectrum of the fundamental spectrophotometric standard. """ def __init__(self, refspectrum, name=None): super(SpectralMagSystem, self).__init__(name) self._refspectrum = refspectrum def _refspectrum_bandflux(self, band): return self._refspectrum.bandflux(band) class ABMagSystem(MagSystem): """Magnitude system where a source with F_nu = 3631 Jansky at all frequencies has magnitude 0 in all bands.""" def _refspectrum_bandflux(self, band): wave, dwave = integration_grid(band.minwave(), band.maxwave(), SPECTRUM_BANDFLUX_SPACING) # AB spectrum is 3631 x 10^{-23} erg/s/cm^2/Hz # # In F_lambda this is 3631e-23 * c[AA/s] / wave[AA]**2 erg/s/cm^2/AA # # To integrate, we do # # sum(f * trans * wave) * dwave / (hc[erg AA]) # # so we can simplify the above into # # sum(3631e-23 * c / wave * trans) * dwave / hc # = 3631e-23 * dwave / h[ERG S] * sum(trans / wave) return 3631e-23 * dwave / H_ERG_S * np.sum(band(wave) / wave) sncosmo-2.12.1/sncosmo/models.py000066400000000000000000002340471476435666400166330ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Classes to represent spectral models of astronomical transients.""" import abc import os from copy import copy as cp from math import ceil from textwrap import dedent import extinction import numpy as np from astropy import (cosmology, units as u) from astropy.utils.misc import isiterable from scipy.interpolate import ( InterpolatedUnivariateSpline as Spline1d, RectBivariateSpline as Spline2d ) from ._registry import Registry from .bandpasses import Bandpass, get_bandpass from .constants import HC_ERG_AA, MODEL_BANDFLUX_SPACING from .io import ( read_griddata_ascii, read_griddata_fits, read_multivector_griddata_ascii ) from .magsystems import get_magsystem from .salt2utils import BicubicInterpolator, SALT2ColorLaw from .utils import integration_grid, sine_interp __all__ = ['get_source', 'Source', 'TimeSeriesSource', 'StretchSource', 'SUGARSource', 'SALT2Source', 'SALT3Source', 'MLCS2k2Source', 'SNEMOSource', 'Model', 'PropagationEffect', 'CCM89Dust', 'OD94Dust', 'F99Dust'] _SOURCES = Registry() def _check_for_fitpack_error(e, a, name): """Raise a more informative error message for fitpack errors. This is implemented as a separate function rather than subclassing Spline2d so that we can raise the error closer to user-facing functions. We may wish to change this behavior in the future. For example, if some models are implemented not based on RectBivariateSpline so that they don't have this restriction. Parameters ---------- e : ValueError a : `~numpy.ndarray` (0-d or 1-d) name : str """ # Check if the error is a specific one raise by RectBivariateSpline # If it is, check if supplied array is *not* monotonically increasing if (len(e.args) > 0 and e.args[0].startswith("Error code returned by bispev: 10") and np.any(np.ediff1d(a) < 0.)): raise ValueError(name + ' must be monotonically increasing') def get_source(name, version=None, copy=False): """Retrieve a Source from the registry by name. Parameters ---------- name : str Name of source in the registry. version : str, optional Version identifier for sources with multiple versions. Default is `None` which corresponds to the latest, or only, version. copy : bool, optional If True and if `name` is already a Source instance, return a copy of it. (If `name` is a str a copy of the instance in the registry is always returned, regardless of the value of this parameter.) Default is False. """ # If we need to retrieve from the registry, we want to return a shallow # copy, in order to keep the copy in the registry "pristene". However, we # *don't* want a shallow copy otherwise. Therefore, # we need to check if `name` is already an instance of Model before # going to the registry, so we know whether or not to make a shallow copy. if isinstance(name, Source): if copy: return cp(name) else: return name else: return cp(_SOURCES.retrieve(name, version=version)) def _bandflux_single(model, band, time_or_phase): """Synthetic photometry of model through a single bandpass. Parameters ---------- model : Source or Model band : Bandpass time_or_phase : `~numpy.ndarray` (1-d) """ # Check that bandpass wavelength range is fully contained in model # wavelength range. if (band.minwave() < model.minwave() or band.maxwave() > model.maxwave()): raise ValueError('bandpass {0!r:s} [{1:.6g}, .., {2:.6g}] ' 'outside spectral range [{3:.6g}, .., {4:.6g}]' .format(band.name, band.minwave(), band.maxwave(), model.minwave(), model.maxwave())) # Set up wavelength grid. Spacing (dwave) evenly divides the bandpass, # closest to 5 angstroms without going over. wave, dwave = integration_grid(band.minwave(), band.maxwave(), MODEL_BANDFLUX_SPACING) trans = band(wave) f = model._flux(time_or_phase, wave) return np.sum(wave * trans * f, axis=1) * dwave / HC_ERG_AA def _bandflux(model, band, time_or_phase, zp, zpsys): """Support function for bandflux in Source and Model. This is necessary to have outside because ``phase`` is used in Source and ``time`` is used in Model, and we want the method signatures to have the right variable name. """ if zp is not None and zpsys is None: raise ValueError('zpsys must be given if zp is not None') # broadcast arrays if zp is None: time_or_phase, band = np.broadcast_arrays(time_or_phase, band) else: time_or_phase, band, zp, zpsys = \ np.broadcast_arrays(time_or_phase, band, zp, zpsys) # Convert all to 1-d arrays. ndim = time_or_phase.ndim # Save input ndim for return val. time_or_phase = np.atleast_1d(time_or_phase) band = np.atleast_1d(band) if zp is not None: zp = np.atleast_1d(zp) zpsys = np.atleast_1d(zpsys) # initialize output arrays bandflux = np.zeros(time_or_phase.shape, dtype=float) # Loop over unique bands. for b in set(band): mask = band == b b = get_bandpass(b) fsum = _bandflux_single(model, b, time_or_phase[mask]) if zp is not None: zpnorm = 10.**(0.4 * zp[mask]) bandzpsys = zpsys[mask] for ms in set(bandzpsys): mask2 = bandzpsys == ms ms = get_magsystem(ms) zpnorm[mask2] = zpnorm[mask2] / ms.zpbandflux(b) fsum *= zpnorm bandflux[mask] = fsum if ndim == 0: return bandflux[0] return bandflux def _bandmag(model, band, magsys, time_or_phase): """Support function for bandflux in Source and Model. This is necessary to have outside the models because ``phase`` is used in Source and ``time`` is used in Model. """ bandflux = _bandflux(model, band, time_or_phase, None, None) band, magsys, bandflux = np.broadcast_arrays(band, magsys, bandflux) return_scalar = (band.ndim == 0) band = band.ravel() magsys = magsys.ravel() bandflux = bandflux.ravel() result = np.empty(bandflux.shape, dtype=float) for i, (b, ms, f) in enumerate(zip(band, magsys, bandflux)): ms = get_magsystem(ms) zpf = ms.zpbandflux(b) result[i] = -2.5 * np.log10(f / zpf) if return_scalar: return result[0] return result class _ModelBase(object): """Base class for anything with parameters. Derived classes must have properties ``_param_names`` (list of str) and ``_parameters`` (1-d numpy.ndarray). """ @property def param_names(self): """List of parameter names.""" return self._param_names @property def parameters(self): """Parameter value array""" return self._parameters @parameters.setter def parameters(self, value): value = np.asarray(value) if value.shape != self._parameters.shape: raise ValueError("Incorrect number of parameters.") self._parameters[:] = value def set(self, **param_dict): """Set parameters of the model by name.""" self.update(param_dict) def update(self, param_dict): """Set parameters of the model from a dictionary.""" for key, value in param_dict.items(): self[key] = value def __setitem__(self, key, value): """Set a single parameter of the model by name.""" try: i = self._param_names.index(key) except ValueError: raise KeyError("Unknown parameter: " + repr(key)) self._parameters[i] = value def get(self, name): """Get parameter of the model by name.""" return self[name] def __getitem__(self, name): """Get parameter of the model by name""" try: i = self._param_names.index(name) except ValueError: raise KeyError("Model has no parameter " + repr(name)) return self._parameters[i] def _headsummary(self): return '' def __str__(self): parameter_lines = [self._headsummary(), 'parameters:'] if len(self._param_names) > 0: m = max(map(len, self._param_names)) extralines = [' ' + k.ljust(m) + ' = ' + repr(v) for k, v in zip(self._param_names, self._parameters)] parameter_lines.extend(extralines) return '\n'.join(parameter_lines) def __copy__(self): """Like a normal shallow copy, but makes an actual copy of the parameter array.""" new_model = self.__new__(self.__class__) for key, val in self.__dict__.items(): new_model.__dict__[key] = val new_model._parameters = self._parameters.copy() return new_model class Source(_ModelBase): """An abstract base class for transient models. A "transient model" in this case is the spectral time evolution of a source, as defined in the rest-frame of the transient: ``Source`` subclass instances define a spectral flux density (in, e.g., erg / s / cm^2 / Angstrom) as a function of phase and wavelength, where phase and wavelength are in the source's rest-frame. (The ``Model`` class wraps a ``Source`` instance and takes care of redshift and time dilation.) This two-dimensional spectral surface can be a function of any number of parameters that alter its amplitude or shape. Different subclasses will have different parameters. This is an abstract base class -- You can't create instances of this class. Instead, you must work with subclasses such as ``TimeSeriesSource``. Subclasses must define (at minimum): * `__init__()` * `_param_names` (list of str) * `_parameters` (`numpy.ndarray`) * `_flux(ndarray, ndarray)` * `minphase()` * `maxphase()` * `minwave()` * `maxwave()` """ __metaclass__ = abc.ABCMeta @abc.abstractmethod def __init__(self): pass def minphase(self): return self._phase[0] def maxphase(self): return self._phase[-1] def minwave(self): return self._wave[0] def maxwave(self): return self._wave[-1] @abc.abstractmethod def _flux(self, phase, wave): pass def flux(self, phase, wave): """The spectral flux density at the given phase and wavelength values. Parameters ---------- phase : float or list_like, optional Phase(s) in days. Must be monotonically increasing. If `None` (default), the native phases of the model are used. wave : float or list_like, optional Wavelength(s) in Angstroms. Must be monotonically increasing. If `None` (default), the native wavelengths of the model are used. Returns ------- flux : float or `~numpy.ndarray` Spectral flux density values in ergs / s / cm^2 / Angstrom. """ phase = np.asarray(phase) wave = np.asarray(wave) if np.any(wave < self.minwave()) or np.any(wave > self.maxwave()): raise ValueError('requested wavelength value(s) outside ' 'model range') try: f = self._flux(phase, wave) except ValueError as e: _check_for_fitpack_error(e, phase, 'phase') _check_for_fitpack_error(e, wave, 'wave') raise e if phase.ndim == 0: if wave.ndim == 0: return f[0, 0] return f[0, :] return f def bandflux(self, band, phase, zp=None, zpsys=None): """Flux through the given bandpass(es) at the given phase(s). Default return value is flux in photons / s / cm^2. If zp and zpsys are given, flux(es) are scaled to the requested zeropoints. Parameters ---------- band : str or list_like Name(s) of bandpass(es) in registry. phase : float or list_like, optional Phase(s) in days. Default is `None`, which corresponds to the full native phase sampling of the model. zp : float or list_like, optional If given, zeropoint to scale flux to (must also supply ``zpsys``). If not given, flux is not scaled. zpsys : str or list_like, optional Name of a magnitude system in the registry, specifying the system that ``zp`` is in. Returns ------- bandflux : float or `~numpy.ndarray` Flux in photons / s /cm^2, unless `zp` and `zpsys` are given, in which case flux is scaled so that it corresponds to the requested zeropoint. Return value is `float` if all input parameters are scalars, `~numpy.ndarray` otherwise. """ try: return _bandflux(self, band, phase, zp, zpsys) except ValueError as e: _check_for_fitpack_error(e, phase, 'phase') raise e def bandmag(self, band, magsys, phase): """Magnitude at the given phase(s) through the given bandpass(es), and for the given magnitude system(s). Parameters ---------- band : str or list_like Name(s) of bandpass in registry. magsys : str or list_like Name(s) of `~sncosmo.MagSystem` in registry. phase : float or list_like Phase(s) in days. Returns ------- mag : float or `~numpy.ndarray` Magnitude for each item in band, magsys, phase. The return value is a float if all parameters are not iterables. The return value is an `~numpy.ndarray` if any are iterable. """ return _bandmag(self, band, magsys, phase) def peakphase(self, band_or_wave, sampling=1.): """Determine phase of maximum flux for the given band/wavelength. This method generates the light curve in the given band/wavelength and finds the highest-flux point. It then finds the parabola that passes through this point and the two neighboring points, and returns the position of the peak of the parabola. """ # Array of phases to sample at. nsamples = int(ceil((self.maxphase()-self.minphase()) / sampling)) + 1 phases = np.linspace(self.minphase(), self.maxphase(), nsamples) if isinstance(band_or_wave, (str, Bandpass)): fluxes = self.bandflux(band_or_wave, phases) else: fluxes = self.flux(phases, band_or_wave)[:, 0] i = np.argmax(fluxes) if (i == 0) or (i == len(phases) - 1): return phases[i] x = phases[i-1: i+2] y = fluxes[i-1: i+2] A = np.hstack([x.reshape(3, 1)**2, x.reshape(3, 1), np.ones((3, 1))]) a, b, c = np.linalg.solve(A, y) return -b / (2 * a) def peakmag(self, band, magsys, sampling=1.0): """Peak apparent magnitude in rest-frame bandpass.""" peakphase = self.peakphase(band, sampling=sampling) return self.bandmag(band, magsys, peakphase) def set_peakmag(self, m, band, magsys, sampling=1.0): """Set peak apparent magnitude in rest-frame bandpass.""" m_current = self.peakmag(band, magsys, sampling=sampling) factor = 10.**(0.4 * (m_current - m)) self._parameters[0] = factor * self._parameters[0] def __repr__(self): name = '' version = '' if self.name is not None: name = ' {0!r:s}'.format(self.name) if self.version is not None: version = ' version={0!r:s}'.format(self.version) return "<{0:s}{1:s}{2:s} at 0x{3:x}>".format( self.__class__.__name__, name, version, id(self)) def _headsummary(self): summary = """\ class : {0} name : {1!r} version : {2} phases : [{3:.6g}, .., {4:.6g}] days wavelengths: [{5:.6g}, .., {6:.6g}] Angstroms"""\ .format( self.__class__.__name__, self.name, self.version, self.minphase(), self.maxphase(), self.minwave(), self.maxwave()) return dedent(summary) class TimeSeriesSource(Source): """A single-component spectral time series model. The spectral flux density of this model is given by .. math:: F(t, \\lambda) = A \\times M(t, \\lambda) where _M_ is the flux defined on a grid in phase and wavelength and _A_ (amplitude) is the single free parameter of the model. The amplitude _A_ is a simple unitless scaling factor applied to whatever flux values are used to initialize the ``TimeSeriesSource``. Therefore, the _A_ parameter has no intrinsic meaning. It can only be interpreted in conjunction with the model values. Thus, it is meaningless to compare the _A_ parameter between two different ``TimeSeriesSource`` instances with different model data. Parameters ---------- phase : `~numpy.ndarray` Phases in days. wave : `~numpy.ndarray` Wavelengths in Angstroms. flux : `~numpy.ndarray` Model spectral flux density in erg / s / cm^2 / Angstrom. Must have shape ``(num_phases, num_wave)``. zero_before : bool, optional If True, flux at phases before minimum phase will be zeroed. The default is False, in which case the flux at such phases will be equal to the flux at the minimum phase (``flux[0, :]`` in the input array). time_spline_degree : int, optional Degree of the spline used for interpolation in the time (phase) direction. By default this is set to 3 (i.e. cubic spline). For models that are defined with sparse time grids this can lead to large interpolation uncertainties and negative fluxes. If this is a problem, set time_spline_degree to 1 to use linear interpolation instead. name : str, optional Name of the model. Default is `None`. version : str, optional Version of the model. Default is `None`. """ _param_names = ['amplitude'] param_names_latex = ['A'] def __init__(self, phase, wave, flux, zero_before=False, time_spline_degree=3, name=None, version=None): self.name = name self.version = version self._phase = phase self._wave = wave self._parameters = np.array([1.]) self._model_flux = Spline2d(phase, wave, flux, kx=time_spline_degree, ky=3) self._zero_before = zero_before def _flux(self, phase, wave): f = self._parameters[0] * self._model_flux(phase, wave) if self._zero_before: mask = np.atleast_1d(phase) < self.minphase() f[mask, :] = 0. return f class StretchSource(Source): """A single-component spectral time series model, that "stretches" in time. The spectral flux density of this model is given by .. math:: F(t, \\lambda) = A \\times M(t / s, \\lambda) where _A_ is the amplitude and _s_ is the "stretch". Parameters ---------- phase : `~numpy.ndarray` Phases in days. wave : `~numpy.ndarray` Wavelengths in Angstroms. flux : `~numpy.ndarray` Model spectral flux density in erg / s / cm^2 / Angstrom. Must have shape `(num_phases, num_disp)`. """ _param_names = ['amplitude', 's'] param_names_latex = ['A', 's'] def __init__(self, phase, wave, flux, name=None, version=None): self.name = name self.version = version self._phase = phase self._wave = wave self._parameters = np.array([1., 1.]) self._model_flux = Spline2d(phase, wave, flux, kx=3, ky=3) def minphase(self): return self._parameters[1] * self._phase[0] def maxphase(self): return self._parameters[1] * self._phase[-1] def _flux(self, phase, wave): return (self._parameters[0] * self._model_flux(phase / self._parameters[1], wave)) class SUGARSource(Source): """ The SUGAR Type Ia supernova spectral time series template. The spectral energy distribution of this model is given by .. math:: F(t, \\lambda) = q_0 10^{-0.4 (M_0(t, \\lambda) + q_1 \\alpha_1(t, \\lambda) + q_2 \\alpha_2(t, \\lambda) + q_3 \\alpha_3(t, \\lambda) + A_v CCM(\\lambda))} (10^{-3} c\\lambda^{2}) where ``q_0``, ``q_1``, ``q_2``, ``q_3``, and ``A_v`` are the free parameters of the model,``alpha_0``, ``alpha_1``, `alpha_2``, `alpha_3``, `CCM`` are the template vectors of the model. The ``q_0`` is the equivalent parameter in flux of the ``Delta M_{gray}`` parameter define in Leget et al. 2020. Parameters ---------- modeldir : str, optional Directory path containing model component files. Default is `None`, which means that no directory is prepended to filenames when determining their path. m0file : str or fileobj, optional alpha1file : str or fileobj, optional alpha2file : str or fileobj, optional alpha3file : str or fileobj, optional CCMfile: str or fileobj, optional Filenames of various model components. Defaults are: * m0file = 'sugar_template_0.dat' (2-d grid) * alpha1file = 'sugar_template_1.dat' (2-d grid) * alpha2file = 'sugar_template_2.dat' (2-d grid) * alpha3file = 'sugar_template_3.dat' (2-d grid) * CCMfile = 'sugar_template_4.dat' (2-d grid) Notes ----- The "2-d grid" files have the format `` `` on each line. """ _param_names = ['q0', 'q1', 'q2', 'q3', 'Av'] param_names_latex = ['q_0', 'q_1', 'q_2', 'q_3', 'A_v'] def __init__(self, modeldir=None, m0file='sugar_template_0.dat', alpha1file='sugar_template_1.dat', alpha2file='sugar_template_2.dat', alpha3file='sugar_template_3.dat', CCMfile='sugar_template_4.dat', name=None, version=None): self.name = name self.version = version self._model = {} self.M_keys = ['M0', 'ALPHA1', 'ALPHA2', 'ALPHA3', 'CCM'] self._parameters = np.zeros(len(self.M_keys)) self._parameters[0] = 1e-15 names_or_objs = {'M0': m0file, 'ALPHA1': alpha1file, 'ALPHA2': alpha2file, 'ALPHA3': alpha3file, 'CCM': CCMfile} # Make filenames into full paths. if modeldir is not None: for k in names_or_objs: v = names_or_objs[k] if (v is not None and isinstance(v, str)): names_or_objs[k] = os.path.join(modeldir, v) for i, key in enumerate(self.M_keys): phase, wave, values = read_griddata_ascii(names_or_objs[key]) self._model[key] = BicubicInterpolator(phase, wave, values) if key == 'M0': # The "native" phases and wavelengths of the model are those self._phase = np.linspace(-12., 48, 21) self._wave = wave def _flux(self, phase, wave): mag_sugar = self._model['M0'](phase, wave) for i, key in enumerate(self.M_keys): if key != 'M0': comp = self._model[key](phase, wave) * self._parameters[i] mag_sugar += comp # Mag AB used in the training of SUGAR. mag_sugar += 48.59 wave_factor = (wave ** 2 / 299792458. * 1.e-10) flux = (self._parameters[0] * 10. ** (-0.4 * mag_sugar) / wave_factor) if hasattr(phase, '__iter__'): not_define = ~((phase > -12) & (phase < 48)) flux[not_define] = 0 return flux else: if phase < -12 or phase > 48: return np.zeros_like(wave) else: return flux class SALT2Source(Source): """The SALT2 Type Ia supernova spectral timeseries model. The spectral flux density of this model is given by .. math:: F(t, \\lambda) = x_0 (M_0(t, \\lambda) + x_1 M_1(t, \\lambda)) \\times 10^{-0.4 CL(\\lambda) c} where ``x0``, ``x1`` and ``c`` are the free parameters of the model, ``M_0``, ``M_1`` are the zeroth and first components of the model, and ``CL`` is the colorlaw, which gives the extinction in magnitudes for ``c=1``. Parameters ---------- modeldir : str, optional Directory path containing model component files. Default is `None`, which means that no directory is prepended to filenames when determining their path. m0file, m1file, clfile : str or fileobj, optional Filenames of various model components. Defaults are: * m0file = 'salt2_template_0.dat' (2-d grid) * m1file = 'salt2_template_1.dat' (2-d grid) * clfile = 'salt2_color_correction.dat' errscalefile, lcrv00file, lcrv11file, lcrv01file, cdfile : str or fileobj (optional) Filenames of various model components for model covariance in synthetic photometry. See ``bandflux_rcov`` for details. Defaults are: * errscalefile = 'salt2_lc_dispersion_scaling.dat' (2-d grid) * lcrv00file = 'salt2_lc_relative_variance_0.dat' (2-d grid) * lcrv11file = 'salt2_lc_relative_variance_1.dat' (2-d grid) * lcrv01file = 'salt2_lc_relative_covariance_01.dat' (2-d grid) * cdfile = 'salt2_color_dispersion.dat' (1-d grid) Notes ----- The "2-d grid" files have the format `` `` on each line. The phase and wavelength values of the various components don't necessarily need to match. (In the most recent salt2 model data, they do not all match.) The phase and wavelength values of the first model component (in ``m0file``) are taken as the "native" sampling of the model, even though these values might require interpolation of the other model components. """ # These files are distributed with SALT2 model data but not currently # used: # v00file = 'salt2_spec_variance_0.dat' : 2dgrid # v11file = 'salt2_spec_variance_1.dat' : 2dgrid # v01file = 'salt2_spec_covariance_01.dat' : 2dgrid _param_names = ['x0', 'x1', 'c'] param_names_latex = ['x_0', 'x_1', 'c'] _SCALE_FACTOR = 1e-12 def __init__(self, modeldir=None, m0file='salt2_template_0.dat', m1file='salt2_template_1.dat', clfile='salt2_color_correction.dat', cdfile='salt2_color_dispersion.dat', errscalefile='salt2_lc_dispersion_scaling.dat', lcrv00file='salt2_lc_relative_variance_0.dat', lcrv11file='salt2_lc_relative_variance_1.dat', lcrv01file='salt2_lc_relative_covariance_01.dat', name=None, version=None): self.name = name self.version = version self._model = {} self._parameters = np.array([1., 0., 0.]) names_or_objs = {'M0': m0file, 'M1': m1file, 'LCRV00': lcrv00file, 'LCRV11': lcrv11file, 'LCRV01': lcrv01file, 'errscale': errscalefile, 'cdfile': cdfile, 'clfile': clfile} # Make filenames into full paths. if modeldir is not None: for k in names_or_objs: v = names_or_objs[k] if (v is not None and isinstance(v, str)): names_or_objs[k] = os.path.join(modeldir, v) # model components are interpolated to 2nd order for key in ['M0', 'M1']: phase, wave, values = read_griddata_ascii(names_or_objs[key]) values *= self._SCALE_FACTOR self._model[key] = BicubicInterpolator(phase, wave, values) # The "native" phases and wavelengths of the model are those # of the first model component. if key == 'M0': self._phase = phase self._wave = wave # model covariance is interpolated to 1st order for key in ['LCRV00', 'LCRV11', 'LCRV01', 'errscale']: phase, wave, values = read_griddata_ascii(names_or_objs[key]) self._model[key] = BicubicInterpolator(phase, wave, values) # Set the colorlaw based on the "color correction" file. self._set_colorlaw_from_file(names_or_objs['clfile']) # Set the color dispersion from "color_dispersion" file w, val = np.loadtxt(names_or_objs['cdfile'], unpack=True) self._colordisp = Spline1d(w, val, k=1) # linear interp. def _flux(self, phase, wave): m0 = self._model['M0'](phase, wave) m1 = self._model['M1'](phase, wave) return (self._parameters[0] * (m0 + self._parameters[1] * m1) * 10. ** (-0.4 * self._colorlaw(wave) * self._parameters[2])) def _bandflux_rvar_single(self, band, phase): """Model relative variance for a single bandpass.""" # Raise an exception if bandpass is out of model range. if (band.minwave() < self._wave[0] or band.maxwave() > self._wave[-1]): raise ValueError('bandpass {0!r:s} [{1:.6g}, .., {2:.6g}] ' 'outside spectral range [{3:.6g}, .., {4:.6g}]' .format(band.name, band.wave[0], band.wave[-1], self._wave[0], self._wave[-1])) x1 = self._parameters[1] # integrate m0 and m1 components wave, dwave = integration_grid(band.minwave(), band.maxwave(), MODEL_BANDFLUX_SPACING) trans = band(wave) m0 = self._model['M0'](phase, wave) m1 = self._model['M1'](phase, wave) tmp = trans * wave f0 = np.sum(m0 * tmp, axis=1) * dwave / HC_ERG_AA m1int = np.sum(m1 * tmp, axis=1) * dwave / HC_ERG_AA ftot = f0 + x1 * m1int # In the following, the "[:,0]" reduces from a 2-d array of shape # (nphase, 1) to a 1-d array. lcrv00 = self._model['LCRV00'](phase, band.wave_eff)[:, 0] lcrv11 = self._model['LCRV11'](phase, band.wave_eff)[:, 0] lcrv01 = self._model['LCRV01'](phase, band.wave_eff)[:, 0] scale = self._model['errscale'](phase, band.wave_eff)[:, 0] v = lcrv00 + 2.0 * x1 * lcrv01 + x1 * x1 * lcrv11 # v is supposed to be variance but can go negative # due to interpolation. Correct negative values to some small # number. (at present, use prescription of snfit : set # negatives to 0.0001) v[v < 0.0] = 0.0001 # avoid warnings due to evaluating 0. / 0. in f0 / ftot with np.errstate(invalid='ignore'): result = v * (f0 / ftot)**2 * scale**2 # treat cases where ftot is negative the same as snfit result[ftot <= 0.0] = 10000. return result def bandflux_rcov(self, band, phase): """Return the *relative* model covariance (or "model error") on synthetic photometry generated from the model in the given restframe band(s). This model covariance represents the scatter of real SNe about the model. The covariance matrix has two components. The first component is diagonal (pure variance) and depends on the phase :math:`t` and bandpass central wavelength :math:`\\lambda_c` of each photometry point: .. math:: (F_{0, \\mathrm{band}}(t) / F_{1, \\mathrm{band}}(t))^2 S(t, \\lambda_c)^2 (V_{00}(t, \\lambda_c) + 2 x_1 V_{01}(t, \\lambda_c) + x_1^2 V_{11}(t, \\lambda_c)) where the 2-d functions :math:`S`, :math:`V_{00}`, :math:`V_{01}`, and :math:`V_{11}` are given by the files ``errscalefile``, ``lcrv00file``, ``lcrv01file``, and ``lcrv11file`` respectively and :math:`F_0` and :math:`F_1` are given by .. math:: F_{0, \\mathrm{band}}(t) = \\int_\\lambda M_0(t, \\lambda) T_\\mathrm{band}(\\lambda) \\frac{\\lambda}{hc} d\\lambda .. math:: F_{1, \\mathrm{band}}(t) = \\int_\\lambda (M_0(t, \\lambda) + x_1 M_1(t, \\lambda)) T_\\mathrm{band}(\\lambda) \\frac{\\lambda}{hc} d\\lambda As this first component can sometimes be negative due to interpolation, there is a floor applied wherein values less than zero are set to ``0.01**2``. This is to match the behavior of the original SALT2 code, snfit. The second component is block diagonal. It has constant covariance between all photometry points within a bandpass (regardless of phase), and no covariance between photometry points in different bandpasses: .. math:: CD(\\lambda_c)^2 where the 1-d function :math:`CD` is given by the file ``cdfile``. Adding these two components gives the *relative* covariance on model photometry. Parameters ---------- band : `~numpy.ndarray` of `~sncosmo.Bandpass` Bandpasses of observations. phase : `~numpy.ndarray` (float) Phases of observations. Returns ------- rcov : `~numpy.ndarray` Model relative covariance for given bandpasses and phases. """ # construct covariance array with relative variance on diagonal diagonal = np.zeros(phase.shape, dtype=np.float64) for b in set(band): mask = band == b diagonal[mask] = self._bandflux_rvar_single(b, phase[mask]) result = np.diagflat(diagonal) # add kcorr errors for b in set(band): mask1d = band == b mask2d = mask1d * mask1d[:, None] # mask for result array kcorrerr = self._colordisp(b.wave_eff) result[mask2d] += kcorrerr**2 return result def _set_colorlaw_from_file(self, name_or_obj): """Read color law file and set the internal colorlaw function.""" # Read file if isinstance(name_or_obj, str): f = open(name_or_obj, 'r') else: f = name_or_obj words = f.read().split() f.close() # Get colorlaw coeffecients. ncoeffs = int(words[0]) colorlaw_coeffs = [float(word) for word in words[1: 1 + ncoeffs]] self._colorlaw_coeffs = colorlaw_coeffs # If there are more than 1+ncoeffs words in the file, we expect them to # be of the form `keyword value`. version = None colorlaw_range = [3000., 7000.] for i in range(1+ncoeffs, len(words), 2): if words[i] == 'Salt2ExtinctionLaw.version': version = int(words[i+1]) elif words[i] == 'Salt2ExtinctionLaw.min_lambda': colorlaw_range[0] = float(words[i+1]) elif words[i] == 'Salt2ExtinctionLaw.max_lambda': colorlaw_range[1] = float(words[i+1]) else: raise RuntimeError("Unexpected keyword: {}".format(words[i])) # Set extinction function to use. if version == 0: raise RuntimeError("Salt2ExtinctionLaw.version 0 not supported.") elif version == 1: self._colorlaw = SALT2ColorLaw(colorlaw_range, colorlaw_coeffs) else: raise RuntimeError('unrecognized Salt2ExtinctionLaw.version: ' + version) def colorlaw(self, wave=None): """Return the value of the CL function for the given wavelengths. Parameters ---------- wave : float or list_like Returns ------- colorlaw : float or `~numpy.ndarray` Values of colorlaw function, which can be interpreted as extinction in magnitudes. """ if wave is None: wave = self._wave else: wave = np.asarray(wave) if wave.ndim == 0: return self._colorlaw(np.ravel(wave))[0] else: return self._colorlaw(wave) class SALT3Source(SALT2Source): """The SALT3 Type Ia supernova spectral timeseries model. Kenworthy et al., 2021, ApJ, submitted. Model definitions are the same as SALT2 except for the errors, which are now given in flux space. Unlike SALT2, no file is used for scaling the errors. The spectral flux density of this model is given by .. math:: F(t, \\lambda) = x_0 (M_0(t, \\lambda) + x_1 M_1(t, \\lambda)) \\times 10^{-0.4 CL(\\lambda) c} where ``x0``, ``x1`` and ``c`` are the free parameters of the model, ``M_0``, ``M_1`` are the zeroth and first components of the model, and ``CL`` is the colorlaw, which gives the extinction in magnitudes for ``c=1``. Parameters ---------- modeldir : str, optional Directory path containing model component files. Default is `None`, which means that no directory is prepended to filenames when determining their path. m0file, m1file, clfile : str or fileobj, optional Filenames of various model components. Defaults are: * m0file = 'salt2_template_0.dat' (2-d grid) * m1file = 'salt2_template_1.dat' (2-d grid) * clfile = 'salt2_color_correction.dat' lcrv00file, lcrv11file, lcrv01file, cdfile : str or fileobj (optional) Filenames of various model components for model covariance in synthetic photometry. See ``bandflux_rcov`` for details. Defaults are: * lcrv00file = 'salt2_lc_relative_variance_0.dat' (2-d grid) * lcrv11file = 'salt2_lc_relative_variance_1.dat' (2-d grid) * lcrv01file = 'salt2_lc_relative_covariance_01.dat' (2-d grid) * cdfile = 'salt2_color_dispersion.dat' (1-d grid) Notes ----- The "2-d grid" files have the format `` `` on each line. The phase and wavelength values of the various components don't necessarily need to match. (In the most recent salt2 model data, they do not all match.) The phase and wavelength values of the first model component (in ``m0file``) are taken as the "native" sampling of the model, even though these values might require interpolation of the other model components. """ _param_names = ['x0', 'x1', 'c'] param_names_latex = ['x_0', 'x_1', 'c'] _SCALE_FACTOR = 1e-12 def __init__(self, modeldir=None, m0file='salt3_template_0.dat', m1file='salt3_template_1.dat', clfile='salt3_color_correction.dat', cdfile='salt3_color_dispersion.dat', lcrv00file='salt3_lc_variance_0.dat', lcrv11file='salt3_lc_variance_1.dat', lcrv01file='salt3_lc_covariance_01.dat', name=None, version=None): self.name = name self.version = version self._model = {} self._parameters = np.array([1., 0., 0.]) names_or_objs = {'M0': m0file, 'M1': m1file, 'LCRV00': lcrv00file, 'LCRV11': lcrv11file, 'LCRV01': lcrv01file, 'cdfile': cdfile, 'clfile': clfile} # Make filenames into full paths. if modeldir is not None: for k in names_or_objs: v = names_or_objs[k] if (v is not None and isinstance(v, str)): names_or_objs[k] = os.path.join(modeldir, v) # model components are interpolated to 2nd order for key in ['M0', 'M1']: phase, wave, values = read_griddata_ascii(names_or_objs[key]) values *= self._SCALE_FACTOR self._model[key] = BicubicInterpolator(phase, wave, values) # The "native" phases and wavelengths of the model are those # of the first model component. if key == 'M0': self._phase = phase self._wave = wave # model covariance is interpolated to 1st order for key in ['LCRV00', 'LCRV11', 'LCRV01']: phase, wave, values = read_griddata_ascii(names_or_objs[key]) values *= self._SCALE_FACTOR**2. self._model[key] = BicubicInterpolator(phase, wave, values) # Set the colorlaw based on the "color correction" file. self._set_colorlaw_from_file(names_or_objs['clfile']) # Set the color dispersion from "color_dispersion" file w, val = np.loadtxt(names_or_objs['cdfile'], unpack=True) self._colordisp = Spline1d(w, val, k=1) # linear interp. def _bandflux_rvar_single(self, band, phase): """Model relative variance for a single bandpass.""" # Raise an exception if bandpass is out of model range. if (band.minwave() < self._wave[0] or band.maxwave() > self._wave[-1]): raise ValueError('bandpass {0!r:s} [{1:.6g}, .., {2:.6g}] ' 'outside spectral range [{3:.6g}, .., {4:.6g}]' .format(band.name, band.wave[0], band.wave[-1], self._wave[0], self._wave[-1])) x1 = self._parameters[1] # integrate m0 and m1 components wave, dwave = integration_grid(band.minwave(), band.maxwave(), MODEL_BANDFLUX_SPACING) trans = band(wave) m0 = self._model['M0'](phase, wave) m1 = self._model['M1'](phase, wave) tmp = trans * wave # evaluate avg M0 + x1*M1 across a bandpass f0 = np.sum(m0 * tmp, axis=1)/tmp.sum() m1int = np.sum(m1 * tmp, axis=1)/tmp.sum() ftot = f0 + x1 * m1int # In the following, the "[:,0]" reduces from a 2-d array of shape # (nphase, 1) to a 1-d array. lcrv00 = self._model['LCRV00'](phase, band.wave_eff)[:, 0] lcrv11 = self._model['LCRV11'](phase, band.wave_eff)[:, 0] lcrv01 = self._model['LCRV01'](phase, band.wave_eff)[:, 0] # variance in M0 + x1*M1 at the effective wavelength # of a bandpass v = lcrv00 + 2.0 * x1 * lcrv01 + x1 * x1 * lcrv11 # v is supposed to be variance but can go negative # due to interpolation. Correct negative values to some small # number. (at present, use prescription of snfit : set # negatives to 0.0001) v[v < 0.0] = 0.0001 # avoid warnings due to evaluating 0. / 0. in f0 / ftot with np.errstate(invalid='ignore'): # turn M0+x1*M1 error into a relative error result = v/ftot**2. # treat cases where ftot is negative the same as snfit result[ftot <= 0.0] = 10000. return result class MLCS2k2Source(Source): """A spectral time series model based on the MLCS2k2 model light curves, using the Hsiao template at each phase, mangled to match the model photometry. The spectral flux density of this model is given by .. math:: F(t, \\lambda) = A \\times M(\\Delta, t, \\lambda) where _A_ is the amplitude and _Delta_ is the MLCS2k2 light curve shape parameter. .. note:: Requires scipy version 0.14 or higher. Parameters ---------- fluxfile : str or obj Filename (or open file-like object) of a FITS file containing 3-d array of spectral flux density values for a grid of delta, phase and wavelength values. """ _param_names = ['amplitude', 'delta'] param_names_latex = ['A', '\\Delta'] def __init__(self, fluxfile, name=None, version=None): # RegularGridInterpolator is only available in recent scipy # versions. try: from scipy.interpolate import RegularGridInterpolator except ImportError: import scipy # to get scipy version raise ImportError("scipy version 0.14 or greater required for " "MLCS2k2Source. Installed version: " + scipy.__version__) self.name = name self.version = version self._parameters = np.array([1., 0.]) delta, phase, wave, values = read_griddata_fits(fluxfile) self._phase = phase self._wave = wave self._delta = delta self._3d_model_flux = RegularGridInterpolator((delta, phase, wave), values, bounds_error=False, fill_value=0.) def _flux(self, phase, wave): # "outer cartesian product" code from fast cartesian_product2 from # http://stackoverflow.com/questions/11144513/numpy-cartesian-product- # of-x-and-y-array-points-into-single-array-of-2d-points arrays = [[self.parameters[1]], phase, wave] lp = len(phase) lw = len(wave) arr = np.empty((1, lp, lw, 3), dtype=np.float64) for i, a in enumerate(np.ix_(*arrays)): arr[..., i] = a points = arr.reshape((-1, 3)) return (self._parameters[0] * self._3d_model_flux(points).reshape(lp, lw)) class SNEMOSource(Source): """The SNEMO Type Ia supernova spectral timeseries model The spectral flux density of this model is given by .. math:: F(t, \\lambda) = c_0(e_0(t, \\lambda) + \\sum_{i=1}^{n} c_i e_i(t, \\lambda)) \\times FM07(\\lambda, A_s) where ``c_0``, ``c_i``, and ``A_s`` are the free parameters of the model. Parameters ---------- fluxfile : str or obj, optional Filename of an ascii file containing 2-d array of spectral flux density values for a grid of phase and wavelength values. Assuming columns ``phase``, ``wavelength``, ``e_0``, ``e_1``, ``e_2``... """ def __init__(self, fluxfile, name=None, version=None): self.name = name self.version = version phase, wave, values = read_multivector_griddata_ascii(fluxfile) n_vector = values.shape[0] self._parameters = np.zeros(n_vector+1) self._parameters[0] = 1 _param_names = ['c0', 'As', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'c10', 'c11', 'c12', 'c13', 'c14'] param_names_latex = ['c_0', 'A_s', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5', 'c_6', 'c_7', 'c_8', 'c_9', 'c_{10}', 'c_{11}', 'c_{12}', 'c_{13}', 'c_{14}'] self._param_names = _param_names[:n_vector + 1] self.param_names_latex = param_names_latex[:n_vector + 1] self._phase = phase self._wave = wave self._model_fluxes = np.array([Spline2d(phase, wave, v, kx=2, ky=2) for v in values]) def _flux(self, phase, wave): c_0 = self._parameters[0] A_s = self._parameters[1] color = extinction.fm07(wave * u.angstrom, A_s) model_fluxes = np.array([mf(phase, wave) for mf in self._model_fluxes]) model_ev = c_0 * (model_fluxes[0] + (self._parameters[2:, None, None] * model_fluxes[1:]).sum(axis=0)) model_c = 10**(-0.4 * color) return model_ev * model_c class Model(_ModelBase): """An observer-frame model, composed of a Source and zero or more effects. Parameters ---------- source : `~sncosmo.Source` or str The model for the spectral evolution of the source. If a string is given, it is used to retrieve a `~sncosmo.Source` from the registry. effects : list of `~sncosmo.PropagationEffect` List of `~sncosmo.PropagationEffect` instances to add. effect_names : list of str Names of effects (same length as `effects`). The names are used to label the parameters. effect_frames : list of str The frame that each effect is in (same length as `effects`). Must be one of {'rest', 'obs'}. Notes ----- The Source and PropagationEffects are copied upon instanciation. Examples -------- >>> model = sncosmo.Model(source='hsiao') """ def __init__(self, source, effects=None, effect_names=None, effect_frames=None): # Set parameter names, initial values (inital values set to zero) self._param_names = ['z', 't0'] self.param_names_latex = ['z', 't_0'] self._parameters = np.zeros(2, dtype=float) # Set source and add source parameter names self._source = get_source(source, copy=True) self._param_names.extend(self._source.param_names) self.param_names_latex.extend(self._source.param_names_latex) # Add PropagationEffects self._effects = [] self._effect_names = [] self._effect_frames = [] if (effects is not None or effect_names is not None or effect_frames is not None): try: same_length = (len(effects) == len(effect_names) and len(effects) == len(effect_frames)) except TypeError: raise TypeError('effects, effect_names, and effect_frames ' 'should all be iterables.') if not same_length: raise ValueError('effects, effect_names and effect_frames ' 'must have matching lengths') for effect, name, frame in zip(effects, effect_names, effect_frames): self._add_effect_partial(effect, name, frame) # sync self._sync_parameter_arrays() self._update_description() def add_effect(self, effect, name, frame): """ Add a PropagationEffect to the model. Parameters ---------- effect : `~sncosmo.PropagationEffect` Propagation effect. name : str Name of the effect. frame : {'rest', 'obs', 'free'} """ self._add_effect_partial(effect, name, frame) self._sync_parameter_arrays() self._update_description() @property def source(self): """The Source instance.""" return self._source @property def effect_names(self): """Names of propagation effects (list of str).""" return self._effect_names @property def effects(self): """List of constituent propagation effects.""" return self._effects def _add_effect_partial(self, effect, name, frame): """Like 'add effect', but don't sync parameter arrays""" if not isinstance(effect, PropagationEffect): raise TypeError('effect is not a PropagationEffect') if frame not in ['rest', 'obs', 'free']: raise ValueError("frame must be one of: {'rest', 'obs', 'free'}") self._effects.append(cp(effect)) self._effect_names.append(name) self._effect_frames.append(frame) # for 'free' effects, add a redshift parameter if frame == 'free': self._param_names.append(name + 'z') self.param_names_latex.append('{\\rm ' + name + '}\\,z') # add all of this effect's parameters for param_name in effect.param_names: self._param_names.append(name + param_name) self.param_names_latex.append('{\\rm ' + name + '}\\,' + param_name) def _sync_parameter_arrays(self): """Synchronize parameter names and parameter arrays between the aggregated parameters and those of the individual source and effects. This is a bit tricksy, pay attention! After this, self._parameters holds all the model parameters. The source._parameters and effect._parameters arrays (for each effect) are changed to reference self._parameters. This works because ``B = A[start:stop]`` on numpy arrays makes ``B`` a reference to a block of memory in ``A``. We take advantage of this to make the model and it's components reference the same block of memory, so that updates to the model's parameters are automatically reflected in the components. This assumes that we are in a state where self._effects and self._effect_frames have been set, and self._parameters is an iterable. """ # save a reference to old parameter values, in case there are # effect redshifts that have been set. old_parameters = self._parameters # Calculate total length of model's parameter array l = 2 + len(self._source._parameters) for effect, frame in zip(self._effects, self._effect_frames): l += (frame == 'free') + len(effect._parameters) # allocate new array (zeros so that new 'free' effects redshifts # initialize to 0) self._parameters = np.zeros(l, dtype=float) # copy old parameters: we do this to make sure we copy # non-default values of any parameters that the model alone # holds, such as z, t0 and effect redshifts. self._parameters[0:len(old_parameters)] = old_parameters # cross-reference source's parameters pos = 2 l = len(self._source._parameters) self._parameters[pos:pos+l] = self._source._parameters # copy self._source._parameters = self._parameters[pos:pos+l] # reference pos += l # initialize a list of ints that keeps track of where the redshift # parameter of each effect is. Value is 0 if effect_frame is not 'free' self._effect_zindicies = [] # for each effect, cross-reference the effect's parameters for i in range(len(self._effects)): effect = self._effects[i] # for 'free' effects, add a redshift parameter if self._effect_frames[i] == 'free': self._effect_zindicies.append(pos) pos += 1 else: self._effect_zindicies.append(-1) # add all of this effect's parameters l = len(effect._parameters) self._parameters[pos:pos+l] = effect._parameters # copy effect._parameters = self._parameters[pos:pos+l] # reference pos += l def _update_description(self): # Make a name for myself. We have to watch out for None values here. # If all constituents are None, name is None. Otherwise, replace # None's with '?' names = [self._source.name] + self._effect_names if all([name is None for name in names]): self.description = None else: names = ['?' if name is None else name for name in names] self.description = '+'.join(names) def mintime(self): """Minimum observer-frame time at which the model is defined.""" return (self._parameters[1] + (1. + self._parameters[0]) * self._source.minphase()) def maxtime(self): """Maximum observer-frame time at which the model is defined.""" return (self._parameters[1] + (1. + self._parameters[0]) * self._source.maxphase()) def minwave(self): """Minimum observer-frame wavelength of the model.""" source_shift = (1. + self._parameters[0]) max_minwave = self._source.minwave() * source_shift for effect, frame, zindex in zip(self._effects, self._effect_frames, self._effect_zindicies): effect_minwave = effect.minwave() if frame == 'rest': effect_minwave *= source_shift elif frame == 'free': effect_minwave *= (1. + self._parameters[zindex]) if effect_minwave > max_minwave: max_minwave = effect_minwave return max_minwave def maxwave(self): """Maximum observer-frame wavelength of the model.""" source_shift = (1. + self._parameters[0]) min_maxwave = self._source.maxwave() * source_shift for effect, frame, zindex in zip(self._effects, self._effect_frames, self._effect_zindicies): effect_maxwave = effect.maxwave() if frame == 'rest': effect_maxwave *= source_shift elif frame == 'free': effect_maxwave *= (1. + self._parameters[zindex]) if effect_maxwave < min_maxwave: min_maxwave = effect_maxwave return min_maxwave def _baseflux(self, time, wave): """Array flux function.""" a = 1. / (1. + self._parameters[0]) phase = (time - self._parameters[1]) * a restwave = wave * a # Note that below we multiply by the scale factor to conserve # bolometric luminosity. f = a * self._source._flux(phase, restwave) return f def _flux(self, time, wave): """Array flux function.""" a = 1. / (1. + self._parameters[0]) obsphase = (time - self._parameters[1]) restphase = obsphase * a restwave = wave * a # Note that below we multiply by the scale factor to conserve # bolometric luminosity. f = a * self._source._flux(restphase, restwave) # Pass the flux through the PropagationEffects. for effect, frame, zindex in zip(self._effects, self._effect_frames, self._effect_zindicies): if frame == 'obs': effect_wave = wave effect_phase = obsphase elif frame == 'rest': effect_wave = restwave effect_phase = restphase else: # frame == 'free' effect_a = 1. / (1. + self._parameters[zindex]) effect_wave = wave * effect_a effect_phase = obsphase * effect_a try: f = effect.propagate(effect_wave, f, phase=effect_phase) except TypeError: f = effect.propagate(effect_wave, f) return f def flux(self, time, wave): """The spectral flux density at the given time and wavelength values. Parameters ---------- time : float or list_like Time(s) in days. If `None` (default), the times corresponding to the native phases of the model are used. wave : float or list_like Wavelength(s) in Angstroms. If `None` (default), the native wavelengths of the model are used. Returns ------- flux : float or `~numpy.ndarray` Spectral flux density values in ergs / s / cm^2 / Angstrom. """ time = np.asarray(time) wave = np.asarray(wave) # Check wavelength values if np.any(wave < self.minwave()) or np.any(wave > self.maxwave()): raise ValueError('requested wavelength value(s) outside ' 'model range') # Get the flux try: f = self._flux(time, wave) except ValueError as e: _check_for_fitpack_error(e, time, 'time') _check_for_fitpack_error(e, wave, 'wave') raise e # Return array according to dimension of inputs. if np.isscalar(time) or time.ndim == 0: if np.isscalar(wave) or wave.ndim == 0: return f[0, 0] return f[0, :] return f # ---------------------------------------------------------------------- # Bandpass-related functions def bandoverlap(self, band, z=None): """Return True if model dispersion range fully overlaps the band. Parameters ---------- band : `~sncosmo.Bandpass`, str or list_like Bandpass, name of bandpass in registry, or list or array thereof. z : float or list_like, optional If given, evaluate the overlap when the model is at the given redshifts. If `None`, use the model redshift. Returns ------- overlap : bool or `~numpy.ndarray` """ band = np.asarray(band) if z is None: z = self._parameters[0] z = np.asarray(z) ndim = (band.ndim, z.ndim) band = band.ravel() z = z.ravel() overlap = np.empty((len(band), len(z)), dtype=bool) shift = (1. + z)/(1+self._parameters[0]) for i, b in enumerate(band): b = get_bandpass(b) overlap[i, :] = ((b.minwave() > self.minwave() * shift) & (b.maxwave() < self.maxwave() * shift)) if ndim == (0, 0): return overlap[0, 0] if ndim[1] == 0: return overlap[:, 0] return overlap def bandflux(self, band, time, zp=None, zpsys=None): """Flux through the given bandpass(es) at the given time(s). Default return value is flux in photons / s / cm^2. If zp and zpsys are given, flux(es) are scaled to the requested zeropoints. Parameters ---------- band : str or list_like Name(s) of Bandpass(es) in registry. time : float or list_like Time(s) in days. zp : float or list_like, optional If given, zeropoint to scale flux to (must also supply ``zpsys``). If not given, flux is not scaled. zpsys : str or list_like, optional Name of a magnitude system in the registry, specifying the system that ``zp`` is in. Returns ------- bandflux : float or `~numpy.ndarray` Flux in photons / s /cm^2, unless `zp` and `zpsys` are given, in which case flux is scaled so that it corresponds to the requested zeropoint. Return value is `float` if all input parameters are scalars, `~numpy.ndarray` otherwise. """ try: return _bandflux(self, band, time, zp, zpsys) except ValueError as e: _check_for_fitpack_error(e, time, 'time') raise e def _bandflux_rcov(self, band, time): """Relative covariance in given bandpass and times. Parameters ---------- band : str or list_like Name(s) of Bandpass(es) in registry. time : float or list_like Time(s) in days. Must be in ascending order. """ a = 1. / (1. + self._parameters[0]) # convert to 1-d arrays time, band = np.broadcast_arrays(time, band) ndim = time.ndim # save input ndim for return val time = np.atleast_1d(time) band = np.atleast_1d(band) # Convert `band` to an array of rest-frame bands restband = np.empty(len(time), dtype='object') for b in set(band): mask = band == b b = get_bandpass(b) restband[mask] = b.shifted(a) phase = (time - self._parameters[1]) * a # Note that not all sources have this method. The idea # is that this will automatically fail if the method doesn't exist # for self._source. rcov = self._source.bandflux_rcov(restband, phase) if ndim == 0: return rcov[0, 0] return rcov def bandfluxcov(self, band, time, zp=None, zpsys=None): """Like bandflux(), but also returns model covariance on values. Parameters ---------- band : `~sncosmo.bandpass` or str or list_like Bandpass(es) or name(s) of bandpass(es) in registry. time : float or list_like time(s) in days. zp : float or list_like, optional If given, zeropoint to scale flux to. if `none` (default) flux is not scaled. zpsys : `~sncosmo.magsystem` or str (or list_like), optional Determines the magnitude system of the requested zeropoint. cannot be `none` if `zp` is not `none`. Returns ------- bandflux : float or `~numpy.ndarray` Model bandfluxes. cov : float or `~numpy.array` Covariance on ``bandflux``. If ``bandflux`` is an array, this will be a 2-d array. """ f = self.bandflux(band, time, zp=zp, zpsys=zpsys) rcov = self._bandflux_rcov(band, time) if isinstance(f, np.ndarray): cov = f * rcov * f[:, np.newaxis] else: cov = f * rcov * f return f, cov def bandmag(self, band, magsys, time): """Magnitude at the given time(s) through the given bandpass(es), and for the given magnitude system(s). Parameters ---------- band : str or list_like Name(s) of bandpass in registry. magsys : str or list_like Name(s) of `~sncosmo.MagSystem` in registry. time : float or list_like Observer-frame time(s) in days. Returns ------- mag : float or `~numpy.ndarray` Magnitude for each item in time, band, magsys. The return value is a float if all parameters are not interables. The return value is an `~numpy.ndarray` if any are interable. """ return _bandmag(self, band, magsys, time) def color(self, band1, band2, magsys, time): """band1 - band2 color at the given time(s) through the given pair of bandpasses, and for the given magnitude system. Parameters ---------- band1 : str Name of first bandpass in registry. band2 : str Name of second bandpass in registry. magsys : str Name of `~sncosmo.MagSystem` in registry. time : float or list_like Observer-frame time(s) in days. Returns ------- mag : float or `~numpy.ndarray` Color for each item in time, band, magsys. The return value is a float if all parameters are not iterables. The return value is an `~numpy.ndarray` if phase is iterable. """ band1_isiterable = isiterable(band1) and not isinstance(band1, str) band2_isiterable = isiterable(band2) and not isinstance(band2, str) if band1_isiterable or band2_isiterable: raise TypeError("Band arguments must be scalars.") if (isiterable(magsys) and not isinstance(magsys, str)): raise TypeError("Magnitude system argument must be scalar.") return (self.bandmag(band1, magsys, time) - self.bandmag(band2, magsys, time)) def source_peakmag(self, band, magsys, sampling=1.0): """Peak apparent magnitude of source in a rest-frame bandpass. Note that this is the peak magnitude of just the *source* component of the model, not including effects such as dust. Parameters ---------- band : str or `~sncosmo.Bandpass` Bandpass or name of bandpass in registry. magsys : str or `~sncosmo.MagSystem` Magnitude system or name of magnitude system in registry. sampling : float, optional Sampling in rest-frame days used to find the peak of the light curve. Returns ------- float Peak apparent magnitude of just the source component of the model. """ return self._source.peakmag(band, magsys, sampling=sampling) def set_source_peakmag(self, m, band, magsys, sampling=1.0): """Set the amplitude of the source component of the model according to a peak apparent magnitude. Note that this is the peak magnitude of just the *source* component of the model, not including effects such as dust. Parameters ---------- m : float Desired apparent magnitude. band : str or `~sncosmo.Bandpass` Bandpass or name of bandpass in registry. magsys : str or `~sncosmo.MagSystem` Magnitude system or name of magnitude system in registry. sampling : float, optional Sampling in rest-frame days used to find the peak of the light curve. Default is 1.0. """ self._source.set_peakmag(m, band, magsys, sampling=sampling) def source_peakabsmag(self, band, magsys, sampling=1.0, cosmo=cosmology.WMAP9): """Peak absolute magnitude of the source in rest-frame bandpass. Note that this is the peak absolute magnitude of just the *source* component of the model, not including effects such as dust. Parameters ---------- band : str or `~sncosmo.Bandpass` Bandpass or name of bandpass in registry. magsys : str or `~sncosmo.MagSystem` Magnitude system or name of magnitude system in registry. sampling : float, optional Sampling in rest-frame days used to find the peak of the light curve. Default is 1.0. cosmo : astropy Cosmology, optional Instance of a cosmology from ``astropy.cosmology``, used to calculate distance modulus, given the model's redshift. Default is WMAP9. Returns ------- float Peak absolute magnitude of just the source component of the model. """ return (self._source.peakmag(band, magsys, sampling=sampling) - cosmo.distmod(self._parameters[0]).value) def set_source_peakabsmag(self, absmag, band, magsys, sampling=1.0, cosmo=cosmology.WMAP9): """Set the amplitude of the source component of the model according to the desired absolute magnitude in the specified band. Parameters ---------- absmag : float Desired absolute magnitude. band : str or `~sncosmo.Bandpass` Bandpass or name of bandpass in registry. magsys : str or `~sncosmo.MagSystem` Magnitude system or name of magnitude system in registry. sampling : float, optional Sampling in rest-frame days used to find the peak of the light curve. Default is 1.0. cosmo : astropy Cosmology, optional Instance of a cosmology from ``astropy.cosmology``, used to calculate distance modulus, given the model's redshift. Default is WMAP9. """ if self._parameters[0] <= 0.: raise ValueError('absolute magnitude undefined when z<=0.') m = absmag + cosmo.distmod(self._parameters[0]).value self._source.set_peakmag(m, band, magsys, sampling=sampling) def _headsummary(self): head = "<{0:s} at 0x{1:x}>".format(self.__class__.__name__, id(self)) s = 'source:\n' + self._source._headsummary() summaries = [head, s.replace('\n', '\n ')] for effect, name, frame in zip(self._effects, self._effect_names, self._effect_frames): s = ('effect (name={0} frame={1}):\n{2}' .format(repr(name), repr(frame), effect._headsummary())) summaries.append(s.replace('\n', '\n ')) return '\n'.join(summaries) def __copy__(self): new = Model(self._source, effects=self._effects, effect_names=self._effect_names, effect_frames=self._effect_frames) new._parameters[:] = self._parameters return new def __deepcopy__(self, memo): return cp(self) class PropagationEffect(_ModelBase): """Abstract base class for propagation effects. Derived classes must define _minwave (float), _maxwave (float). They may also define _minphase (float), and _maxphase (float). """ __metaclass__ = abc.ABCMeta def minwave(self): return self._minwave def maxwave(self): return self._maxwave def minphase(self): try: return self._minphase except AttributeError: return np.nan def maxphase(self): try: return self._maxphase except AttributeError: return np.nan @abc.abstractmethod def propagate(self, wave, flux, phase=None): pass def _headsummary(self): summary = """\ class : {0} wavelength range: [{1:.6g}, {2:.6g}] Angstroms phase range : [{3:.2g}, {4:.2g}]"""\ .format(self.__class__.__name__, self._minwave, self._maxwave, self.minphase(), self.maxphase()) return dedent(summary) class CCM89Dust(PropagationEffect): """Cardelli, Clayton, Mathis (1989) extinction model dust.""" _param_names = ['ebv', 'r_v'] param_names_latex = ['E(B-V)', 'R_V'] _minwave = 1000. _maxwave = 33333.33 def __init__(self): self._parameters = np.array([0., 3.1]) def propagate(self, wave, flux, phase=None): """Propagate the flux.""" ebv, r_v = self._parameters return extinction.apply(extinction.ccm89(wave, ebv * r_v, r_v), flux) class OD94Dust(PropagationEffect): """O'Donnell (1994) extinction model dust.""" _param_names = ['ebv', 'r_v'] param_names_latex = ['E(B-V)', 'R_V'] _minwave = 909.09 _maxwave = 33333.33 def __init__(self): self._parameters = np.array([0., 3.1]) def propagate(self, wave, flux, phase=None): """Propagate the flux.""" ebv, r_v = self._parameters return extinction.apply(extinction.odonnell94(wave, ebv * r_v, r_v), flux) class F99Dust(PropagationEffect): """Fitzpatrick (1999) extinction model dust with fixed R_V.""" _minwave = 909.09 _maxwave = 60000. def __init__(self, r_v=3.1): self._param_names = ['ebv'] self.param_names_latex = ['E(B-V)'] self._parameters = np.array([0.]) self._r_v = r_v self._f = extinction.Fitzpatrick99(r_v=r_v) def propagate(self, wave, flux, phase=None): """Propagate the flux.""" ebv = self._parameters[0] return extinction.apply(self._f(wave, ebv * self._r_v), flux) class G10(PropagationEffect): """Guy (2010) SNe Ia non-coherent scattering. Implementation is done following arxiv:1209.2482.""" _param_names = ['L0', 'F0', 'F1', 'dL'] param_names_latex = [r'\lambda_0', 'F_0', 'F_1', 'd_L'] def __init__(self, SALTsource): """Initialize G10 class.""" self._parameters = np.array([2157.3, 0.0, 1.08e-4, 800]) self._colordisp = SALTsource._colordisp self._minwave = SALTsource.minwave() self._maxwave = SALTsource.maxwave() self._seed = np.random.SeedSequence() def compute_sigma_nodes(self): """Computes the sigma nodes.""" L0, F0, F1, dL = self._parameters lam_nodes = np.arange(self._minwave, self._maxwave, dL) if lam_nodes.max() < self._maxwave: lam_nodes = np.append(lam_nodes, self._maxwave) siglam_values = self._colordisp(lam_nodes) siglam_values[lam_nodes < L0] *= (1 + (lam_nodes[lam_nodes < L0] - L0) * F0 ) siglam_values[lam_nodes > L0] *= (1 + (lam_nodes[lam_nodes > L0] - L0) * F1 ) return lam_nodes, siglam_values def propagate(self, wave, flux): """Propagate the effect to the flux.""" # Draw the scattering lam_nodes, siglam_values = self.compute_sigma_nodes() siglam_values *= np.random.default_rng( self._seed).normal(size=len(lam_nodes)) magscat = sine_interp(wave, lam_nodes, siglam_values) return flux * 10**(-0.4 * magscat) class C11(PropagationEffect): """C11 scattering effect for sncosmo. Use COV matrix between the vUBVRI bands from N. Chottard thesis. Implementation is done following arxiv:1209.2482.""" _param_names = ["CvU", 'Sf'] param_names_latex = [r"\rho_\mathrm{vU}", 'S_f'] _minwave = 2000 _maxwave = 11000 def __init__(self): """Initialise C11 class.""" self._parameters = np.array([0., 1.3]) # vUBVRI lambda eff self._lam_nodes = np.array([2500.0, 3560.0, 4390.0, 5490.0, 6545.0, 8045.0]) # vUBVRI correlation matrix extract from SNANA from N.Chotard thesis self._corr_matrix = np.array( [ [+1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000], [0.000000, +1.000000, -0.118516, -0.768635, -0.908202, -0.219447], [0.000000, -0.118516, +1.000000, +0.570333, -0.238470, -0.888611], [0.000000, -0.768635, +0.570333, +1.000000, +0.530320, -0.399538], [0.000000, -0.908202, -0.238470, +0.530320, +1.000000, +0.490134], [0.000000, -0.219447, -0.888611, -0.399538, +0.490134, +1.000000] ] ) # vUBVRI sigma self._variance = np.array([0.5900, 0.06001, 0.040034, 0.050014, 0.040017, 0.080007]) self._seed = np.random.SeedSequence() def build_cov(self): CvU, Sf = self._parameters cov_matrix = self._corr_matrix.copy() # Set up the vU correlation cov_matrix[0, 1:] = CvU * self._corr_matrix[1, 1:] cov_matrix[1:, 0] = CvU * self._corr_matrix[1:, 1] # Convert corr to cov cov_matrix *= np.outer(self._variance, self._variance) # Rescale covariance as in arXiv:1209.2482 cov_matrix *= Sf return cov_matrix def propagate(self, wave, flux): """Propagate the effect to the flux.""" cov_matrix = self.build_cov() # Draw the scattering siglam_values = np.random.default_rng( self._seed).multivariate_normal(np.zeros(len(self._lam_nodes)), cov_matrix) inf_mask = wave <= self._lam_nodes[0] sup_mask = wave >= self._lam_nodes[-1] magscat = np.zeros(len(wave)) magscat[inf_mask] = siglam_values[0] magscat[sup_mask] = siglam_values[-1] magscat[~inf_mask & ~sup_mask] = sine_interp( wave[~inf_mask & ~sup_mask], self._lam_nodes, siglam_values) return flux * 10**(-0.4 * magscat) sncosmo-2.12.1/sncosmo/photdata.py000066400000000000000000000175511476435666400171530ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Convenience functions for photometric data.""" import copy from collections import OrderedDict import numpy as np from astropy.table import Table from .bandpasses import get_bandpass from .magsystems import get_magsystem from .utils import alias_map __all__ = ['select_data'] PHOTDATA_ALIASES = OrderedDict([ ('time', {'time', 'date', 'jd', 'mjd', 'mjdobs', 'mjd_obs'}), ('band', {'band', 'bandpass', 'filter', 'flt'}), ('flux', {'flux', 'f'}), ('fluxerr', {'fluxerr', 'fe', 'fluxerror', 'flux_error', 'flux_err'}), ('zp', {'zp', 'zpt', 'zeropoint', 'zero_point', 'zeropt'}), ('zpsys', {'zpsys', 'zpmagsys', 'magsys'}), ('fluxcov', {'cov', 'covar', 'covariance', 'covmat', 'fluxcov'}) ]) PHOTDATA_REQUIRED_ALIASES = ('time', 'band', 'flux', 'fluxerr', 'zp', 'zpsys') class PhotometricData(object): """Internal standardized representation of photometric data table. Has attributes ``time``, ``band``, ``flux``, ``fluxerr``, ``zp`` and ``zpsys``, which are all numpy arrays of the same length sorted by ``time``. ``band`` is an array of Bandpass objects. This is intended for use within sncosmo; its implementation may change without warning in future versions. Has attribute ``fluxcov`` which may be ``None``. Parameters ---------- data : `~astropy.table.Table`, dict, `~numpy.ndarray` Astropy Table, dictionary of arrays or structured numpy array containing the "correct" column names. """ def __init__(self, data): # get column names in input data if isinstance(data, Table): colnames = data.colnames elif isinstance(data, np.ndarray): colnames = data.dtype.names elif isinstance(data, dict): colnames = data.keys() else: raise ValueError('unrecognized data type') mapping = alias_map(colnames, PHOTDATA_ALIASES, required=PHOTDATA_REQUIRED_ALIASES) self.time = np.asarray(data[mapping['time']]) # ensure self.band contains Bandpass objects. (We could check # if the original array already contains all bandpass objects, # but constructing a new array is simpler.) band_orig = data[mapping['band']] self.band = np.empty(len(band_orig), dtype=object) for i in range(len(band_orig)): self.band[i] = get_bandpass(band_orig[i]) self.flux = np.asarray(data[mapping['flux']]) self.fluxerr = np.asarray(data[mapping['fluxerr']]) self.zp = np.asarray(data[mapping['zp']]) self.zpsys = np.asarray(data[mapping['zpsys']]) self.fluxcov = (np.asarray(data[mapping['fluxcov']]) if 'fluxcov' in mapping else None) # ensure columns are equal length if isinstance(data, dict): if not (len(self.time) == len(self.band) == len(self.flux) == len(self.fluxerr) == len(self.zp) == len(self.zpsys)): raise ValueError("unequal column lengths") # handle covariance if present if self.fluxcov is not None: # check shape OK n = len(self.time) if self.fluxcov.shape != (n, n): raise ValueError( "Flux covariance must be shape (N, N). Did you slice " "the data? Use ``sncosmo.select_data(data, mask)`` in " "place of ``data[mask]`` to properly slice covariance.") def sort_by_time(self): if not np.all(np.ediff1d(self.time) >= 0.0): idx = np.argsort(self.time) self.time = self.time[idx] self.band = self.band[idx] self.flux = self.flux[idx] self.fluxerr = self.fluxerr[idx] self.zp = self.zp[idx] self.zpsys = self.zpsys[idx] self.fluxcov = (None if self.fluxcov is None else self.fluxcov[np.ix_(idx, idx)]) def __len__(self): return len(self.time) def __getitem__(self, key): newdata = copy.copy(self) newdata.time = self.time[key] newdata.band = self.band[key] newdata.flux = self.flux[key] newdata.fluxerr = self.fluxerr[key] newdata.zp = self.zp[key] newdata.zpsys = self.zpsys[key] newdata.fluxcov = (None if self.fluxcov is None else self.fluxcov[np.ix_(key, key)]) return newdata def normalized(self, zp=25., zpsys='ab'): """Return a copy of the data with all flux and fluxerr values normalized to the given zeropoint. """ factor = self._normalization_factor(zp, zpsys) newdata = copy.copy(self) newdata.flux = factor * self.flux newdata.fluxerr = factor * self.fluxerr newdata.zp = np.full(len(self), zp, dtype=np.float64) newdata.zpsys = np.full(len(self), zpsys, dtype=np.array(zpsys).dtype) if newdata.fluxcov is not None: newdata.fluxcov = factor * factor[:, None] * self.fluxcov return newdata def normalized_flux(self, zp=25., zpsys='ab'): return self._normalization_factor(zp, zpsys) * self.flux def _normalization_factor(self, zp, zpsys): """Factor such that multiplying by this amount brings all fluxes onto the given zeropoint and zeropoint system.""" normmagsys = get_magsystem(zpsys) factor = np.empty(len(self), dtype=float) for b in set(self.band.tolist()): mask = self.band == b bandfactor = 10.**(0.4 * (zp - self.zp[mask])) bandzpsys = self.zpsys[mask] for ms in set(bandzpsys): mask2 = bandzpsys == ms ms = get_magsystem(ms) bandfactor[mask2] *= (ms.zpbandflux(b) / normmagsys.zpbandflux(b)) factor[mask] = bandfactor return factor def photometric_data(data): if isinstance(data, PhotometricData): return data else: return PhotometricData(data) def select_data(data, index): """Convenience function for indexing photometric data with covariance. This is like ``data[index]`` on an astropy Table, but handles covariance columns correctly. Parameters ---------- data : `~astropy.table.Table` Table of photometric data. index : slice or array or int Row selection to apply to table. Returns ------- `~astropy.table.Table` Examples -------- We have a small table of photometry with a covariance column and we want to select some rows based on a mask: >>> data = Table([[1., 2., 3.], ... ['a', 'b', 'c'], ... [[1.1, 1.2, 1.3], ... [2.1, 2.2, 2.3], ... [3.1, 3.2, 3.3]]], ... names=['time', 'x', 'cov']) >>> mask = np.array([True, True, False]) Selecting directly on the table, the covariance column is not sliced in each row: it has shape (2, 3) when it should be (2, 2): >>> data[mask] time x cov [3] float64 str1 float64 ------- ---- ---------- 1.0 a 1.1 .. 1.3 2.0 b 2.1 .. 2.3 Using ``select_data`` solves this: >>> sncosmo.select_data(data, mask)
time x cov [2] float64 str1 float64 ------- ---- ---------- 1.0 a 1.1 .. 1.2 2.0 b 2.1 .. 2.2 """ mapping = alias_map(data.colnames, {'fluxcov': PHOTDATA_ALIASES['fluxcov']}) result = data[index] if 'fluxcov' in mapping: colname = mapping['fluxcov'] fluxcov = result[colname][:, index] # replace_column method not available in astropy 1.0 i = result.index_column(colname) del result[colname] result.add_column(fluxcov, i) return result sncosmo-2.12.1/sncosmo/plotting.py000066400000000000000000000442661476435666400172120ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Functions to plot light curve data and models.""" import numpy as np from .bandpasses import get_bandpass from .magsystems import get_magsystem from .models import Model from .photdata import photometric_data from .utils import format_value __all__ = ['plot_lc'] _model_ls = ['-', '--', ':', '-.'] def _add_errorbar(ax, x, y, yerr, filled, markersize=None, color=None): """Add an errorbar to Axes `ax`, allowing an array of markers.""" ax.errorbar(x[filled], y[filled], yerr[filled], ls='None', marker='o', markersize=markersize, color=color) notfilled = ~filled ax.errorbar(x[notfilled], y[notfilled], yerr[notfilled], ls='None', mfc='None', marker='o', markersize=markersize, color=color) def _add_plot(ax, x, y, filled, markersize=None, color=None): ax.plot(x[filled], y[filled], marker='o', markersize=markersize, color=color, ls='None') notfilled = ~filled ax.plot(x[notfilled], y[notfilled], marker='o', mfc='None', markersize=markersize, color=color, ls='None') def plot_lc(data=None, model=None, bands=None, zp=25., zpsys='ab', pulls=True, xfigsize=None, yfigsize=None, figtext=None, model_label=None, errors=None, ncol=2, figtextsize=1., show_model_params=True, tighten_ylim=False, color=None, cmap=None, cmap_lims=(3000., 10000.), fill_data_marker=None, fname=None, fill_percentiles=None, **kwargs): """Plot light curve data or model light curves. Parameters ---------- data : astropy `~astropy.table.Table` or similar, optional Table of photometric data. Must include certain column names. See the "Photometric Data" section of the documentation for required columns. model : `~sncosmo.Model` or list thereof, optional If given, model light curve is plotted. If a string, the corresponding model is fetched from the registry. If a list or tuple of `~sncosmo.Model`, multiple models are plotted. model_label : str or list, optional If given, model(s) will be labeled in a legend in the upper left subplot. Must be same length as model. errors : dict, optional Uncertainty on model parameters. If given, along with exactly one model, uncertainty will be displayed with model parameters at the top of the figure. bands : list, optional List of Bandpasses, or names thereof, to plot. zp : float, optional Zeropoint to normalize the flux in the plot (for the purpose of plotting all observations on a common flux scale). Default is 25. zpsys : str, optional Zeropoint system to normalize the flux in the plot (for the purpose of plotting all observations on a common flux scale). Default is ``'ab'``. pulls : bool, optional If True (and if model and data are given), plot pulls. Pulls are the deviation of the data from the model divided by the data uncertainty. Default is ``True``. figtext : str, optional Text to add to top of figure. If a list of strings, each item is placed in a separate "column". Use newline separators for multiple lines. ncol : int, optional Number of columns of axes. Default is 2. xfigsize, yfigsize : float, optional figure size in inches in x or y. Specify one or the other, not both. Default is to set axes panel size to 3.0 x 2.25 inches. figtextsize : float, optional Space to reserve at top of figure for figtext (if not None). Default is 1 inch. show_model_params : bool, optional If there is exactly one model plotted, the parameters of the model are added to ``figtext`` by default (as two additional columns) so that they are printed at the top of the figure. Set this to False to disable this behavior. tighten_ylim : bool, optional If true, tighten the y limits so that the model is visible (if any models are plotted). color : str or mpl_color, optional Color of data and model lines in each band. Can be any type of color that matplotlib understands. If None (default) a colormap will be used to choose a color for each band according to its central wavelength. cmap : Colormap, optional A matplotlib colormap to use, if color is None. If both color and cmap are None, a default colormap will be used. cmap_lims : (float, float), optional The wavelength limits for the colormap, in Angstroms. Default is (3000., 10000.), meaning that a bandpass with a central wavelength of 3000 Angstroms will be assigned a color at the low end of the colormap and a bandpass with a central wavelength of 10000 will be assigned a color at the high end of the colormap. fill_data_marker : array_like, optional Array of booleans indicating whether to plot a filled or unfilled marker for each data point. Default is all filled markers. fname : str, optional Filename to pass to savefig. If None (default), figure is returned. fill_percentiles : (float, float, float), optional When multiple models are given, the percentiles for a light curve confidence interval. The upper and lower perceniles define a fill between region, and the middle percentile defines a line that will be plotted over the fill between region. kwargs : optional Any additional keyword args are passed to `~matplotlib.pyplot.savefig`. Popular options include ``dpi``, ``format``, ``transparent``. See matplotlib docs for full list. Returns ------- fig : matplotlib `~matplotlib.figure.Figure` Only returned if `fname` is `None`. Display to screen with ``plt.show()`` or save with ``fig.savefig(filename)``. When creating many figures, be sure to close with ``plt.close(fig)``. Examples -------- >>> import sncosmo >>> import matplotlib.pyplot as plt Load some example data: >>> data = sncosmo.load_example_data() Plot the data, displaying to the screen: >>> fig = plot_lc(data) >>> plt.show() Plot a model along with the data: >>> model = sncosmo.Model('salt2') >>> model.set(z=0.5, c=0.2, t0=55100., x0=1.547e-5) >>> sncosmo.plot_lc(data, model=model) .. image:: /pyplots/plotlc_example.png Plot just the model, for selected bands: >>> sncosmo.plot_lc(model=model, ... bands=['sdssg', 'sdssr']) Plot figures on a multipage pdf: >>> from matplotlib.backends.backend_pdf import PdfPages >>> pp = PdfPages('output.pdf') >>> # Do the following as many times as you like: >>> sncosmo.plot_lc(data, fname=pp, format='pdf') >>> # Don't forget to close at the end: >>> pp.close() """ from matplotlib import pyplot as plt from matplotlib import cm from matplotlib.ticker import MaxNLocator from mpl_toolkits.axes_grid1 import make_axes_locatable if data is None and model is None: raise ValueError('must specify at least one of: data, model') if data is None and bands is None: raise ValueError('must specify bands to plot for model(s)') # Get the model(s). if model is None: models = [] elif isinstance(model, (tuple, list)): models = model else: models = [model] if not all([isinstance(m, Model) for m in models]): raise TypeError('model(s) must be Model instance(s)') # Get the model labels if model_label is None: model_labels = [None] * len(models) elif isinstance(model_label, str): model_labels = [model_label] else: model_labels = model_label if len(model_labels) != len(models): raise ValueError('if given, length of model_label must match ' 'that of model') # Color options. if color is None: if cmap is None: cmap = plt.get_cmap('jet_r') # Standardize and normalize data. if data is not None: data = photometric_data(data) data = data.normalized(zp=zp, zpsys=zpsys) if not np.all(np.ediff1d(data.time) >= 0.0): sortidx = np.argsort(data.time) data = data[sortidx] else: sortidx = None # Bands to plot if data is None: bands = set(bands) elif bands is None: bands = set(data.band) else: bands = set(data.band) & set(bands) # ensure bands is a list of Bandpass objects bands = [get_bandpass(b) for b in bands] # filled: used only if data is not None. Guarantee array of booleans if data is not None: if fill_data_marker is None: fill_data_marker = np.ones(data.time.shape, dtype=bool) else: fill_data_marker = np.asarray(fill_data_marker) if fill_data_marker.shape != data.time.shape: raise ValueError("fill_data_marker shape does not match data") if sortidx is not None: # sort like we sorted the data fill_data_marker = fill_data_marker[sortidx] # Build figtext (including model parameters, if there is exactly 1 model). if errors is None: errors = {} if figtext is None: figtext = [] elif isinstance(figtext, str): figtext = [figtext] if len(models) == 1 and show_model_params: model = models[0] lines = [] for i in range(len(model.param_names)): name = model.param_names[i] lname = model.param_names_latex[i] v = format_value(model.parameters[i], errors.get(name), latex=True) lines.append('${0} = {1}$'.format(lname, v)) # Split lines into two columns. n = len(model.param_names) - len(model.param_names) // 2 figtext.append('\n'.join(lines[:n])) figtext.append('\n'.join(lines[n:])) if len(figtext) == 0: figtextsize = 0. # Calculate layout of figure (columns, rows, figure size). We have to # calculate these explicitly because plt.tight_layout() doesn't space the # subplots as we'd like them when only some of them have xlabels/xticks. wspace = 0.6 # All in inches. hspace = 0.3 lspace = 1.0 bspace = 0.7 trspace = 0.2 nrow = (len(bands) - 1) // ncol + 1 if xfigsize is None and yfigsize is None: hpanel = 2.25 wpanel = 3. elif xfigsize is None: hpanel = (yfigsize - figtextsize - bspace - trspace - hspace * (nrow - 1)) / nrow wpanel = hpanel * 3. / 2.25 elif yfigsize is None: wpanel = (xfigsize - lspace - trspace - wspace * (ncol - 1)) / ncol hpanel = wpanel * 2.25 / 3. else: raise ValueError('cannot specify both xfigsize and yfigsize') figsize = (lspace + wpanel * ncol + wspace * (ncol - 1) + trspace, bspace + hpanel * nrow + hspace * (nrow - 1) + trspace + figtextsize) # Create the figure and axes. fig, axes = plt.subplots(nrow, ncol, figsize=figsize, squeeze=False) fig.subplots_adjust(left=lspace / figsize[0], bottom=bspace / figsize[1], right=1. - trspace / figsize[0], top=1. - (figtextsize + trspace) / figsize[1], wspace=wspace / wpanel, hspace=hspace / hpanel) # Write figtext at the top of the figure. for i, coltext in enumerate(figtext): if coltext is not None: xpos = (trspace / figsize[0] + (1. - 2.*trspace/figsize[0]) * (i/len(figtext))) ypos = 1. - trspace / figsize[1] fig.text(xpos, ypos, coltext, va="top", ha="left", multialignment="left") # If there is exactly one model, offset the time axis by the model's t0. if len(models) == 1 and data is not None: toff = models[0].parameters[1] else: toff = 0. # Global min and max of time axis. tmin, tmax = [], [] if data is not None: tmin.append(np.min(data.time) - 10.) tmax.append(np.max(data.time) + 10.) for model in models: tmin.append(model.mintime()) tmax.append(model.maxtime()) tmin = min(tmin) tmax = max(tmax) tgrid = np.linspace(tmin, tmax, int(tmax - tmin) + 1) # Loop over bands waves = [b.wave_eff for b in bands] waves_and_bands = sorted(zip(waves, bands)) for axnum in range(ncol * nrow): row = axnum // ncol col = axnum % ncol ax = axes[row, col] if axnum >= len(waves_and_bands): ax.set_visible(False) ax.set_frame_on(False) continue wave, band = waves_and_bands[axnum] bandname_coords = (0.92, 0.92) bandname_ha = 'right' if color is None: bandcolor = cmap((cmap_lims[1] - wave) / (cmap_lims[1] - cmap_lims[0])) else: bandcolor = color # Plot data if there are any. if data is not None: mask = data.band == band time = data.time[mask] flux = data.flux[mask] fluxerr = data.fluxerr[mask] bandfilled = fill_data_marker[mask] _add_errorbar(ax, time - toff, flux, fluxerr, bandfilled, color=bandcolor, markersize=3.) # Plot model(s) if there are any. lines = [] labels = [] mflux_ranges = [] mfluxes = [] plotci = len(models) > 1 and fill_percentiles is not None for i, model in enumerate(models): if model.bandoverlap(band): mflux = model.bandflux(band, tgrid, zp=zp, zpsys=zpsys) if not plotci: mflux_ranges.append((mflux.min(), mflux.max())) l, = ax.plot(tgrid - toff, mflux, ls=_model_ls[i % len(_model_ls)], marker='None', color=bandcolor) lines.append(l) else: mfluxes.append(mflux) else: # Add a dummy line so the legend displays all models in the # first panel. lines.append(plt.Line2D([0, 1], [0, 1], ls=_model_ls[i % len(_model_ls)], marker='None', color=bandcolor)) labels.append(model_labels[i]) if plotci: lo, med, up = np.percentile(mfluxes, fill_percentiles, axis=0) l, = ax.plot(tgrid - toff, med, marker='None', color=bandcolor) lines.append(l) ax.fill_between(tgrid - toff, lo, up, color=bandcolor, alpha=0.4) # Add a legend, if this is the first axes and there are two # or more models to distinguish between. if row == 0 and col == 0 and model_label is not None: leg = ax.legend(lines, labels, loc='upper right', fontsize='small', frameon=True) bandname_coords = (0.08, 0.92) # Move bandname to upper left bandname_ha = 'left' # Band name in corner text = band.name if band.name is not None else str(band) ax.text(bandname_coords[0], bandname_coords[1], text, color='k', ha=bandname_ha, va='top', transform=ax.transAxes) ax.axhline(y=0., ls='--', c='k') # horizontal line at flux = 0. ax.set_xlim((tmin-toff, tmax-toff)) # If we plotted any models, narrow axes limits so that the model # is visible. if tighten_ylim and len(mflux_ranges) > 0: mfluxmin = min([r[0] for r in mflux_ranges]) mfluxmax = max([r[1] for r in mflux_ranges]) ymin, ymax = ax.get_ylim() ymax = min(ymax, 4. * mfluxmax) ymin = max(ymin, mfluxmin - (ymax - mfluxmax)) ax.set_ylim(ymin, ymax) if col == 0: ax.set_ylabel('flux ($ZP_{{{0}}} = {1}$)' .format(get_magsystem(zpsys).name.upper(), zp)) show_pulls = (pulls and data is not None and len(models) == 1 and models[0].bandoverlap(band)) # steal part of the axes and plot pulls if show_pulls: divider = make_axes_locatable(ax) axpulls = divider.append_axes('bottom', size='30%', pad=0.15, sharex=ax) mflux = models[0].bandflux(band, time, zp=zp, zpsys=zpsys) fluxpulls = (flux - mflux) / fluxerr axpulls.axhspan(ymin=-1., ymax=1., color='0.95') axpulls.axhline(y=0., color=bandcolor) _add_plot(axpulls, time - toff, fluxpulls, bandfilled, markersize=4., color=bandcolor) # Ensure y range is centered at 0. ymin, ymax = axpulls.get_ylim() absymax = max(abs(ymin), abs(ymax)) axpulls.set_ylim((-absymax, absymax)) # Set x limits to global values. axpulls.set_xlim((tmin-toff, tmax-toff)) # Set small number of y ticks so tick labels don't overlap. axpulls.yaxis.set_major_locator(MaxNLocator(5)) # Label the y axis and make sure ylabels align between axes. if col == 0: axpulls.set_ylabel('pull') axpulls.yaxis.set_label_coords(-0.75 * lspace / wpanel, 0.5) ax.yaxis.set_label_coords(-0.75 * lspace / wpanel, 0.5) # Set top axis ticks invisible for l in ax.get_xticklabels(): l.set_visible(False) # Set ax to axpulls in order to adjust plots. bottomax = axpulls else: bottomax = ax # If this axes is one of the last `ncol`, set x label. # Otherwise don't show tick labels. if (len(bands) - axnum - 1) < ncol: if toff == 0.: bottomax.set_xlabel('time') else: bottomax.set_xlabel('time - {0:.2f}'.format(toff)) else: for l in bottomax.get_xticklabels(): l.set_visible(False) if fname is None: return fig plt.savefig(fname, **kwargs) plt.close() sncosmo-2.12.1/sncosmo/registry.py000066400000000000000000000105151476435666400172100ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Public interface functions for registering and retrieving from registries""" from . import bandpasses from . import magsystems from . import models __all__ = ['register_loader', 'register'] def _get_registry(data_class): if issubclass(data_class, bandpasses.Bandpass): return bandpasses._BANDPASSES elif issubclass(data_class, magsystems.MagSystem): return magsystems._MAGSYSTEMS elif issubclass(data_class, models.Source): return models._SOURCES raise ValueError("No registry for type: {}".format(data_class)) def register_loader(data_class, name, func, args=None, version=None, meta=None, force=False): """Register a data reading function. Parameters ---------- data_class : classobj The class of the object that the loader returns. name : str The data identifier. func : callable The function to read in the data. Must accept a name and version keyword argument. args : list, optional Arguments to pass to the function. Default is an empty list. version : str, optional Sub-version of name, if desired. Use formats such as ``'1'``, ``'1.0'``, ``'1.0.0'``, etc. Default is `None`. force : bool, optional Whether to override any existing function if already present. meta : dict, optional Metadata describing this loader. Default is an empty dictionary. """ _get_registry(data_class).register_loader( name, func, args=args, version=version, meta=meta, force=force) def register(instance, name=None, data_class=None, force=False): """Register a class instance. Parameters ---------- instance : object The object to be registered. name : str, optional Identifier. If `None`, the name is taken from the `name` attribute of the instance, if it exists and is a string. data_class : classobj, optional If given, the instance is registered as an instance of this class rather the the class of the instance itself. Use this for registering subclasses when you wish them to be accessible from their superclass. force : bool, optional Whether to override any existing instance of the same name. Note: this may not play well with versioned instances. """ if data_class is None: data_class = instance.__class__ _get_registry(data_class).register(instance, name=name, force=force) def retrieve(data_class, name, version=None): """Retrieve a class instance from a registered identifier. Parameters ---------- data_class : classobj The class of the object requested. name : str Identifier of the specific instance. `name` is case-independent, however, note the following: Internally, names are stored in lowercase. The retrieval is first tried assuming the requested name is also lowercase, then `name` is converted to lowercase. So it should be slightly faster to use lowercase names everywhere. version : str Sub-identifier. If `None`, default to highest or only version. Returns ------- instance : data_class (or subclass thereof) Notes ----- **Precedence** The following are tried in this order: 1. If `name` is already an instance of `data_class` (rather than a string), it is immediately returned. 2. If (`data_class`, `name`) is already in the registry, that instance is returned. 3. If there is a loader defined for (`data_class`, `name`), it is used to create an instance, save it to the registry and return it. 4. An Exception is raised listing the available registered names for the requested data class. **Versioning** There is support for multiple versions of data for the same `name`. 1. If `version` is specified, the registry and its loaders are searched for (`data_class`, `name`, `version`). 2. If `version` is not specified but there are registered loaders for (`data_class`, `name`, `version`), the latest version is used, and both (`data_class`, `name`) and (`data_class`, `name`, `version`) are saved to the registry. "Latest" is defined by string comparision. """ _get_registry(data_class).retrieve(name, version=version) sncosmo-2.12.1/sncosmo/salt2utils.pyx000066400000000000000000000377571476435666400176570ustar00rootroot00000000000000# cython: boundscheck=False, wraparound=False, initializedcheck=False # cython: cdivision=True, auto_pickle=False, language_level=3 # (need auto_pickle=False because we've implemented explicit pickle support via getnewargs() below) """ mimic Grid2DFunction function in salt2 software snfit because it doesn't use spline interpolation; it does bicubic convolution. """ import numpy as np cimport numpy as np from cpython.mem cimport PyMem_Malloc, PyMem_Free from libc.math cimport fabs from libc.string cimport memcpy cdef int find_index_binary(double *values, int n, double x): """Find index i in array such that values[i] <= x < values[i+1]. using binary search. Guaranteed to return values between 0 and n-2 inclusive. """ cdef int lo, hi, mid lo = 0 hi = n mid = n/2 if (x < values[0]): return 0 if (x >= values[n-1]): return n-2 while (hi - lo > 1): if (x >= values[mid]): lo = mid else: hi = mid mid = lo + (hi - lo) / 2 return mid cdef int find_index_unsafe(double *values, int n, double x, int start): """Return i such that values[i] <= x < values[i+1] via linear search, starting from guess `start`. If x == values[n-1], n-2 is returned instead of n-1. This *assumes* that values[0] <= x <= values[n-1], and that 0 <= start <= n-2. If that's not true, this will segfault. """ cdef int i # search up if (x >= values[start]): i = start + 1 while (i < n and x >= values[i]): i += 1 if i == n: i -= 1 return i-1 # search down else: i = start - 1 while (i > -1 and x < values[i]): i -= 1 return i # -1 should never be returned b/c we assume x >= values[0] cdef bint is_strictly_ordered(double[:] x): cdef int i for i in range(1, x.shape[0]): if x[i] <= x[i-1]: return 0 return 1 DEF A = -0.5 DEF B = A + 2.0 DEF C = A + 3.0 cdef double kernval(double xval): cdef double x = fabs(xval) if x > 2.0: return 0.0 if x < 1.0: return x * x * (B * x - C) + 1.0 return A * (-4.0 + x * (8.0 + x * (-5.0 + x))) cdef class BicubicInterpolator(object): """Equivalent of Grid2DFunction in snfit software. Bicubic convolution using kernel (and bilinear when next to edge). The kernel is defined by: x = fabs((distance of point to node)/(distance between nodes)) W(x) = (a+2)*x**3-(a+3)*x**2+1 for x<=1 W(x) = a( x**3-5*x**2+8*x-4) for 12 """ cdef double* xval cdef double* yval cdef double* fval_storage cdef double** fval cdef double xmin cdef double xmax cdef double ymin cdef double ymax cdef int nx cdef int ny def __cinit__(self, x, y, z): cdef: double[:] xc = np.asarray(x, dtype=np.float64) double[:] yc = np.asarray(y, dtype=np.float64) double[:,:] zc = np.asarray(z, dtype=np.float64) int i int j if not (is_strictly_ordered(xc) and is_strictly_ordered(yc)): raise ValueError("x and y values must be strictly increasing") self.nx = xc.shape[0] self.ny = yc.shape[0] # allocate xval self.xval = PyMem_Malloc(self.nx * sizeof(double)) if not self.xval: raise MemoryError() for i in range(self.nx): self.xval[i] = xc[i] self.xmin = self.xval[0] self.xmax = self.xval[self.nx-1] # allocate yval self.yval = PyMem_Malloc(self.ny * sizeof(double)) if not self.yval: raise MemoryError() for i in range(self.ny): self.yval[i] = yc[i] self.ymin = self.yval[0] self.ymax = self.yval[self.ny - 1] # copy values array self.fval_storage = PyMem_Malloc(self.nx * self.ny * sizeof(double)) if not self.fval_storage: raise MemoryError() for i in range(self.nx): for j in range(self.ny): self.fval_storage[i * self.ny + j] = zc[i, j] # allocate fval: pointers to rows of main array self.fval = PyMem_Malloc(self.nx * sizeof(double*)) if not self.fval: raise MemoryError() for i in range(self.nx): self.fval[i] = self.fval_storage + i * self.ny def __dealloc__(self): PyMem_Free(self.xval) PyMem_Free(self.yval) PyMem_Free(self.fval) PyMem_Free(self.fval_storage) def __call__(self, x, y): cdef: int i,j double[:, :] result_view double[:] xc = np.atleast_1d(np.asarray(x, dtype=np.float64)) double[:] yc = np.atleast_1d(np.asarray(y, dtype=np.float64)) double x_i, y_j int ix = 0 int iy = 0 double ax, ay, ay2, dx, dy int nxc = xc.shape[0] int nyc = yc.shape[0] double *wyvec int *iyvec int *yflagvec int xflag double *wx = [0., 0., 0., 0.] # allocate result result = np.empty((nxc, nyc), dtype=np.float64) result_view = result # allocate arrays of y indicies and weights # (could use static storage here for small vectors) wyvec = PyMem_Malloc(nyc * 4 * sizeof(double)) iyvec = PyMem_Malloc(nyc * sizeof(int)) # flags: -1 == "skip, return 0", 0 == "linear", 1 == "cubic" yflagvec = PyMem_Malloc(nyc * sizeof(int)) # find initial indicies by binary search, because they could be # anywhere. if nxc > 0: ix = find_index_binary(self.xval, self.nx, xc[0]) if nyc > 0: iy = find_index_binary(self.yval, self.ny, yc[0]) # fill above three arrays with y value info for j in range(nyc): y_j = yc[j] # if y is out of range, we won't be using the value at all # the inverted comparison here also catches y_j=nan, which # would otherwise cause find_index_unsafe() to return -1 # below, leading to a segfault. if (not (y_j >= self.ymin and y_j <= self.ymax)): yflagvec[j] = -1 else: iy = find_index_unsafe(self.yval, self.ny, y_j, iy) iyvec[j] = iy # if we're too close to border, we will use linear # interpolation # so don't compute weights here if (self.ny < 3 or iy == 0 or iy > (self.ny - 3)): yflagvec[j] = 0 else: # OK to use full cubic interpolation yflagvec[j] = 1 # precompute weights dy = ((self.yval[iy] - y_j) / (self.yval[iy+1] - self.yval[iy])) wyvec[4*j+0] = kernval(dy-1.0) wyvec[4*j+1] = kernval(dy) wyvec[4*j+2] = kernval(dy+1.0) wyvec[4*j+3] = kernval(dy+2.0) # main loop for i in range(nxc): x_i = xc[i] # precompute some stuff for x # again, the inverted comparison shields find_index_unsafe() # from invalid input if (not (x_i >= self.xmin and x_i <= self.xmax)): xflag = -1 else: ix = find_index_unsafe(self.xval, self.nx, x_i, ix) if (self.nx < 3 or ix == 0 or ix > (self.nx - 3)): xflag = 0 else: # OK to use full cubic interpolation xflag = 1 # compute weights dx = ((self.xval[ix] - x_i) / (self.xval[ix+1] - self.xval[ix])) wx[0] = kernval(dx-1.0) wx[1] = kernval(dx) wx[2] = kernval(dx+1.0) wx[3] = kernval(dx+2.0) # innermost loop for j in range(nyc): yflag = yflagvec[j] # out-of-bounds: return 0. if xflag == -1 or yflag == -1: result_view[i, j] = 0.0 else: iy = iyvec[j] y_j = yc[j] # linear interpolation in *both* dimensions if *either* is # too close to the border. This is how the original code # works, so we mimic it here, even though its dumb. if xflag == 0 or yflag == 0: # If either dimension is 1, just return a close-ish # value if self.nx == 1 or self.ny == 1: result_view[i, j] = self.fval[ix][iy] else: ax = ((x_i - self.xval[ix]) / (self.xval[ix+1] - self.xval[ix])) ay = ((y_j - self.yval[iy]) / (self.yval[iy+1] - self.yval[iy])) ay2 = 1.0 - ay result_view[i, j] = ( (1.0 - ax) * (ay2 * self.fval[ix ][iy ] + ay * self.fval[ix ][iy+1]) + ax * (ay2 * self.fval[ix+1][iy ] + ay * self.fval[ix+1][iy+1])) # Full cubic convolution else: result_view[i, j] = ( wx[0] * (wyvec[4*j+0] * self.fval[ix-1][iy-1] + wyvec[4*j+1] * self.fval[ix-1][iy ] + wyvec[4*j+2] * self.fval[ix-1][iy+1] + wyvec[4*j+3] * self.fval[ix-1][iy+2]) + wx[1] * (wyvec[4*j+0] * self.fval[ix ][iy-1] + wyvec[4*j+1] * self.fval[ix ][iy ] + wyvec[4*j+2] * self.fval[ix ][iy+1] + wyvec[4*j+3] * self.fval[ix ][iy+2]) + wx[2] * (wyvec[4*j+0] * self.fval[ix+1][iy-1] + wyvec[4*j+1] * self.fval[ix+1][iy ] + wyvec[4*j+2] * self.fval[ix+1][iy+1] + wyvec[4*j+3] * self.fval[ix+1][iy+2]) + wx[3] * (wyvec[4*j+0] * self.fval[ix+2][iy-1] + wyvec[4*j+1] * self.fval[ix+2][iy ] + wyvec[4*j+2] * self.fval[ix+2][iy+1] + wyvec[4*j+3] * self.fval[ix+2][iy+2])) PyMem_Free(iyvec) PyMem_Free(wyvec) PyMem_Free(yflagvec) return result def __getnewargs__(self): """Return arguments to pass to constructor (to support pickling)""" cdef: np.ndarray[np.double_t, ndim=1] x np.ndarray[np.double_t, ndim=1] y np.ndarray[np.double_t, ndim=2] z x = np.empty(self.nx, dtype=np.float64) y = np.empty(self.ny, dtype=np.float64) z = np.empty((self.nx, self.ny), dtype=np.float64) memcpy(&x[0], self.xval, self.nx * sizeof(double)) memcpy(&y[0], self.yval, self.ny * sizeof(double)) memcpy(&z[0,0], self.fval_storage, self.nx * self.ny * sizeof(double)) return x, y, z cdef double polyval(double *coeffs, int n, double x): "coeffs[0]*x + coeffs[1]*x^2 + ... + coeffs[n-1]*x^ncoeffs""" cdef double out = 0.0 while n > 0: n -= 1 out = x * (coeffs[n] + out) return out # constants used in SALT2ColorLaw DEF SALT2CL_B = 4302.57 # B-band-ish wavelength DEF SALT2CL_V = 5428.55 # V-band-ish wavelength DEF SALT2CL_V_MINUS_B = SALT2CL_V - SALT2CL_B cdef class SALT2ColorLaw(object): """Callable returning extinction in magnitudes for c=1. This is the version 1 extinction law used in SALT2 2.0 (SALT2-2-0) and later. Parameters ---------- wave_range : (float, float) coeffs : list_like Notes ----- From snfit code comments: if(l_B<=l<=l_R): ext = exp(color * constant * (alpha*l + params(0)*l^2 + params(1)*l^3 + ... )) = exp(color * constant * P(l)) where alpha = 1 - params(0) - params(1) - ... if (l > l_R): ext = exp(color * constant * (P(l_R) + P'(l_R) * (l-l_R))) if (l < l_B): ext = exp(color * constant * (P(l_B) + P'(l_B) * (l-l_B))) """ cdef: int ncoeffs double coeffs[7] # can store up to 6 coeffs (should be only 4) double l_lo double l_hi double p_lo double p_hi double pprime_lo double pprime_hi def __cinit__(self, wave_range, coeffs): cdef: int i double wave_lo double wave_hi double[:] ccoeffs = np.asarray(coeffs, dtype=np.float64) double dcoeffs[6] if ccoeffs.shape[0] > 6: raise ValueError("number of coefficients must be equal to or " "less than 6.") # set wave_range wave_lo, wave_hi = wave_range self.l_lo = (wave_lo - SALT2CL_B) / SALT2CL_V_MINUS_B self.l_hi = (wave_hi - SALT2CL_B) / SALT2CL_V_MINUS_B for i in range(ccoeffs.shape[0]): self.coeffs[i+1] = ccoeffs[i] # first coefficient is 'alpha' = 1.0 - sum(other coeffs) self.ncoeffs = ccoeffs.shape[0] + 1 self.coeffs[0] = 1.0 for i in range(1, self.ncoeffs): self.coeffs[0] -= self.coeffs[i] # precompute value of # P(l) = c[0]*l + c[1]*l^2 + c[2]*l^3 + ... at l_lo and l_hi self.p_lo = polyval(self.coeffs, self.ncoeffs, self.l_lo) self.p_hi = polyval(self.coeffs, self.ncoeffs, self.l_hi) # precompute derivative of P(l) at l_lo and l_hi # P'(l) = c[0] + 2*c[1]*l + 3*c[2]*l^2 + ...) for i in range(self.ncoeffs-1): dcoeffs[i] = (i+2) * self.coeffs[i+1] # [2*c[1], 3*c[2], ...] self.pprime_lo = self.coeffs[0] + polyval(dcoeffs, self.ncoeffs-1, self.l_lo) self.pprime_hi = self.coeffs[0] + polyval(dcoeffs, self.ncoeffs-1, self.l_hi) def __call__(self, double[:] wave): cdef: double l int i, n np.ndarray[np.float64_t, ndim=1] out n = wave.shape[0] out = np.empty(n, dtype=np.float64) for i in range(n): l = (wave[i] - SALT2CL_B) / SALT2CL_V_MINUS_B # Blue side if l < self.l_lo: out[i] = self.p_lo + self.pprime_lo * (l - self.l_lo) # in between elif l <= self.l_hi: out[i] = polyval(self.coeffs, self.ncoeffs, l) # red side else: out[i] = self.p_hi + self.pprime_hi * (l - self.l_hi) out[i] = -out[i] return out def __getnewargs__(self): """Return arguments to pass to constructor (to support pickling).""" # Note: an alternative to this would be moving the current __cinit__ # contents to __init__, then defining __getstate__ and __setstate__ # to copy the entire struct contents to a bytearray object and back. # This would avoid the need to do the initialization again, but # seems quite fiddly because the size to copy is unclear: # it depends on the struct layout. # reconstruct input wavelengths wave_lo = self.l_lo * SALT2CL_V_MINUS_B + SALT2CL_B wave_hi = self.l_hi * SALT2CL_V_MINUS_B + SALT2CL_B coeffs = [self.coeffs[i+1] for i in range(self.ncoeffs - 1)] return (wave_lo, wave_hi), coeffs sncosmo-2.12.1/sncosmo/simulation.py000066400000000000000000000205101476435666400175200ustar00rootroot00000000000000"""Tools for simulation of transients.""" import copy from collections import OrderedDict import numpy as np from astropy.cosmology import FlatLambdaCDM from astropy.table import Table from numpy import random from scipy.interpolate import InterpolatedUnivariateSpline as Spline1d from .utils import alias_map __all__ = ['zdist', 'realize_lcs'] WHOLESKY_SQDEG = 4. * np.pi * (180. / np.pi) ** 2 def zdist(zmin, zmax, time=365.25, area=1., ratefunc=lambda z: 1.e-4, cosmo=FlatLambdaCDM(H0=70.0, Om0=0.3)): """Generate a distribution of redshifts. Generates the correct redshift distribution and number of SNe, given the input volumetric SN rate, the cosmology, and the observed area and time. Parameters ---------- zmin, zmax : float Minimum and maximum redshift. time : float, optional Time in days (default is 1 year). area : float, optional Area in square degrees (default is 1 square degree). ``time`` and ``area`` are only used to determine the total number of SNe to generate. ratefunc : callable A callable that accepts a single float (redshift) and returns the comoving volumetric rate at each redshift in units of yr^-1 Mpc^-3. The default is a function that returns ``1.e-4``. cosmo : `~astropy.cosmology.Cosmology`, optional Cosmology used to determine volume. The default is a FlatLambdaCDM cosmology with ``Om0=0.3``, ``H0=70.0``. Examples -------- Loop over the generator: >>> for z in zdist(0.0, 0.25): ... print(z) ... 0.151285827576 0.204078030595 0.201009196731 0.181635472172 0.17896188781 0.226561237264 0.192747368762 This tells us that in one observer-frame year, over 1 square degree, 7 SNe occured at redshifts below 0.35 (given the default volumetric SN rate of 10^-4 SNe yr^-1 Mpc^-3). The exact number is drawn from a Poisson distribution. Generate the full list of redshifts immediately: >>> zlist = list(zdist(0., 0.25)) Define a custom volumetric rate: >>> def snrate(z): ... return 0.5e-4 * (1. + z) ... >>> zlist = list(zdist(0., 0.25, ratefunc=snrate)) """ # Get comoving volume in each redshift shell. z_bins = 100 # Good enough for now. z_binedges = np.linspace(zmin, zmax, z_bins + 1) z_binctrs = 0.5 * (z_binedges[1:] + z_binedges[:-1]) sphere_vols = cosmo.comoving_volume(z_binedges).value shell_vols = sphere_vols[1:] - sphere_vols[:-1] # SN / (observer year) in shell shell_snrate = np.array([shell_vols[i] * ratefunc(z_binctrs[i]) / (1.+z_binctrs[i]) for i in range(z_bins)]) # SN / (observer year) within z_binedges vol_snrate = np.zeros_like(z_binedges) vol_snrate[1:] = np.add.accumulate(shell_snrate) # Create a ppf (inverse cdf). We'll use this later to get # a random SN redshift from the distribution. snrate_cdf = vol_snrate / vol_snrate[-1] snrate_ppf = Spline1d(snrate_cdf, z_binedges, k=1) # Total numbe of SNe to simulate. nsim = vol_snrate[-1] * (time/365.25) * (area/WHOLESKY_SQDEG) for i in range(random.poisson(nsim)): yield float(snrate_ppf(random.random())) OBSERVATIONS_ALIASES = OrderedDict([ ('time', set(['time', 'date', 'jd', 'mjd', 'mjdobs', 'mjd_obs'])), ('band', set(['band', 'bandpass', 'filter', 'flt'])), ('zp', set(['zp', 'zpt', 'zeropoint', 'zero_point'])), ('zpsys', set(['zpsys', 'zpmagsys', 'magsys'])), ('gain', set(['gain'])), ('skynoise', set(['skynoise'])) ]) OBSERVATIONS_REQUIRED_ALIASES = ('time', 'band', 'zp', 'zpsys', 'gain', 'skynoise') def realize_lcs(observations, model, params, thresh=None, trim_observations=False, scatter=True): """Realize data for a set of SNe given a set of observations. Parameters ---------- observations : `~astropy.table.Table` or `~numpy.ndarray` Table of observations. Must contain the following column names: ``band``, ``time``, ``zp``, ``zpsys``, ``gain``, ``skynoise``. model : `sncosmo.Model` The model to use in the simulation. params : list (or generator) of dict List of parameters to feed to the model for realizing each light curve. thresh : float, optional If given, light curves are skipped (not returned) if none of the data points have signal-to-noise greater than ``thresh``. trim_observations : bool, optional If True, only observations with times between ``model.mintime()`` and ``model.maxtime()`` are included in result table for each SN. Default is False. scatter : bool, optional If True, the ``flux`` value of the realized data is calculated by adding a random number drawn from a Normal Distribution with a standard deviation equal to the ``fluxerror`` of the observation to the bandflux value of the observation calculated from model. Default is True. Returns ------- sne : list of `~astropy.table.Table` Table of realized data for each item in ``params``. Notes ----- ``skynoise`` is the image background contribution to the flux measurement error (in units corresponding to the specified zeropoint and zeropoint system). To get the error on a given measurement, ``skynoise`` is added in quadrature to the photon noise from the source. It is left up to the user to calculate ``skynoise`` as they see fit as the details depend on how photometry is done and possibly how the PSF is is modeled. As a simple example, assuming a Gaussian PSF, and perfect PSF photometry, ``skynoise`` would be ``4 * pi * sigma_PSF * sigma_pixel`` where ``sigma_PSF`` is the standard deviation of the PSF in pixels and ``sigma_pixel`` is the background noise in a single pixel in counts. """ RESULT_COLNAMES = ('time', 'band', 'flux', 'fluxerr', 'zp', 'zpsys') lcs = [] # Copy model so we don't mess up the user's model. model = copy.copy(model) # get observations as a Table if not isinstance(observations, Table): if isinstance(observations, np.ndarray): observations = Table(observations) else: raise ValueError("observations not understood") # map column name aliases colname = alias_map(observations.colnames, OBSERVATIONS_ALIASES, required=OBSERVATIONS_REQUIRED_ALIASES) # result dtype used when there are no observations band_dtype = observations[colname['band']].dtype zpsys_dtype = observations[colname['zpsys']].dtype result_dtype = ('f8', band_dtype, 'f8', 'f8', 'f8', zpsys_dtype) for p in params: model.set(**p) # Select times for output that fall within tmin amd tmax of the model if trim_observations: mask = ((observations[colname['time']] > model.mintime()) & (observations[colname['time']] < model.maxtime())) snobs = observations[mask] else: snobs = observations # explicitly detect no observations and add an empty table if len(snobs) == 0: if thresh is None: lcs.append(Table(names=RESULT_COLNAMES, dtype=result_dtype, meta=p)) continue flux = model.bandflux(snobs[colname['band']], snobs[colname['time']], zp=snobs[colname['zp']], zpsys=snobs[colname['zpsys']]) fluxerr = np.sqrt(snobs[colname['skynoise']]**2 + np.abs(flux) / snobs[colname['gain']]) # Scatter fluxes by the fluxerr # np.atleast_1d is necessary here because of an apparent bug in # np.random.normal: when the inputs are both length 1 arrays, # the output is a Python float! if scatter: flux = np.atleast_1d(np.random.normal(flux, fluxerr)) # Check if any of the fluxes are significant if thresh is not None and not np.any(flux/fluxerr > thresh): continue data = [snobs[colname['time']], snobs[colname['band']], flux, fluxerr, snobs[colname['zp']], snobs[colname['zpsys']]] lcs.append(Table(data, names=RESULT_COLNAMES, meta=p)) return lcs sncosmo-2.12.1/sncosmo/snanaio.py000066400000000000000000000440141476435666400167710ustar00rootroot00000000000000from collections import OrderedDict as odict import numpy as np from astropy.io import fits from astropy.table import Table, vstack import yaml __all__ = ['read_snana_ascii', 'read_snana_fits', 'read_snana_simlib'] def read_snana_fits(head_file, phot_file, snids=None, n=None): """Read the SNANA FITS format: two FITS files jointly representing metadata and photometry for a set of SNe. Parameters ---------- head_file : str Filename of "HEAD" ("header") FITS file. phot_file : str Filename of "PHOT" ("photometry") FITS file. snids : list of str, optional If given, only return the single entry with the matching SNIDs. n : int If given, only return the first `n` entries. Returns ------- sne : list of `~astropy.table.Table` Each item in the list is an astropy Table instance. Notes ----- If `head_file` contains a column 'SNID' containing strings, leading and trailing whitespace is stripped from all the values in that column. If `phot_file` contains a column 'FLT', leading and trailing whitespace is stripped from all the values in that column. Examples -------- >>> sne = read_snana_fits('HEAD.fits', 'PHOT.fits') >>> for sn in sne: ... sn.meta # Metadata in an OrderedDict. ... sn['MJD'] # MJD column """ # Should we memmap? Only if we're going to read only a part of the file memmap = (snids is not None or n is not None) # Get metadata for all the SNe head_data = fits.getdata(head_file, 1, view=np.ndarray) phot_data = fits.getdata(phot_file, 1, view=np.ndarray, memmap=memmap) # Strip trailing whitespace characters from SNID. if 'SNID' in head_data.dtype.names: try: head_data['SNID'][:] = np.char.strip(head_data['SNID']) except TypeError: pass # Check which indicies to return. if snids is None and n is None: idx = range(len(head_data)) elif n is None: if 'SNID' not in head_data.dtype.names: raise RuntimeError('Specific snids requested, but head file does' ' not contain SNID column') idx = [] for snid in snids: i = np.flatnonzero(head_data['SNID'] == snid) if len(i) != 1: raise RuntimeError('Unique snid requested, but there are ' '{0:d} matching entries'.format(len(i))) idx.append(i[0]) elif snids is None: idx = range(n) else: raise ValueError("cannot specify both 'snids' and 'n' arguments") # Loop over SNe in HEAD file sne = [] for i in idx: meta = odict(zip(head_data.dtype.names, head_data[i])) j0 = head_data['PTROBS_MIN'][i] - 1 j1 = head_data['PTROBS_MAX'][i] data = phot_data[j0:j1] if 'FLT' in data.dtype.names: data['FLT'][:] = np.char.strip(data['FLT']) sne.append(Table(data, meta=meta, copy=False)) return sne def read_snana_ascii(fname, default_tablename=None): """Read an SNANA-format ascii file. Such files may contain metadata lines and one or more tables. See Notes for a summary of the format. Parameters ---------- fname : str Filename of object to read. default_tablename : str, optional Default tablename, or the string that indicates a table row, when a table starts with 'NVAR:' rather than 'NVAR_TABLENAME:'. array : bool, optional If True, each table is converted to a numpy array. If False, each table is a dictionary of lists (each list is a column). Default is True. Returns ------- meta : OrderedDict Metadata from keywords. tables : dict of `~astropy.table.Table` Tables, indexed by table name. Notes ----- The file can contain one or more tables, as well as optional metadata. Here is an example of the expected format:: META1: a META2: 6 NVAR_SN: 3 VARNAMES: A B C SN: 1 2.0 x SN: 4 5.0 y Behavior: * Any strings ending in a colon (:) are treated as keywords. * The start of a new table is indicated by a keyword starting with 'NVAR'. * If the 'NVAR' is followed by an underscore (e.g., 'NVAR_TABLENAME'), then 'TABLENAME' is taken to be the name of the table. Otherwise the user *must specify* a ``default_tablename``. This is because data rows are identified by the tablename. * After a keyword starting with 'NVAR', the next keyword must be 'VARNAMES'. The strings following give the column names. * Any other keywords anywhere in the file are treated as metadata. The first string after the keyword is treated as the value for that keyword. * **Note:** Newlines are treated as equivalent to spaces; they do not indicate a new row. This is necessary because some SNANA-format files have multiple metadata on a single row or single table rows split over multiple lines, making newline characters meaningless. Examples -------- >>> from io import StringIO # StringIO behaves like a file >>> f = StringIO('META1: a\\n' ... 'META2: 6\\n' ... 'NVAR_SN: 3\\n' ... 'VARNAMES: A B C\\n' ... 'SN: 1 2.0 x\\n' ... 'SN: 4 5.0 y\\n') ... >>> meta, tables = read_snana_ascii(f) The first object is a dictionary of metadata: >>> meta OrderedDict([('META1', 'a'), ('META2', 6)]) The second is a dictionary of all the tables in the file: >>> tables['SN']
array([(1, 2.0, 'x'), (4, 5.0, 'y')], dtype=[('A', '>> meta, tables = read_snana_ascii(f, default_tablename='SN') """ meta = odict() # initialize structure to hold metadata. tables = {} # initialize structure to hold data. if isinstance(fname, str): fh = open(fname, 'r') else: fh = fname words = fh.read().split() fh.close() i = 0 nvar = None tablename = None while i < len(words): word = words[i] # If the word starts with 'NVAR', we are starting a new table. if word.startswith('NVAR'): nvar = int(words[i + 1]) # Infer table name. The name will be used to designate a data row. if '_' in word: pos = word.find('_') + 1 tablename = word[pos:].rstrip(':') elif default_tablename is not None: tablename = default_tablename else: raise ValueError( 'Table name must be given as part of NVAR keyword so ' 'that rows belonging to this table can be identified. ' 'Alternatively, supply the default_tablename keyword.') table = odict() tables[tablename] = table i += 2 # If the word starts with 'VARNAMES', the following `nvar` words # define the column names of the table. elif word.startswith('VARNAMES') or word.startswith('VARLIST'): # Check that nvar is defined and that no column names are defined # for the current table. if nvar is None or len(table) > 0: raise Exception('NVAR must directly precede VARNAMES') # Read the column names for j in range(i + 1, i + 1 + nvar): table[words[j]] = [] i += nvar + 1 # If the word matches the current tablename, we are reading a data row. elif word.rstrip(':') == tablename: for j, colname in enumerate(table.keys()): table[colname].append(words[i + 1 + j]) i += nvar + 1 # Otherwise, we are reading metadata or some comment # If the word ends with ":", it is metadata. elif word[-1] == ':': name = word[:-1] # strip off the ':' if len(words) >= i + 2: try: val = int(words[i + 1]) except ValueError: try: val = float(words[i + 1]) except ValueError: val = words[i + 1] meta[name] = val else: meta[name] = None i += 2 else: # It is some comment; continue onto next word. i += 1 # All values in each column are currently strings. Convert to int or # float if possible. for table in tables.values(): for colname, values in table.items(): try: table[colname] = [int(val) for val in values] except ValueError: try: table[colname] = [float(val) for val in values] except ValueError: pass # All tables are dictionaries. Convert them to Tables for tablename in tables.keys(): tables[tablename] = Table(tables[tablename]) return meta, tables def read_snana_ascii_multi(fnames, default_tablename=None): """Like ``read_snana_ascii()``, but read from multiple files containing the same tables and glue results together into big tables. Parameters ---------- fnames : list of str List of filenames. Returns ------- tables : dictionary of `~astropy.table.Table` Tables indexed by table names. Examples -------- >>> tables = read_snana_ascii_multi(['data1.txt', 'data1.txt']) """ alltables = {} for fname in fnames: meta, tables = read_snana_ascii(fname, default_tablename=default_tablename) for key, table in tables.items(): if key in alltables: alltables[key].append(table) else: alltables[key] = [table] for key in alltables.keys(): alltables[key] = vstack(alltables[key]) return alltables def _parse_meta_from_line(line): """Return dictionary from key, value pairs on a line. Helper function for snana_read_simlib.""" meta = odict() # Find position of all the colons colon_pos = [] i = line.find(':') while i != -1: colon_pos.append(i) i = line.find(':', i+1) # Find position of start of words before colons key_pos = [] for i in colon_pos: j = line.rfind(' ', 0, i) key_pos.append(j+1) # append an extra key position so that we know when to end the last value. key_pos.append(len(line)) # get the keys, values based on positions above. for i in range(len(colon_pos)): key = line[key_pos[i]: colon_pos[i]] val = line[colon_pos[i]+1: key_pos[i+1]].strip() try: val = int(val) except ValueError: try: val = float(val) except ValueError: pass meta[key] = val return meta def read_snana_simlib(fname): """Read an SNANA 'simlib' (simulation library) ascii file. Parameters ---------- fname : str Filename. Returns ------- meta : `OrderedDict` Global meta data, not associated with any one LIBID. If DOCANA is present, it is stored in `meta['DOCUMENTATION']`. observation_sets : `OrderedDict` of `astropy.table.Table` keys are LIBIDs, values are observation sets. Notes ----- * Anything following '#' on each line is ignored as a comment. * Keywords are space separated strings ending wth a colon. * If a line starts with 'LIBID:', the following lines are associated with the value of LIBID, until 'END_LIBID:' is encountered. * While reading a given LIBID, lines starting with 'S' or 'T' keywords are assumed to contain 12 space-separated values after the keyword. These are (1) MJD, (2) IDEXPT, (3) FLT, (4) CCD GAIN, (5) CCD NOISE, (6) SKYSIG, (7) PSF1, (8) PSF2, (9) PSF 2/1 RATIO, (10) ZPTAVG, (11) ZPTSIG, (12) MAG. * Column (2) may represent co-added observations in a '111*1' format. In this case, the 'IDEXPT' column is split at the '*' into 'IDEXPT' and 'NEXPOSE' * Other lines inside a 'LIBID:'/'END_LIBID:' pair are treated as metadata for that LIBID. * Any other keywords outside a 'LIBID:'/'END_LIBID:' pair are treated as global header keywords and are returned in the `meta` dictionary. Examples -------- >>> meta, obs_sets = read_snana_simlib('filename') The second object is a dictionary of astropy Tables indexed by LIBID: >>> obs_sets.keys() [0, 1, 2, 3, 4] Each table (libid) has metadata: >>> obs_sets[0].meta OrderedDict([('LIBID', 0), ('RA', 52.5), ('DECL', -27.5), ('NOBS', 161), ('MWEBV', 0.0), ('PIXSIZE', 0.27)]) Each table has the following columns: >>> obs_sets[0].colnames ['SEARCH', 'MJD', 'IDEXPT', 'FLT', 'CCD_GAIN', 'CCD_NOISE', 'SKYSIG', 'PSF1', 'PSF2', 'PSFRATIO', 'ZPTAVG', 'ZPTSIG', 'MAG'] """ from astropy.table import Table COLNAMES = ['SEARCH', 'MJD', 'IDEXPT', 'FLT', 'CCD_GAIN', 'CCD_NOISE', 'SKYSIG', 'PSF1', 'PSF2', 'PSFRATIO', 'ZPTAVG', 'ZPTSIG', 'MAG'] # Not used yet... if present in header, add to table. SPECIAL = ['FIELD', 'TELESCOPE', 'PIXSIZE'] meta = odict() # global metadata observation_sets = odict() # dictionary of tables indexed by LIBID reading_obsset = False reading_docana = False docana = '' with open(fname, 'r') as infile: for line in infile.readlines(): # strip comments idx = line.find('#') if idx != -1: line = line[0:idx] # split on spaces. words = line.split() if len(words) == 0: continue # If we're not currently reading an obs set, check if this line # is the start of one. If it isn't, update the global metadata. if not reading_obsset: if line[0:6] == 'LIBID:': reading_obsset = True current_meta = _parse_meta_from_line(line) current_data = odict([(key, []) for key in COLNAMES]) else: # If we're currently reading an documentation block, add # it to the DOCANA string. # If we're not, check if this line is the start of one, # then update the global metadata. if reading_docana: if line[0:18] == 'DOCUMENTATION_END:': reading_docana = False # And then skip line meta.update( odict({'DOCUMENTATION': yaml.safe_load(docana)}) ) docana = '' else: docana += line + '\n' else: if line[0:14] == 'DOCUMENTATION:': reading_docana = True # And then skip line else: meta.update(_parse_meta_from_line(line)) # If we are currently reading an obsset... else: # Check for the explicit end of the obs set. if line[0:10] == 'END_LIBID:': reading_obsset = False observation_sets[current_meta['LIBID']] = \ Table(current_data, meta=current_meta) # Sometimes there's not an explicit end, but the next one # starts anyway. elif line[0:6] == 'LIBID:': observation_sets[current_meta['LIBID']] = \ Table(current_data, meta=current_meta) current_meta = _parse_meta_from_line(line) current_data = odict([(key, []) for key in COLNAMES]) # Otherwise, read the line into the current obs set. elif line[0:2] in ['S:', 'T:']: words = line.split() colnames = ['SEARCH', 'MJD', 'IDEXPT', 'FLT', 'CCD_GAIN', 'CCD_NOISE', 'SKYSIG', 'PSF1', 'PSF2', 'PSFRATIO', 'ZPTAVG', 'ZPTSIG', 'MAG'] values = [words[0] == 'S:', float(words[1]), None, words[3], float(words[4]), float(words[5]), float(words[6]), float(words[7]), float(words[8]), float(words[9]), float(words[10]), float(words[11]), float(words[12])] if "*" in words[2]: # 'IDEXPT' can be a co-add of the form: '2063*2' # re-process assuming co-added expsoures # ('IDEXPT' -> 'IDEXPT', 'NEXPOSE' ) colnames.insert(3, "NEXPOSE") if 'NEXPOSE' not in current_data: # add an empty list only on the first data line current_data['NEXPOSE'] = [] values[2] = int(words[2].split('*')[0]) values.insert(3, int(words[2].split('*')[1])) else: values[2] = int(words[2]) for colname, val in zip(colnames, values): current_data[colname].append(val) else: current_meta.update(_parse_meta_from_line(line)) # At the end, check for the case where there's not an explicit end # to the last obs set: if reading_obsset: observation_sets[current_meta['LIBID']] = \ Table(current_data, meta=current_meta) return meta, observation_sets sncosmo-2.12.1/sncosmo/sncosmo.cfg000066400000000000000000000005651476435666400171340ustar00rootroot00000000000000 ## Directory containing SFD (1998) dust maps, with names: ## 'SFD_dust_4096_ngp.fits' and 'SFD_dust_4096_sgp.fits' ## Example: sfd98_dir = /home/user/data/sfd98 # sfd98_dir = None ## Directory where sncosmo will store and read downloaded data resources. ## If None, ASTROPY_CACHE_DIR/sncosmo will be used. ## Example: data_dir = /home/user/data/sncosmo # data_dir = None sncosmo-2.12.1/sncosmo/snfitio.py000066400000000000000000000164251476435666400170210ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Functions for reading formats specific to snfit (SALT2) software. (except lightcurves, which are in io.py). """ import os from collections import OrderedDict import numpy as np from .bandpasses import BandpassInterpolator from .io import _read_salt2 def _collect_scalars(values): """Given a list containing scalars (float or int) collect scalars into a single prefactor. Input list is modified.""" prefactor = 1.0 for i in range(len(values)-1, -1, -1): if isinstance(values[i], (int, float)): prefactor *= values.pop(i) return prefactor def _parse_value(s): try: x = int(s) except ValueError: try: x = float(s) except ValueError: x = s return x def read_cards(fname): cards = OrderedDict() with open(fname, 'r') as f: for line in f: if line[0] != '@': continue words = line.split() key = words[0][1:] # words is at least length 1 tokens = words[1:] if len(tokens) == 0: value = None elif len(tokens) == 1: value = _parse_value(tokens[0]) else: value = [_parse_value(v) for v in tokens] cards[key] = value return cards def read_filterwheel(fname): """Read a single definition from a filterwheel file.""" # find filter in filterwheel file result = {} with open(fname, 'r') as f: for line in f: words = line.split() # each line can have 2 or 3 words. if len(words) not in (2, 3): raise Exception("illegal line in filterwheel file") result[words[0]] = words[-1] return result def read_radial_filterwheel(fname): """Read radially variable filterwheel transmissions. Parameters ---------- fname : str Returns ------- dict of list Dictionary where keys are filter names and values are lists. Each item in the list is a two-tuple giving the radius and filter function (in the form of a SampledFunction). """ # read filter filenames (multiple files per filter) result = {} with open(fname, 'r') as f: for line in f: band, _, bandfname = line.split() if band not in result: result[band] = [] result[band].append(bandfname) return result def read_snfit_bandpass(dirname, filtername): """Read an snfit-format bandpass. The instrument.cards file should contain a ``FILTERS`` keyword. Parameters ---------- dirname : str Directory in which to look for files. Should contain an ``instrument.cards`` file. filtername : str Filter name (typically a single lowercase letter). Returns ------- Bandpass, AggregateBandpass """ # we do this a lot below def expand(x): return os.path.join(dirname, x) cards = read_cards(expand("instrument.cards")) # Check that this is really a bandpass and not a generator. if "FILTERS" not in cards: raise Exception("FILTERS key not in instrument.cards file. For " "radially variable filters (with " "RADIALLY_VARIABLE_FILTER KEY) use " "`read_snfit_bandpass_generator`.") transmissions = [] # scalars or (wave, trans) pairs # required keys (transmissions apply to all filters) for key in ("MIRROR_REFLECTIVITY", "OPTICS_TRANS", "QE", "ATMOSPHERIC_TRANS"): value = cards[key] if type(value) is int or type(value) is float: transmissions.append(value) else: x, y = np.loadtxt(expand(value), unpack=True) transmissions.append((x, y)) # optional key (filter-specific) if "CHROMATIC_CORRECTIONS" in cards: fname = read_filterwheel( expand(cards["CHROMATIC_CORRECTIONS"]))[filtername] x, y = np.loadtxt(expand(fname), unpack=True, skiprows=3) transmissions.append((x, y)) # Read filters fname = read_filterwheel(expand(cards["FILTERS"]))[filtername] x, y = np.loadtxt(expand(fname), unpack=True) transmissions.append((x, y)) # simplify transmissions prefactor = _collect_scalars(transmissions) # if there's only one non-scalar, we can construct a normal # bandpass if len(transmissions) == 1: wave, trans = transmissions[0] return sncosmo.Bandpass(wave, prefactor * trans, wave_unit=u.AA) elif len(transmissions) > 1: return AggregateBandpass(transmissions, prefactor=prefactor) else: raise Exception("bandpass consists only of scalars") def read_snfit_bandpass_interpolator(dirname, filtername, name=None): """Read an snfit-format bandpass or bandpass generator. Parameters ---------- dirname : str Directory in which to look for files. Should contain an ``instrument.cards`` file. filtername : str Filter name (typically a single lowercase letter). Returns ------- BandpassInterpolator """ # we do this a lot below def expand(x): return os.path.join(dirname, x) cards = read_cards(expand("instrument.cards")) transmissions = [] # scalars or (wave, trans) pairs # Check that this is really a generator. if "RADIALLY_VARIABLE_FILTERS" not in cards: raise Exception("RADIALLY_VARIABLE_FILTERS key not in " "instrument.cards file. For non-variable " "filters (with FILTER key) use `read_snfit_bandpass`.") # required keys (transmissions apply to all filters) for key in ("MIRROR_REFLECTIVITY", "OPTICS_TRANS", "QE", "ATMOSPHERIC_TRANS"): value = cards[key] if type(value) is int or type(value) is float: transmissions.append(value) else: x, y = np.loadtxt(expand(value), unpack=True) transmissions.append((x, y)) # optional key: if "CHROMATIC_CORRECTIONS" in cards: corr_fnames = read_filterwheel(expand(cards["CHROMATIC_CORRECTIONS"])) # skip if correction file not defined for this band if filtername in corr_fnames: fname = corr_fnames[filtername] x, y = np.loadtxt(expand(fname), unpack=True, skiprows=3) transmissions.append((x, y)) fnames = read_radial_filterwheel( expand(cards["RADIALLY_VARIABLE_FILTERS"]))[filtername] # read transmission functions at each radius radial_transmissions = [] for fname in fnames: # TODO: re-organize the salt2-format reader. with open(expand(fname), 'r') as f: meta, data = _read_salt2(f) try: r_str = meta["MEASUREMENT_RADIUS"] except KeyError: raise Exception("MEASUREMENT_RADIUS keyword not found in " + os.path.join(dirname, bandfname)) r = float(r_str.split()[0]) # parse string like '0 cm' radial_transmissions.append((r, data['lambda'], data['tr'])) # simplify the list prefactor = _collect_scalars(transmissions) return BandpassInterpolator(transmissions, radial_transmissions, prefactor=prefactor, name=name) sncosmo-2.12.1/sncosmo/specmodel.py000066400000000000000000000062441476435666400173170ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst import astropy.units as u import numpy as np from scipy.interpolate import splev, splrep from .bandpasses import get_bandpass from .constants import HC_ERG_AA, SPECTRUM_BANDFLUX_SPACING, FLAMBDA_UNIT from .utils import integration_grid __all__ = ['SpectrumModel'] class SpectrumModel(object): """A model spectrum, representing wavelength and spectral density values. Parameters ---------- wave : list_like Wavelength values. flux : list_like Spectral flux density values. wave_unit : `~astropy.units.Unit` Unit on wavelength. unit : `~astropy.units.BaseUnit` For now, only units with flux density in energy (not photon counts). """ def __init__(self, wave, flux, wave_unit=u.AA, unit=(u.erg / u.s / u.cm**2 / u.AA)): self.wave = np.asarray(wave, dtype=np.float64) self.flux = np.asarray(flux, dtype=np.float64) if self.wave.shape != self.flux.shape: raise ValueError('shape of wavelength and flux must match') if self.wave.ndim != 1: raise ValueError('only 1-d arrays supported') # internally, wavelength is in Angstroms: if wave_unit != u.AA: self.wave = wave_unit.to(u.AA, self.wave, u.spectral()) self._wave_unit = u.AA # internally, flux is in F_lambda: if unit != FLAMBDA_UNIT: self.flux = unit.to(FLAMBDA_UNIT, self.flux, u.spectral_density(self.wave * u.AA)) self._unit = FLAMBDA_UNIT # Set up interpolation. # This appears to be the fastest-evaluating interpolant in # scipy.interpolate. self._tck = splrep(self.wave, self.flux, k=1) def bandflux(self, band): """Perform synthentic photometry in a given bandpass. The bandpass transmission is interpolated onto the wavelength grid of the spectrum. The result is a weighted sum of the spectral flux density values (weighted by transmission values). Parameters ---------- band : Bandpass or str Bandpass object or name of registered bandpass. Returns ------- float Total flux in ph/s/cm^2. """ band = get_bandpass(band) # Check that bandpass wavelength range is fully contained in spectrum # wavelength range. if (band.minwave() < self.wave[0] or band.maxwave() > self.wave[-1]): raise ValueError('bandpass {0!r:s} [{1:.6g}, .., {2:.6g}] ' 'outside spectral range [{3:.6g}, .., {4:.6g}]' .format(band.name, band.minwave(), band.maxwave(), self.wave[0], self.wave[-1])) # Set up wavelength grid. Spacing (dwave) evenly divides the bandpass, # closest to 5 angstroms without going over. wave, dwave = integration_grid(band.minwave(), band.maxwave(), SPECTRUM_BANDFLUX_SPACING) trans = band(wave) f = splev(wave, self._tck, ext=1) return np.sum(wave * trans * f) * dwave / HC_ERG_AA sncosmo-2.12.1/sncosmo/spectrum.py000066400000000000000000000457471476435666400172210ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Convenience functions for interfacing with spectra.""" from scipy.sparse import csr_matrix import astropy.units as u import numpy as np from .bandpasses import get_bandpass from .constants import HC_ERG_AA, SPECTRUM_BANDFLUX_SPACING, FLAMBDA_UNIT from .magsystems import get_magsystem from .utils import integration_grid __all__ = ['Spectrum'] def _recover_bin_edges(wave): """Recover the edges of a set of wavelength bins given the bin centers. This function is designed to work for standard linear binning along with other more exotic forms of binning such as logarithmic bins. We do a second order correction to try to get the bin widths as accurately as possible. For linear binning there is only machine precision error with either a first or second order estimate. For higher order binnings (eg: log), the fractional error is of order (dA / A)**2 for linear estimate and (dA / A)**4 for the second order estimate that we do here. Parameters ---------- wave : array-like Central wavelength values of each wavelength bin. Returns ------- bin_edges : `~numpy.ndarray` The recovered edges of each wavelength bin. """ wave = np.asarray(wave) # First order estimate o1 = (wave[:-1] + wave[1:]) / 2. # Second order correction o2 = 1.5*o1[1:-1] - (o1[2:] + o1[:-2]) / 4. # Estimate front and back edges f2 = 2 * wave[1] - o2[0] f1 = 2 * wave[0] - f2 b2 = 2 * wave[-2] - o2[-1] b1 = 2 * wave[-1] - b2 # Stack everything together bin_edges = np.hstack([f1, f2, o2, b2, b1]) return bin_edges def _parse_wavelength_information(wave, bin_edges): """Parse wavelength information and return a set of bin edges. Either the central wavelength for each bin can be passed as ``wave``, or the bin edges can be passed directly as ``bin_edges``. This function will recover the bin edges from either input and verify that they are a valid monotonically-increasing list. """ # Make sure that a valid combination of inputs was given. valid_count = 0 if wave is not None: valid_count += 1 if bin_edges is not None: valid_count += 1 if valid_count != 1: raise ValueError('must specify exactly one of wave or bin_edges') # Extract the bin starts and ends. if wave is not None: bin_edges = _recover_bin_edges(np.asarray(wave)) # Make sure that the bin ends are larger than the bin starts. if np.any(bin_edges[1:] <= bin_edges[:-1]): raise ValueError('wavelength must be monotonically increasing') return bin_edges class Spectrum(object): """An observed spectrum of an object. This class is designed to represent an observed spectrum. An observed spectrum is a set of contiguous bins in wavelength (referred to as "spectral elements") with associated flux measurements. We assume that each spectral element has uniform transmission in wavelength. A spectrum can optionally have associated uncertainties or covariance between the observed fluxes of the different spectral elements. A spectrum can also optionally have a time associated with it. Internally, we store the edges of each of the spectral element wavelength bins. These are automatically recovered in the common case where a user has a list of central wavelengths for each bin. The wavelengths are stored internally in units of Angstroms. The flux is stored as a spectral flux density F_λ (units of erg / s / cm^2 / Angstrom). Parameters ---------- wave : list-like Central wavelengths of each spectral element. This must be monotonically increasing. This is assumed to be in units of Angstroms unless ``wave_unit`` is specified. flux : list-like Observed fluxes for each spectral element. By default this is assumed to be a spectral flux density F_λ unless ``unit`` is explicitly specified. fluxerr : list-like Uncertainties on the observed fluxes for each spectral element. fluxcov : two-dimensional `~numpy.ndarray` Covariance of the observed fluxes for each spectral element. Only one of ``fluxerr`` and ``fluxcov`` may be specified. bin_edges : list-like Edges of each spectral element in wavelength. This should be a list that is length one longer than ``flux``. Only one of ``wave`` and ``bin_edges`` may be specified. wave_unit : `~astropy.units.Unit` Wavelength unit. Default is Angstroms. unit : `~astropy.units.Unit` Flux unit. Default is F_λ (erg / s / cm^2 / Angstrom). time : float The time associated with the spectrum. This is required if fitting a model to the spectrum. """ def __init__(self, wave=None, flux=None, fluxerr=None, fluxcov=None, bin_edges=None, wave_unit=u.AA, unit=FLAMBDA_UNIT, time=None): # Extract the bin edges bin_edges = _parse_wavelength_information(wave, bin_edges) self.bin_edges = bin_edges # Make sure that the flux data matches up with the wavelength data. self.flux = np.asarray(flux) if not (len(self.bin_edges) - 1 == len(self.flux)): raise ValueError("unequal column lengths") # Extract uncertainty information in whatever form it came in. self._fluxerr = None self._fluxcov = None if fluxerr is not None: if fluxcov is not None: raise ValueError("can only specify one of fluxerr and fluxcov") self._fluxerr = np.array(fluxerr) if len(self._fluxerr) != len(self.flux): raise ValueError("unequal column lengths") elif fluxcov is not None: self._fluxcov = np.array(fluxcov) if not (len(self.flux) == self._fluxcov.shape[0] == self._fluxcov.shape[1]): raise ValueError("unequal column lengths") # Internally, wavelength is in Angstroms: if wave_unit != u.AA: self.bin_edges = wave_unit.to(u.AA, self.bin_edges, u.spectral()) self._wave_unit = u.AA # Internally, flux is in F_lambda: if unit != FLAMBDA_UNIT: unit_scale = unit.to( FLAMBDA_UNIT, equivalencies=u.spectral_density(u.AA, self.wave) ) self.flux = unit_scale * self.flux if self._fluxerr is not None: self._fluxerr = unit_scale * self._fluxerr if self._fluxcov is not None: self._fluxcov = np.outer(unit_scale, unit_scale) \ .dot(self._fluxcov) self._unit = FLAMBDA_UNIT self.time = time # We use a sampling matrix to evaluate models/bands for the spectrum. # This matrix is expensive to compute but rarely changes, so we cache # it. self._cache_sampling_matrix = None def __len__(self): return len(self.flux) @property def bin_starts(self): """Return the start of each bin.""" return self.bin_edges[:-1] @property def bin_ends(self): """Return the end of each bin.""" return self.bin_edges[1:] @property def wave(self): """Return the centers of each bin.""" return (self.bin_starts + self.bin_ends) / 2. @property def fluxerr(self): """Return the uncertainties on each flux bin""" if self._fluxerr is not None: return self._fluxerr elif self._fluxcov is not None: return np.sqrt(np.diag(self._fluxcov)) else: raise ValueError("no uncertainty information available") @property def fluxcov(self): """Return the covariance matrix""" if self._fluxcov is not None: return self._fluxcov elif self._fluxerr is not None: return np.diag(self._fluxerr**2) else: raise ValueError("no uncertainty information available") def has_uncertainties(self): """Check whether there is uncertainty information available.""" return self._fluxcov is not None or self._fluxerr is not None def rebin(self, wave=None, bin_edges=None): """Rebin the spectrum on a new wavelength grid. We assume that the spectrum is constant for each spectral element with a value given by its observed flux. If the new bin edges are not aligned with the old ones, then this will introduce covariance between spectral elements. We propagate that covariance properly. Parameters ---------- wave : list-like Central wavelengths of the rebinned spectrum. bin_edges : list-like Bin edges of the rebinned spectrum. Only one of ``wave`` and ``bin_edges`` may be specified. Returns ------- rebinned_spectrum : `~sncosmo.Spectrum` A new `~sncosmo.Spectrum` with the rebinned spectrum. """ new_bin_edges = _parse_wavelength_information(wave, bin_edges) new_bin_starts = new_bin_edges[:-1] new_bin_ends = new_bin_edges[1:] old_bin_starts = self.bin_starts old_bin_ends = self.bin_ends # Generate a weight matrix for the transformation. overlap_starts = np.max(np.meshgrid(old_bin_starts, new_bin_starts), axis=0) overlap_ends = np.min(np.meshgrid(old_bin_ends, new_bin_ends), axis=0) overlaps = overlap_ends - overlap_starts overlaps[overlaps < 0] = 0 # Normalize by the total overlap in each bin to keep everything in # units of f_lambda total_overlaps = np.sum(overlaps, axis=1) if np.any(total_overlaps == 0): raise ValueError("new binning not contained within original " "spectrum") weight_matrix = overlaps / total_overlaps[:, None] new_flux = weight_matrix.dot(self.flux) new_fluxcov = weight_matrix.dot(self.fluxcov.dot(weight_matrix.T)) return Spectrum( bin_edges=new_bin_edges, flux=new_flux, fluxcov=new_fluxcov, time=self.time, ) def get_sampling_matrix(self): """Build an appropriate sampling for the spectral elements. For spectra with wide spectral elements, it is important to integrate models over the spectral element rather than simply sampling at the central wavelength. This function first determines where to sample for each spectral element and returns the corresponding list of wavelengths ``sample_wave``. This function also returns a matrix ``sampling_matrix`` that provided the mapping between the sampled wavelengths and the spectral elements. Given a set of model fluxes evaluated at ``sample_wave``, the dot product of ``sampling_matrix`` with these fluxes gives the corresponding fluxes in each spectral element in units of (erg / s / cm^2). ``sampling_matrix`` is stored as a compressed sparse row matrix that can be very efficiently used for dot products with vectors. This matrix is somewhat expensive to calculate and only changes if the bin edges of the spectral elements change, so we cache it and only recompute it if the bin edges change. Returns ------- sample_wave : `~numpy.ndarray` Wavelengths to sample a model at. sampling_matrix : `~scipy.sparse.csr_matrix` Matrix giving the mapping from the sampled bins to the spectral elements. """ # Check if we have cached the sampling matrix already. if self._cache_sampling_matrix is not None: cache_bin_edges, sampling_matrix_result = \ self._cache_sampling_matrix if np.all(cache_bin_edges == self.bin_edges): # No changes to the spectral elements so the sampling matrix # hasn't changed. return sampling_matrix_result indices = [] sample_wave = [] sample_dwave = [] for bin_idx in range(len(self.flux)): bin_start = self.bin_starts[bin_idx] bin_end = self.bin_ends[bin_idx] bin_wave, bin_dwave = integration_grid(bin_start, bin_end, SPECTRUM_BANDFLUX_SPACING) indices.append(bin_idx * np.ones_like(bin_wave, dtype=int)) sample_wave.append(bin_wave) sample_dwave.append(bin_dwave * np.ones_like(bin_wave)) indices = np.hstack(indices) sample_wave = np.hstack(sample_wave) sample_dwave = np.hstack(sample_dwave) sampling_matrix = csr_matrix( (sample_dwave, (indices, np.arange(len(indices)))), shape=(len(self), len(indices)), dtype=np.float64, ) # Cache the result sampling_matrix_result = (sample_wave, sampling_matrix) self._cache_sampling_matrix = (self.bin_edges.copy(), sampling_matrix_result) return sampling_matrix_result def _band_weights(self, band, zp, zpsys): """Calculate the weights for synthetic photometry. Parameters ---------- band : `~sncosmo.Bandpass`, str or list_like Bandpass, name of bandpass in registry, or list or array thereof. zp : float or list_like, optional If given, zeropoint to scale flux to (must also supply ``zpsys``). If not given, flux is not scaled. zpsys : str or list_like, optional Name of a magnitude system in the registry, specifying the system that ``zp`` is in. Returns ------- band_weights : numpy.array The weights to multiply each bin by for synthetic photometry in the given band(s). This has a shape of (number of bands, number of spectral elements). The dot product of this array with the flux array gives the desired band flux. """ band_weights = [] if zp is not None and zpsys is None: raise ValueError('zpsys must be given if zp is not None') # broadcast arrays if zp is None: band = np.atleast_1d(band) else: band, zp, zpsys = np.broadcast_arrays(np.atleast_1d(band), zp, zpsys) for idx in range(len(band)): iter_band = get_bandpass(band[idx]) # Check that bandpass wavelength range is fully contained in # spectrum wavelength range. if (iter_band.minwave() < self.bin_starts[0] or iter_band.maxwave() > self.bin_ends[-1]): raise ValueError( 'bandpass {0!r:s} [{1:.6g}, .., {2:.6g}] ' 'outside spectral range [{3:.6g}, .., {4:.6g}]' .format(iter_band.name, iter_band.minwave(), iter_band.maxwave(), self.bin_starts[0], self.bin_ends[-1]) ) sample_wave, sampling_matrix = self.get_sampling_matrix() trans = iter_band(sample_wave) sample_weights = sample_wave * trans / HC_ERG_AA row_band_weights = sampling_matrix * sample_weights if zp is not None: ms = get_magsystem(zpsys[idx]) zp_bandflux = ms.zpbandflux(iter_band) zpnorm = 10. ** (0.4 * zp[idx]) / zp_bandflux row_band_weights *= zpnorm band_weights.append(row_band_weights) band_weights = np.vstack(band_weights) return band_weights def bandflux(self, band, zp=None, zpsys=None): """Perform synthentic photometry in a given bandpass. We assume that the spectrum is constant for each spectral element with a value given by its observed flux. The bandpass is sampled on an appropriate high-resolution grid and multiplied with the observed fluxes to give the corresponding integrated band flux over this band. Parameters ---------- band : `~sncosmo.bandpass` or str or list_like Bandpass(es) or name(s) of bandpass(es) in registry. zp : float or list_like, optional If given, zeropoint to scale flux to. if `none` (default) flux is not scaled. zpsys : `~sncosmo.magsystem` or str (or list_like), optional Determines the magnitude system of the requested zeropoint. cannot be `none` if `zp` is not `none`. Returns ------- bandflux : float or `~numpy.ndarray` Flux in photons / s / cm^2, unless `zp` and `zpsys` are given, in which case flux is scaled so that it corresponds to the requested zeropoint. Return value is `float` if all input parameters are scalars, `~numpy.ndarray` otherwise. """ ndim = np.ndim(band) band_weights = self._band_weights(band, zp, zpsys) band_flux = band_weights.dot(self.flux) if ndim == 0: band_flux = band_flux[0] return band_flux def bandfluxcov(self, band, zp=None, zpsys=None): """Like bandflux(), but also returns model covariance on values. Parameters ---------- band : `~sncosmo.bandpass` or str or list_like Bandpass(es) or name(s) of bandpass(es) in registry. zp : float or list_like, optional If given, zeropoint to scale flux to. if `none` (default) flux is not scaled. zpsys : `~sncosmo.magsystem` or str (or list_like), optional Determines the magnitude system of the requested zeropoint. cannot be `none` if `zp` is not `none`. Returns ------- bandflux : float or `~numpy.ndarray` Model bandfluxes. cov : float or `~numpy.array` Covariance on ``bandflux``. If ``bandflux`` is an array, this will be a 2-d array. """ ndim = np.ndim(band) band_weights = self._band_weights(band, zp, zpsys) band_flux = band_weights.dot(self.flux) band_cov = band_weights.dot(self.fluxcov).dot(band_weights.T) if ndim == 0: band_flux = band_flux[0] band_cov = band_cov[0, 0] return band_flux, band_cov def bandmag(self, band, magsys): """Magnitude through the given bandpass(es), and for the given magnitude system(s). Parameters ---------- band : str or list_like Name(s) of bandpass in registry. magsys : str or list_like Name(s) of `~sncosmo.MagSystem` in registry. Returns ------- mag : float or `~numpy.ndarray` Magnitude for each item in band, magsys. The return value is a float if all parameters are not iterables. The return value is an `~numpy.ndarray` if any are iterable. """ return -2.5 * np.log10(self.bandflux(band, 0., magsys)) sncosmo-2.12.1/sncosmo/tests/000077500000000000000000000000001476435666400161265ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/tests/__init__.py000066400000000000000000000001451476435666400202370ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This package contains tests. """ sncosmo-2.12.1/sncosmo/tests/data/000077500000000000000000000000001476435666400170375ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/tests/data/README.md000066400000000000000000000006761476435666400203270ustar00rootroot00000000000000Example files for I/O tests --------------------------- - `dust_subsection` : Contains a subsection of the SFD map. The code expects a specific filename, so the file is in a subdirectory to make it clear that it is a subsection of the original file. - `salt2_example.dat` : Example `lc-03D4ag.list` from http://supernovae.in2p3.fr/salt/doku.php?id=usage - `SNLS3-04D3gx` : Example data from SNLS3-04D3gx, distributed with SALT2 data. sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/000077500000000000000000000000001476435666400206505ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/lc2fit_g.dat000066400000000000000000000021761476435666400230410ustar00rootroot00000000000000@INSTRUMENT MEGACAM @BAND g @MAGSYS VEGA @WEIGHTMAT weightmat_g_9.dat @X_FOCAL_PLANE -2.972197 @Y_FOCAL_PLANE -10.319079 #Date : MJD #Flux : number of ADUs in reference image 851062p30_enlarged #Fluxerr : errors (but look at the weight matrix) #ZP : as computed by builder CalibratedAlignedImageSet #end 53083.60 81.219 72.303 32.381008 53109.58 -27.647 107.519 32.381008 53117.48 155.947 56.068 32.381008 53121.51 152.368 53.117 32.381008 53138.46 40.650 70.349 32.381008 53146.49 -27.660 58.952 32.381008 53149.33 -2.429 46.244 32.381008 53165.41 -73.533 51.472 32.381008 53175.38 -200.851 63.037 32.381008 53178.37 11.370 61.585 32.381008 53195.34 45.123 56.868 32.381008 53198.30 -20.393 571.621 32.381008 53200.26 -78.608 62.536 32.381008 53390.65 39.611 65.149 32.381008 53411.62 -39.484 56.823 32.381008 53446.53 10.035 51.490 32.381008 53462.49 175.484 116.858 32.381008 53463.57 -359.533 315.632 32.381008 53466.61 -57.916 62.362 32.381008 53469.52 -34.254 63.031 32.381008 53473.59 48.200 79.154 32.381008 53495.44 -78.999 61.882 32.381008 53500.31 -0.218 48.387 32.381008 53502.39 45.320 64.024 32.381008 53505.39 -78.404 98.855 32.381008 sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/lc2fit_i.dat000066400000000000000000000027121476435666400230370ustar00rootroot00000000000000@BAND i @INSTRUMENT MEGACAM @MAGSYS VEGA @WEIGHTMAT weightmat_i_9.dat @X_FOCAL_PLANE -2.972197 @Y_FOCAL_PLANE -10.319079 #Date : MJD #Flux : number of ADUs in reference image #Fluxerr : errors (but look at the weight matrix) #ZP : zero point #end 53080.59 -43.356 85.072 31.56057 53083.53 -120.695 98.577 31.56057 53094.54 41.443 78.505 31.56057 53109.49 966.089 103.438 31.56057 53112.53 1262.245 133.007 31.56057 53116.5 1381.292 102.588 31.56057 53117.45 1395.481 213.004 31.56057 53121.49 1461.473 103.967 31.56057 53123.4 1793.254 154.912 31.56057 53134.28 1496.969 113.902 31.56057 53138.41 1156.937 122.032 31.56057 53146.43 842.082 119.559 31.56057 53151.38 590.663 151.586 31.56057 53165.32 137.279 138.565 31.56057 53169.35 205.541 103.183 31.56057 53175.33 273.895 94.503 31.56057 53178.31 -58.89 91.603 31.56057 53193.34 -17.789 85.271 31.56057 53197.28 -47.671 139.702 31.56057 53202.3 217.347 87.608 31.56057 53226.27 216.87 113.694 31.56057 53227.26 76.683 88.54 31.56057 53386.61 -12.469 91.94 31.56057 53390.6 178.355 124.316 31.56057 53409.63 32.983 70.061 31.56057 53415.55 -121.319 108.081 31.56057 53419.6 67.47 88.903 31.56057 53440.53 -115.102 124.481 31.56057 53446.47 8.867 79.074 31.56057 53461.49 221.195 156.582 31.56057 53466.55 66.583 122.167 31.56057 53469.46 31.032 110.775 31.56057 53473.48 2.277 118.097 31.56057 53494.47 -240.339 90.893 31.56057 53498.36 -41.546 120.051 31.56057 53502.33 -117.977 90.647 31.56057 53505.32 -44.397 83.647 31.56057 sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/lc2fit_r.dat000066400000000000000000000030741476435666400230520ustar00rootroot00000000000000@INSTRUMENT MEGACAM @BAND r @MAGSYS VEGA @WEIGHTMAT weightmat_r_9.dat @X_FOCAL_PLANE -2.972197 @Y_FOCAL_PLANE -10.319079 #Date : MJD #Flux : number of ADUs in reference image 800717p30_enlarged #Fluxerr : errors (but look at the weight matrix) #ZP : as computed by builder CalibratedAlignedImageSet #end 53081.60 89.154 91.283 32.044657 53083.58 158.057 84.342 32.044657 53094.59 203.881 99.560 32.044657 53109.54 634.164 95.447 32.044657 53117.46 1204.990 72.342 32.044657 53121.41 1322.061 71.268 32.044657 53123.45 1102.041 91.775 32.044657 53134.34 664.961 65.576 32.044657 53138.43 370.159 75.030 32.044657 53146.46 165.462 80.750 32.044657 53149.31 283.731 94.726 32.044657 53151.45 152.651 126.301 32.044657 53165.39 -49.902 56.299 32.044657 53169.38 186.572 92.200 32.044657 53175.36 85.747 72.997 32.044657 53178.35 125.116 64.900 32.044657 53182.32 -0.001 94.629 32.044657 53193.38 52.431 115.801 32.044657 53197.34 0.338 71.354 32.044657 53198.30 87.623 89.348 32.044657 53204.35 114.766 69.000 32.044657 53228.25 -134.175 99.101 32.044657 53386.65 184.333 69.825 32.044657 53390.63 186.592 101.464 32.044657 53411.60 2.178 65.763 32.044657 53415.58 -17.432 105.467 32.044657 53419.64 28.002 124.295 32.044657 53440.56 930.735 570.660 32.044657 53446.51 37.881 69.812 32.044657 53461.54 -197.156 150.465 32.044657 53466.58 98.274 100.348 32.044657 53469.49 89.118 81.532 32.044657 53473.56 -111.317 93.436 32.044657 53494.51 151.033 128.587 32.044657 53495.42 -30.895 94.900 32.044657 53498.38 93.848 91.917 32.044657 53502.36 92.347 72.282 32.044657 53505.36 -97.986 86.738 32.044657 sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/lc2fit_z.dat000066400000000000000000000016351476435666400230630ustar00rootroot00000000000000@INSTRUMENT MEGACAM @BAND z @MAGSYS VEGA @WEIGHTMAT weightmat_z_9.dat @X_FOCAL_PLANE -2.972197 @Y_FOCAL_PLANE -10.319079 #Date : MJD #Flux : number of ADUs in reference image 851067p30_enlarged #Fluxerr : errors (but look at the weight matrix) #ZP : as computed by builder CalibratedAlignedImageSet #end 53083.63 41.919 224.654 30.725741 53094.61 81.465 166.445 30.725741 53123.49 730.248 106.364 30.725741 53134.38 677.623 118.751 30.725741 53151.33 408.032 106.594 30.725741 53169.43 -24.382 143.943 30.725741 53177.38 199.876 89.823 30.725741 53182.36 60.897 85.126 30.725741 53204.30 162.909 81.319 30.725741 53415.62 402.556 143.819 30.725741 53446.56 92.973 76.540 30.725741 53461.59 -46.954 231.116 30.725741 53462.57 -318.013 255.109 30.725741 53469.55 -97.741 104.526 30.725741 53475.43 -45.353 106.584 30.725741 53495.47 20.838 120.830 30.725741 53502.28 -109.875 95.274 30.725741 53505.35 -58.674 81.687 30.725741 sncosmo-2.12.1/sncosmo/tests/data/SNLS3-04D3gx/lightfile000066400000000000000000000002051476435666400225370ustar00rootroot00000000000000NAME 04D3gx Redshift 0.910000 z_heliocentric 0.910000 z_source S RA 215.056948 DEC 52.282777 MWEBV 0.011700 FIELD D3 SET SNLS CCD 30 sncosmo-2.12.1/sncosmo/tests/data/covmat_lc-03D4ag.dat000066400000000000000000000631701476435666400224270ustar00rootroot0000000000000062 62 0.867712297284 0.01139998771 0.01119398747 0.01167625535 0.01148453483 0.01151087169 0.01145852469 0.01149226888 0.01146109273 0.01134276091 0.01119017098 0.01155768499 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01139998771 2.03512047975 0.01190299234 0.01109265252 0.01134276542 0.01115583809 0.01147674517 0.01140772754 0.01148397926 0.01159218545 0.01186099853 0.01102848498 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01119398747 0.01190299234 1.3663344852 0.01032342272 0.01107845855 0.01054615453 0.01154540986 0.01126136519 0.01149341748 0.01177551772 0.01307294333 0.01019186126 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01167625535 0.01109265252 0.01032342272 0.344436086886 0.01175670207 0.0123880409 0.01134268183 0.01154836286 0.01134056151 0.01121185022 0.01034941088 0.01271429186 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01148453483 0.01134276542 0.01107845855 0.01175670207 0.428400086556 0.01172592645 0.01139320936 0.01149347253 0.01142795797 0.01148011396 0.01107797275 0.0117418595 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01151087169 0.01115583809 0.01054615453 0.0123880409 0.01172592645 0.461868857206 0.01131009634 0.01154798187 0.0113416851 0.01146934961 0.01061542773 0.01239234527 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01145852469 0.01147674517 0.01154540986 0.01134268183 0.01139320936 0.01131009634 0.570723316396 0.01142219518 0.01145431529 0.01140982053 0.01151553689 0.01128548717 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01149226888 0.01140772754 0.01126136519 0.01154836286 0.01149347253 0.01154798187 0.01142219518 0.466808243406 0.01144492995 0.01147352893 0.01128502224 0.01154878138 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01146109273 0.01148397926 0.01149341748 0.01134056151 0.01142795797 0.0113416851 0.01145431529 0.01144492995 0.590039111969 0.01146544451 0.01147163615 0.01130370734 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01134276091 0.01159218545 0.01177551772 0.01121185022 0.01148011396 0.01146934961 0.01140982053 0.01147352893 0.01146544451 0.645109393561 0.01176394266 0.01127418828 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01119017098 0.01186099853 0.01307294333 0.01034941088 0.01107797275 0.01061542773 0.01151553689 0.01128502224 0.01147163615 0.01176394266 0.923382154326 0.0102993111 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01155768499 0.01102848498 0.01019186126 0.01271429186 0.0117418595 0.01239234527 0.01128548717 0.01154878138 0.01130370734 0.01127418828 0.0102993111 0.470372487364 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.53859685208 0.01368751959 0.01430662019 0.01342180348 0.01352319756 0.01386289616 0.0141507269 0.01417543825 0.01402167964 0.01385929148 0.01405417649 0.01360218753 0.01400785689 0.01397231271 0.01293840672 0.01372471119 0.01341381967 0.0142738183 0.01366036385 0.0138254953 0.01295199831 0.01396342402 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01368751959 1.51669852277 0.01386686932 0.0135336413 0.01368958291 0.01373673869 0.01400575075 0.01407195708 0.01388450727 0.01394847862 0.01403234808 0.01384410736 0.01404375126 0.01381263444 0.01297360081 0.0135594792 0.01358156982 0.01406735146 0.01383209817 0.0139713964 0.0131979694 0.01400846721 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01430662019 0.01386686932 0.923928186734 0.01323292497 0.01356034423 0.01464200265 0.01565285272 0.01562761835 0.01515216255 0.01453678939 0.01524802415 0.01388905757 0.01510265797 0.01508315066 0.01208473895 0.01430700887 0.01329186748 0.0160857243 0.01409633975 0.01448919195 0.01207897382 0.01498377406 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01342180348 0.0135336413 0.01323292497 0.898042513657 0.01352438651 0.0134329703 0.01334868233 0.01335041519 0.01337223674 0.01346721357 0.01338172725 0.01350140626 0.01342717906 0.01337042206 0.01346675073 0.01344720177 0.01352358723 0.01329971514 0.01349178869 0.01349036899 0.01350414129 0.01344688527 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01352319756 0.01368958291 0.01356034423 0.01352438651 0.582025383756 0.01357961356 0.01365649683 0.01366628036 0.01360198445 0.01366646338 0.01364305006 0.01365001795 0.01369046426 0.01357277644 0.01325917308 0.01350482659 0.01354987866 0.01364645872 0.01364738801 0.01368938936 0.01335853898 0.0136827783 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01386289616 0.01373673869 0.01464200265 0.0134329703 0.01357961356 1.34374047764 0.01440874647 0.01442186201 0.01421320851 0.01401370078 0.01425571582 0.0137011479 0.0142159563 0.01415749649 0.01280498549 0.01388108646 0.01342918425 0.01454592415 0.01380295026 0.01398342836 0.01279819911 0.01416644166 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0141507269 0.01400575075 0.01565285272 0.01334868233 0.01365649683 0.01440874647 1.72805627922 0.01525643713 0.01482168754 0.01445521741 0.01497570067 0.01398516159 0.01487646087 0.01470767398 0.01231095724 0.01405384334 0.01340326864 0.01554115189 0.01412488599 0.01444002952 0.01241784543 0.01476650952 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01417543825 0.01407195708 0.01562761835 0.01335041519 0.01366628036 0.01442186201 0.01525643713 1.10892271326 0.01486345722 0.01449594186 0.01503699731 0.01402091402 0.01490970988 0.01473150484 0.01227337111 0.01402695094 0.01341219348 0.01558091193 0.01413635347 0.01445877448 0.01238557679 0.01478089592 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01402167964 0.01388450727 0.01515216255 0.01337223674 0.01360198445 0.01421320851 0.01482168754 0.01486345722 0.51161503404 0.01423253109 0.01461700051 0.01383641565 0.01453764223 0.01444944452 0.01252565561 0.01390925234 0.01340284674 0.01506849596 0.01394260424 0.0141856285 0.01260794598 0.01444410965 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01385929148 0.01394847862 0.01453678939 0.01346721357 0.01366646338 0.01401370078 0.01445521741 0.01449594186 0.01423253109 0.628767693323 0.0143738038 0.01387839444 0.01433753524 0.01414601585 0.01273795937 0.01378003407 0.01350908282 0.01459485917 0.01393616003 0.0141272457 0.01287765812 0.01427237587 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01405417649 0.01403234808 0.01524802415 0.01338172725 0.01364305006 0.01425571582 0.01497570067 0.01503699731 0.01461700051 0.0143738038 1.26874159426 0.0139964539 0.01475644348 0.01450290418 0.01246267975 0.01398239356 0.01341163418 0.01523700526 0.01406537207 0.01438661179 0.01253242151 0.01465072434 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01360218753 0.01384410736 0.01388905757 0.01350140626 0.01365001795 0.0137011479 0.01398516159 0.01402091402 0.01383641565 0.01387839444 0.0139964539 0.660922851018 0.01399576798 0.01377305518 0.01307289166 0.01353921029 0.01357145639 0.01404380018 0.01387183714 0.01391727491 0.01324019242 0.01396493617 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01400785689 0.01404375126 0.01510265797 0.01342717906 0.01369046426 0.0142159563 0.01487646087 0.01490970988 0.01453764223 0.01433753524 0.01475644348 0.01399576798 0.518411385551 0.01443250234 0.01250700437 0.01395097257 0.0134827854 0.0150971712 0.01408365711 0.01435661671 0.01263215588 0.01459005739 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01397231271 0.01381263444 0.01508315066 0.01337042206 0.01357277644 0.01415749649 0.01470767398 0.01473150484 0.01444944452 0.01414601585 0.01450290418 0.01377305518 0.01443250234 0.609266990647 0.01260436468 0.01389917098 0.0133968642 0.01493670942 0.01387889697 0.01410725289 0.01265688291 0.01435191237 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01293840672 0.01297360081 0.01208473895 0.01346675073 0.01325917308 0.01280498549 0.01231095724 0.01227337111 0.01252565561 0.01273795937 0.01246267975 0.01307289166 0.01250700437 0.01260436468 1.69552469776 0.01306039992 0.01342082607 0.01212826189 0.01299574823 0.01277524915 0.01409952205 0.01258782529 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01372471119 0.0135594792 0.01430700887 0.01344720177 0.01350482659 0.01388108646 0.01405384334 0.01402695094 0.01390925234 0.01378003407 0.01398239356 0.01353921029 0.01395097257 0.01389917098 0.01306039992 0.910447267757 0.01341289233 0.01412141993 0.01364388326 0.01381342101 0.01294807402 0.01394648001 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01341381967 0.01358156982 0.01329186748 0.01352358723 0.01354987866 0.01342918425 0.01340326864 0.01341219348 0.01340284674 0.01350908282 0.01341163418 0.01357145639 0.0134827854 0.0133968642 0.01342082607 0.01341289233 0.542244761886 0.01336886948 0.01354936606 0.0135377265 0.01350708588 0.01349154657 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0142738183 0.01406735146 0.0160857243 0.01329971514 0.01364645872 0.01454592415 0.01554115189 0.01558091193 0.01506849596 0.01459485917 0.01523700526 0.01404380018 0.0150971712 0.01493670942 0.01212826189 0.01412141993 0.01336886948 1.42748042686 0.01419135347 0.01455345691 0.012233812 0.0149622851 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01366036385 0.01383209817 0.01409633975 0.01349178869 0.01364738801 0.01380295026 0.01412488599 0.01413635347 0.01394260424 0.01393616003 0.01406537207 0.01387183714 0.01408365711 0.01387889697 0.01299574823 0.01364388326 0.01354936606 0.01419135347 1.01206341745 0.01397137011 0.01314387291 0.01405174534 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0138254953 0.0139713964 0.01448919195 0.01349036899 0.01368938936 0.01398342836 0.01444002952 0.01445877448 0.0141856285 0.0141272457 0.01438661179 0.01391727491 0.01435661671 0.01410725289 0.01277524915 0.01381342101 0.0135377265 0.01455345691 0.01397137011 0.446215368187 0.01290675844 0.01430486301 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01295199831 0.0131979694 0.01207897382 0.01350414129 0.01335853898 0.01279819911 0.01241784543 0.01238557679 0.01260794598 0.01287765812 0.01253242151 0.01324019242 0.01263215588 0.01265688291 0.01409952205 0.01294807402 0.01350708588 0.012233812 0.01314387291 0.01290675844 2.67550647707 0.01269244291 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.01396342402 0.01400846721 0.01498377406 0.01344688527 0.0136827783 0.01416644166 0.01476650952 0.01478089592 0.01444410965 0.01427237587 0.01465072434 0.01396493617 0.01459005739 0.01435191237 0.01258782529 0.01394648001 0.01349154657 0.0149622851 0.01405174534 0.01430486301 0.01269244291 1.6708757967 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.840549648673 0.008593426466 0.008556357297 0.00861789607 0.008538215662 0.008603103172 0.008588989857 0.008606095068 0.008553227985 0.008475770196 0.008463580678 0.008233612311 0.008597594721 0.008613942519 0.00860282843 0.008424326568 0.008465292407 0.008574429849 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008593426466 0.719474077341 0.008737875905 0.008797509665 0.008994967454 0.008853163711 0.008627291885 0.008841548517 0.008813801578 0.008394481035 0.008470247675 0.008047056155 0.008813212529 0.008641592911 0.008775402662 0.008298698368 0.008375366658 0.008550302138 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008556357297 0.008737875905 0.830171975697 0.008953395859 0.009290603963 0.009014721889 0.008646478755 0.008972569189 0.008973367289 0.008348131247 0.008480606503 0.007912506472 0.008987593643 0.008622839362 0.008955656541 0.008172476259 0.008291215222 0.008511441157 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00861789607 0.008797509665 0.008953395859 1.05536527552 0.01021463302 0.009544955356 0.008847648457 0.009448352812 0.009380250842 0.008313917449 0.008454682773 0.007617622418 0.009527026016 0.008822102709 0.009470860588 0.008045116035 0.00820335312 0.008561351895 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008538215662 0.008994967454 0.009290603963 0.01021463302 1.08188951458 0.01036213848 0.008981237218 0.0101504524 0.01015586064 0.008105600813 0.008444095891 0.007062807781 0.01033569571 0.008874138878 0.01020590117 0.007624557946 0.007902607729 0.008451772724 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008603103172 0.008853163711 0.009014721889 0.009544955356 0.01036213848 0.741294474215 0.008854153892 0.009519656312 0.009471940411 0.008282661079 0.008464228378 0.007535713625 0.009596739066 0.008816922113 0.009521527634 0.007984844605 0.00816295397 0.008543921088 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008588989857 0.008627291885 0.008646478755 0.008847648457 0.008981237218 0.008854153892 0.331494772345 0.008834813295 0.008773001314 0.008455405737 0.008468279898 0.008107629589 0.008845727254 0.008698376658 0.008830651079 0.008364061522 0.008420843211 0.008582168159 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008606095068 0.008841548517 0.008972569189 0.009448352812 0.0101504524 0.009519656312 0.008834813295 0.620308145473 0.009376765077 0.008305337477 0.008465352909 0.007607255514 0.009490991419 0.008806701542 0.009428255021 0.00803592573 0.008199863498 0.008554666682 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008553227985 0.008813801578 0.008973367289 0.009380250842 0.01015586064 0.009471940411 0.008773001314 0.009376765077 0.749498636949 0.00827110621 0.00847119312 0.007615769005 0.009445512149 0.00871715936 0.009384687878 0.007992268432 0.008165004637 0.008499189066 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008475770196 0.008394481035 0.008348131247 0.008313917449 0.008105600813 0.008282661079 0.008455405737 0.008305337477 0.00827110621 0.403957686291 0.008449109042 0.00858136755 0.0082918124 0.00848048097 0.008307024407 0.008577821182 0.00854360635 0.008498434778 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008463580678 0.008470247675 0.008480606503 0.008454682773 0.008444095891 0.008464228378 0.008468279898 0.008465352909 0.00847119312 0.008449109042 0.40412425236 0.008385739007 0.008459217287 0.008462673976 0.008457748294 0.008419957467 0.008443442127 0.00846054175 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008233612311 0.008047056155 0.007912506472 0.007617622418 0.007062807781 0.007535713625 0.008107629589 0.007607255514 0.007615769005 0.00858136755 0.008385739007 0.594156813489 0.007563737031 0.008152234191 0.007603559453 0.008883998411 0.008684905028 0.008330415485 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008597594721 0.008813212529 0.008987593643 0.009527026016 0.01033569571 0.009596739066 0.008845727254 0.009490991419 0.009445512149 0.0082918124 0.008459217287 0.007563737031 0.912204394688 0.008806961723 0.009518422596 0.007995507388 0.008170495875 0.008543553441 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008613942519 0.008641592911 0.008622839362 0.008822102709 0.008874138878 0.008816922113 0.008698376658 0.008806701542 0.00871715936 0.00848048097 0.008462673976 0.008152234191 0.008806961723 0.523133050576 0.008793291702 0.008411332728 0.008455392534 0.008609780675 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00860282843 0.008775402662 0.008955656541 0.009470860588 0.01020590117 0.009521527634 0.008830651079 0.009428255021 0.009384687878 0.008307024407 0.008457748294 0.007603559453 0.009518422596 0.008793291702 0.713755397502 0.008016435585 0.008188444469 0.008549631966 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008424326568 0.008298698368 0.008172476259 0.008045116035 0.007624557946 0.007984844605 0.008364061522 0.00803592573 0.007992268432 0.008577821182 0.008419957467 0.008883998411 0.007995507388 0.008411332728 0.008016435585 0.477427625399 0.008642133555 0.008479938911 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008465292407 0.008375366658 0.008291215222 0.00820335312 0.007902607729 0.00816295397 0.008420843211 0.008199863498 0.008165004637 0.00854360635 0.008443442127 0.008684905028 0.008170495875 0.008455392534 0.008188444469 0.008642133555 0.601770495111 0.008497761101 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008574429849 0.008550302138 0.008511441157 0.008561351895 0.008451772724 0.008543921088 0.008582168159 0.008554666682 0.008499189066 0.008498434778 0.00846054175 0.008330415485 0.008543553441 0.008609780675 0.008549631966 0.008479938911 0.008497761101 0.617138402238 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.43685381093 0.00846966017 0.007860527775 0.008100266848 0.00815492548 0.008289807704 0.008271345002 0.007927546638 0.007900007636 0.008327232982 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00846966017 0.575921924198 0.007699165973 0.008070223656 0.008045811304 0.008446760547 0.008398327996 0.007846767161 0.007813114398 0.008474502363 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.007860527775 0.007699165973 1.49295099806 0.008329310108 0.008237463887 0.007914638124 0.007919111552 0.008494126516 0.008512433996 0.007921730938 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008100266848 0.008070223656 0.008329310108 0.312097590024 0.008196664973 0.008166025785 0.00817101126 0.008272418806 0.00827552587 0.008196384441 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00815492548 0.008045811304 0.008237463887 0.008196664973 0.315540051384 0.008046087182 0.008076146959 0.008198754088 0.008189861056 0.008066288941 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008289807704 0.008446760547 0.007914638124 0.008166025785 0.008046087182 0.591015741879 0.008361158911 0.008004329483 0.007969186163 0.008433900097 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008271345002 0.008398327996 0.007919111552 0.00817101126 0.008076146959 0.008361158911 0.39445121486 0.008006662188 0.007987181161 0.00837432522 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.007927546638 0.007846767161 0.008494126516 0.008272418806 0.008198754088 0.008004329483 0.008006662188 0.361510819409 0.008403468157 0.007997904339 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.007900007636 0.007813114398 0.008512433996 0.00827552587 0.008189861056 0.007969186163 0.007987181161 0.008403468157 0.625837962547 0.007987051647 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.008327232982 0.008474502363 0.007921730938 0.008196384441 0.008066288941 0.008433900097 0.00837432522 0.007997904339 0.007987051647 0.770727677983 sncosmo-2.12.1/sncosmo/tests/data/dust_subsection/000077500000000000000000000000001476435666400222545ustar00rootroot00000000000000sncosmo-2.12.1/sncosmo/tests/data/dust_subsection/SFD_dust_4096_ngp.fits000066400000000000000000001320001476435666400261400ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 2 / number of data axes NAXIS1 = 100 / length of data axis 1 NAXIS2 = 100 / length of data axis 2 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H DATE = '1999-05-09' / Creation date (CCYY-MM-DD) of FITS header OBJECT = 'E(B-V) ' / BUNIT = 'mag ' / CRPIX1 = -9.515000000000000E+02 / 1-indexed X pixel number of pole CRVAL1 = 90.0000 / CTYPE1 = 'GLON-ZEA' / X=sqrt(1-NSGP*sin(b))*cos(l)*SCALE CRPIX2 = -9.515000000000000E+02 / 1-indexed Y pixel number of pole CRVAL2 = 90.0000 / CTYPE2 = 'GLAT-ZEA' / Y=-NSGP*sqrt(1-NSGP*sin(b))*sin(l)*SCALE CD1_1 = -0.0395646818624 / CD1_2 = 0.00000 / CD2_1 = 0.00000 / CD2_2 = 0.0395646818624 / LONPOLE = 180 / LAM_NSGP= 1 / NSGP=+1 for north polar, =-1 for south polar LAM_SCAL= 2048 / SCALE=number of pixels from b=0 to b=90 deg AUTHOR = 'David J. Schlegel, Douglas P. Finkbeiner, and Marc Davis' / COMMENT --------------------------------------------------------------------- COMMENT COMMENT Schlegel-Finkbeiner-Davis E(B-V) Galactic Reddening Map COMMENT COMMENT A detailed description of this data product may be found in: COMMENT COMMENT "Maps of Dust IR Emission for Use in Estimation of Reddening COMMENT and CMBR Foregrounds", COMMENT by D. J. Schlegel, D. P. Finkbeiner, & M. Davis, COMMENT ApJ 500, 525 (June, 1998) COMMENT COMMENT Release 2 (May, 1999) COMMENT - file now contains standard FITS header; data unchanged COMMENT COMMENT --------------------------------------------------------------------- END =E==~=|V=z =w9 =w=z=}*==:=my=8/=֙=F====d==C= == =:=ե=c==H=N='=== B==b=m:==|=A=8\=|=yQ=xL=x =w=x=zw'=y=u9Z=m@=ii=c{=b8D=c=d"=e&=g=jO=l=h1=aF=^D=^SL=a=c=`E=\S=Y=[Hr=]]=a=d=f: =gZi=h=iܤ=i=hX=h2=iNM=m'C=ui=w=zr6=x=z=|==%=~ٓ=!= =C=7= ===A=J==~9=y=uZY=u@5=qdk=qD)=v={<=0=Q==~===Q===u=u=n=[_==Z=X==b===={=9==N=N====%M=@=~ f=y=vP=u =s=s=u=x(=x=tl=ni=hrU=cͶ=cC0=cM=ba=b=cG=gn]=g=d=`E=^r=]=^=_”=]xl=Z֪=Zs=Z=^s0=c=g!=ibF=j2=jD=jdK=i=i8==ij=jo=n?=vf=zu<={(=zj=|= =E==}u3====P=m='==8=M= ={ۙ=wO=s!H=p/=o\=o=u=}?===P= =S =(==d= ===@=ٍ==f =j=@H=°=rb=C=:|=BC=g==u===(|=F===|g=w=t$=rh=p=qJ\=tD>=v=w =s=nI=jN0=f+=e-N=b=^5=_ <=a0|=d!=d=`=^>=[ށ=ZI4=Zw=Y9=YOW=Y=Z=\<=_=b-=d=hzf=j=kS=j=i=i=i1:=iٯ=m˾=ro=t=xX=zk=|=ҟ=n=|'4=wB={&= =-<=Z=F===:=)=sW=~<={T=u(=p]=o*=oŀ=rK=y=~wN=Q=(=%==#==P=Ew=DL=I=W=e===QB=0==)===^====;==h=m=&t=~`==y =wu=t=r5[=p.=o=p=tH~=u=tM=r <=p=m=ii=g=dA=`u&=]=`S=bR=aR=]=[+ =X;=V_=S]=R4=SG=VN=XkD=Z =\=^_=a =fy=i=k=k<=iȥ=h=g=h=lA=n=qs=u=w=w=xS=s=qx=p=vh=~^=V===p3===,=}8=x6=sp=q;=q=sV=w=~=n==1= =NI=F==C=+===X= =u=K=&9=٤===w==3======H==Y=l}=yJ=ss=qp=r2^=q=p=p$=q =s,=t =s_=r_=qΛ=oD=la7=iL=f)=cx=a=`,=a.=\7=Z?=Xa=V\=S|7=MS=KF8=M):=P=T=Um=V;=Z=]L!=aB=hY=l2`=k=h7=f=eπ=f=inr=m=p\=r0=s}=oy=j=i-=j=lw=sj={^=~=+=L=2=T=9=ߙ={J=wG=u b=sĦ=w=y)/=|]= = ==!===DŽ===a=&=Do=<=!==V==79=======?==`=R==1==}=q{=n=o=p3=q=q0=q=qw=sm=sI=s+=s3=r@;=nL=l=jN=hN+=e=d+=b =_9=[=Y\h=Y=Yg=UW'=No=HQi=H{v=L<=P=Q=SBn=Uq=Z}=a=hܼ=lQ=jw5=g=eg=d+=f?X=i=lO=n=n=ly=g=dS=eD=h=m'=r;=ym=|_=~==ƒ=)W=J=={Q=xd =w:#=zv=\=i==5=_=>$=|=mB=t===9==i=Y=P=@=u=)==j=E>=Uk=Bu=vl=,=====2===s7=n0=l=n* =p^w=q=r=t|=vE=v0=u=sV=q-=p^s=mkk=kR=i=iz=g=f=e =a =[}(=Y`=\P=_=Y=Ov=G =H)|=K =P=S.=T=W-=[%=bf=g=k=kO=h+r=f/=e=g=iD=k=mL=l*1=g=e=c=g+=m֝=rL=vs=z={(={*=}=Q7===]=|q=zL=}=O=YS=== |=X=[=w=:== =d==k=(=2=E==U==Լ==y=l=;=~==0=,W=c=Ɛ=a=U=x1=mu=l=k=nL<=q=r=u={=~!O=zk=vm=q=nB=l^=jq=j=jW<=i=iT=h=gJ=aR=Z=XI=[P=^=Z"=Q#=Jmf=H=M?=R =V:3=Wö=YT=[#=_f=e=kA=l=ip=h3=i=jQq=l:=mV=n]=l=i T=d6=dD=j%=pX=we={&={={3={;=|=====~y=~==[i=j==^=co=F\====mN==&==v=b=ư==`=X===J=,=V==ۧ== ==}x==7=(=vl=n =j٨=l=o=q0=t=zc=~m=X={=v}=rw=l8=j_\=k43=l=k=k=j=j=hT=_=Y=V]=XA(=Y:=Vw=QH=L3b=NR=Th=V}=[=[e=Z=[+=_)=e?=iU=j=jZ=j=k =lF=m=o=q=q\=n:-=k==ga=mt=vY^=~yz=Ѥ=~{x=~k=p[=N=j_==B==a=c=Nd=[w===8=====T===U=4O=R==a======?==+=!=^=ǩ=cq===b3=$==~-=u=mqm=k=m=pv%=sK=w=| S==~2~=yƣ=vlx=q;=mW=l =lR:=lU=kp=k#=kM=k.=f=^=V=R=R=T|=T=R!=PNs=S*H=Y=])=^l=]w=Z=Z =\N=a==f_b=gZ=h|[=j*=jW=j.=l=r=v8=y=t=q=p =uy0=~n=======R=e=S==KW=$===q==[=l==Q====m=\=p===ٸ==U=9=J=@=;=n=ih=Y=K`==Y=r== =""={Tw=rm=m6x=kC=n\=qq=sO=w=o=|w=~=}e=zB4=us=q<=o5q=m=k7=j=hwv=h=h=iZ=d@=Z=P=L=Lϻ=O=R=So=SJ=X=]=_4=^=\ە=[T=[e=^ M=`=da=e=fZX=f0/=f/]=g)o=j'=q=z D=} ={=x)=|=|_.==q=F=kT=RX=j=d==]=b=q=={J===t=s=8K=~=~@===֗===Jx= =F=9i===@==g====E=[====={/=n=B===z 1=r=lS=j=m(=p&=sp=x=|=}l={=v=r'e=qQ1=o=k=e=e6=aQ=`=_=^R=Yp=N\=GX=Fe=H=LVX=Olx=Qq=Up=ZN'=\&S=\=\=[H=[ =[=]=`"=b =d}=ey=d=c=ep_=j̶=sm|={3=}=}f=~j=v=0==B==[=M=N=*=g=`&=M={O= ==S= ==>=z=L== 5==9==q=t=T=^=!=O=W\==o={===E= =3=%====U$=]={q=t=n=i|=i=jF=m=rx]=u=w3=x>=w=t5=p=n=l=h=a#=[=Y j=V`=TI=L=H=D=A0=A=D,W=H X=L=P#2=S&=V+=Y+=Y=Y=YR=Z=]Q=_f=av:=b=e =eT]=di=dt=gT=m=t& =z=~==O===c=h==w=th=[==nm===v=2=b= ==5b="==O= ==-=k==b==O==N=S)=6==a===)f=y===h==,=q==~_=x =p~=j=hu=hbJ=g=g%=l~=p~=q7=p=s[7=oJ=j˪=gh =hJ=b=WQ=Q=RK=N!=G=@ d=<==+=?i}=B:r=DI=G=JE=M=PA=T=W=X=W=Y=[=^ =`y=c"=di=dB=e\=f=i=ju=pk!=v_={=~==I="=@=v==z==Dm=9==a=C=~S=tb)=4G=>==k==C ==-=ޛ==+==~=E=i="=J===dž=&8=)=(W=6==l=s==|==d==d]=y=q=k=h׏=g=e5=c=bs=e=h/=ix=j1=i=gT6=d\=b=_=Y(=RV=N~=L2,=G=Az=:9=7=;=?o=AKu=Ck+=D=GS=J\=NC=RG=U=Vy=WNj=Z%=[Z|=_=Q=cZ=f=eq=d=gx=j=m}o=p;=s=v=y[={=ʝ===`=z===g|= ==l=I=ī=z=qN;==m=%O=F=e==}=A===m= ===,j===L=y==[=2a==A=E=ܱ=H=Y=m==`{==}З=uR=o-=i=f]0=e=c\=`=`J=_/=b=c1b=b'=_=a3=`.=^=X+=V"4=O=L;=JD=F3=<;=4=7t= =\TB=\d=[Z=\l=]Z=Y=T=Q=OG=M =J#=@q=5=2~=40=9sX=<\=>=?=@s=D=Hj=J)=NW=U=W=[R=a/=fhD=f=g=g=f=h =j+<=l$=lh=m=p=rUU=rf>=s?~=ui={j=a===[===Y===P=G=K=y6====x===9=e====6= === =hV=k+= /=#'=q=zv=f==Y==A=25= =}P=|=y=rB=j=c=_=^?;=_b=`U=`W=^!=[=X9=Y-\=XR=Y$=Y9=ZT=U =P؁=OKi=P=N¹=H2=A=B =G=H=LP=Qď=WE=]_V=bC=h?=j=jA=ii=g=fm=g>=iqP=id=iA=l3=n=o'=o=p\=s=z9k=~=Y|=iB==d====7==O=Q=K=|y=E=k=dK== -==-i=="=Z=G=@=====)M=qr=y=e=m==3=[==?= ==|D=y`=zBx=wƗ=rQ=jH=a=^.S=\ =\@=]d#=^j=\~=Y=X'*=X=WMm=W#+=Uʣ=RZ=NtM=Le=ND=P[=M=C=:s=3i=2!:=4=6.=9+==+=Ao=EE=Jv=M7=O-=SWk=[p=d=h!D=k=m=lf=i=g==f"=fr=g=h==h]=j=k=k=l=m=r=y=/="?==N=`u= =:== B===;={>=X=^=o==c=)=`==P^=q=,=F==}=G;=h====-l=a=~=}|=~5=b==O=={4=zB={G==z=u4\=mm=e=]p=Z"=Z=Z=ZG=Y=Wxc=V]=V=Un=Tϧ=RbT=M=JK,=J=Mv=Ox=L=C=:C=76=4=5=85h==8=w=X=s=*==|(=v=2=}@R=zJ={ ={{=zW=zi}=}=XV=u==P=}=}d={r#=x~=n w=a=^=\ a=[=YQ=V"S=U=Sx=S=Tч=Ty=S8=O=J=Gt=In=N=O=JK;=B.===;~=9Z`=8V=;c=?=DE=K=Q*=T=VD=Z:=cB=j=qX=t=s=p=m[=j0=h=e?=dA=d6=dDQ=eS=d=aI=bW=ckE=es=h=sN=~y==(=$====~d==f=}@=|`=w[=n==(=n>= 8==== ==^B=0==VE=#==~=yKl=x8=x&K=xDi=y =ye=yj=} =l=/=T=v=vQ=~=z=q=f=`o=`=`=]n=YL=T =O=O[=Pn=R=R=Q=N=I!=Ey5=G =LwT=N=JR=FA=B=@==1=<;8===C =L=QC=Sl=V=[mo=d=hz=q=s`=s'w=qQ=o=m=k|1=h=c=a=aq=`ن=`MS=^=]q=^{8=^=^=c=kh=z==b=M=c=X=L=c}==m=~,={=v:=ux=g=/G=ܡ=|=====Y==6=$=}==|K=t:=q=t}=ux=u9=x]=y2=zO=|R==h=?=F=HP=}5=v"=k=e=`\=a=b=`=XyR=O=L{=My=NA)=Mx=M"=O=Kg=F%E=DXK=H^ =Lv=M=K=H^O=E[=Cu=A0=@=D=IG=M=O=R=Wp-=^!=hjl=qq=se?=p#=n+=on=oF=m=k=h|=c6=`X=^=\g=[`=\ =[?=[?=ZU=ZŤ=`=l =x)=}F=O=G[=I=~l=}N==|B=K=~=x0=q==4=k=X=s=M= === J=R=G==}=y=s5=o=o=pX=qo=r_=v4=xs=xd3=zs=6===X=={=r =k(.=b=`=`=_=Z=S=N=K<=K.=K4=Iį=Hy=H=G=D=CoC=G,=Lw=M=Ky=Jo=H=E =D=E67=Ez=Hy=L=M=O;c=TPG=a,=q=t=r=l=n=r=r '=o=mۜ=i=e>=`=[=ZKR=Z =Y0=YD=Xt;=W, =X~=_=jR=q=u=s=tF=v=v=w=}ӣ=M=K=xA=q=m<=Z=6=8=E==+0=T==E=ph=W=F=|TF=w=r=p/=n=m=l+=l=nE=oK=pN=s.=x=~=o===-={K=rU=js|=d=`Z=] =X|c=S=Q=M](=J=I:7=I0=GT==F\=E=E =C=F*=J<=L -=K=KP=K=J=H=HH=FGU=F0-=G=I:=K0=M*=Vvs=f=n=p'=l=k=o+=s]=u=t%=p|=lb=f=`=\%=Z&=Y5=Yp=Y3=VKx=S"=U=^Y=f؈=k]=ia=f+t=f=i$=m=s@!=yOQ=}ފ=z=r9d=m=jC==p=JH=?=X=04==$=q==}U=yp=v=s+<=n=lp=k=kC=i=i4=i,=iš=kn=qHr=x={=N+=T===xj7=q'=k=f=^p=W*=R_H=Nf=LP=J=Ha=Ge=Fj=FF=F?=F=E+=ES=FC=H]C=I V=I%=J_=KD=L ^=K=I%=E=D=G^=Ijj=J =L\=Q=\ =fԟ=isz=i,=k)L=pq=t}=v =tp=q=j=e=b=_}D=\=ZQ=YT=V^=R+s=O҈=SQ=Z6=`p=bvy=_=[%=Y=^=e=m|U=r"=pt=nn'=k#F=i=k=б==h====X=P=D==yE=tRD=rY =p!=k=i=i=h=f8]=e/=f]=fk2=g=mC=u=x4=y=}g3===wh=r"=oP=hDI=[k=T{$=OE@=J=GQ=F=FM=F{ =F=FmY=F=F[=E\|=E=F=G9=H =Ii{=J+=M=N4K=L=I)=F@y=G==Jl=J=Jx=JXh=Nk=XD=a=eH=hC=lY=o=pH=pj=p#=m S=h=f=d*=cc6=`=])I=Y=Rs=N4Q=N$n=R=W&=W=Z~O=UM=RS=S~=X/=^=eh.=i=g=c%=c=i=mR=ڎ==T@==-=T=E=D=m2=x=sw=os=m=j܈=h=h=g)=fk=eC=f=e=c}=f=n=s=t=vf={=C}==ym]=s=n=f=[=R=K=G=FZ=EXC=Ef=F=F=F=F?=F=Eo=E =Fd!=G6=H=K=M;=N =Nh=Mo=Is?=G1=G=J=JxL=H4=HW=N$=X=]IU=a=ed=g)=je=im=h1=gT=efX=e=b=d=d|=`=\yo=VnR=P{=K=Kz=P#=Rf=R =P܉=O@=O*=O=Q=X=_ߐ=c7=`=^Q=a˷=h=k==O=}=h=4=q= =aE={&=v=r ;=m|=i=hK=hl=g=d=c#=c=e=cw=`e=c:=kk=o]=r "=t=z=.==y =s1=oP=h==[ =P^O=J%=H@=F/=F=F>v=FS=E==FKa=G=GT=F3=EI=E=G=K=N]=Pz =O[=N=Lq=I =H=I=Iv=G=F=JT=RTx=W|=Z=]=b|=cL=b,=`{=`=an=_*=]=_y=b=b0=_c=[=V=O(v=I=H\=L%=M'=K =L_=ME=MV=L?=N,=T=[=^=]uO=\8=^Ly=e0[=gA=n=c=n=_=+j=؄=p=@=z^=u?=p=l=i1=g$=fJ=cݙ=a$G=`+=`}={=zTq=u=p=i/=_=UT=Nk=Iz=G=G=F=E=E+~=D==D=Ff=G=EԴ=D=Eߥ=Hp=L?=NB=PGW=Oa=N/=L=K=KV=J=HM;=E#=Eh=J=QK=W)=Ze$=[N=^=`4=_}=]k=]=\|=[?_=[=^Q =a=b=^=Y3=Tr=L=Go=F =F =E==F,=I=KD=J>=Jߘ=M=T =Z.=]=\ߔ=[+=au=mb=jR===n=HQ====~}={e2=v ,=q=m!=jx=f=d =as=_=^ou=\=Z|=Y=[:=a=ij=n=n=pd=u/=tPC=rH=p=k3=`=Z1=T0=Mv=Gq=E=FA{=EQ=E[=D=C =C\=Do=F =FD=E=HdZ=H=L=O =Q$>=R=P{=P\=QC=P1=K =G=E$=G7=K=U3 =Z=Z)=[=^=a@=`=^F=[=[=[}=^=a=d`=do=`#=ZY=T5=M~=G% =C0==>v=@=C=F5=G=H=Jܼ=P,E=W=[=\B=YB=[m=g=w*=wM]=g=4f=B=bq==G=%=}=z=uF=qF3=no=ioV=c==`U^=_Il=_"=]6K=Z=Xs=Z=_=c,=nH=oj3=n5=o=p=mq=j#=hf=`=X,=SM=P =I5=C{=B=C=D:=DX=D =B=A=Bc=D=FZ=I\1=K,=L O=M=PF=S=Tƍ=T=T=TRg=Sf1=N=H%=E =HU=N=Ug=Y#=[>=\:=^ͨ=a=bS=`ː=^B=\=^O=en=hF=hQ=f=`z=Y=R`&=L4=DJ=>=;(=<=?=C\=E6d=F=J!B=P=W&=[~=Z*=X!=Z=ks={G=o==/===Y====z=vi=r=mIe=gB=be=^J=\٩=\'=Z=W =V;=X>g=_=i)=nM=oq=n=m=l=i<=bV=\J=X/=S=O=LRP=GIM=B=B i=B =Cp=C=C=B?=AW<=A=B=F6=J=M*=M=M=P =T=T=Q=S{=T=S=:=;B=>l=@=A@=C{=In=P=YDz=\%=YHD=V=\9=hE=ja=a=p====m=e==;e={=uBQ=osq=ijl=dU=_=[=Y=XQ=U=R5=S[=VR=_S=i=m=mF=kgD=i=i=b=Y8u=R,=OҌ=Mi=KD=I=F :=D=CS=CG/=C/=B\=B;=A5P=?u=>=Aoa=HW=Mq=NX=M=O =S$=Tc=P=L=L=O=N=K=H$=H_=K=Pd=V]=[W=^-=`&=d =f=h>1=h=gg=fq =iA[=o m=pm=k?q=f=^v=T3=K=EC=Ay==&=;k=;=<==H=?=B=G=S =\%-=]b=ZfH=V&=Vx=Z=ZW=T=o==,=~v==z==x=xb=sO=nɠ=g=aP=]=[]=W)=S=Qy=P4)=P=VEP=]=fj=i=g=f=e=bD =]H=Vj=N|=J!=I=H\S=G{=GK=F?=E=Dd=B=Aw=@V=?\=>/m===@|Z=G=MS=OS=M=MZ=N=OWc=K=H_=H*=I =J\L=J4=L$=KA=P}=V=[=]l=a^=e:M=h=i{=i=iг=i{1=i,=nA=r =pY(=kw=b,=YN!=R=M5=H=F=A=?====B===>=B =MC=W=] =]=Z=WSu=UZ=Tޖ=S=T~S=3=%j=Xu=z-=z=~9=~B=w!$=qW=pXy=h{j=bc=^=\=Yi=V4^=R=P/=P6=S=UV=]=_u=_Ĺ=a0=`=_6=Z?=W]=Mc=G0=C=D2=Bz)=C=Ew=E=D#=B=@=?=?L===;@=;u=?=Fv=L,f=M=LR7=J=J=H#=G6=F=G =H=KN=N=P&/=R? =U=Yc=^j\=cQH=f[=iD=kO=kM=k4T=kL=kh<=k=m=pw!=nC=g- =^-,=Z==R]=N =NS==LlE=FU=B'=?J=?D=@=A=F[g=L=S=YQ=[=Zj=Y2=XC=V =U1=WH=:=p=~e=xz=uB=rق=r{=oT=kZ=h4C=dX=aL,=^=];J=[=Y=Uo=Rv=QS=S@=V9=Y+u=Yu=Y==[D=^ D=\Rh=UW=N=G X=Am==A=;K=;=JA=:u=9=< =B|[=GU=Kx=K^=H=E`=D=E\=GMT=IA=L=N=Q=T2=Vr=WՃ=\g=e=j =k($=nO=oփ=n=mQ=m.I=mN=m=mS1=m=i=c>z=\W=V|_=R:=Q=SMM=QA=K=FeL=C=A\!=@=@=DF'=JWO=N0C=Q=VU=X\=ZS=\d=Y=X=Z/=!i=F=yBw=t1=ox=m/3=kv>=it?=eR=b\=a=_0=`-=`=^Z1=ZT)=T=R5=Q=T=U; =U7_=T=V =ZG=[Y=X=PV=Fn=Ai=:ӭ=6=3=5lM=71k=;6=?=@=?=?=@=?l=:8=6=6H=: =?=F =J=J=G=D=D=Feq=I[=L=O_=S=U=Wh=Y'2=\=b|=hb=k؅=m=pU=q;=p=oy=o*S=oڡ=ov=nop=k%V=h=bY=Zw=Vo=U=U=U=S(=M=I@=D=@=>=?=B=FGf=Hh=J =O =Up=Z=]wz=\=Z.-=ZQ=J=D=vu=p=lL=j%=f2H=c=bs=`=`=`3=`=` =]WC=XrT=T=Q=Rk=Sm=R=Qa=R!F=S=UX(=V!=Sl=Mn=EO==n =61b=2(=0-9=0_b=4R=9 =< ==>=<=<=<=;=7F=4a=4=7=<=A-=GC=I=H=Ep=E=GI=Kw=O=T=WN=W=Z=]Y=a=d5=j=m=n=p=rʢ=q=p=q =rCy=q=o=lK=g$H=_q=Xݸ=V$=UCB=U=T=RM=M/=Gρ=C=A+R=>ù===? D=B=Dt=ET=IQ=P=X=]+=[=Y&h=X ==wy=p=mk=kJ=e[}=`r=_P=`,=a=`+=`(=`6=\w=W=T =Q$=R)5=Q=R5X=P=O =NQ=O=QP?=Q=O=H=Aݖ=w=Anz=D=I$=R=Y=]=_,j=^=]=o=n.=l=l"=iK>=e=b=c!=d=ckW=^7O=Zq=Z=WW#=VR=Vxx=T$=WF=X=T7{=P:#=K=G]F=F=G(=FN=DZh=BQ=>V=;z=8 =3.=/J=.=/Y=.=0=1 =2=0=/S1=.=.=/=1tn=3=79===Bʪ=Gu=I>=IE=Jɻ=Nӛ=U=\1Z=`V=b~=cO=fw=k=m=l=l@=ox=tg=tE=sf=q=q$=qf =q{=o|=mg=i=el=bF=`=[=U=Qt=OL$=K=F}L=CY=@$=>5=;$=;9=;M=>=A=F?=L8=S54=[=at =e=f=fu=n=m =m=l=is=db=b/h=bh=c=`=[=X=X[4=Yp=Y=Zn=[6=\O=YO=S*=NL=J=F;!=CE=B1=B[=@==~'=9=7==5=4T=2=3w=5=4=2=3=4rY=1 =-=-1=."=/|H=0=3=6}=;%=@=E=Iu=J=LH:=N:=U"=\(=b*=d2=e=jA=nU=q=o$=p =qY=u( =u =uT=shX=r>=rT=p*=m=l=h=e=cf=`3K=Y=R=Nb=J=G=E=C=A-=>==B$1=G4=N@v=U=_=e=m;=pO=r=rN=uu=pv=la=f??=`=`=_=^=_n=Yu=U=V) =Y=\H*=^4=_ E=]o=X1=RcP=O6=K8=G=DN=Ag=@x7====8=6=3(=5d=;=8=>N=J@A=J@=<=>[=9?=0=-=,/=,^=.=0=2f=5C=9=?=Fm=H=JBz=MJ=S0=Z@%=^@=b_=dkt=f\=j F=pU=r=r=t5=w^0=yJ=zc=yZ=wd=ve=u!=r-=mm=i=f=c=_B=X=U@i=QZ=L=H=F=F E=D^=A=@=?==za=L%=D<=Jbg=Q=YM=b%=m=vO_=xX=x[=xm=|c=w=l[=ez=^*=[ͧ=[=Zy=Y6=U=R`=R8=V}=\=]e=[=Y=U8=Q=N=Lt?=ID=F8=CA=@<=<=6c=2$=4=;;=E=SF=]ϲ=dr=gK=_ND=P1=C@:=5=+=)J=+B=-=/v=1}`=4X=:=ATf=F'>=H=K`N=Qߖ=X=^5=a=c=dJ=ev=j2a=oD=r|=uh=yl=}qG=}?="T=~=y=w=vA=p=j =d=c=]=U=Q.=O=N=K=H" =Fm=En=D=Cٮ=C=A=?5=>=@ 4=F<@=Nt=U =Z=d=r6=x5=z =xzG=}n==w=m=c}B=\@=W=X=WW=UE=S=Pqy=P A=TLE=W8=X)=U[=T;=T=Q =Nn=L=JH=G=D =@=:\=4=6=8=G=_=w=uԖ={͇=3=~=k=N#.=2RV=( O=([=*=,=.=1=6b=<פ=B=F;=J=QA=[=_=e?=h4=g=f-=j!=nK=q@=u"=z9=~V=7=p===zK=v=s:-=m=g=b=\=Vx\=Q =Nk=NH=OS=M=I=G=G(O=F =EZ=E=D*0=B`=B,=G=M@=SR=W =`@=i;=ou=t2=v==s*t={3'=|h=v=k=a=[rq=W=V2=Ug=S=P6=Mi=Ne=P=TlL=T=R"=Q=Q=Q =O =L=J=H=D=?+=9/=7*l=;=L5=eC=z==lV==e=^=pAq=G=3u=*6=(d=+8+=-=0,=3p=8Y=?-p=Ev=KE=P=W=a=i=lb=m=m)=l=n=qI=t`=xCE={= p=g[=\=@(==|=v=qx=l{=f=^ó=X=Tw=PU=Nu=O=P)=M=I=Hh=I=JM=H=G=F=G=I/=K=Nε=Sm=XS=^=d=jZ=k=mh=mBZ=v:=ug=nYv=g=a@=Y=UH=S~=T,=Q֭=On=MA=N=Qh=R=RM(=P|k=OM=Ps=Q%=P=Ph=L=I89=Bt=<3=9p=:b=Cd=\v=o`/=rS==(== ==pu=R7=6P=*S=*==-=/k=3=7=;=Db=M=T+=W=_=f=mlw=pe=r=qȋ=r=s0=wI={ =}m=G!===u=j =t'=~gE={2=w==o1F=f=_9=ZPa=U#=RY=P=P=O{=LG=IHv=I$=L8=N=L0#=Jc=Kg(=MJ=MBl=NH(=OG=P=TTk=Z=`=deh=dW=d=bT=r9=o=k=fm=_=X=Ta=R[=Q=P=O~=No=P[=RT=R =Os=NBn=N&:=O=T=Ua=SӔ=N=G=?&=:[=95=<*=HJ=_7p=0====m~===rs=IR+=2^k=-=.=/=1=5=9rZ=?=IV=QY=W=]Cm=dP=i=mM=s-=v;=u[=v={U==,d==s==+"==W==Ĵ==|q=s d=k=a=["5=W=U=Sk=Qu=Mg=J8=If=J=M=N=N_=M=M=N=Nw=M=Lɳ=Mnn=P=U$=Zc=^2=]=\=\=lm=g 0=f=`=[ M=X =TB+=QZ=O=O\=Nu=NN=P~=R/=P =N<=Mu=Om=S`=V=Xn=V=M=F=?2e=:=:kx=<"=Hi=ga====Xd==Y=A=c@=A=7=0+0=/=09=3=8)e=<=B:=H=Q=Z$5=c"=gi=lx=q[=wm=yK=y^=}=+=7===C=~h=rc=_=a==E=#=~]=uZ=jgB=a=]=[I=Yf=Vix=SiH=O*=Kr=K u=K&=L=N]=M{=N=M;=Lr=LDa=K9=Ki=KW=Mğ=Q=V[[=X(=Vh=X-=Z2L=iG=b=`=^f=[=X=U=R>=OG=M/=M3=M)=O8=QQ=Q=RZ=TM=V=y=MP=9=/hH=.u=09=3=7=:==v|=B0=J=Sc=[8=b3=iY=nn=s6=x=}eR=T===`=+=:=v==؎==!=0==#=~=u =m=f.=bP=`=_=[=V\=P%:=L{=L=L'=L=L+=K=J=I)=Iu=I=IJ=IӉ=J=Ko=M=O/=P9=Q=S=U;=c_=_,=_@=]=[=Y=Xd=Sސ=O~=N=Ma=M=Q=S=V+=Z@=]fP=[=]=]=Z=T,=M`=G=C=@ ===@=HJ=X=x&= ===V/=uz=bZX=?=.=)b=.=3 =8==3=>O=@2=E==M =SE=[K=c!=i=m=t=|m=I=W====,=~==z====u== =b=}=wAB=s=l=f=d%=b=_L=Xw=RM=N=L =CV=HY=J=&=Ht=Iϭ=I1=J*=Lk=T߷=_%=hC=o=w`5=wҤ=w|=~A==E&==T==={=s%=pS=qB =s:*=s=tHg=s`=l=e'=`[,=]k=[=Y/=Uԑ=Ng]=FN=C%=Ciu=C+=A3=Av=D =Jr =P =R=Sn=R4o=OL8=MDW=J=DR=====A=F=bs=`=_=_=^=[-=Y\S=W2=X=^=d~=iD=i4=i=i=f!=b=avr=au=a=_=[a=Vob=Sr=Qr =MB=GØ=AX=;=8o=:V=>Vb=C=EC=B==}u=vI=n]=n=p =pp=m=g=c=c$=c=dB=ar=]Ml=Y=S=MO=G-=B#=A=A=A =A=CQ=H=O=R=Q)=OFK=O(>=P=M=H=E=J4=K==If=`m=^=]9=_ m=`X=`D=]i=]g=_=d;=h`=jV=l>=n=nH=mJ=k =i =edž=b}=_J=[=V*=Tb=Q%!=Ib=E1=C]=A =?4=>F=@>R=CZe=B=>2=;_=9=9ݏ=:=;{==jS=?q=B K=C%=E<=F#=FB=E,=G=N3=V$=_=eG]=fm=dJ(=g=oo=yz=~c=χ==Db=S=z=r(=mK=nv=s =t/=p,=i@=a=`==cS=er=b=[P=X=S~=L=Fh=Bni=B=D9=EA"=D =D&H=Iw=N=N=M=O=Q`=Q>=M=J_T=KD=M=MT=I6=^x=^=^3"=_U=a> =b=`=_I=a=d;=fpb=h{P=k=m%=o7=pv=q=ns=h =bk=\Q=Y=Us=P=M=I=H=I/=Fc=Bԓ=A =Bq=D=D|=?=;=;=<===>=>=?=A<=A=E=GB=J=L}=L Y=Qm=Yjb=_=d׸=db=a=byI=i=q=w@=}=R==N=zZ=t=p#=sf=w.@=v=r3=fN=_!=`;=c`=c1=]~=W=T)g=PG=K=G=EQf=E=G=G=F.=F>=I =K3=K1=Lq=Pu`=R .=O7=J7<=I%=L=M=KoF=H}=^=^=_r=`=b=d=cé=br=c$=e=f=iT=k=mɷ=o=qs=px=l%=e{=_u=Z}=Vii=R=P=M>=KN=J=HP=F[=D=C2=Dm=E=E@o=A2<=====S=?5=@=?Rc=?`=?{=AOm=C{ =G=M|=RM=S=Rf=TJ^=Yd1=\ =_=_׳=_"=_c=dv=kl=s=w=}}=Q=|u=yI=w{=v\=vl=v;=rw+=k^=d=`$=^=_ =^#=Y.=S=O=O[=M>=I_=HM=G=H#=Hg=G=Ho>=H+=H-=I=Q=T@=R=JZ=Fh=F#=J5S=K#=I:=I#=`~I=a-=c=d=dER=f]=f>=e==f=g%=h=ic=k=k T=k=l=j83=de-=am=]=X =Ui=TL=R=O=MP=J=Hs=G;=G}=Fѳ=Ge9=F=Es=BR5=>\=>yY=A$u=BN*=@=@nl=A=C#`=F=Kg=R v=W_=X{K=Vf=U=X=ZB=]L=^=`0=bSL=g4=k:=r/+=uf=s5=w&=vk=v`=zU={%=xq=s=k=d>=bq=^b=[==X=W=RW=P=O=OM=N=M=L&=JL=HN=HPC=H =I=G_=D=H=Qr=R=N=G=DS=E=H|=JE=J=K=f=f=g=g=gn=g=gV=g=h7&=i=hm=g=g<=f=fK=d =aD=^=[A=X=V%=V=U=T=R=Owg=LY=J=Kva=KB=Kq=Ir6=E=B9c=?===?=A1=BB=B&=Cl=D=F]`=I{=Ny=T[=Xw=Xl=WM=X(B=[]a=^>=`=b7=e}=g=k=p=sB=qe=m=l=o?(=vOa={P=|=w*=nS=g=b=_ =[=XdA=UH*=Sws=R=P =OU>=P =R=ST@=Q<=L=HD-=Gc=HW=F =B =B=HEb=M2=Mf=Jo=G=G=HO=K A=Lϡ=N=O^=ij=i'U=ig=h5p=gt=g*=hPw=h[=hqJ=hr=f=c=c=b͔=`+=_=]:g=Y=V&6=UA=T՞=U=W!=W=U'=R_=Od=M=MkI=L=J۟=H=Dk=>=<==WY=XP,=\n=_j=a'=cl%=f=h=h=k=o=qJ=l=fv=f=kO=t=|l=zȿ=r=l=e==a=\ҍ=YJ=UX=S}=S=R==OR)=Oi=R=WN=X0y=Tt=N0=I I=F=Ev=C`=@J=@=D=I=J,=K=NE=M=Nv=P).=Pי=Pr=P:=hɥ=hN=gy=gV'=h=i=iy=i=h=g:=dx>=bZ=`=^=\=[ =Yh=V=T =TԜ=U_ =Uu=U=W=T=QՇ=Ob=NR@=L==IE=F߆=E=Al==?=;==D=@D=C3a=F"=H=J=K=Kʄ=Lt=O8=Q6=Sm=V=Zp=^=b4 =c=e[=g8;=hm=g@=iMY=j]=h=d(=b=du=kh=uy&=z=xN=r=j=e =bd=\=WBT=T[=SP=R=O=M2=M=RO=W=X=U‰=N =Gϰ=Di=C=A=@#w=Aw=D=G.=J=PX=Tӧ=V=UC=S]=P =N/=L=hB=f=f=hH=k0m=k)=lo=k'=ig=g}1=d`=aU=\"=[L=Y=X=X>=U<=T}=Uo =U=Tg=S=RA=S.G=Q:=N=M=L@<=I*=E n=CG=B=?f=1W=Aq=E>f=IY=K0=M&=M==M=N =O}=P=Q=T`=Z=`I=c]=d=f9=h=j&=i%w=h=g=b=`6=a=e_=o =u2(=xD"=t"=q=lv=gup=b=[ =V%=Ta=Rs==O}=L=J1=Lx=QSO=So=T!=P=K`=G}=D=CNJ=Bt=By=C=EF=GH=J=O)=V=Yx=Xs=Ss2=M=H=H&=juB=is=j_=l5|=lA=mC=n.=m;=j\=hW=eg=`;H=[t=Xr=W=W=Ve=TQy=RB=SZ=S=P=Nz=N=P=P1<=O=OG=M=J=F=D=Br=@=@=Bf=EH=H\=K=Mu>=Ne=O2T=O5=P=Q=Q$#=R_=W@=])=b =d=g=jA+=j\=jE=k:=j =ej@=bC=a<=b`=h<=nux=s=u-=t-=tO>=rV=jO=`=Z =Un=S=P8=M=I=G0=G{=JC=M=L=L=bH=e:=d=dI=h=p=t\ =r=ip=d[=a4M=^=ZE}=T/=N=RZ=X+=U)=Lh=H=J=M=N,1=Nh=OQ =O̥=O$n=NС=Ndh=L/A=Iփ=ICw=J=Lv*=J=Gʨ=E=CU=C?=D=q=lH=iS=hjD=h$=g\=g}=e=b=\=VR=R=S=V=Vk=TW=UW=W=XJ=V=T=RD=Sf=Vq=V|=R=PI=L=LB=M\=OX=T~=X=WW=V.=T=R=RV=Q=Q`=R6=Q1=Rς=U=Y,=_W=d=b=b3p=c=b&=]J=Z=^C=h&.=q =q`=iO=a=_-w=`[y=by=c%=e=i=n=n|%=jv=fA=c=c,=cT=`u1=];=Zd3=]+=`X=\=Sc=N=O:=Q=S4=SfE=RN=P3=O H=Nmn=N=K=I=H%=IQ=Jt=HZ=G=Gl:=F=FJ=Fp[=o=mu=j=k=j_|=ht=fX=c%=`M=[o=T9(=Q+=Sc-=V=Wc=Vcp=Wc=X=Y.=X=WՖ=V}=Xb=Y=X=R=Of=Ng@=O<=PI=S=Xx=\S=[q=Wn=Ts=S*=S+z=S=T=T2=Rr=Q=U=Woa=V=X7=Z\=[=Z%=Z=X/=Yd=_=f=n&=m5z=d=_=]j=^ =`=d%=i =m=l@=hdg=gK=d7Q=cH=d=d=c=`=] =_$=^=X]=R=P#=R =SJ=TBt=T2=SE=Q=P=N[=Ks=Ih=G=G=I=J=J=KJ=L|=P=N=K=mw^=lY=m 4=mJ=j=fq'=d}=c`=^{=Wo=R=R=T+=Uڊ=V8|=V=XN+=X\=YBf=Z=Z/=[?z=\w=]Q=Z\=Wh=VK=T_=X=Y*=\6=]z=a=^=Zz=W=V(=Uh4=UF;=U^*=R=O=Q=Sl=T&==R=R=T}=V=V}=V6=V=Zy=`g=f=hu=f=b7=^~q=]W=^7S=`!=gt=n4=m=j=f=d=cR=b.#=aB=`|=_c=\@m=Wz=U7=T=RH=P=O@=P7=Rp=S=To4=T=S>=P=M=J=I,=G=HC=J=O<0=O)5=SĦ=W=Z\=WS=R=jF=k=mo==lPl=f_=b=cE=bp=\=X=Ů=T(=QP=S3^=T\=V_G=W =W=Y=^==`9=a=a=a=_)=]?=^b=_n=`q=b`=d\Q=f=fN=d=b=`y=[L=X=U=SY=P=N=O=S&<=T=S=S2:=T1=TF=Ta=S=Ui=Yi9=^ =`=c=d=bk=_9'=\=\.k=_io=cd=g(=hn0=f=d=c=bX=`Dl=[=W*r=X7=R1=KȌ=H^=J=Km=L=L˖=NH=P=S=U=U;=SUV=N=K{=K=KP=J=IQ=K=P=UF=Y|=]u=^2=Z=U =i W=kv=jٹ=fAq=aS=_M=_=_Z =\Ky=X{=Uk=Q=Os=P=Sb=V=WK=W=[=c=gY=h%=fZ=cL=c!B=d =e=hx=h2=m=lb=l=i=g+=g+=b=^V=W*=Qc=M=K=Kœ=Ot=T-=U{=Upe=U=UQ=S=QWB=O=RH=U=X)=]o=a=d =a*=^K=]I=^=ae=a`=a =a=br=a|Q=`#=^7=ZP9=US=PU=L=H=DS=Aw=BH=F=0=J=J=L _=O6=S=V1=Tw=O=M3$=Lt=Oq=P u=M=K\.=L=Qo=Xs=\=`.=]=Y==U Q=i$=kr=h=ey=`6=]=[}=][=Zi=Vj=T5=RM=Q5N=T,"=U=W'=X=X=\Y+=d=j8=j[r=fg=b=c=fU=h~?=k=p=q=o=n=j6=ge<=fs=_|=XWk=S=N=Jy=H=J^=M=RS=T)=V1=W-=V܃=T =Pq=M=Mp=O=T=e=Yb(=[J=\8=\=]=` f=b7=d=b1i=]=[v=[=Z=YP=W =U=PG=Iۅ=EI=C=>.=:`=<=B%=F=G=Jy=On=Rڬ=R=P=Nu=N$=Q=S=Q=NX=K=L6N=Om=V]=[=.=[B=[=Wr&=S 7=l2=kV=g=a7=\u=Y=X/=Xʻ=W=V8=U\=V'=W=Y0=Z.=Z7=W=V;=[=`k=eYN=dw/=aGu=^W=b|=f&=i3]=l=n,~=o =m^=k=g-=f =^Wh=Uv=Pȟ=O=KͰ=I=H=J=Mq=R =U4=WdW=Z>g=YF=Uv=Qk=N6=Mv=Nw=Q=S0P=S=Tk=Wu=\=bi=g׿=hg=b r=Z;=UCo=R`=SL=T!S=S|=O#=J=F=B]Z==V=8s=6a=7=<=@m=B=E=KD=MQ=MQ=M=N=R$=S=SL=O=L]=Kh=LN=O&(=Px#=Rx=UO=U=R=No=i=h=b<=[ˏ=[3=W<=Vj=V=W$=V=Y}=ZV=\=];7=[6=ZD{=V=R=UA=[I=`j=_=]=\=a=gFP=ib=k^=jŜ=j=jg=h-=d&H=`=[!=Ti=O=Nd!=M=L?=I=J+=N`=S=YV>=]?=^"=]x=X`=S=P i=N=Nd=NȘ=O1=N^=P=U{=\ =b/=gF=h=cSO=Z.=R=OP=Ol=QG=P?)=LT=Ip8=C&=<=9,=7=6=9=={p=>=?lr=CU=Fu=He=I=LZ=NW=OL=P)=M=J=I=K=N=L$=K=Lv=N=P@=Oa=Ku=eݍ=`(=[U]=XC=VC=U=V=Wa=W3=W=Zi=]TD=^z=]=[=W=R =P=T=ZY=_kF=a$=`WC=`ʰ=fT=k=k=j?=f=fQ=gN=b-I=_”=\˙=Y%=R6$=P=Pq=O=L@=K==O=TC=] =`+=aȽ=aJ=\kG=V;=RUe=P=O==MM=M3=L=M]I=S=Y˱=]|=a=c=cu=_(=Y=U=Tt=Tj=Rm=N{=Jg=E=?%=;1=9F =8=9==%=@X=@ =>P=?o=AQ\=D+\=G=Jg=Kc=I]=Fм=HY=G=J=P@2=PEq=L=H[=I=Muj=O=NZ=J=aL=Z=Xy=U=Uъ=VN=Wע=X=X7=Xt=Z=] =^=^=\n^=X9=T/=T*[=Ws]=\Ԁ=aH=e=f=g=kH=n=l|m=h0=ec=f!=d=`dd=^#=[#=VX=Sy=S=Rz#=P=NUQ=N=Tk=\=ak=bY=a^=^%q=XW=S(U=OP=MϺ=M5 =K=J&=Ko=N8 =U5=\=] =^i=_l%=]6=\_=Z=Z6,=]=[8=U=OM=IQ=E=?3=;[=; =<=<=@V=B=?t==w=C=B!=E(=Fc =D?=C=C!m=D=H]=O=S@t=R=M=IRv=I=Lą=OI]=OH=K=_=Yo=V=V[u=V+=Wb=Xm=Y=Y\=[)=]=^=^=^=^=\=Z=\z=`2C=c:=f=i=l=oh=r69=r]=kβ=e=d^=e'h=bXU=_=\08=Y=WG=U1=T=TJ=Tc=R`-=UFw=Z=]Y=`b=_ =[ =Vs=Q=K0=I=IG=I=I=KB=N6=RK=X=[=\=\=\0 =[pS=[z=]M=bT=d'w=a#=W=Op=KS-=H s=C=?I#=>W== == =?j=?P=>-n=;yH=9=:K=%=F=H|=J=O=SR=V=XA:=ZrY=\[=\ =[>=\>=^=c=eX=a?=X=R=N{=L@=HJ=C=?M=9=7s=9=:=9=97=7k=64=7K=<=>=>=@i=E=K=R] =V=V=S=N$=I8=Gm=H=K=L8=Md =X=V=W˥=X;k=X7=XϬ=Y)=YA=\=`s=c=a=_'=`I=cJ=e=h`Z=jM=k=mn:=o=qԻ=u,=v-=s=n7=d=`c=a=a=^Y =[=Y=Z=\S=[! =Xe=V =W=Yi=ZA=Z3=V3=S=Un8=R*=N_=J=HT=F=E8 =E =G=Mit=RF=S=S=V=\=^`=]=\%=]v=``=dK=cd=]=VÍ=Q=O<=LK=G=?C=8-=4=3=4=6=8A=88N=5=3S=4=7h=:w=<=@]=E=Nr =V/=W=V=Q=K==F\=Eg =FD=G#=J*=O=X@<=W.=Ww=WR<=X=X=X=Z6=]=a<=c01=c=ap@=c=da=g4=kn=odR=po=r_=tK=t=tI=p=gv=bq=^!=]w=^=_=^^O=]+=]3=]=^r=[P=W=U=VA=Y=\d=[=V=Q=Qs\=Qo=O}`=L =IY=G=E.=E/=I=N=R=Tς=VTt=Z6=`R=aCj=]=[=\=]f=]=]ߜ=X 5=ObV=If=K/=Fx ==1`=5#s=4`7=1%=3K=5j=6x=7%=6=4=3z=3X=6=7W=:=A4=J=Rq=Ue=U1M=S=O=K=Gυ=DS=B=B[=GS=R=Y=WV=WU=W=W=X<=Y=\;=`E7=c_=e =e1=ei=f=f=j=nM=rU0=u'=v=vK=t=p~=h=`=[R=Yv=Z)=]=_ =_\J=b4=`e=^R=\L=Yv=T=T#=V?.=Zb=];j=[=S=P4=P=Q=M=K`=JK=I:=I==L0=Q0=T=X=Zn=]{B=ak=d=`s=\=Z=Y=X&k=W=UN=Mk=EM=A=?=9 =4A=05=0 t=2o+=5=8g=9(=9d=8=7=6}=6=5=6Y(=:ݝ=DK=J=O=R^=O]=Mc=MK=J7=H =D|=A=A=Lg=Zo=[=Y=Y5*=Z^<=[I=[ =\=_=a=fm=j=m(=j@=h)=hL=j$=oI=u=y=x=vB=r/=l=g=a=\ep=Z=[=_bb=b=ebq=g=b}7=]=\=Y=U=T@=V*=Y(=\Tv=Yk=S:=O.=O=Nw=Lb=KC=MO=Ou=P =Ty;=[%=]=\0a=[]=^P*=aO=aG=]N=ZS=X=V=S=O-8=Ll=Hz]=A==,=:(=6 =2=/&=1=3^|=8==3==+=<=:=9U=:=;=7q=9b=>=Ck=Hd=J=J9=I/=H5=HO=If=H=G=G=Kn=Ro=_2=^#=]=]8=`<=a_=_%=_=b=iJ=p=t4P=r=l3=hK=g=km=r1=y5=z=w=r=mV=jo=i^z=f]=a4\=`=br=g=jC=l=jգ=d=a=^l=[@=X=Y=YM=]p=[^=W=R=O=M?=Kd^=J^=L=R+=W_a=Z=c=f?=bV=]=Y=Y=[l=[=Z~#=YT=WB=Tg=PZ=Lu=J=FhS=AE#=>=<+=7=3V=2+=5+1=;5=@L=A=?=S===;[===A3=B =D=D=D=E&7=E=FB=Hq=NS=W=^=b4=kL=`R!=aR=b=f<=e =bA\=b=h=q=v9=x5=s=l3V=i0=hX=l+=r=x5r=xU=p=o]=k-=i0=j=i3=f=it=l=m=oh=pj=lwL=f=d&=c=_F=^U=_q=bɱ=b=_=XL=R=O-{=L:&=H=H~=M3D=Su=]zZ=g%y=l=lZ=dx=Zg=U=S=UW{=W=YU=X^=V'=SO=P^@=L=K z=I=D=B;F=@-=<=7?=7.r=<=FZ9=Fu=E=?"=;V=:==AQ=D=D=Cڳ=E`=L=V=bD=p=y"j=x=a=c!W=eP=f9=d8"=b=e(=kF=p=t=r%=nh=k-=iEZ=j=m=r =t`8=p =ka=gh$=ew=e=g@@=g |=h6H=nv=rK=rx=q!'=n@=j6v=g3=f=f =dQ=f;=i-=j=g$=`=V\=O=L=F=E=I=N2=Zj=e=lK=ng=f=\=VE=RA=SF=UГ=X7=Y=X=V=U _=R=O,=M,]=J~=G=Dd=B0L=>i=٤=A%=D=D\=Em=K;=P`=`j=uD&=*==4=bQ=c?u=bF=b=`(=a|=ed=i9=j=h=l^{=jʴ=i%=iL=j=lY=lI=oe=k=e{=]j=_־=_Y=a=c=g1=mH=n=nƁ=m.=j_=h=h=h=j9=lb=mV=n=lnf=g=`C=Wb=N:=H=E=EZ=KF=S=[8=`m=b=`=]=X =U=UGM=U=V=X.=ZZ=Y=XO=Z7~=X =Sϗ=O=Ki=Hz=E՜=CR=Ag=C:=C4=IY=L=GnU=E7=>W=7T=4|=6O=A=L=Ii=? =7̝=7^=;T=>E=B"=C1=D=GZ=M=Yg=l=+=<==m\=d2=dQ=`t=^=^\=a!=d_=e=dS=c*=e1O=g=hl=h~=g=i;=iwb=gآ=c0=]=Ywm=Y=[=`l,=d=f=f,=fN=gG[=g=g}"=j=kV=l =o=s=rv=o=hzC=a=]Y6=R.=Lk=J =G?=IRT=O=W '=[ s=Yb=T] =Rg=R[j=TF=W6=XV=X)=Z|k=\=]=]$4=^G=_Zq=]=X=RL=LU=H?=Gh=F=Ga=H]=H=H5<=D=>=:2=7Cb=48=/N=/=8r=Ay=?Jx=81=6=7z=:`==Y=?=A`=D7=Klc=X=l+=vJ8==P=w==f=e7=av=^=_'=ce=f~=eT=c>=aY=a=d=e=cs =ep=g=h>|=b=`=\=W=X]=\=c8o=f=e~=b=`d=aVh=c=g[=kb"=m=n/=qb=tR=sr=o=hz=_Y=X=Ry=Opb=L]=K =K=OA=TD+=V,F=S=O=K|=M6=Ql=U _=X=ZE=\=^Ҷ=`N=`=`=`=_&=[4(=TB=N x=J=Kv=K=LH=J>=E.=C==*=55=0t=/|=.A=)a=*%=-k=3=1: =2=6=9t=:,=:0==M=OX=RJ0=U,=Z =\=\hh=]|=`@=a2=c =d=bY=\צ=T=Py=Q=OF=L=D@=;=4[=0 =+t=(D=&='a='?=(=*nk=*ذ=*N=,=-N=-=/=3S=97=?r>=J=e=D=u3=;=8=F==~X=d=d'o=d2=ec=gJ=g+=ex=a=\4=XI=W.=Z(W=]=a=d~=dW=af=^;:=^k=`o=d#=g=ic=kN=lY=jp=gV=f=h:=g0=f =g'=i=lE=m=po=r!=o=i"=`)=[=Z=Y=[c7=X=S6=P9i=O=P`=O?=O =P%>=Q C=Q=R$=Vd=Z0?=[ =\GC=_;=b=eH=gP1=jfn=hGP=cLx=Z=T=R=Om=I'p=@=:=4=/ y=*_=&=%=$=&b =)eg=+=+K=*!=(ӧ=(~=*N=/=2=7y=?B=X ={KE=ڒ==e=b=K=d={N=b} =b*=a=c=b=_=_J=Z/=W(=Vp=XN=]=`=`+=_/=[m=[r=\=_=d=h=kd=mP=o=nD =ki=i=i=h=f(x=d=e=h=j=m=o7=pY=m/=g4=a:=]P=]Ae=^=]=Z̲=U=QI=P=P=O =PtX=RI=Sqw=RՑ=S*=VD=YPa=]=aaq=b=d?=gx=k=mI=l =go=_l=W2V=S=P,c=Lj=Dy=<=7=4;r=-]=(@=#="?=&)=)=,=*h=(P='=]=)ng=.=33=7"=<A=Hp&=\/=w>=)>=q===*=a=}?=z=`=^R=\2=_=`^=\I=X=Vk=U=Wq=\{=aӵ=d=aE=\=Zk=Ye=Z=^`+=bI=hJ=l=o5G=o\ =m߷=lc=j=hŭ=fv=bJ=aC~=cJ=eZ>=g=i=kI=j9=f=bT=^==^5=a =b!=_=Y=Ur=T3=SW=RM3=Qy=R=S=S=T4=V =X}=^$=e=hmx=gp=g=ie=l=n3=m=g!N=_34=W}=SP=R%=O=I}S=AU==J=9m=1N=+n7=%'=#[=&iq=)=)k~='M=&=(@=,=2=83=?5_=@=K&=cg=`=== = >=9=z=zC=Y=W =Y{=] =]_=Wh=R2=R=T=\U=c==fX=hi=c=`aN=[=WǺ=X\^=Z=^c=e0=kƒ=niN=lp_=j=l9=j=d%=_=]k=\=\=^@=`x=cx=b] =b]6=b+5=_=]s=_E=dJ=e=bai=[=X=Y->=Xk=U=Sl=R=Q3=Rh=R=U=Y=`x=g=k=i%=go=i=l)=jʀ=j.=c3=[uH=W=U >=S=R1=O=I9Q=B=<ċ=6=0\=)m=)=(=&=%A=#=&=,C=4/==X)=X=Y=[=[N=Y*=YŮ=[,=]k=`-=eU=k4=l]o=h'=`NP=_܀=_b=[=W!=Tkp=Qg=O=P=SpP=W=]/!=b=h/==h=f$=el=g=g۝=c=_=[=YJ=W=U=T=SY(=Q=M=E/=?=:O=6=1P[=-A;=*=%=!=!(=%=/x=>=A=FH=Nh=eW=l=}T==J==`S=z=pU=nG(=l=U=WZ=[e=\U=YYu=T=S=V6=Yj=`R=ba=`=bLo=ZD=Tc=T=Tv=V =W=Zq@=`=d=f =dE=g=i =h=^ޠ=X=WnU=WyM=W=X=X=V.=Q\C=Pd=VW=]=d+=kb=pV=rJG=m=hi=f}=e=_[=X=Sa=PDh=N=P{L=S=W);=\=b=e=eyb=c Y=b=ck=`=ZX=X=Y=Y =X"=T=SH=RY=R*=P=J8=CR=>=9B=44=/=*Q=$=#=^="=-=8)=A%=F=O,=_=q?=-=q= =F(==w*=mQ2=ji =j=h~sncosmo-2.12.1/sncosmo/tests/data/interpolation_test_evalx.dat000066400000000000000000000003431476435666400246560ustar00rootroot00000000000000-2.000000000000000000e+00 -1.000000000000000000e+00 5.000000000000000000e-01 2.399999999999999911e+00 3.000000000000000000e+00 4.000000000000000000e+00 4.500000000000000000e+00 6.500000000000000000e+00 8.000000000000000000e+00 sncosmo-2.12.1/sncosmo/tests/data/interpolation_test_evaly.dat000066400000000000000000000003721476435666400246610ustar00rootroot000000000000000.000000000000000000e+00 5.000000000000000000e-01 1.000000000000000000e+00 1.500000000000000000e+00 2.799999999999999822e+00 3.500000000000000000e+00 4.000000000000000000e+00 4.500000000000000000e+00 5.000000000000000000e+00 6.000000000000000000e+00 sncosmo-2.12.1/sncosmo/tests/data/interpolation_test_input.dat000066400000000000000000000010501476435666400246720ustar00rootroot00000000000000-1 1 -0.8153117 -1 2 -0.7384603 -1 3 -0.615695 -1 4 -0.4546487 -1 5 -0.2653346 0 1 0 0 2 0 0 3 0 0 4 0 0 5 0 2 1 0.8810296 2 2 0.7979836 2 3 0.6653228 2 4 0.4912955 2 5 0.2867218 4 1 -0.7332753 4 2 -0.6641567 4 3 -0.553744 4 4 -0.4089021 4 5 -0.2386368 5 1 -0.9291136 5 2 -0.8415352 5 3 -0.7016342 5 4 -0.518109 5 5 -0.3023703 6 1 -0.2707291 6 2 -0.2452102 6 3 -0.2044452 6 4 -0.1509688 6 5 -0.08810595 6.5 1 0.2084324 6.5 2 0.1887856 6.5 3 0.1574009 6.5 4 0.1162298 6.5 5 0.06783214 7 1 0.6365625 7 2 0.57656 7 3 0.4807098 7 4 0.3549714 7 5 0.2071626 sncosmo-2.12.1/sncosmo/tests/data/interpolation_test_result.dat000066400000000000000000000010431476435666400250530ustar00rootroot000000000000000 0 0 0 0 0 0 0 0 0 0 0 -0.815312 -0.776886 -0.640248 -0.535172 -0.454649 -0.359992 -0.265335 0 0 0 0.220257 0.209877 0.216332 0.181332 0.122824 0.0972522 0.0716805 0 0 0 0.558169 0.531862 0.548622 0.45986 0.311256 0.246453 0.18165 0 0 0 0.0738772 0.0703953 0.111418 0.0933919 0.0411967 0.0326196 0.0240425 0 0 0 -0.733275 -0.698716 -0.578691 -0.485064 -0.408902 -0.323769 -0.238637 0 0 0 -0.831194 -0.79202 -0.768066 -0.6438 -0.463506 -0.367005 -0.270504 0 0 0 0.208432 0.198609 0.163678 0.136815 0.11623 0.092031 0.0678321 0 0 0 0 0 0 0 0 0 0 0 sncosmo-2.12.1/sncosmo/tests/data/lc-03D4ag.list000066400000000000000000000113331476435666400212530ustar00rootroot00000000000000@Z_HELIO 0.285 @MWEBV 0.0258 @ZPERR_MEGACAMPSF::r 0 @Redshift 0.285 @SNTYPE SNIa @z_source H @RA 333.690959 @COVMAT covmat_lc-03D4ag.dat @FIELD D4 @DayMax 52831.055 0.12399468 @SURVEY SNLS @SN 03D4ag @CCD 24.0 @ZPERR_MEGACAMPSF::z 0 @X_FOCAL_PLANE 4.5669825 @DEC -17.739655 @ZPERR_MEGACAMPSF::i 0 @ZPERR_MEGACAMPSF::g 0 @Y_FOCAL_PLANE -0.2403 #Date: #Flux: #Fluxerr: #ZP: #Filter: #MagSys: #end 52816.54 92.096288 0.931508914546 27.036167 MEGACAMPSF::g AB_B12 52824.59 213.41892 1.42657520235 27.036167 MEGACAMPSF::g AB_B12 52851.53 58.660624 1.16890634907 27.036167 MEGACAMPSF::g AB_B12 52873.4 11.284263 0.586887838063 27.036167 MEGACAMPSF::g AB_B12 52876.42 10.22804 0.654522652074 27.036167 MEGACAMPSF::g AB_B12 52880.43 10.000479 0.679613243731 27.036167 MEGACAMPSF::g AB_B12 52900.35 6.3065515 0.755462410445 27.036167 MEGACAMPSF::g AB_B12 52904.4 5.4119675 0.68323579835 27.036167 MEGACAMPSF::g AB_B12 52908.35 6.3488776 0.768138870401 27.036167 MEGACAMPSF::g AB_B12 52934.38 6.1984642 0.803187267952 27.036167 MEGACAMPSF::g AB_B12 52938.3 3.9581022 0.960925706936 27.036167 MEGACAMPSF::g AB_B12 52942.26 3.4201647 0.685834651886 27.036167 MEGACAMPSF::g AB_B12 52795.59 -0.19163269 1.24040237007 26.321667 MEGACAMPSF::i AB_B12 52796.59 0.45434671 1.23153849526 26.321667 MEGACAMPSF::i AB_B12 52812.57 18.937188 0.961211367301 26.321667 MEGACAMPSF::i AB_B12 52816.57 44.81644 0.947651621056 26.321667 MEGACAMPSF::i AB_B12 52820.58 74.675611 0.762905387663 26.321667 MEGACAMPSF::i AB_B12 52824.55 97.506371 1.15920135853 26.321667 MEGACAMPSF::i AB_B12 52842.51 94.53923 1.31456026676 26.321667 MEGACAMPSF::i AB_B12 52851.49 66.06683 1.05305542122 26.321667 MEGACAMPSF::i AB_B12 52872.46 42.566171 0.715274264485 26.321667 MEGACAMPSF::i AB_B12 52876.48 36.393239 0.792948067628 26.321667 MEGACAMPSF::i AB_B12 52880.38 30.776254 1.12638130022 26.321667 MEGACAMPSF::i AB_B12 52881.39 30.303593 0.812973660349 26.321667 MEGACAMPSF::i AB_B12 52885.46 26.461881 0.720006252966 26.321667 MEGACAMPSF::i AB_B12 52900.38 16.62709 0.780553625866 26.321667 MEGACAMPSF::i AB_B12 52904.33 17.561076 1.30212533492 26.321667 MEGACAMPSF::i AB_B12 52908.27 15.08437 0.954174486221 26.321667 MEGACAMPSF::i AB_B12 52913.3 14.645789 0.736371656387 26.321667 MEGACAMPSF::i AB_B12 52929.34 6.3209963 1.19477049271 26.321667 MEGACAMPSF::i AB_B12 52934.32 7.3614576 1.00601476374 26.321667 MEGACAMPSF::i AB_B12 52938.26 6.8792098 0.667994310356 26.321667 MEGACAMPSF::i AB_B12 52942.22 6.6769606 1.63569093099 26.321667 MEGACAMPSF::i AB_B12 52962.27 2.0899349 1.2926205388 26.321667 MEGACAMPSF::i AB_B12 52797.6 -0.66638647 0.916813294564 26.494846 MEGACAMPSF::r AB_B12 52814.56 39.887957 0.848220219896 26.494846 MEGACAMPSF::r AB_B12 52818.57 83.761505 0.911136231208 26.494846 MEGACAMPSF::r AB_B12 52842.55 118.67978 1.02731094938 26.494846 MEGACAMPSF::r AB_B12 52851.55 72.258533 1.04014149064 26.494846 MEGACAMPSF::r AB_B12 52852.5 68.543412 0.86098450987 26.494846 MEGACAMPSF::r AB_B12 52876.44 18.92891 0.575754781317 26.494846 MEGACAMPSF::r AB_B12 52885.5 15.734936 0.787597934626 26.494846 MEGACAMPSF::r AB_B12 52900.42 11.027349 0.865737966588 26.494846 MEGACAMPSF::r AB_B12 52904.37 11.482178 0.635575165927 26.494846 MEGACAMPSF::r AB_B12 52908.33 9.0585934 0.6357082305 26.494846 MEGACAMPSF::r AB_B12 52916.26 8.0693826 0.77081406853 26.494846 MEGACAMPSF::r AB_B12 52930.31 6.9334124 0.955092793604 26.494846 MEGACAMPSF::r AB_B12 52934.35 7.8494682 0.723278885835 26.494846 MEGACAMPSF::r AB_B12 52938.34 7.0368061 0.84484069074 26.494846 MEGACAMPSF::r AB_B12 52939.25 6.9476039 0.690958311727 26.494846 MEGACAMPSF::r AB_B12 52942.28 5.3984061 0.775737530304 26.494846 MEGACAMPSF::r AB_B12 52962.22 7.0549938 0.785583793331 26.494846 MEGACAMPSF::r AB_B12 52814.58 11.720391 0.660947952933 25.274074 MEGACAMPSF::z AB_B12 52818.6 18.150013 0.758896874356 25.274074 MEGACAMPSF::z AB_B12 52873.45 15.431639 1.22186172862 25.274074 MEGACAMPSF::z AB_B12 52878.37 11.883394 0.558656080942 25.274074 MEGACAMPSF::z AB_B12 52886.4 8.5571967 0.561728092273 25.274074 MEGACAMPSF::z AB_B12 52900.45 5.9726218 0.768773247383 25.274074 MEGACAMPSF::z AB_B12 52908.38 4.5875386 0.62805205454 25.274074 MEGACAMPSF::z AB_B12 52939.28 1.9882888 0.601257103677 25.274074 MEGACAMPSF::z AB_B12 52940.21 -0.065313851 0.791099517405 25.274074 MEGACAMPSF::z AB_B12 52942.32 2.7327355 0.87790928638 25.274074 MEGACAMPSF::z AB_B12 sncosmo-2.12.1/sncosmo/tests/data/salt2_rcov_params_times.dat000066400000000000000000000003731476435666400243560ustar00rootroot00000000000000# parameters and rest-frame times to evaluate model relative covariance @Redshift 0.1500000 @DayMax 6.500000 @X0 1.000000e-05 @X1 -1.000000 @Color 0.1000000 -30.00000 -20.00000 -10.00000 0.000000 10.00000 20.00000 30.00000 40.00000 50.00000 80.00000 sncosmo-2.12.1/sncosmo/tests/data/salt2_rcov_snfit_SDSSg.dat000066400000000000000000000027361476435666400240250ustar00rootroot0000000000000010 10 10000.00141 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 10000.00141 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.5968129798 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.0016728091 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001427228176 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.002736743717 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.003781842397 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.00191806556 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.002434036826 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 0.001406393051 10000.00141 sncosmo-2.12.1/sncosmo/tests/data/salt2_rcov_snfit_SDSSi.dat000066400000000000000000000027401476435666400240220ustar00rootroot0000000000000010 10 10000.00043 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 10000.00043 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.1785849626 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.001917011802 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.0008989955285 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.00392029419 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.004731337638 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.002538841262 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.00324395546 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 0.000429147907 10000.00043 sncosmo-2.12.1/sncosmo/tests/data/salt2_rcov_snfit_SDSSr.dat000066400000000000000000000027431476435666400240360ustar00rootroot0000000000000010 10 10000.0003 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 10000.0003 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.2074197594 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.0006136569311 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.0003313199096 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.0005601900509 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.0006353695942 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.0004759913125 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.002072262807 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 0.000295180187 10000.0003 sncosmo-2.12.1/sncosmo/tests/data/salt2_timeseries_1.dat000066400000000000000000000077531476435666400232430ustar00rootroot00000000000000# output from snfit Salt2Model.SpectrumFlux with following parameters @Redshift 0.000000 @DayMax 0.000000 @X0 1.000000 @X1 0.000000 @Color 0.000000 -30.00000 3000.000 0.0000000000000000000 -30.00000 3500.000 0.0000000000000000000 -30.00000 4000.000 0.0000000000000000000 -30.00000 4500.000 0.0000000000000000000 -30.00000 5000.000 0.0000000000000000000 -30.00000 5500.000 0.0000000000000000000 -30.00000 6000.000 0.0000000000000000000 -30.00000 6500.000 0.0000000000000000000 -30.00000 7000.000 0.0000000000000000000 -30.00000 7500.000 0.0000000000000000000 -30.00000 8000.000 0.0000000000000000000 -30.00000 8500.000 0.0000000000000000000 -30.00000 9000.000 0.0000000000000000000 -10.00000 3000.000 7.6407109859999999248e-14 -10.00000 3500.000 1.5871462989999997713e-13 -10.00000 4000.000 2.1590473730000000870e-13 -10.00000 4500.000 1.5625707929999997500e-13 -10.00000 5000.000 1.0491374059999999351e-13 -10.00000 5500.000 8.7084444400000001085e-14 -10.00000 6000.000 5.5204378249999999836e-14 -10.00000 6500.000 6.1749201469999995823e-14 -10.00000 7000.000 4.7294640729999995624e-14 -10.00000 7500.000 3.6777095859999997967e-14 -10.00000 8000.000 2.1008181559999999445e-14 -10.00000 8500.000 2.4721864310000001345e-14 -10.00000 9000.000 2.0115513790000001836e-14 0.000000 3000.000 1.5182164299999999155e-13 0.000000 3500.000 3.3922857069999997976e-13 0.000000 4000.000 4.5419822329999996161e-13 0.000000 4500.000 3.9836395200000002655e-13 0.000000 5000.000 2.1428238210000000645e-13 0.000000 5500.000 1.9238417609999998630e-13 0.000000 6000.000 1.8310606030000000745e-13 0.000000 6500.000 1.3710747919999998271e-13 0.000000 7000.000 7.9925211880000003730e-14 0.000000 7500.000 4.2950279149999998024e-14 0.000000 8000.000 4.6097734319999998174e-14 0.000000 8500.000 5.2081855969999996200e-14 0.000000 9000.000 5.2480859949999999009e-14 10.00000 3000.000 4.2419661859999998392e-14 10.00000 3500.000 1.1885906320000000248e-13 10.00000 4000.000 2.3111711979999999848e-13 10.00000 4500.000 2.3424364500000001349e-13 10.00000 5000.000 1.3375206440000000504e-13 10.00000 5500.000 1.5288849630000000157e-13 10.00000 6000.000 1.2851812530000001040e-13 10.00000 6500.000 1.0479309859999999197e-13 10.00000 7000.000 5.2407415679999999121e-14 10.00000 7500.000 3.0679892539999998807e-14 10.00000 8000.000 2.7904897639999998353e-14 10.00000 8500.000 4.2820877160000000658e-14 10.00000 9000.000 2.5508485340000001308e-14 25.00000 3000.000 2.7163360680000001932e-14 25.00000 3500.000 1.8821192390000000414e-14 25.00000 4000.000 5.3618779829999996821e-14 25.00000 4500.000 4.4673619079999998939e-14 25.00000 5000.000 4.9259603689999996949e-14 25.00000 5500.000 1.1924467869999998608e-13 25.00000 6000.000 5.2187934339999999196e-14 25.00000 6500.000 9.5943498919999987902e-14 25.00000 7000.000 3.3541980489999996740e-14 25.00000 7500.000 3.7876668129999997398e-14 25.00000 8000.000 2.6966949720000000951e-14 25.00000 8500.000 3.8959159020000001936e-14 25.00000 9000.000 3.6330995550000000497e-14 50.00000 3000.000 -1.3873843139999999277e-14 50.00000 3500.000 1.3280552659999999538e-14 50.00000 4000.000 2.1088041759999997515e-14 50.00000 4500.000 1.4815601850000001623e-14 50.00000 5000.000 1.9810258160000000502e-14 50.00000 5500.000 3.9926701289999999101e-14 50.00000 6000.000 2.1184770169999998882e-14 50.00000 6500.000 3.0726640850000002484e-14 50.00000 7000.000 1.0487859100000000154e-14 50.00000 7500.000 1.0228861779999999210e-14 50.00000 8000.000 1.2181502639999999166e-14 50.00000 8500.000 9.8742082899999994886e-15 50.00000 9000.000 1.8298111480000000764e-14 80.00000 3000.000 0.0000000000000000000 80.00000 3500.000 0.0000000000000000000 80.00000 4000.000 0.0000000000000000000 80.00000 4500.000 0.0000000000000000000 80.00000 5000.000 0.0000000000000000000 80.00000 5500.000 0.0000000000000000000 80.00000 6000.000 0.0000000000000000000 80.00000 6500.000 0.0000000000000000000 80.00000 7000.000 0.0000000000000000000 80.00000 7500.000 0.0000000000000000000 80.00000 8000.000 0.0000000000000000000 80.00000 8500.000 0.0000000000000000000 80.00000 9000.000 0.0000000000000000000 sncosmo-2.12.1/sncosmo/tests/data/salt2_timeseries_2.dat000066400000000000000000000077601476435666400232420ustar00rootroot00000000000000# output from snfit Salt2Model.SpectrumFlux with following parameters @Redshift 0.1500000 @DayMax 0.000000 @X0 1.000000e-05 @X1 0.000000 @Color 0.000000 -30.00000 3000.000 0.0000000000000000000 -30.00000 3500.000 0.0000000000000000000 -30.00000 4000.000 0.0000000000000000000 -30.00000 4500.000 0.0000000000000000000 -30.00000 5000.000 0.0000000000000000000 -30.00000 5500.000 0.0000000000000000000 -30.00000 6000.000 0.0000000000000000000 -30.00000 6500.000 0.0000000000000000000 -30.00000 7000.000 0.0000000000000000000 -30.00000 7500.000 0.0000000000000000000 -30.00000 8000.000 0.0000000000000000000 -30.00000 8500.000 0.0000000000000000000 -30.00000 9000.000 0.0000000000000000000 -10.00000 3000.000 4.0695765097455027958e-19 -10.00000 3500.000 1.4073515655240458711e-18 -10.00000 4000.000 2.7402841039035835403e-18 -10.00000 4500.000 3.3673242784512887999e-18 -10.00000 5000.000 2.1980988847201108905e-18 -10.00000 5500.000 1.6280133669071691339e-18 -10.00000 6000.000 1.3706895372499555890e-18 -10.00000 6500.000 1.2401409945679102258e-18 -10.00000 7000.000 7.2532461451868876494e-19 -10.00000 7500.000 8.9691114859078655117e-19 -10.00000 8000.000 6.8043556679153200286e-19 -10.00000 8500.000 5.5690947334334392694e-19 -10.00000 9000.000 3.7567824947968867514e-19 0.000000 3000.000 2.5050235295513277462e-19 0.000000 3500.000 1.7795535945578448225e-18 0.000000 4000.000 3.6505231894303404137e-18 0.000000 4500.000 5.8767500360575631238e-18 0.000000 5000.000 3.5535683788108721905e-18 0.000000 5500.000 2.8933287363383732505e-18 0.000000 6000.000 2.8185945067562294190e-18 0.000000 6500.000 2.6055181011249520766e-18 0.000000 7000.000 1.0723995070401720307e-18 0.000000 7500.000 1.5724390824566163768e-18 0.000000 8000.000 9.4556325098697576217e-19 0.000000 8500.000 6.3743249546609599688e-19 0.000000 9000.000 7.0566596771609699130e-19 10.00000 3000.000 1.3732750581175319054e-19 10.00000 3500.000 5.5361351982908431659e-19 10.00000 4000.000 1.4218061385667488726e-18 10.00000 4500.000 3.9890032770693803111e-18 10.00000 5000.000 1.9249148617757641216e-18 10.00000 5500.000 1.8509106940525344329e-18 10.00000 6000.000 2.7842239823829356427e-18 10.00000 6500.000 1.7474907151099702560e-18 10.00000 7000.000 9.5615674777873071180e-19 10.00000 7500.000 1.2552122563588949570e-18 10.00000 8000.000 6.7990425444456481420e-19 10.00000 8500.000 4.4855243303289165790e-19 10.00000 9000.000 4.5535544164413159387e-19 25.00000 3000.000 1.8884535588819321146e-19 25.00000 3500.000 5.3554804123765608302e-19 25.00000 4000.000 2.9652118360203886643e-19 25.00000 4500.000 8.8390102254440616813e-19 25.00000 5000.000 6.0138260197911173998e-19 25.00000 5500.000 7.2588317447343274055e-19 25.00000 6000.000 9.1746708830902407793e-19 25.00000 6500.000 8.8024853892995244484e-19 25.00000 7000.000 6.6545392409632010217e-19 25.00000 7500.000 1.1072590390791248696e-18 25.00000 8000.000 4.1809424567447846914e-19 25.00000 8500.000 4.9774337087204771938e-19 25.00000 9000.000 3.9756729394033686180e-19 50.00000 3000.000 2.9493627510473481126e-19 50.00000 3500.000 -1.9605198770899560157e-19 50.00000 4000.000 9.5795782871024170287e-20 50.00000 4500.000 2.8384883939897279432e-19 50.00000 5000.000 2.2530815280662969077e-19 50.00000 5500.000 1.9932313749954837021e-19 50.00000 6000.000 2.6596536999578185942e-19 50.00000 6500.000 3.4402959168797219978e-19 50.00000 7000.000 2.4513564119908722221e-19 50.00000 7500.000 4.5551411657281257575e-19 50.00000 8000.000 1.6353530379501337562e-19 50.00000 8500.000 2.2442873494837071619e-19 50.00000 9000.000 2.5229160974420551450e-19 80.00000 3000.000 0.0000000000000000000 80.00000 3500.000 0.0000000000000000000 80.00000 4000.000 0.0000000000000000000 80.00000 4500.000 0.0000000000000000000 80.00000 5000.000 0.0000000000000000000 80.00000 5500.000 0.0000000000000000000 80.00000 6000.000 0.0000000000000000000 80.00000 6500.000 0.0000000000000000000 80.00000 7000.000 0.0000000000000000000 80.00000 7500.000 0.0000000000000000000 80.00000 8000.000 0.0000000000000000000 80.00000 8500.000 0.0000000000000000000 80.00000 9000.000 0.0000000000000000000 sncosmo-2.12.1/sncosmo/tests/data/salt2_timeseries_3.dat000066400000000000000000000077571476435666400232510ustar00rootroot00000000000000# output from snfit Salt2Model.SpectrumFlux with following parameters @Redshift 0.1500000 @DayMax 5.000000 @X0 1.000000e-05 @X1 1.000000 @Color 0.000000 -30.00000 3000.000 0.0000000000000000000 -30.00000 3500.000 0.0000000000000000000 -30.00000 4000.000 0.0000000000000000000 -30.00000 4500.000 0.0000000000000000000 -30.00000 5000.000 0.0000000000000000000 -30.00000 5500.000 0.0000000000000000000 -30.00000 6000.000 0.0000000000000000000 -30.00000 6500.000 0.0000000000000000000 -30.00000 7000.000 0.0000000000000000000 -30.00000 7500.000 0.0000000000000000000 -30.00000 8000.000 0.0000000000000000000 -30.00000 8500.000 0.0000000000000000000 -30.00000 9000.000 0.0000000000000000000 -10.00000 3000.000 6.3723345271733509361e-20 -10.00000 3500.000 9.1510257776241630645e-19 -10.00000 4000.000 7.8273129177586742662e-19 -10.00000 4500.000 1.4342711144719226262e-18 -10.00000 5000.000 1.0182661533108460279e-18 -10.00000 5500.000 7.8533850878364874256e-19 -10.00000 6000.000 6.8663944139393960589e-19 -10.00000 6500.000 5.8359478946294953591e-19 -10.00000 7000.000 3.9985220060036131043e-19 -10.00000 7500.000 4.0356905535553021710e-19 -10.00000 8000.000 3.1208176615396342755e-19 -10.00000 8500.000 2.5128150750808168781e-19 -10.00000 9000.000 1.4856899871661802811e-19 0.000000 3000.000 6.3800509196367856240e-19 0.000000 3500.000 3.1349264328438327380e-18 0.000000 4000.000 4.7756696755153847779e-18 0.000000 4500.000 5.3665506645247463304e-18 0.000000 5000.000 3.8091671816659439832e-18 0.000000 5500.000 2.9534148120045489433e-18 0.000000 6000.000 2.4621357354692896458e-18 0.000000 6500.000 2.1550134468178720741e-18 0.000000 7000.000 1.3336778905458005723e-18 0.000000 7500.000 1.3123311993214084865e-18 0.000000 8000.000 9.4561712407688012742e-19 0.000000 8500.000 7.9227207011810071041e-19 0.000000 9000.000 6.5989768950115399655e-19 10.00000 3000.000 1.8592993667193080255e-19 10.00000 3500.000 1.2552358910364369936e-18 10.00000 4000.000 2.5249267365757424922e-18 10.00000 4500.000 5.2830338214744630747e-18 10.00000 5000.000 2.9047483681628775469e-18 10.00000 5500.000 2.4844527271278140083e-18 10.00000 6000.000 2.9431829724684887095e-18 10.00000 6500.000 2.4399905909410924553e-18 10.00000 7000.000 1.3798107449632351301e-18 10.00000 7500.000 1.5385206174999314083e-18 10.00000 8000.000 8.6575918848036299360e-19 10.00000 8500.000 5.4406367808426357376e-19 10.00000 9000.000 4.9418588896430885327e-19 25.00000 3000.000 2.3205824697531529082e-20 25.00000 3500.000 6.1396210335048542197e-20 25.00000 4000.000 5.5477311601296526062e-19 25.00000 4500.000 1.8436360828136291598e-18 25.00000 5000.000 1.1033927832599847962e-18 25.00000 5500.000 1.1382585937219544961e-18 25.00000 6000.000 1.6655207304155099788e-18 25.00000 6500.000 8.1429779393260993745e-19 25.00000 7000.000 9.0002959541731681665e-19 25.00000 7500.000 9.7096663316767822180e-19 25.00000 8000.000 4.4150244572079497500e-19 25.00000 8500.000 3.6153235689994942252e-19 25.00000 9000.000 3.2751520183077147383e-19 50.00000 3000.000 7.0325228332513321777e-20 50.00000 3500.000 3.8983205800277243025e-19 50.00000 4000.000 2.5552412368109527471e-19 50.00000 4500.000 3.2665646994521876870e-19 50.00000 5000.000 2.8414259968714421437e-19 50.00000 5500.000 2.7893931475839633085e-19 50.00000 6000.000 3.3715287837023624692e-19 50.00000 6500.000 5.1776870662088452003e-19 50.00000 7000.000 3.5833206754469225305e-19 50.00000 7500.000 6.2699624422878964090e-19 50.00000 8000.000 2.5209648706406133870e-19 50.00000 8500.000 3.1005000961475769855e-19 50.00000 9000.000 3.5596898433612743609e-19 80.00000 3000.000 0.0000000000000000000 80.00000 3500.000 0.0000000000000000000 80.00000 4000.000 0.0000000000000000000 80.00000 4500.000 0.0000000000000000000 80.00000 5000.000 0.0000000000000000000 80.00000 5500.000 0.0000000000000000000 80.00000 6000.000 0.0000000000000000000 80.00000 6500.000 0.0000000000000000000 80.00000 7000.000 0.0000000000000000000 80.00000 7500.000 0.0000000000000000000 80.00000 8000.000 0.0000000000000000000 80.00000 8500.000 0.0000000000000000000 80.00000 9000.000 0.0000000000000000000 sncosmo-2.12.1/sncosmo/tests/data/salt2_timeseries_4.dat000066400000000000000000000077611476435666400232450ustar00rootroot00000000000000# output from snfit Salt2Model.SpectrumFlux with following parameters @Redshift 0.1500000 @DayMax 5.000000 @X0 1.000000e-05 @X1 0.000000 @Color 0.1000000 -30.00000 3000.000 0.0000000000000000000 -30.00000 3500.000 0.0000000000000000000 -30.00000 4000.000 0.0000000000000000000 -30.00000 4500.000 0.0000000000000000000 -30.00000 5000.000 0.0000000000000000000 -30.00000 5500.000 0.0000000000000000000 -30.00000 6000.000 0.0000000000000000000 -30.00000 6500.000 0.0000000000000000000 -30.00000 7000.000 0.0000000000000000000 -30.00000 7500.000 0.0000000000000000000 -30.00000 8000.000 0.0000000000000000000 -30.00000 8500.000 0.0000000000000000000 -30.00000 9000.000 0.0000000000000000000 -10.00000 3000.000 8.2557896860227609275e-20 -10.00000 3500.000 1.6173650994619110931e-19 -10.00000 4000.000 4.6401447924016669088e-19 -10.00000 4500.000 9.5868497687569172250e-19 -10.00000 5000.000 7.2398062532605616654e-19 -10.00000 5500.000 5.1171541777360921004e-19 -10.00000 6000.000 5.6490109635731776688e-19 -10.00000 6500.000 4.6595122220988504059e-19 -10.00000 7000.000 3.2552043466550081792e-19 -10.00000 7500.000 3.8335022534454047159e-19 -10.00000 8000.000 2.9767504405541307980e-19 -10.00000 8500.000 2.3811915595555111586e-19 -10.00000 9000.000 1.0316046010781523256e-19 0.000000 3000.000 2.1417529203817503234e-19 0.000000 3500.000 1.8261669134451386717e-18 0.000000 4000.000 4.0421121995302347018e-18 0.000000 4500.000 5.2420062276837219773e-18 0.000000 5000.000 3.5599633912068163229e-18 0.000000 5500.000 2.7898806259029253602e-18 0.000000 6000.000 2.4753520979216198943e-18 0.000000 6500.000 2.4551883423145763907e-18 0.000000 7000.000 1.1469869621160136455e-18 0.000000 7500.000 1.6356315420932637402e-18 0.000000 8000.000 1.1390581418118856399e-18 0.000000 8500.000 8.8904537239275986169e-19 0.000000 9000.000 8.2731110525877258375e-19 10.00000 3000.000 -4.1793308940225629563e-20 10.00000 3500.000 6.4275009869018625148e-19 10.00000 4000.000 2.0802796398752293707e-18 10.00000 4500.000 4.9576001885488124470e-18 10.00000 5000.000 2.7754579334963240685e-18 10.00000 5500.000 2.5439046683779919386e-18 10.00000 6000.000 3.1704672507571982355e-18 10.00000 6500.000 2.7852340397180387647e-18 10.00000 7000.000 1.3202994824305408308e-18 10.00000 7500.000 1.8538017347837552347e-18 10.00000 8000.000 1.0450700466197065163e-18 10.00000 8500.000 6.4416024052239586070e-19 10.00000 9000.000 7.1184578621885339944e-19 25.00000 3000.000 1.1784395628876718037e-19 25.00000 3500.000 1.5368494023896972993e-19 25.00000 4000.000 3.8326761319178693256e-19 25.00000 4500.000 1.3954482491423150680e-18 25.00000 5000.000 9.0008977011906531535e-19 25.00000 5500.000 1.0743408766156770878e-18 25.00000 6000.000 1.5858344072858666943e-18 25.00000 6500.000 9.4086118813834150042e-19 25.00000 7000.000 8.5721051713857326837e-19 25.00000 7500.000 1.2187639418595244292e-18 25.00000 8000.000 5.1593677984269495625e-19 25.00000 8500.000 4.7484561358319479325e-19 25.00000 9000.000 5.1997018573169311242e-19 50.00000 3000.000 1.5376092771107537139e-19 50.00000 3500.000 8.8252403587887881945e-20 50.00000 4000.000 1.3906444573701468963e-19 50.00000 4500.000 2.9655156156819323399e-19 50.00000 5000.000 2.4001284840543597006e-19 50.00000 5500.000 2.4614828904750862461e-19 50.00000 6000.000 3.4082751131360617217e-19 50.00000 6500.000 4.5075788914876373332e-19 50.00000 7000.000 3.2501287351640197488e-19 50.00000 7500.000 6.3124597592594554841e-19 50.00000 8000.000 2.4696675900571238178e-19 50.00000 8500.000 3.3975970155674391230e-19 50.00000 9000.000 3.3132489681036613241e-19 80.00000 3000.000 0.0000000000000000000 80.00000 3500.000 0.0000000000000000000 80.00000 4000.000 0.0000000000000000000 80.00000 4500.000 0.0000000000000000000 80.00000 5000.000 0.0000000000000000000 80.00000 5500.000 0.0000000000000000000 80.00000 6000.000 0.0000000000000000000 80.00000 6500.000 0.0000000000000000000 80.00000 7000.000 0.0000000000000000000 80.00000 7500.000 0.0000000000000000000 80.00000 8000.000 0.0000000000000000000 80.00000 8500.000 0.0000000000000000000 80.00000 9000.000 0.0000000000000000000 sncosmo-2.12.1/sncosmo/tests/data/snana_ascii_example.dat000066400000000000000000000032111476435666400235110ustar00rootroot00000000000000SURVEY: DES SNID: 5407 SNTYPE: 0 # FILTERS: griz PIXSIZE: 0.27 # arcsec NXPIX: 2048 NYPIX: 4096 FAKE: 0 RA: 9.209158 deg DECL: -44.913342 deg MWEBV: 0 # WARNING: see OPT_MWEBV in SNANA manual MWEBV_ERR: 0 REDSHIFT_HELIO: 0.3614 +- 0.0779 # zPHOT(host) REDSHIFT_FINAL: 0.3614 +- 0.0779 HOSTGAL_OBJID: 747 HOSTGAL_PHOTOZ: 0.3614 +- 0.0779 HOSTGAL_SPECZ: -9.0000 +- -9.0000 HOSTGAL_SNSEP: 0.32 # arcsec HOSTGAL_MAG: 23.56 22.97 22.85 22.28 # Aper_4 HOSTGAL_SB_FLUXCAL: 7.81 12.57 16.28 25.07 # griz FLUXCAL/asec^2 PRIVATE(DES_snid): 5407 PRIVATE(DES_cand_type): 0 PRIVATE(DES_ccdnum): 60 PRIVATE(DES_numepochs): 12 PRIVATE(DES_numepochs_ml): 12 PRIVATE(DES_nobs_offccd): 0 PRIVATE(DES_swap_aflux): 0 (PHOTFLAG & 32768) PRIVATE(DES_hostgal_dlr): 0.71 PRIVATE(DES_hostgal_gradient_g): -3.61 PRIVATE(DES_hostgal_gradient_r): -6.36 PRIVATE(DES_hostgal_gradient_i): -7.48 PRIVATE(DES_hostgal_gradient_z): -15.07 # ------------------ NOBS: 4 NVAR: 13 VARLIST: MJD FLT FIELD FLUXCAL FLUXCALERR PHOTFLAG PHOTPROB ZPFLUX PSF SKYSIG SKYSIG_T XPIX YPIX NITE OBS: 56534.185 g E2 5.8777e+00 3.7100e+00 2049 -9 0.000 0.000 0.0 0.0 1254.25 3417.99 OBS: 56534.188 r E2 6.0339e+00 5.2144e+00 2049 -9 0.000 0.000 0.0 0.0 1252.46 3422.74 OBS: 56534.190 i E2 -7.4055e-01 6.1795e+00 2049 -9 0.000 0.000 0.0 0.0 1251.38 3424.79 OBS: 56534.192 z E2 2.6296e+00 8.6619e+00 2049 -9 0.000 0.000 0.0 0.0 1249.99 3426.95 sncosmo-2.12.1/sncosmo/tests/data/snana_fits_example_head.fits000066400000000000000000000416001476435666400245500ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 1 / number of data axes NAXIS1 = 0 / length of data axis 1 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H HIERARCH CODE_IVERSION = 4 / Internal SNFTSIO code version SNANA = '/project/rkessler/SN/SNANA/SNANA_CURRENT' / SNANA code directory SURVEY = 'SNLS ' / Survey FILTERS = 'griz ' / List of Filters VERSION = 'snana_fits' / Photometry Version PHOTFILE= 'snana_fits_PHOT.FITS' / Photometry FITS file NPRIVATE= 0 / Number of private variables DATATYPE= 'DATA ' / real data END XTENSION= 'BINTABLE' / binary table extension BITPIX = 8 / 8-bit bytes NAXIS = 2 / 2-dimensional binary table NAXIS1 = 162 / width of table in bytes NAXIS2 = 2 / number of rows in table PCOUNT = 0 / size of special data area GCOUNT = 1 / one data group (required keyword) TFIELDS = 36 / number of fields in each row TTYPE1 = 'SNID ' / label for field 1 TFORM1 = '12A ' / data format of field: ASCII Character TUNIT1 = ' ' / physical unit of field TTYPE2 = 'IAUC ' / label for field 2 TFORM2 = '8A ' / data format of field: ASCII Character TUNIT2 = ' ' / physical unit of field TTYPE3 = 'FAKE ' / label for field 3 TFORM3 = '1I ' / data format of field: 2-byte INTEGER TUNIT3 = ' ' / physical unit of field TTYPE4 = 'RA ' / label for field 4 TFORM4 = '1D ' / data format of field: 8-byte DOUBLE TUNIT4 = ' ' / physical unit of field TTYPE5 = 'DECL ' / label for field 5 TFORM5 = '1D ' / data format of field: 8-byte DOUBLE TUNIT5 = ' ' / physical unit of field TTYPE6 = 'PIXSIZE ' / label for field 6 TFORM6 = '1E ' / data format of field: 4-byte REAL TUNIT6 = ' ' / physical unit of field TTYPE7 = 'NXPIX ' / label for field 7 TFORM7 = '1I ' / data format of field: 2-byte INTEGER TUNIT7 = ' ' / physical unit of field TTYPE8 = 'NYPIX ' / label for field 8 TFORM8 = '1I ' / data format of field: 2-byte INTEGER TUNIT8 = ' ' / physical unit of field TTYPE9 = 'SNTYPE ' / label for field 9 TFORM9 = '1J ' / data format of field: 4-byte INTEGER TUNIT9 = ' ' / physical unit of field TTYPE10 = 'NOBS ' / label for field 10 TFORM10 = '1J ' / data format of field: 4-byte INTEGER TUNIT10 = ' ' / physical unit of field TTYPE11 = 'PTROBS_MIN' / label for field 11 TFORM11 = '1J ' / data format of field: 4-byte INTEGER TUNIT11 = ' ' / physical unit of field TTYPE12 = 'PTROBS_MAX' / label for field 12 TFORM12 = '1J ' / data format of field: 4-byte INTEGER TUNIT12 = ' ' / physical unit of field TTYPE13 = 'MWEBV ' / label for field 13 TFORM13 = '1E ' / data format of field: 4-byte REAL TUNIT13 = ' ' / physical unit of field TTYPE14 = 'MWEBV_ERR' / label for field 14 TFORM14 = '1E ' / data format of field: 4-byte REAL TUNIT14 = ' ' / physical unit of field TTYPE15 = 'REDSHIFT_HELIO' / label for field 15 TFORM15 = '1E ' / data format of field: 4-byte REAL TUNIT15 = ' ' / physical unit of field TTYPE16 = 'REDSHIFT_HELIO_ERR' / label for field 16 TFORM16 = '1E ' / data format of field: 4-byte REAL TUNIT16 = ' ' / physical unit of field TTYPE17 = 'REDSHIFT_FINAL' / label for field 17 TFORM17 = '1E ' / data format of field: 4-byte REAL TUNIT17 = ' ' / physical unit of field TTYPE18 = 'REDSHIFT_FINAL_ERR' / label for field 18 TFORM18 = '1E ' / data format of field: 4-byte REAL TUNIT18 = ' ' / physical unit of field TTYPE19 = 'HOSTGAL_OBJID' / label for field 19 TFORM19 = '1K ' / data format of field: 8-byte INTEGER TUNIT19 = ' ' / physical unit of field TTYPE20 = 'HOSTGAL_PHOTOZ' / label for field 20 TFORM20 = '1E ' / data format of field: 4-byte REAL TUNIT20 = ' ' / physical unit of field TTYPE21 = 'HOSTGAL_PHOTOZ_ERR' / label for field 21 TFORM21 = '1E ' / data format of field: 4-byte REAL TUNIT21 = ' ' / physical unit of field TTYPE22 = 'HOSTGAL_SPECZ' / label for field 22 TFORM22 = '1E ' / data format of field: 4-byte REAL TUNIT22 = ' ' / physical unit of field TTYPE23 = 'HOSTGAL_SPECZ_ERR' / label for field 23 TFORM23 = '1E ' / data format of field: 4-byte REAL TUNIT23 = ' ' / physical unit of field TTYPE24 = 'HOSTGAL_SNSEP' / label for field 24 TFORM24 = '1E ' / data format of field: 4-byte REAL TUNIT24 = ' ' / physical unit of field TTYPE25 = 'HOSTGAL_LOGMASS' / label for field 25 TFORM25 = '1E ' / data format of field: 4-byte REAL TUNIT25 = ' ' / physical unit of field TTYPE26 = 'HOSTGAL_LOGMASS_ERR' / label for field 26 TFORM26 = '1E ' / data format of field: 4-byte REAL TUNIT26 = ' ' / physical unit of field TTYPE27 = 'HOSTGAL_MAG_g' / label for field 27 TFORM27 = '1E ' / data format of field: 4-byte REAL TUNIT27 = ' ' / physical unit of field TTYPE28 = 'HOSTGAL_MAG_r' / label for field 28 TFORM28 = '1E ' / data format of field: 4-byte REAL TUNIT28 = ' ' / physical unit of field TTYPE29 = 'HOSTGAL_MAG_i' / label for field 29 TFORM29 = '1E ' / data format of field: 4-byte REAL TUNIT29 = ' ' / physical unit of field TTYPE30 = 'HOSTGAL_MAG_z' / label for field 30 TFORM30 = '1E ' / data format of field: 4-byte REAL TUNIT30 = ' ' / physical unit of field TTYPE31 = 'HOSTGAL_SB_FLUXCAL_g' / label for field 31 TFORM31 = '1E ' / data format of field: 4-byte REAL TUNIT31 = ' ' / physical unit of field TTYPE32 = 'HOSTGAL_SB_FLUXCAL_r' / label for field 32 TFORM32 = '1E ' / data format of field: 4-byte REAL TUNIT32 = ' ' / physical unit of field TTYPE33 = 'HOSTGAL_SB_FLUXCAL_i' / label for field 33 TFORM33 = '1E ' / data format of field: 4-byte REAL TUNIT33 = ' ' / physical unit of field TTYPE34 = 'HOSTGAL_SB_FLUXCAL_z' / label for field 34 TFORM34 = '1E ' / data format of field: 4-byte REAL TUNIT34 = ' ' / physical unit of field TTYPE35 = 'PEAKMJD ' / label for field 35 TFORM35 = '1E ' / data format of field: 4-byte REAL TUNIT35 = ' ' / physical unit of field TTYPE36 = 'SEARCH_TYPE' / label for field 36 TFORM36 = '1J ' / data format of field: 4-byte INTEGER TUNIT36 = ' ' / physical unit of field EXTNAME = 'Header ' / name of this binary table extension END 03D1aw UNKNOWN @B3U7 00<;o?K:oGNl03D1ax UNKNOWN @B r\=xi/2`<;P>:oGNsncosmo-2.12.1/sncosmo/tests/data/snana_fits_example_phot.fits000066400000000000000000000473001476435666400246240ustar00rootroot00000000000000SIMPLE = T / file does conform to FITS standard BITPIX = -32 / number of bits per data pixel NAXIS = 1 / number of data axes NAXIS1 = 0 / length of data axis 1 EXTEND = T / FITS dataset may contain extensions COMMENT FITS (Flexible Image Transport System) format is defined in 'AstronomyCOMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H HIERARCH CODE_IVERSION = 4 / Internal SNFTSIO code version SNANA = '/project/rkessler/SN/SNANA/SNANA_CURRENT' / SNANA code directory SURVEY = 'SNLS ' / Survey FILTERS = 'griz ' / List of Filters VERSION = 'snana_fits' / Photometry Version PHOTFILE= 'snana_fits_PHOT.FITS' / Photometry FITS file DATATYPE= 'DATA ' / real data END XTENSION= 'BINTABLE' / binary table extension BITPIX = 8 / 8-bit bytes NAXIS = 2 / 2-dimensional binary table NAXIS1 = 102 / width of table in bytes NAXIS2 = 97 / number of rows in table PCOUNT = 0 / size of special data area GCOUNT = 1 / one data group (required keyword) TFIELDS = 19 / number of fields in each row TTYPE1 = 'MJD ' / label for field 1 TFORM1 = '1D ' / data format of field: 8-byte DOUBLE TUNIT1 = ' ' / physical unit of field TTYPE2 = 'FLT ' / label for field 2 TFORM2 = '2A ' / data format of field: ASCII Character TUNIT2 = ' ' / physical unit of field TTYPE3 = 'FIELD ' / label for field 3 TFORM3 = '12A ' / data format of field: ASCII Character TUNIT3 = ' ' / physical unit of field TTYPE4 = 'TELESCOPE' / label for field 4 TFORM4 = '20A ' / data format of field: ASCII Character TUNIT4 = ' ' / physical unit of field TTYPE5 = 'PHOTFLAG' / label for field 5 TFORM5 = '1J ' / data format of field: 4-byte INTEGER TUNIT5 = ' ' / physical unit of field TTYPE6 = 'PHOTPROB' / label for field 6 TFORM6 = '1E ' / data format of field: 4-byte REAL TUNIT6 = ' ' / physical unit of field TTYPE7 = 'FLUXCAL ' / label for field 7 TFORM7 = '1E ' / data format of field: 4-byte REAL TUNIT7 = ' ' / physical unit of field TTYPE8 = 'FLUXCALERR' / label for field 8 TFORM8 = '1E ' / data format of field: 4-byte REAL TUNIT8 = ' ' / physical unit of field TTYPE9 = 'MAG ' / label for field 9 TFORM9 = '1E ' / data format of field: 4-byte REAL TUNIT9 = ' ' / physical unit of field TTYPE10 = 'MAGERR ' / label for field 10 TFORM10 = '1E ' / data format of field: 4-byte REAL TUNIT10 = ' ' / physical unit of field TTYPE11 = 'PSF_SIG1' / label for field 11 TFORM11 = '1E ' / data format of field: 4-byte REAL TUNIT11 = ' ' / physical unit of field TTYPE12 = 'PSF_SIG2' / label for field 12 TFORM12 = '1E ' / data format of field: 4-byte REAL TUNIT12 = ' ' / physical unit of field TTYPE13 = 'PSF_RATIO' / label for field 13 TFORM13 = '1E ' / data format of field: 4-byte REAL TUNIT13 = ' ' / physical unit of field TTYPE14 = 'SKY_SIG ' / label for field 14 TFORM14 = '1E ' / data format of field: 4-byte REAL TUNIT14 = ' ' / physical unit of field TTYPE15 = 'SKY_SIG_T' / label for field 15 TFORM15 = '1E ' / data format of field: 4-byte REAL TUNIT15 = ' ' / physical unit of field TTYPE16 = 'RDNOISE ' / label for field 16 TFORM16 = '1E ' / data format of field: 4-byte REAL TUNIT16 = ' ' / physical unit of field TTYPE17 = 'ZEROPT ' / label for field 17 TFORM17 = '1E ' / data format of field: 4-byte REAL TUNIT17 = ' ' / physical unit of field TTYPE18 = 'ZEROPT_ERR' / label for field 18 TFORM18 = '1E ' / data format of field: 4-byte REAL TUNIT18 = ' ' / physical unit of field TTYPE19 = 'GAIN ' / label for field 19 TFORM19 = '1E ' / data format of field: 4-byte REAL TUNIT19 = ' ' / physical unit of field EXTNAME = 'Photometry' / name of this binary table extension END @0i NULL ALL AW =@6$An@1?|r NULL ALL @?ٺ^A9X@1Sz NULL ALL A@?}A@Ci NULL ALL B2{@:-A1@ԐFi NULL ALL B#@:~AA@ԑSr NULL ALL Bx@jA @ԒGz NULL ALL BA 1Ax@Fi NULL ALL Bp@-pA/@=pr NULL ALL B\\)@uA-@ՑSr NULL ALL BZ? =AE@ՓCi NULL ALL BQ@;A^5@ճ|hz NULL ALL B33@bA@-i NULL ALL BH@7lA =@R=pr NULL ALL B+=q@,(A^5@s;dZi NULL ALL Bx\)@WlA"@֎or NULL ALL B=q?(AdZ@bMr NULL ALL A @cFA(@0bMi NULL ALL A=q@uAh@2~#r NULL ALL u@FD&@P =z NULL ALL B)AĜA@ϝ-i NULL ALL A=qAVA{@1r NULL ALL @{@xĜAʑh@ Iz NULL ALL A'A^{AǙ@, ěi NULL ALL A=q@NyAȴ@/\(r NULL ALL @H@AO@ISz NULL ALL A@TA`@܊=pi NULL ALL A@>ȴAZ@܌j~r NULL ALL ?ff@-Am@܎1z NULL ALL @pAv\A@ ;dZr NULL ALL @e@A`@݈ =i NULL ALL z@mpD&@bMi NULL ALL @@9#Ap@Gr NULL ALL ??VA@i NULL ALL @@CAy@ ?|r NULL ALL @D&@kCi NULL ALL @E@D/A;d@lnr NULL ALL >ff?A/@睲-i NULL ALL ?Tz@<Aݝ@Fr NULL ALL ?8Q?"A@|hz NULL ALL AA8A@-z NULL ALL A \@1'A?}@HbMi NULL ALL @c33@3tA@ISr NULL ALL l@rD&@?|i NULL ALL @=q@D9XAX@=pr NULL ALL # @D&@\(r NULL ALL ?Q?ĜAݼj@H =i NULL ALL @"\@8 A`@JGr NULL ALL Fff?rD&H- XXXX XXXX B@B@@0i NULL ALL y@-VD&@0Fr NULL ALL Tz?QD&@1Sz NULL ALL @Å@AI@Ci NULL ALL @{@@ A&@ԐFi NULL ALL B!p@9AH@ԑSr NULL ALL B.@/A1'@ԒGz NULL ALL BNA%wA@Fi NULL ALL Bk@#mA33@=pr NULL ALL Bu?%AbN@ՑSr NULL ALL B?ƨAS@ՓCi NULL ALL BǮ@4/A/@ճ|hz NULL ALL ChAbNA\@-i NULL ALL B@4DA@R=pr NULL ALL B@9XA @s;dZi NULL ALL Bf@SoA@֎or NULL ALL B{?+A@.1i NULL ALL Bk@i%A@0 =r NULL ALL B@xA@K;dZr NULL ALL Bw@ JA(@MQz NULL ALL B\A A@د Ii NULL ALL B@yAS@ذr NULL ALL BD33@+A/@r NULL ALL B7H?&A@/\(i NULL ALL B3@# A\@2=pr NULL ALL A@AAS@PbMz NULL ALL BBA)"AA@ Ii NULL ALL B3G@G =A@ |hi NULL ALL BI{@:A@ \)r NULL ALL A|(?-A J@oz NULL ALL B]AbNA@bMi NULL ALL A@5AH@Gr NULL ALL z?5?D&@-i NULL ALL A {@;"AG@ ?|r NULL ALL @Q?/A@kCi NULL ALL @@6A5?@lnr NULL ALL @K?ٺ^A@睲-i NULL ALL AE@.A&@Fr NULL ALL ?ԛD&@|hz NULL ALL @W =AMAx@-z NULL ALL ?z@ڸRAڸR@HbMi NULL ALL @ۅ@,AE@ISr NULL ALL *=q@(D&@?|i NULL ALL A{@AAɉ7@=pr NULL ALL \@jMD&@\(r NULL ALL \)?$D&@H =i NULL ALL @ =@.RA@JGr NULL ALL ޸R?D&H- XXXX XXXX B@B@D&sncosmo-2.12.1/sncosmo/tests/data/snana_simlib_example.dat000066400000000000000000000073011476435666400237040ustar00rootroot00000000000000SURVEY: LSST FILTERS: ugrizY TELESCOPE: LSST USER: cinabro HOST: motor1 COMMENT: LSST BEGIN LIBGEN # ---------------------------------------- LIBID: 519 RA: 69.453790 DECL: -53.470480 NOBS: 18279 MWEBV: 0.000 PIXSIZE: 0.200 FIELD: 519 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTSIG MAG S: 49353.13317 10519 g 1.00 0.25 260.07 2.13 0.00 0.000 35.34 0.005 -99.000 S: 49353.17576 10519 g 1.00 0.25 197.00 2.10 0.00 0.000 34.57 0.005 -99.000 S: 49353.12388 10519 r 1.00 0.25 218.39 1.97 0.00 0.000 35.17 0.005 -99.000 S: 49353.16647 10519 r 1.00 0.25 226.03 2.28 0.00 0.000 35.16 0.005 -99.000 S: 49353.11458 10519 i 1.00 0.25 226.96 1.71 0.00 0.000 34.85 0.005 -99.000 S: 49353.15718 10519 i 1.00 0.25 233.05 2.44 0.00 0.000 34.86 0.005 -99.000 S: 49353.14788 10519 z 1.00 0.25 226.19 1.99 0.00 0.000 34.47 0.005 -99.000 S: 49355.14252 10519 i 1.00 0.25 203.09 1.54 0.00 0.000 34.86 0.005 -99.000 S: 49356.13317 10519 g 1.00 0.25 101.32 1.76 0.00 0.000 35.32 0.005 -99.000 S: 49356.12388 10519 r 1.00 0.25 123.83 1.76 0.00 0.000 35.16 0.005 -99.000 S: 49356.15182 10519 r 1.00 0.25 124.83 1.54 0.00 0.000 35.16 0.005 -99.000 S: 49356.11458 10519 i 1.00 0.25 173.17 1.65 0.00 0.000 34.86 0.005 -99.000 S: 49356.14252 10519 i 1.00 0.25 174.50 1.56 0.00 0.000 34.85 0.005 -99.000 S: 49360.13317 10519 g 1.00 0.25 102.12 1.22 0.00 0.000 35.33 0.005 -99.000 END_LIBID: 519 # ---------------------------------------- LIBID: 1427 RA: 53.008960 DECL: -27.438830 NOBS: 10218 MWEBV: 0.000 PIXSIZE: 0.200 FIELD: 1427 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTSIG MAG S: 49353.21836 11427 g 1.00 0.25 303.06 2.00 0.00 0.000 35.21 0.005 -99.000 S: 49353.20906 11427 r 1.00 0.25 247.28 1.76 0.00 0.000 35.11 0.005 -99.000 S: 49353.19977 11427 i 1.00 0.25 251.60 1.73 0.00 0.000 34.84 0.005 -99.000 S: 49353.19047 11427 z 1.00 0.25 242.66 1.79 0.00 0.000 34.46 0.005 -99.000 S: 49353.18118 11427 Y 1.00 0.25 249.37 2.02 0.00 0.000 33.47 0.005 -99.000 S: 49354.21836 11427 g 1.00 0.25 258.94 2.36 0.00 0.000 35.20 0.005 -99.000 S: 49354.20906 11427 r 1.00 0.25 221.32 2.11 0.00 0.000 35.11 0.005 -99.000 S: 49354.19977 11427 i 1.00 0.25 236.98 2.03 0.00 0.000 34.83 0.005 -99.000 S: 49354.19047 11427 z 1.00 0.25 235.85 1.87 0.00 0.000 34.46 0.005 -99.000 S: 49354.18118 11427 Y 1.00 0.25 249.47 1.98 0.00 0.000 33.46 0.005 -99.000 S: 49355.16117 11427 u 1.00 0.25 55.71 1.82 0.00 0.000 33.73 0.005 -99.000 S: 49356.19834 11427 g 1.00 0.25 185.65 2.11 0.00 0.000 35.24 0.005 -99.000 S: 49356.18905 11427 r 1.00 0.25 174.29 1.98 0.00 0.000 35.11 0.005 -99.000 S: 49356.17976 11427 i 1.00 0.25 209.79 1.59 0.00 0.000 34.84 0.005 -99.000 S: 49356.17046 11427 z 1.00 0.25 218.24 1.61 0.00 0.000 34.47 0.005 -99.000 S: 49356.16117 11427 Y 1.00 0.25 247.41 1.46 0.00 0.000 33.50 0.005 -99.000 S: 49360.19834 11427 g 1.00 0.25 107.66 1.28 0.00 0.000 35.21 0.005 -99.000 S: 49360.18905 11427 r 1.00 0.25 133.95 1.16 0.00 0.000 35.10 0.005 -99.000 S: 49364.16117 11427 u 1.00 0.25 28.13 2.09 0.00 0.000 33.58 0.005 -99.000 S: 49365.21836 11427 g 1.00 0.25 110.70 1.77 0.00 0.000 35.10 0.005 -99.000 S: 49365.20906 11427 r 1.00 0.25 142.99 1.41 0.00 0.000 35.05 0.005 -99.000 END_LIBID: 1427sncosmo-2.12.1/sncosmo/tests/data/snana_simlib_example_coadd.dat000066400000000000000000000060501476435666400250360ustar00rootroot00000000000000DOCUMENTATION: PURPOSE: LSST-DDF 3-year cadence for SNANA simulation REF: - AUTHOR: Kessler et al. 2019 (PLASTICC models) ADS: https://ui.adsabs.harvard.edu/abs/2019PASP..131i4501K INTENT: Nominal USAGE_KEY: SIMLIB_FILE USAGE_CODE: snlc_sim.exe NOTES: - total DDF area is 47.6 sq degrees, or solid angle = 0.014509 - from OpSim outout /nfs/brahe/LSST/opsim/minion_1016_recalc.db - created with /home/rbisw/src/OpSimSummary/scripts/make_simlibs.py - This coadd SIMLIB was created with command - simlib_coadd.exe minion_1016_recalc_ddf.simlib - and the following co-add cuts/options - + Select LIBID between 0 and 1000000 - + Reject LIBID with < 3 exposures - + Combine consecutive exposures within 0.400 days - + Multiple exposures are SUMMED VERSIONS: - DATE: 2018-08 AUTHORS: R. Kessler, R.Biswas DOCUMENTATION_END: SURVEY: LSST FILTERS: ugrizY TELESCOPE: LSST NLIBID: 2 #NPE_PIXEL_SATURATE: 100000 NPE_PIXEL_SATURATE: 3900000 # allow 4 mag brighter (Jul 25 2018) PHOTFLAG_SATURATE: 1024 BEGIN LIBGEN # -------------------------------------------- LIBID: 0 RA: 149.414062 DECL: 2.238686 NOBS: 1039 MWEBV: 0.0000 PIXSIZE: 0.200 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTERR MAG S: 59582.3282 2063*2 u 1.00 1.12 14.32 2.94 0.00 0.000 31.92 0.005 99.000 S: 59583.2409 2589*2 u 1.00 1.12 13.86 2.38 0.00 0.000 31.79 0.005 99.000 S: 59584.2432 3308*2 u 1.00 1.12 13.77 2.42 0.00 0.000 31.79 0.005 99.000 S: 59585.2363 4017*2 u 1.00 1.12 13.71 2.75 0.00 0.000 31.78 0.005 99.000 S: 59586.2371 4734*2 u 1.00 1.12 13.56 3.11 0.00 0.000 31.75 0.005 99.000 S: 59588.2190 6145*2 r 1.00 1.12 117.96 2.11 0.00 0.000 34.77 0.005 99.000 S: 59588.2266 6165*2 g 1.00 0.79 51.42 1.99 0.00 0.000 33.98 0.005 99.000 S: 59588.2343 6175*2 i 1.00 1.12 157.96 1.75 0.00 0.000 34.55 0.005 99.000 S: 59588.2452 6195*2 z 1.00 1.27 238.51 1.62 0.00 0.000 34.52 0.005 99.000 ##skipped data S: 63003.9798 2310046*2 u 1.00 1.12 14.94 2.93 0.00 0.000 31.95 0.005 99.000 S: 63004.9797 2311102*2 u 1.00 1.12 15.24 2.13 0.00 0.000 31.99 0.005 99.000 END_LIBID: 1 # -------------------------------------------- LIBID: 1 RA: 352.711273 DECL: -63.823658 NOBS: 1172 MWEBV: 0.0000 PIXSIZE: 0.200 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTERR MAG S: 59750.4229 124860*2 r 1.00 1.12 186.71 1.96 0.00 0.000 34.88 0.005 99.000 S: 59750.4306 124880*2 g 1.00 0.79 125.65 2.14 0.00 0.000 34.20 0.005 99.000 S: 59750.4383 124890*2 i 1.00 1.12 216.99 2.03 0.00 0.000 34.61 0.005 99.000 S: 59750.4450 124910*2 z 1.00 0.56 182.59 2.00 0.00 0.000 32.77 0.005 99.000 ##skipped data S: 63172.0658 2415935*2 Y 1.00 1.12 190.39 2.39 0.00 0.000 33.30 0.005 99.000 END_LIBID: 1 END_OF_SIMLIB: 2 ENTRIESsncosmo-2.12.1/sncosmo/tests/data/snana_simlib_example_doc.dat000066400000000000000000000035041476435666400245320ustar00rootroot00000000000000DOCUMENTATION: PURPOSE: Cadence for NGRST simulation of SNe INTENT: strategy studies USAGE_KEY: SIMLIB_FILE USAGE_CODE: snlc_sim.exe INPUT_CONFIG: configs/NGRST_makeSimlib_SETEXP_KEVIN.config TIME_TOTAL: 137 # total survey time, days TIME_VISIT: 5.0 # time between visits, days TIME_SLEW: 70 # slew time, seconds TIERS: # bands ntile nvisit Area NLIBID zMatch t_expose - DEEP YJHF 25 383 7.03 300 1.70 300.0 300.0 300.0 900.0 DOCUMENTATION_END: # ---------------------------------- SURVEY: NGRST FILTERS: YFHJ PIXSIZE: 0.11 # arcsec SOLID_ANGLE: 0.0021 # (sr) sum of all tiers BEGIN LIBGEN # =========================================== LIBID: 1 FIELD: DEEP RA: 20.0 DEC: 0.0 NOBS: 1528 # READ PSFSIG1,2 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTERR MAG S: 55000.0000 1 Y 1.0 10.13 8.91 0.711 0.0 0.0 31.980 0.001 99 S: 55000.0028 2 J 1.0 10.33 8.39 0.767 0.0 0.0 31.860 0.001 99 S: 55000.0054 3 H 1.0 9.88 9.02 0.891 0.0 0.0 32.054 0.001 99 S: 55000.0083 4 F 1.0 7.42 14.01 0.990 0.0 0.0 32.583 0.001 99 S: 55005.0000 5 Y 1.0 10.13 8.91 0.711 0.0 0.0 31.980 0.001 99 S: 55005.0028 6 J 1.0 10.33 8.39 0.767 0.0 0.0 31.860 0.001 99 S: 55005.0054 7 H 1.0 9.88 9.02 0.891 0.0 0.0 32.054 0.001 99 # skipping lines S: 56310.0083 1524 F 1.0 7.42 14.01 0.990 0.0 0.0 32.583 0.001 99 S: 56315.0000 1525 Y 1.0 10.13 8.91 0.711 0.0 0.0 31.980 0.001 99 S: 56315.0028 1526 J 1.0 10.33 8.39 0.767 0.0 0.0 31.860 0.001 99 S: 56315.0054 1527 H 1.0 9.88 9.02 0.891 0.0 0.0 32.054 0.001 99 S: 56315.0083 1528 F 1.0 7.42 14.01 0.990 0.0 0.0 32.583 0.001 99 END_LIBID: 1 END_OF_SIMLIB:sncosmo-2.12.1/sncosmo/tests/data/snana_simlib_example_invalid.dat000066400000000000000000000013741476435666400254160ustar00rootroot00000000000000SURVEY: LSST FILTERS: ugrizY TELESCOPE: LSST USER: cinabro HOST: motor1 COMMENT_newKeyName: LSST BEGIN LIBGEN # ---------------------------------------- LIBID: 519 RA: 69.453790 DECL: -53.470480 NOBS: 18279 MWEBV: 0.000 PIXSIZE: 0.200 FIELD: 519 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTSIG MAG S: 49353.13317 10+519 g 1.00 0.25 260.07 2.13 0.00 0.000 35.34 0.005 -99.000 S: 49353.17576 10519 g 1.00 0.25 197.00 2.10 0.00 0.000 34.57 0.005 -99.000 S: 49353.12388 10519 r 1.00 0.25 218.39 1.97 0.00 0.000 35.17 0.005 -99.000 S: 49353.16647 10519 r 1.00 0.25 226.03 2.28 0.00 0.000 35.16 0.005 -99.000sncosmo-2.12.1/sncosmo/tests/data/snana_simlib_example_noend.dat000066400000000000000000000072471476435666400251000ustar00rootroot00000000000000SURVEY: LSST FILTERS: ugrizY TELESCOPE: LSST USER: cinabro HOST: motor1 COMMENT: LSST BEGIN LIBGEN # ---------------------------------------- LIBID: 519 RA: 69.453790 DECL: -53.470480 NOBS: 18279 MWEBV: 0.000 PIXSIZE: 0.200 FIELD: 519 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTSIG MAG S: 49353.13317 10519 g 1.00 0.25 260.07 2.13 0.00 0.000 35.34 0.005 -99.000 S: 49353.17576 10519 g 1.00 0.25 197.00 2.10 0.00 0.000 34.57 0.005 -99.000 S: 49353.12388 10519 r 1.00 0.25 218.39 1.97 0.00 0.000 35.17 0.005 -99.000 S: 49353.16647 10519 r 1.00 0.25 226.03 2.28 0.00 0.000 35.16 0.005 -99.000 S: 49353.11458 10519 i 1.00 0.25 226.96 1.71 0.00 0.000 34.85 0.005 -99.000 S: 49353.15718 10519 i 1.00 0.25 233.05 2.44 0.00 0.000 34.86 0.005 -99.000 S: 49353.14788 10519 z 1.00 0.25 226.19 1.99 0.00 0.000 34.47 0.005 -99.000 S: 49355.14252 10519 i 1.00 0.25 203.09 1.54 0.00 0.000 34.86 0.005 -99.000 S: 49356.13317 10519 g 1.00 0.25 101.32 1.76 0.00 0.000 35.32 0.005 -99.000 S: 49356.12388 10519 r 1.00 0.25 123.83 1.76 0.00 0.000 35.16 0.005 -99.000 S: 49356.15182 10519 r 1.00 0.25 124.83 1.54 0.00 0.000 35.16 0.005 -99.000 S: 49356.11458 10519 i 1.00 0.25 173.17 1.65 0.00 0.000 34.86 0.005 -99.000 S: 49356.14252 10519 i 1.00 0.25 174.50 1.56 0.00 0.000 34.85 0.005 -99.000 S: 49360.13317 10519 g 1.00 0.25 102.12 1.22 0.00 0.000 35.33 0.005 -99.000 # ---------------------------------------- LIBID: 1427 RA: 53.008960 DECL: -27.438830 NOBS: 10218 MWEBV: 0.000 PIXSIZE: 0.200 FIELD: 1427 # CCD CCD PSF1 PSF2 PSF2/1 # MJD IDEXPT FLT GAIN NOISE SKYSIG (pixels) RATIO ZPTAVG ZPTSIG MAG S: 49353.21836 11427 g 1.00 0.25 303.06 2.00 0.00 0.000 35.21 0.005 -99.000 S: 49353.20906 11427 r 1.00 0.25 247.28 1.76 0.00 0.000 35.11 0.005 -99.000 S: 49353.19977 11427 i 1.00 0.25 251.60 1.73 0.00 0.000 34.84 0.005 -99.000 S: 49353.19047 11427 z 1.00 0.25 242.66 1.79 0.00 0.000 34.46 0.005 -99.000 S: 49353.18118 11427 Y 1.00 0.25 249.37 2.02 0.00 0.000 33.47 0.005 -99.000 S: 49354.21836 11427 g 1.00 0.25 258.94 2.36 0.00 0.000 35.20 0.005 -99.000 S: 49354.20906 11427 r 1.00 0.25 221.32 2.11 0.00 0.000 35.11 0.005 -99.000 S: 49354.19977 11427 i 1.00 0.25 236.98 2.03 0.00 0.000 34.83 0.005 -99.000 S: 49354.19047 11427 z 1.00 0.25 235.85 1.87 0.00 0.000 34.46 0.005 -99.000 S: 49354.18118 11427 Y 1.00 0.25 249.47 1.98 0.00 0.000 33.46 0.005 -99.000 S: 49355.16117 11427 u 1.00 0.25 55.71 1.82 0.00 0.000 33.73 0.005 -99.000 S: 49356.19834 11427 g 1.00 0.25 185.65 2.11 0.00 0.000 35.24 0.005 -99.000 S: 49356.18905 11427 r 1.00 0.25 174.29 1.98 0.00 0.000 35.11 0.005 -99.000 S: 49356.17976 11427 i 1.00 0.25 209.79 1.59 0.00 0.000 34.84 0.005 -99.000 S: 49356.17046 11427 z 1.00 0.25 218.24 1.61 0.00 0.000 34.47 0.005 -99.000 S: 49356.16117 11427 Y 1.00 0.25 247.41 1.46 0.00 0.000 33.50 0.005 -99.000 S: 49360.19834 11427 g 1.00 0.25 107.66 1.28 0.00 0.000 35.21 0.005 -99.000 S: 49360.18905 11427 r 1.00 0.25 133.95 1.16 0.00 0.000 35.10 0.005 -99.000 S: 49364.16117 11427 u 1.00 0.25 28.13 2.09 0.00 0.000 33.58 0.005 -99.000 S: 49365.21836 11427 g 1.00 0.25 110.70 1.77 0.00 0.000 35.10 0.005 -99.000 S: 49365.20906 11427 r 1.00 0.25 142.99 1.41 0.00 0.000 35.05 0.005 -99.000 sncosmo-2.12.1/sncosmo/tests/data/snfit_filter_g_0.dat000066400000000000000000000003761476435666400227540ustar00rootroot00000000000000@name MEGACAMPSF::g @radius 4.03113 3600 0 3800 0 4000 0 4200 0.444051544606 4400 0.447937332888 4600 0.517262834012 4800 0.541458422811 5000 0.481593253261 5200 0.478309844912 5400 0.512172506786 5600 0.276462888124 5800 0.000914488057559 6000 0 6200 0 sncosmo-2.12.1/sncosmo/tests/data/snfit_filter_g_1.dat000066400000000000000000000001561476435666400227510ustar00rootroot00000000000000@name MEGACAMPSF::g @radius 10.9659 7600 0 8000 0 8400 0 8800 0 9200 0 9600 0 10000 0 10400 0 10800 0 11200 0 sncosmo-2.12.1/sncosmo/tests/data/snfit_filter_z_0.dat000066400000000000000000000002061476435666400227670ustar00rootroot00000000000000@name MEGACAMPSF::z @radius 4.03113 3600 0 3800 0 4000 0 4200 0 4400 0 4600 0 4800 0 5000 0 5200 0 5400 0 5600 0 5800 0 6000 0 6200 0 sncosmo-2.12.1/sncosmo/tests/data/snfit_filter_z_1.dat000066400000000000000000000003001476435666400227630ustar00rootroot00000000000000@name MEGACAMPSF::z @radius 10.9659 7600 0 8000 0.00663775946314 8400 0.276739043157 8800 0.201742662116 9200 0.122657578318 9600 0.0534207311952 10000 0.0303513030405 10400 0 10800 0 11200 0 sncosmo-2.12.1/sncosmo/tests/test_bandpasses.py000066400000000000000000000113731476435666400216670ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import os import numpy as np import pytest from numpy.testing import assert_allclose import sncosmo from sncosmo import Bandpass from sncosmo.tests.test_salt2source import read_header def test_bandpass_access(): b = Bandpass([4000., 4200., 4400.], [0.5, 1.0, 0.5]) assert_allclose(b.wave, [4000., 4200., 4400.]) assert_allclose(b.trans, [0.5, 1., 0.5]) def test_bandpass_interpolation(): b = Bandpass([4000., 4200., 4400.], [0.5, 1.0, 0.5]) assert_allclose(b([4100., 4300.]), [0.75, 0.75]) def test_bandpass_effective_wavelength(): b = Bandpass([4000., 4200., 4400.], [0.5, 1.0, 0.5]) assert b.wave_eff == 4200.0 def test_bandpass_zeros(): """Test that removing outlying zeros works as expected.""" assert_allclose(Bandpass([1., 2., 3., 4., 5.], [0., 0., 1., 1., 1.]).wave, [2., 3., 4., 5.]) assert_allclose(Bandpass([1., 2., 3., 4., 5.], [0., 1., 1., 1., 1.]).wave, [1., 2., 3., 4., 5.]) assert_allclose(Bandpass([1., 2., 3., 4., 5.], [1., 1., 1., 1., 1.]).wave, [1., 2., 3., 4., 5.]) assert_allclose(Bandpass([1., 2., 3., 4., 5.], [0., 0., 1., 1., 0.]).wave, [2., 3., 4., 5.]) assert_allclose(Bandpass([1., 2., 3., 4., 5.], [0., 0., 1., 0., 0.]).wave, [2., 3., 4.]) def test_trimmed(): band = Bandpass([4000., 4100., 4200., 4300., 4400., 4500.], [0.001, 0.002, 0.5, 0.6, 0.003, 0.001], trim_level=0.01) assert np.all(band.wave == np.array([4100., 4200., 4300., 4400.])) assert_allclose(band.trans, np.array([0.002, 0.5, 0.6, 0.003])) # issue 100 def test_bandpass_type(): """Check that bandpass wavelength type is always float64, and that color laws work with them.""" dust = sncosmo.CCM89Dust() for dt in [np.int32, np.int64, np.float32]: wave = np.arange(4000., 5000., 20., dtype=dt) trans = np.ones_like(wave) band = sncosmo.Bandpass(wave, trans) assert band.wave.dtype == np.float64 # Ensure that it works with cython-based propagation effect. # (flux, the second argument, should always be doubles) dust.propagate(band.wave, np.ones_like(wave, dtype=np.float64)) # issue 111 def test_bandpass_bessell(): """Check that Bessell bandpass definitions are scaled by inverse wavelength.""" band = sncosmo.get_bandpass('bessellb') trans = band.trans[[4, 9, 14]] # transmission at 4000, 4500, 5000 # copied from file orig_wave = np.array([4000., 4500., 5000.]) orig_trans = np.array([0.920, 0.853, 0.325]) scaled_trans = orig_trans / orig_wave # scaled_trans should be proportional to trans factor = scaled_trans[0] / trans[0] assert_allclose(scaled_trans, factor * trans) def test_aggregate_bandpass_name(): b = sncosmo.AggregateBandpass([([1000., 2000.], [1., 1.])]) assert repr(b).startswith(" 0 @pytest.mark.might_download @pytest.mark.skipif('not HAS_IMINUIT') def test_fit_lc_vs_snfit(): """Test fit_lc versus snfit result for one SN.""" # keep the same SALT2 version as snfit results source = sncosmo.get_source("salt2", "2.4") # purposefully use CCM dust to match snfit model = sncosmo.Model(source=source, effects=[sncosmo.CCM89Dust()], effect_names=['mw'], effect_frames=['obs']) fname = join(dirname(__file__), "data", "lc-03D4ag.list") data = sncosmo.read_lc(fname, format='salt2', read_covmat=True, expand_bands=True) model.set(mwebv=data.meta['MWEBV'], z=data.meta['Z_HELIO']) result, fitted_model = sncosmo.fit_lc( data, model, ['t0', 'x0', 'x1', 'c'], bounds={'x1': (-3., 3.), 'c': (-0.4, 0.4)}, modelcov=True, phase_range=(-15., 45.), wave_range=(3000., 7000.), warn=False, verbose=False) print(result) assert result.ndof == 25 assert result.nfit == 3 assert_allclose(fitted_model['t0'], 52830.9313, atol=0.01, rtol=0.) assert_allclose(fitted_model['x0'], 5.6578663e-05, atol=0., rtol=0.005) assert_allclose(fitted_model['x1'], 0.937399344, atol=0.005, rtol=0.) assert_allclose(fitted_model['c'], -0.0851965244, atol=0.001, rtol=0.) # errors assert_allclose(result.errors['t0'], 0.0955792638, atol=0., rtol=0.01) assert_allclose(result.errors['x0'], 1.52745001e-06, atol=0., rtol=0.01) assert_allclose(result.errors['x1'], 0.104657847, atol=0., rtol=0.01) assert_allclose(result.errors['c'], 0.0234763446, atol=0., rtol=0.01) sncosmo-2.12.1/sncosmo/tests/test_io.py000066400000000000000000000151071476435666400201520ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import os from io import BytesIO, StringIO from os.path import dirname, join from tempfile import NamedTemporaryFile, mkdtemp import numpy as np from astropy import wcs from astropy.io import fits from astropy.table import Table from numpy.testing import assert_allclose import sncosmo # Dummy data used for read_lc/write_lc round-tripping tests time = [1., 2., 3., 4.] band = ['sdssg', 'sdssr', 'sdssi', 'sdssz'] zp = [25., 25., 25., 25.] zpsys = ['ab', 'ab', 'ab', 'ab'] flux = [1., 1., 1., 1.] fluxerr = [0.1, 0.1, 0.1, 0.1] lcdata = Table(data=(time, band, flux, fluxerr, zp, zpsys), names=('time', 'band', 'flux', 'fluxerr', 'zp', 'zpsys'), meta={'a': 1, 'b': 1.0, 'c': 'one'}) def test_read_griddata_ascii(): # Write a temporary test file. f = StringIO() f.write("0. 0. 0.\n" "0. 1. 0.\n" "0. 2. 0.\n" "1. 0. 0.\n" "1. 1. 0.\n" "1. 2. 0.\n") f.seek(0) x0, x1, y = sncosmo.read_griddata_ascii(f) f.close() assert_allclose(x0, np.array([0., 1.])) assert_allclose(x1, np.array([0., 1., 2.])) def test_write_griddata_ascii(): x0 = np.array([0., 1.]) x1 = np.array([0., 1., 2.]) y = np.zeros((2, 3)) f = StringIO() sncosmo.write_griddata_ascii(x0, x1, y, f) # Read it back f.seek(0) x0_in, x1_in, y_in = sncosmo.read_griddata_ascii(f) f.close() assert_allclose(x0_in, x0) assert_allclose(x1_in, x1) assert_allclose(y_in, y) # with a filename: dirname = mkdtemp() fname = os.path.join(dirname, 'griddata.dat') sncosmo.write_griddata_ascii(x0, x1, y, fname) x0_in, x1_in, y_in = sncosmo.read_griddata_ascii(fname) assert_allclose(x0_in, x0) assert_allclose(x1_in, x1) assert_allclose(y_in, y) os.remove(fname) os.rmdir(dirname) def test_griddata_fits(): """Round tripping with write_griddata_fits() and read_griddata_fits()""" x0 = np.array([0., 1.]) x1 = np.array([0., 1., 2.]) y = np.zeros((2, 3)) f = BytesIO() sncosmo.write_griddata_fits(x0, x1, y, f) # Read it back f.seek(0) x0_in, x1_in, y_in = sncosmo.read_griddata_fits(f) assert_allclose(x0_in, x0) assert_allclose(x1_in, x1) assert_allclose(y_in, y) f.close() # Test reading 3-d grid data. We don't have a writer for # this, so we write a temporary FITS file by hand. x2 = np.array([3., 5., 7., 9]) y = np.zeros((len(x0), len(x1), len(x2))) # write a FITS file that represents x0, x1, x2, y w = wcs.WCS(naxis=3) w.wcs.crpix = [1, 1, 1] w.wcs.crval = [x2[0], x1[0], x0[0]] w.wcs.cdelt = [2., 1., 1.] hdu = fits.PrimaryHDU(y, header=w.to_header()) f = BytesIO() hdu.writeto(f) # Read it back f.seek(0) x0_in, x1_in, x2_in, y_in = sncosmo.read_griddata_fits(f) f.close() assert_allclose(x0_in, x0) assert_allclose(x1_in, x1) assert_allclose(x2_in, x2) assert_allclose(y_in, y) def test_read_lc(): f = StringIO(""" @id 1 @RA 36.0 @description good time band flux fluxerr zp zpsys 50000. g 1. 0.1 25. ab 50000.1 r 2. 0.1 25. ab """) t = sncosmo.read_lc(f, format='ascii') assert str(t) == (" time band flux fluxerr zp zpsys\n" "------- ---- ---- ------- ---- -----\n" "50000.0 g 1.0 0.1 25.0 ab\n" "50000.1 r 2.0 0.1 25.0 ab") assert t.meta['id'] == 1 assert t.meta['RA'] == 36.0 assert t.meta['description'] == 'good' def test_read_salt2(): fname = join(dirname(__file__), "data", "lc-03D4ag.list") data = sncosmo.read_lc(fname, format="salt2") # Test a few columns assert_allclose(data["Date"][0:4], [52816.54, 52824.59, 52851.53, 52873.4]) assert_allclose(data["ZP"][0:4], 27.036167) assert np.all(data["Filter"][0:4] == "MEGACAMPSF::g") assert np.all(data["MagSys"] == "AB_B12") # Test a bit of metadata assert_allclose(data.meta["Z_HELIO"], 0.285) assert_allclose(data.meta["RA"], 333.690959) assert data.meta["z_source"] == "H" def test_read_salt2_cov(): fname = join(dirname(__file__), "data", "lc-03D4ag.list") data = sncosmo.read_lc(fname, format="salt2", read_covmat=True) assert data["Fluxcov"].shape == (len(data), len(data)) assert_allclose(data["Fluxcov"][0:3, 0:3], [[0.867712297284, 0.01139998771, 0.01119398747], [0.01139998771, 2.03512047975, 0.01190299234], [0.01119398747, 0.01190299234, 1.3663344852]]) def test_read_salt2_old(): dname = join(dirname(__file__), "data", "SNLS3-04D3gx") data = sncosmo.read_lc(dname, format="salt2-old") # Test length and column names: assert len(data) == 25 + 37 + 38 + 18 # g + r + i + z lengths assert data.colnames == ["Date", "Flux", "Fluxerr", "ZP", "Filter", "MagSys"] # Test a bit of metadata and data assert data.meta["NAME"] == "04D3gx" assert_allclose(data.meta["Redshift"], 0.91) assert_allclose(data.meta["RA"], 215.056948) assert np.all(data["MagSys"] == "VEGA") def test_roundtripping(): for format in ['json', 'ascii', 'salt2']: f = NamedTemporaryFile(delete=False) f.close() # close to ensure that we can open it in write_lc() # raw=True is for the benefit of salt2 writer that modifies column # and header names by default. sncosmo.write_lc(lcdata, f.name, format=format, raw=True, pedantic=False) data = sncosmo.read_lc(f.name, format=format) for key in lcdata.colnames: assert np.all(data[key] == lcdata[key]) for key in lcdata.meta: assert data.meta[key] == lcdata.meta[key] os.unlink(f.name) def test_write_lc_salt2(): """Extra test to see if column renaming works""" f = NamedTemporaryFile(delete=False) f.close() # close to ensure that we can open it in write_lc() sncosmo.write_lc(lcdata, f.name, format='salt2') os.unlink(f.name) def test_write_lc_snana(): """Just check if the snana writer works without error.""" f = NamedTemporaryFile(delete=False) f.close() # close to ensure that we can open it in write_lc() sncosmo.write_lc(lcdata, f.name, format='snana', pedantic=False) os.unlink(f.name) def test_load_example_data(): data = sncosmo.load_example_data() def test_load_example_spectrum_data(): wave, flux, fluxerr = sncosmo.load_example_spectrum_data() assert len(wave) > 0 assert len(wave) == len(flux) == len(fluxerr) sncosmo-2.12.1/sncosmo/tests/test_magsystems.py000066400000000000000000000042711476435666400217370ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import math import numpy as np import pytest from astropy import units as u from numpy.testing import assert_allclose, assert_almost_equal import sncosmo def test_abmagsystem(): magsys = sncosmo.ABMagSystem() m = magsys.band_flux_to_mag(1.0, 'bessellb') f = magsys.band_mag_to_flux(m, 'bessellb') assert_almost_equal(f, 1.0) def test_spectralmagsystem(): """Check that SpectralMagSystem matches ABMagSystem when the spectrum is the same as AB.""" # construct a spectrum with same flux as AB: 3631 x 10^{-23} erg/s/cm^2/Hz # Use a fine grid to reduce linear interpolation errors when integrating # in SpectrumModel.bandflux(). wave = np.linspace(1000., 20000., 100000) # fine grid flux = 3631.e-23 * np.ones_like(wave) unit = u.erg / u.s / u.cm**2 / u.Hz s = sncosmo.SpectrumModel(wave, flux, unit=unit) magsys1 = sncosmo.SpectralMagSystem(s) magsys2 = sncosmo.ABMagSystem() assert_allclose(magsys1.zpbandflux('bessellb'), magsys2.zpbandflux('bessellb')) @pytest.mark.might_download def test_csp_magsystem(): csp = sncosmo.get_magsystem('csp') # filter zeropoints (copied from # http://csp.obs.carnegiescience.edu/data/filters # on 13 April 2017) zps = {"cspu": 12.986, "cspg": 15.111, "cspr": 14.902, "cspi": 14.535, "cspb": 14.328, "cspv3014": 14.437, "cspv3009": 14.388, "cspv9844": 14.439, "cspys": 13.921, "cspjs": 13.836, "csphs": 13.510, "cspk": 11.968, "cspyd": 13.770, "cspjd": 13.866, "csphd": 13.502} # The "zero point bandflux" should be the flux that corresponds to # magnitude zero. So, 0 = zp - 2.5 log(F) for band, zp in zps.items(): assert abs(2.5 * math.log10(csp.zpbandflux(band)) - zp) < 0.015 @pytest.mark.might_download def test_compositemagsystem_band_error(): """Test that CompositeMagSystem raises an error when band is not in system.""" csp = sncosmo.get_magsystem('csp') with pytest.raises(ValueError): csp.zpbandflux('desi') sncosmo-2.12.1/sncosmo/tests/test_models.py000066400000000000000000000373041476435666400210310ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES from io import StringIO import scipy import numpy as np from numpy.testing import assert_allclose, assert_approx_equal import sncosmo from sncosmo import registry def flatsource(): """Create and return a TimeSeriesSource with a flat spectrum == 1.0 at all times.""" phase = np.linspace(0., 100., 10) wave = np.linspace(800., 20000., 100) flux = np.ones((len(phase), len(wave)), dtype=float) return sncosmo.TimeSeriesSource(phase, wave, flux) class AchromaticMicrolensing(sncosmo.PropagationEffect): """ An achromatic microlensing object. Tests phase dependence. """ _param_names = [] param_names_latex = [] _minwave = np.nan _maxwave = np.nan _minphase = np.nan _maxphase = np.nan def __init__(self, time, magnification): """ Parameters ---------- time: :class:`~list` or :class:`~numpy.array` A time array for your microlensing dmag: :class:`~list` or :class:`~numpy.array` microlensing magnification """ self._parameters = np.array([]) self.mu = scipy.interpolate.interp1d(time, magnification, bounds_error=False, fill_value=1.) self._minphase = np.min(time) self._maxphase = np.max(time) def propagate(self, wave, flux, phase): """ Propagate the magnification onto the model's flux output. """ mu = np.expand_dims(np.atleast_1d(self.mu(phase)), 1) return flux * mu class StepEffect(sncosmo.PropagationEffect): """ Effect with transmission 0 below cutoff wavelength, 1 above. Useful for testing behavior with redshift. """ _param_names = ['stepwave'] param_names_latex = [r'\lambda_{s}'] def __init__(self, minwave, maxwave, stepwave=10000.): self._minwave = minwave self._maxwave = maxwave self._parameters = np.array([stepwave], dtype=np.float64) def propagate(self, wave, flux): return flux * (wave >= self._parameters[0]) class TestTimeSeriesSource: def setup_class(self): self.source = flatsource() def test_flux(self): for a in [1., 2.]: self.source.set(amplitude=a) assert_allclose(self.source.flux(1., 2000.), a) assert_allclose(self.source.flux(1., [2000.]), np.array([a])) assert_allclose(self.source.flux([1.], 2000.), np.array([[a]])) assert_allclose(self.source.flux([1.], [2000.]), np.array([[a]])) def test_getsource(self): # register the source & retrieve it from the registry registry.register(self.source, name="testsource", data_class=sncosmo.Source) m = sncosmo.get_source("testsource") def test_bandflux(self): self.source.set(amplitude=1.0) f = self.source.bandflux("bessellb", 0.) # Correct answer b = sncosmo.get_bandpass("bessellb") dwave = np.gradient(b.wave) ans = np.sum(b.trans * b.wave * dwave) / sncosmo.models.HC_ERG_AA assert_approx_equal(ans, f) def test_bandflux_shapes(self): # Just check that these work. self.source.bandflux("bessellb", 0., zp=25., zpsys="ab") self.source.bandflux("bessellb", [0.1, 0.2], zp=25., zpsys="ab") self.source.bandflux(["bessellb", "bessellv"], [0.1, 0.2], zp=25., zpsys="ab") class TestSALT2Source: def setup_class(self): """Create a SALT2 model with a lot of components set to 1.""" phase = np.linspace(0., 100., 10) wave = np.linspace(1000., 10000., 100) vals1d = np.zeros(len(phase), dtype=np.float64) vals = np.ones([len(phase), len(wave)], dtype=np.float64) # Create some 2-d grid files files = [] for i in [0, 1]: f = StringIO() sncosmo.write_griddata_ascii(phase, wave, vals, f) f.seek(0) # return to start of file. files.append(f) # CL file. The CL in magnitudes will be # CL(wave) = -(wave - B) / (V - B) [B = 4302.57, V = 5428.55] # and transmission will be 10^(-0.4 * CL(wave))^c clfile = StringIO() clfile.write("1\n" "0.0\n" "Salt2ExtinctionLaw.version 1\n" "Salt2ExtinctionLaw.min_lambda 3000\n" "Salt2ExtinctionLaw.max_lambda 7000\n") clfile.seek(0) # Create some more 2-d grid files for factor in [1., 0.01, 0.01, 0.01]: f = StringIO() sncosmo.write_griddata_ascii(phase, wave, factor * vals, f) f.seek(0) # return to start of file. files.append(f) # Create a 1-d grid file (color dispersion) cdfile = StringIO() for w in wave: cdfile.write("{0:f} {1:f}\n".format(w, 0.2)) cdfile.seek(0) # return to start of file. # Create a SALT2Source self.source = sncosmo.SALT2Source(m0file=files[0], m1file=files[1], clfile=clfile, errscalefile=files[2], lcrv00file=files[3], lcrv11file=files[4], lcrv01file=files[5], cdfile=cdfile) for f in files: f.close() cdfile.close() clfile.close() def test_bandflux_rcov(self): # component 1: # ans = (F0/F1)^2 S^2 (V00 + 2 x1 V01 + x1^2 V11) # when x1=0, this reduces to S^2 V00 = 1^2 * 0.01 = 0.01 # # component 2: # cd^2 = 0.04 phase = np.linspace(0., 100., 10) wave = np.linspace(1000., 10000., 100) vals = np.ones([len(phase), len(wave)], dtype=np.float64) band = ['bessellb', 'bessellb', 'bessellr', 'bessellr', 'besselli'] band = np.array([sncosmo.get_bandpass(b) for b in band]) phase = np.array([10., 20., 30., 40., 50.]) self.source.set(x1=0.0) result = self.source.bandflux_rcov(band, phase) expected = np.array([[0.05, 0.04, 0., 0., 0.], [0.04, 0.05, 0., 0., 0.], [0., 0., 0.05, 0.04, 0.], [0., 0., 0.04, 0.05, 0.], [0., 0., 0., 0., 0.05]]) assert_allclose(result, expected) class TestSALT3Source: def setup_class(self): """Create a SALT3 model with a lot of components set to 1.""" phase = np.linspace(0., 100., 10) wave = np.linspace(1000., 10000., 100) vals1d = np.zeros(len(phase), dtype=np.float64) vals = np.ones([len(phase), len(wave)], dtype=np.float64) # Create some 2-d grid files files = [] for i in [0, 1]: f = StringIO() sncosmo.write_griddata_ascii(phase, wave, vals, f) f.seek(0) # return to start of file. files.append(f) # CL file. The CL in magnitudes will be # CL(wave) = -(wave - B) / (V - B) [B = 4302.57, V = 5428.55] # and transmission will be 10^(-0.4 * CL(wave))^c clfile = StringIO() clfile.write("1\n" "0.0\n" "Salt2ExtinctionLaw.version 1\n" "Salt2ExtinctionLaw.min_lambda 3000\n" "Salt2ExtinctionLaw.max_lambda 8000\n") clfile.seek(0) # Create some more 2-d grid files for factor in [1., 0.01, 0.01, 0.01]: f = StringIO() sncosmo.write_griddata_ascii(phase, wave, factor * vals, f) f.seek(0) # return to start of file. files.append(f) # Create a 1-d grid file (color dispersion) cdfile = StringIO() for w in wave: cdfile.write("{0:f} {1:f}\n".format(w, 0.2)) cdfile.seek(0) # return to start of file. # Create a SALT2Source self.source = sncosmo.SALT3Source(m0file=files[0], m1file=files[1], clfile=clfile, lcrv00file=files[3], lcrv11file=files[4], lcrv01file=files[5], cdfile=cdfile) for f in files: f.close() cdfile.close() clfile.close() def test_bandflux_rcov(self): # component 1: # ans = (F0/F1)^2 S^2 (V00 + 2 x1 V01 + x1^2 V11) # when x1=0, this reduces to S^2 V00 = 1^2 * 0.01 = 0.01 # # component 2: # cd^2 = 0.04 phase = np.linspace(0., 100., 10) wave = np.linspace(1000., 10000., 100) vals = np.ones([len(phase), len(wave)], dtype=np.float64) band = ['bessellb', 'bessellb', 'bessellr', 'bessellr', 'besselli'] band = np.array([sncosmo.get_bandpass(b) for b in band]) phase = np.array([10., 20., 30., 40., 50.]) self.source.set(x1=0.0) result = self.source.bandflux_rcov(band, phase) expected = np.array([[0.05, 0.04, 0., 0., 0.], [0.04, 0.05, 0., 0., 0.], [0., 0., 0.05, 0.04, 0.], [0., 0., 0.04, 0.05, 0.], [0., 0., 0., 0., 0.05]]) assert_allclose(result, expected) class TestModel: def setup_class(self): self.model = sncosmo.Model(source=flatsource(), effects=[sncosmo.CCM89Dust()], effect_frames=['obs'], effect_names=['mw']) def test_minwave(self): # at redshift zero, should be determined by effect minwave (~909) self.model.set(z=0.) ans = max(self.model.source.minwave(), self.model.effects[0].minwave()) assert self.model.minwave() == ans # at redshift 1, should be determined by effect minwave (800*2) z = 1. self.model.set(z=z) ans = max(self.model.source.minwave() * (1.+z), self.model.effects[0].minwave()) assert self.model.minwave() == ans def test_maxwave(self): # at redshift zero, should be determined by source maxwave (20000) self.model.set(z=0.) ans = min(self.model.source.maxwave(), self.model.effects[0].maxwave()) assert self.model.maxwave() == ans # at redshift 1, should be determined by effect maxwave (33333) z = 1. self.model.set(z=z) ans = min(self.model.source.maxwave() * (1.+z), self.model.effects[0].maxwave()) assert self.model.maxwave() == ans def test_set_source_peakabsmag(self): # Both Bandpass and str should work band = sncosmo.get_bandpass('bessellb') self.model.set_source_peakabsmag(-19.3, 'bessellb', 'ab') self.model.set_source_peakabsmag(-19.3, band, 'ab') def test_str(self): """Test if string summary works at all.""" str(self.model) def test_add_effect(self): nparams = len(self.model.parameters) self.model.add_effect(sncosmo.F99Dust(), 'host', 'rest') assert len(self.model.effects) == 2 assert 'hostebv' in self.model.param_names assert len(self.model.parameters) == nparams + 1 def test_set_and_get(self): """Test different methods for setting and getting parameters.""" a = self.model.get('amplitude') self.model['amplitude'] = 2 * a # test __setitem__ assert self.model['amplitude'] == 2 * a # test __getitem__ self.model.update({'amplitude': 4 * a}) # test update assert self.model['amplitude'] == 4 * a def test_effect_frame_free(): """Test Model with PropagationEffect with a 'free' frame.""" model = sncosmo.Model(source=flatsource(), effects=[StepEffect(800., 20000.)], effect_frames=['free'], effect_names=['screen']) model.set(z=1.0, screenz=0.5, screenstepwave=10000.) # maxwave is set by the effect at z=0.5 assert model.maxwave() == (1. + 0.5) * 20000. wave = np.array([12000., 13000., 14000., 14999., 15000., 16000.]) assert_allclose(model.flux(0., wave), [0., 0., 0., 0., 0.5, 0.5]) def test_effect_phase_dependent(): """Test Model with PropagationEffect with phase dependence""" model = sncosmo.Model(source=flatsource()) flux = model.flux(50., 5000.).flatten() model_micro = sncosmo.Model(source=flatsource(), effects=[AchromaticMicrolensing([40., 60.], [10., 10.])], effect_frames=['rest'], effect_names=['micro']) assert_allclose(model_micro.flux(50., 5000.).flatten(), flux*10.) def test_G10(): """Test Model with G10 color dependant scatter""" SALT2Source = sncosmo.models.get_source('salt2', version='2.4') ModelRef = sncosmo.Model(SALT2Source) G10 = sncosmo.models.G10(SALT2Source) ModelWithG10 = sncosmo.Model(source=SALT2Source, effects=[G10], effect_frames=['rest'], effect_names=['G10']) lam_nodes, siglam_values = G10.compute_sigma_nodes() # Test how nodes are computed assert_allclose(lam_nodes, np.array([2000., 2800., 3600., 4400., 5200., 6000., 6800., 7600., 8400., 9200.])) # Test how siglam values are computed assert_allclose(siglam_values, np.array([1.308910000, 0.259717301, 0.078368072, 0.035382907, 0.023921785, 0.024232781, 0.036799298, 0.083808031, 0.286347107, 1.468232113])) # Compare with and without G10 random = np.random.default_rng(G10._seed).normal(size=len(lam_nodes)) assert_allclose(ModelWithG10.flux(0, lam_nodes), ModelRef.flux(0, lam_nodes) * 10**(-0.4 * siglam_values * random)) def test_C11(): """Test Model with C11 color dependant scatter""" SALT2Source = sncosmo.models.get_source('salt2', version='2.4') ModelRef = sncosmo.Model(SALT2Source) C11 = sncosmo.models.C11() ModelWithC11 = sncosmo.Model(source=SALT2Source, effects=[C11], effect_frames=['rest'], effect_names=['C11_']) for CvU in [-1, 0, 1]: ModelWithC11.set(C11_CvU=CvU, C11_Sf=1) cov = ModelWithC11.effects[0].build_cov() # Test construvtion of cov matrix corr = cov / np.outer(C11._variance, C11._variance) assert_allclose(corr[0, 1:], CvU * C11._corr_matrix[1, 1:]) assert_allclose(corr[1:, 0], CvU * C11._corr_matrix[1, 1:]) assert_allclose(corr[1:, 1:], C11._corr_matrix[1:, 1:]) # Compare to model without C11 random = np.random.default_rng( C11._seed).multivariate_normal(np.zeros(len(C11._lam_nodes)), cov) assert_allclose(ModelWithC11.flux(0, C11._lam_nodes), ModelRef.flux(0, C11._lam_nodes) * 10**(-0.4 * random)) sncosmo-2.12.1/sncosmo/tests/test_photdata.py000066400000000000000000000017221476435666400213450ustar00rootroot00000000000000import numpy as np from astropy.table import Table import sncosmo def test_select_data(): data = Table([[1., 2., 3.], ['a', 'b', 'c'], [[1.1, 1.2, 1.3], [2.1, 2.2, 2.3], [3.1, 3.2, 3.3]]], names=['time', 'x', 'cov']) for index in (np.array([True, True, False]), [0, 1], slice(0, 2)): selected = sncosmo.select_data(data, index) assert np.all(selected['time'] == np.array([1., 2.])) assert np.all(selected['x'] == np.array(['a', 'b'])) assert np.all(selected['cov'] == np.array([[1.1, 1.2], [2.1, 2.2]])) # integer indexing switching order selected = sncosmo.select_data(data, [2, 0]) assert np.all(selected['time'] == np.array([3., 1.])) assert np.all(selected['x'] == np.array(['c', 'a'])) assert np.all(selected['cov'] == np.array([[3.3, 3.1], [1.3, 1.1]])) sncosmo-2.12.1/sncosmo/tests/test_plotting.py000066400000000000000000000014421476435666400214000ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import numpy as np import pytest import sncosmo try: from matplotlib.figure import Figure HAS_MATPLOTLIB = True except ImportError: HAS_MATPLOTLIB = False @pytest.mark.skipif('not HAS_MATPLOTLIB') class TestPlotLC: def setup_class(self): # Create a TimeSeriesSource with a flat spectrum at all times. phase = np.linspace(0., 100., 10) wave = np.linspace(1000., 10000., 100) flux = np.ones((len(phase), len(wave)), dtype=float) source = sncosmo.TimeSeriesSource(phase, wave, flux) self.model = sncosmo.Model(source=source) def test_plotmodel(self): fig = sncosmo.plot_lc(model=self.model, bands=['bessellb', 'bessellr']) assert isinstance(fig, Figure) sncosmo-2.12.1/sncosmo/tests/test_registry.py000066400000000000000000000107671476435666400214220ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSE.rst """Test registry functions.""" import numpy as np import pytest import sncosmo def basic_loader(arg, name=None, version=None): return arg def basic_loader_no_version(arg, name=None): return arg @pytest.fixture def registry(): # Create a test registry with a few test entries. registry = sncosmo._registry.Registry() registry.register_loader( 'test_loader', basic_loader, args=('test_1',), version=1, ) registry.register_loader( 'test_loader', basic_loader, args=('test_2',), version=2, meta={'metaval': 'metatest_2'} ) registry.register('instance_value', 'test_instance') return registry def test_register_bandpass(): disp = np.array([4000., 4200., 4400., 4600., 4800., 5000.]) trans = np.array([0., 1., 1., 1., 1., 0.]) # create a band, register it, make sure we can get it back. band = sncosmo.Bandpass(disp, trans, name='tophatg') sncosmo.register(band) assert sncosmo.get_bandpass('tophatg') is band def test_register_magsystem(): magsys = sncosmo.get_magsystem('ab') sncosmo.register(magsys, name='test_magsys') assert sncosmo.get_magsystem('test_magsys') is magsys def test_register_source(): phase = np.linspace(0., 100., 10) wave = np.linspace(800., 20000., 100) flux = np.ones((len(phase), len(wave)), dtype=float) source = sncosmo.TimeSeriesSource(phase, wave, flux, name='test_source') sncosmo.register(source) # Retrieving a source makes a copy, so just check that the names are the # same. assert sncosmo.get_source('test_source').name == 'test_source' def test_register_loader(): # Register a loader that returns a MagSystem. magsys = sncosmo.get_magsystem('ab') sncosmo.register_loader( sncosmo.magsystems.MagSystem, 'test_magsystem', basic_loader, args=(magsys,) ) # make sure that we can get the magnitude system back. assert sncosmo.get_magsystem('test_magsystem') is magsys def test_register_invalid(): with pytest.raises(ValueError): sncosmo.register("invalid") def test_retrieve(): sncosmo.registry.retrieve(sncosmo.magsystems.MagSystem, 'ab') def test_registry_loader_exists(registry): with pytest.raises(Exception): registry.register_loader( 'test_loader', basic_loader, args=('new_test'), version=1, ) def test_registry_instance_exists(registry): with pytest.raises(Exception): registry.register('new_instance_value', 'test_instance') def test_registry_no_name(registry): with pytest.raises(ValueError): registry.register('asdf') def test_registry_bad_name(registry): class BadClass(): name = 1 with pytest.raises(ValueError): registry.register(BadClass()) def test_registry_cases(registry): # Should work regardless of case. for name in ['test_loader', 'TEST_LOADER', 'TesT_LoADeR']: registry.retrieve(name) def test_registry_loader_alias(registry): registry.alias('alias_loader', 'test_loader', new_version=1, existing_version=2) assert registry.retrieve('alias_loader') == 'test_2' def test_registry_instance_alias(registry): registry.alias('alias_instance', 'test_instance') assert registry.retrieve('alias_instance') == 'instance_value' def test_registry_bad_alias(registry): with pytest.raises(Exception): registry.alias('alias', 'invalid_key') def test_registry_retrieve(registry): assert registry.retrieve('test_instance') == 'instance_value' def test_registry_no_version(registry): registry.register_loader('noversion_loader', basic_loader_no_version, args=('noversion',)) assert registry.retrieve('noversion_loader') == 'noversion' def test_registry_version(registry): assert registry.retrieve('test_loader', version=1) == 'test_1' assert registry.retrieve('test_loader', version=2) == 'test_2' def test_registry_default_version(registry): assert registry.retrieve('test_loader') == 'test_2' def test_registry_missing(registry): with pytest.raises(Exception): assert registry.retrieve('missing') def test_registry_missing_version(registry): with pytest.raises(Exception): assert registry.retrieve('test_loader', version=3) def test_registry_loaders_metadata(registry): meta = registry.get_loaders_metadata() assert len(meta) == 2 sncosmo-2.12.1/sncosmo/tests/test_salt2source.py000066400000000000000000000062161476435666400220120ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES """Tests for SALT2Source (and wrapped in Model)""" import os import numpy as np import pytest from numpy.testing import assert_allclose import sncosmo def _parse_value(s): try: x = int(s) except ValueError: try: x = float(s) except ValueError: x = s return x def read_header(f): """Read header from the open file `f` until first line that doesn't start with # or @.""" meta = {} # read header until a line doesn't start with # or @ while True: pos = f.tell() line = f.readline() if line[0] == '#': continue elif line[0] == '@': key, value = line[1:-1].split() meta[key] = _parse_value(value) else: f.seek(pos) break return meta @pytest.mark.might_download def test_salt2source_timeseries_vs_snfit(): """Test timeseries output from SALT2Source vs pregenerated timeseries from snfit (SALT2 software).""" source = sncosmo.get_source("salt2", version="2.4") # fixed version model = sncosmo.Model(source) dirname = os.path.join(os.path.dirname(__file__), "data") for fname in ['salt2_timeseries_1.dat', 'salt2_timeseries_2.dat', 'salt2_timeseries_3.dat', 'salt2_timeseries_4.dat']: f = open(os.path.join(dirname, fname), 'r') meta = read_header(f) time, wave, fluxref = sncosmo.read_griddata_ascii(f) f.close() # The output from snfit's Salt2Model.SpectrumFlux() has a # different definition than sncosmo's model.flux() by a factor # of a^2. snfit's definition is the rest-frame flux but at a # blue-shifted wavelength. (There is no correction for photon # energy or time dilation. These corrections are made in the # integration step.) a = 1. / (1. + meta['Redshift']) fluxref *= a**2 model.set(z=meta['Redshift'], t0=meta['DayMax'], x0=meta['X0'], x1=meta['X1'], c=meta['Color']) flux = model.flux(time, wave) # super good agreement! assert_allclose(flux, fluxref, rtol=1e-13) @pytest.mark.might_download def test_salt2source_rcov_vs_snfit(): dirname = os.path.join(os.path.dirname(__file__), "data") # read parameters and times f = open(os.path.join(dirname, "salt2_rcov_params_times.dat"), 'r') meta = read_header(f) times = np.loadtxt(f) f.close() # initialize model and set parameters source = sncosmo.get_source("salt2", version="2.4") # fixed version model = sncosmo.Model(source) model.set(z=meta['Redshift'], t0=meta['DayMax'], x0=meta['X0'], x1=meta['X1'], c=meta['Color']) # Test separate bands separately, as thats how they're written to files. # (And cross-band covariance is zero.) for band in ('SDSSg', 'SDSSr', 'SDSSi'): fname = os.path.join(dirname, "salt2_rcov_snfit_{}.dat".format(band)) ref = np.loadtxt(fname, skiprows=1) rcov = model._bandflux_rcov(band, times) assert_allclose(ref, rcov, rtol=6.e-5) sncosmo-2.12.1/sncosmo/tests/test_salt2utils.py000066400000000000000000000074671476435666400216630ustar00rootroot00000000000000import os import pickle import numpy as np from numpy.testing import assert_allclose from scipy.interpolate import RectBivariateSpline import sncosmo from sncosmo.salt2utils import BicubicInterpolator, SALT2ColorLaw # On Python 2 highest protocol is 2. # Protocols 0 and 1 don't work on the classes here! TEST_PICKLE_PROTOCOLS = (2, 3, 4) def test_bicubic_interpolator_vs_snfit(): datadir = os.path.join(os.path.dirname(__file__), "data") # created by running generate script in `misc` directory fname_input = os.path.join(datadir, "interpolation_test_input.dat") fname_evalx = os.path.join(datadir, "interpolation_test_evalx.dat") fname_evaly = os.path.join(datadir, "interpolation_test_evaly.dat") # result file was created by running snfit software Grid2DFunction fname_result = os.path.join(datadir, "interpolation_test_result.dat") # load arrays x, y, z = sncosmo.read_griddata_ascii(fname_input) xp = np.loadtxt(fname_evalx) yp = np.loadtxt(fname_evaly) result = np.loadtxt(fname_result) f = BicubicInterpolator(x, y, z) assert_allclose(f(xp, yp), result, rtol=1e-5) def test_bicubic_interpolator_shapes(): """Ensure that input shapes are handled like RectBivariateSpline""" x = np.array([1., 2., 3., 4., 5.]) z = np.ones((len(x), len(x))) f = BicubicInterpolator(x, x, z) f2 = RectBivariateSpline(x, x, z) assert f(0., [1., 2.]).shape == f2(0., [1., 2.]).shape assert f([1., 2.], 0.).shape == f2([1., 2.], 0.).shape assert f(0., 0.).shape == f2(0., 0.).shape def test_bicubic_interpolator_pickle(): x = np.arange(5) y = np.arange(10) z = np.ones((len(x), len(y))) f = BicubicInterpolator(x, y, z) for protocol in TEST_PICKLE_PROTOCOLS: f2 = pickle.loads(pickle.dumps(f, protocol=protocol)) assert f2(4., 5.5) == f(4., 5.5) def test_salt2colorlaw_vs_python(): """Compare SALT2ColorLaw vs python implementation""" B_WAVELENGTH = 4302.57 V_WAVELENGTH = 5428.55 colorlaw_coeffs = [-0.504294, 0.787691, -0.461715, 0.0815619] colorlaw_range = (2800., 7000.) # old python implementation def colorlaw_python(wave): v_minus_b = V_WAVELENGTH - B_WAVELENGTH l = (wave - B_WAVELENGTH) / v_minus_b l_lo = (colorlaw_range[0] - B_WAVELENGTH) / v_minus_b l_hi = (colorlaw_range[1] - B_WAVELENGTH) / v_minus_b alpha = 1. - sum(colorlaw_coeffs) coeffs = [0., alpha] coeffs.extend(colorlaw_coeffs) coeffs = np.array(coeffs) prime_coeffs = (np.arange(len(coeffs)) * coeffs)[1:] extinction = np.empty_like(wave) # Blue side idx_lo = l < l_lo p_lo = np.polyval(np.flipud(coeffs), l_lo) pprime_lo = np.polyval(np.flipud(prime_coeffs), l_lo) extinction[idx_lo] = p_lo + pprime_lo * (l[idx_lo] - l_lo) # Red side idx_hi = l > l_hi p_hi = np.polyval(np.flipud(coeffs), l_hi) pprime_hi = np.polyval(np.flipud(prime_coeffs), l_hi) extinction[idx_hi] = p_hi + pprime_hi * (l[idx_hi] - l_hi) # In between idx_between = np.invert(idx_lo | idx_hi) extinction[idx_between] = np.polyval(np.flipud(coeffs), l[idx_between]) return -extinction colorlaw = SALT2ColorLaw(colorlaw_range, colorlaw_coeffs) wave = np.linspace(2000., 9200., 201) np.testing.assert_allclose(colorlaw(wave), colorlaw_python(wave)) def test_salt2colorlaw_pickle(): colorlaw_coeffs = [-0.504294, 0.787691, -0.461715, 0.0815619, 0.0, 0.0] colorlaw_range = (2800., 7000.) colorlaw = SALT2ColorLaw(colorlaw_range, colorlaw_coeffs) for protocol in TEST_PICKLE_PROTOCOLS: colorlaw2 = pickle.loads(pickle.dumps(colorlaw, protocol=protocol)) wave = np.linspace(2000., 9200., 201) assert np.all(colorlaw(wave) == colorlaw2(wave)) sncosmo-2.12.1/sncosmo/tests/test_simulation.py000066400000000000000000000041241476435666400217240ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import numpy as np from astropy.table import Table import sncosmo from .test_models import flatsource def test_zdist(): """Test that zdist function works.""" np.random.seed(0) z = list(sncosmo.zdist(0., 0.25)) # check that zdist returns the same number of SNe as # when this test was written (does not test whether the number is # correct) assert len(z) == 14 # check that all values are indeed between the input limits. zarr = np.array(z) assert np.all((zarr > 0.) & (zarr < 0.25)) def test_realize_lcs(): # here's some completely made-up data: obs1 = Table({'time': [10., 60., 110.], 'band': ['bessellb', 'bessellr', 'besselli'], 'gain': [1., 1., 1.], 'skynoise': [100., 100., 100.], 'zp': [30., 30., 30.], 'zpsys': ['ab', 'ab', 'ab']}) # same made up data with aliased column names: obs2 = Table({'MJD': [10., 60., 110.], 'filter': ['bessellb', 'bessellr', 'besselli'], 'GAIN': [1., 1., 1.], 'skynoise': [100., 100., 100.], 'ZPT': [30., 30., 30.], 'zpmagsys': ['ab', 'ab', 'ab']}) for obs in (obs1, obs2): # A model with a flat spectrum between 0 and 100 days. model = sncosmo.Model(source=flatsource()) # parameters to run params = [{'amplitude': 1., 't0': 0., 'z': 0.}, {'amplitude': 1., 't0': 100., 'z': 0.}, {'amplitude': 1., 't0': 200., 'z': 0.}] # By default, realize_lcs should return all observations for all SNe lcs = sncosmo.realize_lcs(obs, model, params) assert len(lcs[0]) == 3 assert len(lcs[1]) == 3 assert len(lcs[2]) == 3 # For trim_obervations=True, only certain observations will be # returned. lcs = sncosmo.realize_lcs(obs, model, params, trim_observations=True) assert len(lcs[0]) == 2 assert len(lcs[1]) == 1 assert len(lcs[2]) == 0 sncosmo-2.12.1/sncosmo/tests/test_snanaio.py000066400000000000000000000041061476435666400211700ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES from os.path import dirname, join from numpy.testing import assert_allclose import pytest import sncosmo def test_read_snana_ascii(): fname = join(dirname(__file__), "data", "snana_ascii_example.dat") meta, tables = sncosmo.read_snana_ascii(fname, default_tablename="OBS") # test a few different types of metadata assert meta['SURVEY'] == 'DES' assert meta['FAKE'] == 0 assert_allclose(meta['REDSHIFT_HELIO'], 0.3614) # only 1 table assert len(tables) == 1 data = tables["OBS"] assert len(data) == 4 # 4 rows. assert len(data.colnames) == 13 # 13 columns. def test_read_snana_fits(): fname1 = join(dirname(__file__), "data", "snana_fits_example_head.fits") fname2 = join(dirname(__file__), "data", "snana_fits_example_phot.fits") sne = sncosmo.read_snana_fits(fname1, fname2) assert len(sne) == 2 def test_read_snana_simlib(): fname = join(dirname(__file__), "data", "snana_simlib_example.dat") meta, obs_sets = sncosmo.read_snana_simlib(fname) assert len(obs_sets) == 2 def test_read_snana_simlib_noend(): fname = join(dirname(__file__), "data", "snana_simlib_example_noend.dat") meta, obs_sets = sncosmo.read_snana_simlib(fname) assert len(obs_sets) == 2 def test_read_snana_simlib_doc(): """Test when DOCANA header is present in simlib""" fname = join(dirname(__file__), "data", "snana_simlib_example_doc.dat") meta, _ = sncosmo.read_snana_simlib(fname) assert "DOCUMENTATION" in meta def test_read_snana_simlib_coadd(): """Test when co-added `ID*NEXPOSE` key is used.""" fname = join(dirname(__file__), "data", "snana_simlib_example_coadd.dat") meta, obs_sets = sncosmo.read_snana_simlib(fname) assert "NEXPOSE" in obs_sets[0].colnames def test_read_snana_simlib_invalid(): """Test that we fails on simlib files with bad co-add columns.""" fname = join(dirname(__file__), "data", "snana_simlib_example_invalid.dat") with pytest.raises(ValueError) as e_info: _ = sncosmo.read_snana_simlib(fname) sncosmo-2.12.1/sncosmo/tests/test_spectrum.py000066400000000000000000000151761476435666400214130ustar00rootroot00000000000000import numpy as np import pytest from astropy.table import Table from numpy.testing import assert_allclose import sncosmo try: import iminuit HAS_IMINUIT = True except ImportError: HAS_IMINUIT = False def test_bin_edges_linear(): """Ensure that we can recover consistent bin edges for a spectrum from bin centers. Internally, the bin edges are stored rather than the bin centers. """ wave = np.linspace(3000, 8000, 100) flux = np.ones_like(wave) spec = sncosmo.Spectrum(wave, flux) assert_allclose(wave, spec.wave, rtol=1.e-5) def test_bin_edges_log(): """Ensure that we can recover consistent bin edges for a spectrum from bin centers. Internally, the bin edges are stored rather than the bin centers. """ wave = np.logspace(np.log10(3000), np.log10(8000), 100) flux = np.ones_like(wave) spec = sncosmo.Spectrum(wave, flux) assert_allclose(wave, spec.wave, rtol=1.e-5) @pytest.mark.might_download class TestSpectrum: def setup_class(self): # Simulate a spectrum model = sncosmo.Model(source='hsiao-subsampled') params = {'t0': 10., 'amplitude': 1.e-7, 'z': 0.2} start_params = {'t0': 0., 'amplitude': 1., 'z': 0.} model.set(**params) # generate a fake spectrum with no errors. note: we simulate a high # resolution spectrum and then bin it up. we also include large # covariance between spectral elements to verify that we are handling # covariance properly. spec_time = params['t0'] + 5. sim_wave = np.arange(3000, 9000) sim_flux = model.flux(spec_time, sim_wave) sim_fluxcov = 0.01 * np.max(sim_flux)**2 * np.ones((len(sim_flux), len(sim_flux))) sim_fluxcov += np.diag(0.1 * sim_flux**2) spectrum = sncosmo.Spectrum(sim_wave, sim_flux, fluxcov=sim_fluxcov, time=spec_time) # generate a binned up low-resolution spectrum. bin_wave = np.linspace(3500, 8500, 200) bin_spectrum = spectrum.rebin(bin_wave) # generate fake photometry with no errors points_per_band = 12 bands = points_per_band * ['bessellux', 'bessellb', 'bessellr', 'besselli'] times = params['t0'] + np.linspace(-10., 60., len(bands)) zp = len(bands) * [25.] zpsys = len(bands) * ['ab'] flux = model.bandflux(bands, times, zp=zp, zpsys=zpsys) fluxerr = len(bands) * [0.1 * np.max(flux)] photometry = Table({ 'time': times, 'band': bands, 'flux': flux, 'fluxerr': fluxerr, 'zp': zp, 'zpsys': zpsys }) self.model = model self.photometry = photometry self.spectrum = spectrum self.bin_spectrum = bin_spectrum self.params = params self.start_params = start_params def test_bandflux(self): """Check synthetic photometry. We compare synthetic photometry on high and low resolution spectra. It should stay the same. """ bandflux_highres = self.spectrum.bandflux('sdssg') bandflux_lowres = self.bin_spectrum.bandflux('sdssg') assert_allclose(bandflux_highres, bandflux_lowres, rtol=1.e-3) def test_bandflux_multi(self): """Check synthetic photometry with multiple bands.""" bands = ['sdssg', 'sdssr', 'sdssi'] bandflux_highres = self.spectrum.bandflux(bands) bandflux_lowres = self.bin_spectrum.bandflux(bands) assert_allclose(bandflux_highres, bandflux_lowres, rtol=1.e-3) def test_bandflux_zpsys(self): """Check synthetic photometry with a magnitude system.""" bands = ['sdssg', 'sdssr', 'sdssi'] bandflux_highres = self.spectrum.bandflux(bands, 25., 'ab') bandflux_lowres = self.spectrum.bandflux(bands, 25., 'ab') assert_allclose(bandflux_highres, bandflux_lowres, rtol=1.e-3) def test_bandfluxcov(self): """Check synthetic photometry with covariance.""" bands = ['sdssg', 'sdssr', 'sdssi'] flux_highres, cov_highres = self.spectrum.bandfluxcov(bands) flux_lowres, cov_lowres = self.bin_spectrum.bandfluxcov(bands) assert_allclose(flux_highres, flux_lowres, rtol=1.e-3) assert_allclose(cov_highres, cov_lowres, rtol=1.e-3) def test_bandmag(self): """Check synthetic photometry in magnitudes.""" bands = ['sdssg', 'sdssr', 'sdssi'] bandmag_highres = self.spectrum.bandmag(bands, 'ab') bandmag_lowres = self.bin_spectrum.bandmag(bands, 'ab') assert_allclose(bandmag_highres, bandmag_lowres, rtol=1.e-3) @pytest.mark.skipif('not HAS_IMINUIT') def test_fit_lc_spectra(self): """Check fit results for a single high-resolution spectrum.""" self.model.set(**self.start_params) res, fitmodel = sncosmo.fit_lc(model=self.model, spectra=self.bin_spectrum, vparam_names=['amplitude', 'z', 't0'], bounds={'z': (0., 0.3)}) # set model to true parameters and compare to fit results. self.model.set(**self.params) assert_allclose(res.parameters, self.model.parameters, rtol=1.e-3) @pytest.mark.skipif('not HAS_IMINUIT') def test_fit_lc_both(self): """Check fit results for both spectra and photometry.""" self.model.set(**self.start_params) res, fitmodel = sncosmo.fit_lc(self.photometry, model=self.model, spectra=self.bin_spectrum, vparam_names=['amplitude', 'z', 't0'], bounds={'z': (0., 0.3)}) # set model to true parameters and compare to fit results. self.model.set(**self.params) assert_allclose(res.parameters, self.model.parameters, rtol=1.e-3) @pytest.mark.skipif('not HAS_IMINUIT') def test_fit_lc_multiple_spectra(self): """Check fit results for multiple spectra.""" self.model.set(**self.start_params) res, fitmodel = sncosmo.fit_lc(model=self.model, spectra=[self.bin_spectrum, self.bin_spectrum], vparam_names=['amplitude', 'z', 't0'], bounds={'z': (0., 0.3)}) # set model to true parameters and compare to fit results. self.model.set(**self.params) assert_allclose(res.parameters, self.model.parameters, rtol=1.e-3) sncosmo-2.12.1/sncosmo/tests/test_sugarsource.py000066400000000000000000000036201476435666400221020ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES """Tests for SUGARSource (and wrapped in Model)""" import numpy as np import pytest from numpy.testing import assert_allclose import sncosmo def sugar_model(q0=0, q1=0, q2=0, q3=0, Av=0, phase=np.linspace(-5, 30, 10), wave=np.linspace(4000, 8000, 10)): """ Give a spectral time series of SUGAR model for a given set of parameters. """ source = sncosmo.get_source('sugar') mag_sugar = source._model['M0'](phase, wave) keys = ['ALPHA1', 'ALPHA2', 'ALPHA3', 'CCM'] parameters = [q1, q2, q3, Av] for i in range(4): comp = source._model[keys[i]](phase, wave) * parameters[i] mag_sugar += comp # Mag AB used in the training of SUGAR. mag_sugar += 48.59 wave_factor = (wave ** 2 / 299792458. * 1.e-10) return (q0 * 10. ** (-0.4 * mag_sugar) / wave_factor) @pytest.mark.might_download def test_sugarsource(): """Test timeseries output from SUGARSource vs pregenerated timeseries from the original files.""" source = sncosmo.get_source("sugar") model = sncosmo.Model(source) q1 = [-1, 0, 1, 2] q2 = [1, 0, -1, -2] q3 = [-1, 1, 0, -2] Av = [-0.1, 0, 0.2, 0.5] q0 = [10**(-0.4 * 34), 10**(-0.4 * 33), 10**(-0.4 * 38), 10**(-0.4 * 42)] time = np.linspace(-5, 30, 10) wave = np.linspace(4000, 8000, 10) for i in range(len(q1)): fluxref = sugar_model(q0=q0[i], q1=q1[i], q2=q2[i], q3=q3[i], Av=Av[i], phase=time, wave=wave) model.set(z=0, t0=0, q0=q0[i], q1=q1[i], q2=q2[i], q3=q3[i], Av=Av[i]) flux = model.flux(time, wave) assert_allclose(flux, fluxref, rtol=1e-13) sncosmo-2.12.1/sncosmo/tests/test_utils.py000066400000000000000000000036241476435666400207040ustar00rootroot00000000000000# Licensed under a 3-clause BSD style license - see LICENSES import os from tempfile import mkdtemp import numpy as np import pytest from numpy.testing import assert_allclose from scipy.stats import norm from sncosmo import utils def test_result(): res = utils.Result(a=1, b=2) assert res.a == 1 assert res.b == 2 # test deprecating result attributes res.__dict__['deprecated']['c'] = (2, "Use b instead") # for some reason, pytest 3.8 seems to not have warns if hasattr(pytest, 'warns'): with pytest.warns(UserWarning): val = res.c else: val = res.c assert val == 2 def test_format_value(): assert utils.format_value(1.234567) == '1.2345670' assert utils.format_value(0.001234567) == '1.2345670 x 10^-3' assert utils.format_value(1234567, error=1) == '1234567.0 +/- 1.0' assert (utils.format_value(0.001234567, latex=True) == '1.2345670 \\times 10^{-3}') def test_ppf(): """Test the ppf function.""" # Flat prior between 0 and 10 def prior(x): return 1. x = np.array([0.1, 0.2, 0.9, 0.9999]) y = utils.ppf(prior, x, 0., 10.) assert_allclose(y, [1., 2., 9., 9.999]) # test a normal distribution priordist = norm(0., 1.) x = np.linspace(0.05, 0.95, 5) y = utils.ppf(priordist.pdf, x, -np.inf, np.inf) assert_allclose(y, priordist.ppf(x), atol=1.e-10) def test_alias_map(): mapping = utils.alias_map(['A', 'B_', 'foo'], {'a': set(['a', 'a_']), 'b': set(['b', 'b_'])}) assert mapping == {'a': 'A', 'b': 'B_'} def test_data_mirror_rootdir(): dirname = mkdtemp() # rootdir is a string mirror = utils.DataMirror(dirname, "url_goes_here") assert mirror.rootdir() == dirname # rootdir is a callable mirror = utils.DataMirror(lambda: dirname, "url_goes_here") assert mirror.rootdir() == dirname os.rmdir(dirname) sncosmo-2.12.1/sncosmo/utils.py000066400000000000000000000422641476435666400165060ustar00rootroot00000000000000import codecs import math import os import socket import warnings from collections import OrderedDict import numpy as np from scipy import integrate, optimize def dict_to_array(d): """Convert a dictionary of lists (or single values) to a structured numpy.ndarray.""" # Convert all lists/values to 1-d arrays, in order to let numpy # figure out the necessary size of the string arrays. new_d = OrderedDict() for key in d: new_d[key] = np.atleast_1d(d[key]) # Determine dtype of output array. dtype = [(key, arr.dtype) for key, arr in new_d.items()] # Initialize ndarray and then fill it. col_len = max([len(v) for v in new_d.values()]) result = np.empty(col_len, dtype=dtype) for key in new_d: result[key] = new_d[key] return result def format_value(value, error=None, latex=False): """Return a string representing value and uncertainty. If latex=True, use '\\pm' and '\\times'. """ if latex: pm = '\\pm' suffix_templ = ' \\times 10^{{{0:d}}}' else: pm = '+/-' suffix_templ = ' x 10^{0:d}' # First significant digit absval = abs(value) if absval == 0.: first = 0 else: first = int(math.floor(math.log10(absval))) if error is None or error == 0.: last = first - 6 # Pretend there are 7 significant figures. else: last = int(math.floor(math.log10(error))) # last significant digit # use exponential notation if # value > 1000 and error > 1000 or value < 0.01 if (first > 2 and last > 2) or first < -2: value /= 10**first if error is not None: error /= 10**first p = max(0, first - last + 1) suffix = suffix_templ.format(first) else: p = max(0, -last + 1) suffix = '' if error is None: prefix = ('{0:.' + str(p) + 'f}').format(value) else: prefix = (('{0:.' + str(p) + 'f} {1:s} {2:.' + str(p) + 'f}') .format(value, pm, error)) if suffix != '': prefix = '({0})'.format(prefix) return prefix + suffix class Result(dict): """Represents an optimization result. Notes ----- Since this class is essentially a subclass of dict with attribute accessors, one can see which attributes are available using the `keys()` method. Deprecated attributes can be added via, e.g.: >>> res = Result(a=1, b=2) >>> res.__dict__['deprecated']['c'] = (2, "Use b instead") """ # only necessary for deprecation functionality def __init__(self, *args, **kwargs): self.__dict__['deprecated'] = {} dict.__init__(self, *args, **kwargs) # only necessary for deprecation functionality def __getitem__(self, name): try: return dict.__getitem__(self, name) except KeyError: val, msg = self.__dict__['deprecated'][name] warnings.warn(msg) return val def __getattr__(self, name): try: return self[name] except KeyError: raise AttributeError(name) __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ def __repr__(self): if self.keys(): m = max(map(len, list(self.keys()))) + 1 return '\n'.join([k.rjust(m) + ': ' + repr(v) for k, v in self.items()]) else: return self.__class__.__name__ + "()" def _integral_diff(x, pdf, a, q): """Return difference between q and the integral of the function `pdf` between a and x. This is used for solving for the ppf.""" return integrate.quad(pdf, a, x)[0] - q def ppf(pdf, x, a, b): """Percent-point function (inverse cdf), given the probability distribution function pdf and limits a, b. Parameters ---------- pdf : callable Probability distribution function x : array_like Points at which to evaluate the ppf a, b : float Limits (can be -np.inf, np.inf, assuming pdf has finite integral). """ FACTOR = 10. if not b > a: raise ValueError('b must be greater than a') # integral of pdf between a and b tot = integrate.quad(pdf, a, b)[0] # initialize result array x = np.asarray(x) shape = x.shape x = np.ravel(x) result = np.zeros(len(x)) for i in range(len(x)): cumsum = x[i] * tot # target cumulative sum left = a right = b # Need finite limits for the solver. # For inifinite upper or lower limits, find finite limits such that # cdf(left) < cumsum < cdf(right). if left == -np.inf: left = -FACTOR while integrate.quad(pdf, a, left)[0] > cumsum: right = left left *= FACTOR if right == np.inf: right = FACTOR while integrate.quad(pdf, a, right)[0] < cumsum: left = right right *= FACTOR result[i] = optimize.brentq(_integral_diff, left, right, args=(pdf, a, cumsum)) return result.reshape(shape) class Interp1D(object): def __init__(self, xmin, xmax, y): self._xmin = xmin self._xmax = xmax self._n = len(y) self._xstep = (xmax - xmin) / (self._n - 1) self._y = y def __call__(self, x): """works only in range [xmin, xmax)""" nsteps = (x - self._xmin) / self._xstep i = int(nsteps) w = nsteps - i return (1.-w) * self._y[i] + w * self._y[i+1] def _download_file(remote_url, target): """ Accepts a URL, downloads the file to a given open file object. This is a modified version of astropy.utils.data.download_file that downloads to an open file object instead of a cache directory. """ from contextlib import closing from urllib.request import urlopen, Request from urllib.error import URLError, HTTPError from astropy.utils.console import ProgressBarOrSpinner from . import conf timeout = conf.remote_timeout download_block_size = 32768 try: # Pretend to be a web browser (IE 6.0). Some servers that we download # from forbid access from programs. headers = {'User-Agent': 'Mozilla/5.0', 'Accept': ('text/html,application/xhtml+xml,' 'application/xml;q=0.9,*/*;q=0.8')} req = Request(remote_url, headers=headers) with closing(urlopen(req, timeout=timeout)) as remote: # get size of remote if available (for use in progress bar) info = remote.info() size = None if 'Content-Length' in info: try: size = int(info['Content-Length']) except ValueError: pass dlmsg = "Downloading {0}".format(remote_url) with ProgressBarOrSpinner(size, dlmsg) as p: bytes_read = 0 block = remote.read(download_block_size) while block: target.write(block) bytes_read += len(block) p.update(bytes_read) block = remote.read(download_block_size) # Append a more informative error message to HTTPErrors, URLErrors. except HTTPError as e: e.msg = "{}. requested URL: {!r}".format(e.msg, remote_url) raise except URLError as e: append_msg = (hasattr(e, 'reason') and hasattr(e.reason, 'errno') and e.reason.errno == 8) if append_msg: msg = "{0}. requested URL: {1}".format(e.reason.strerror, remote_url) e.reason.strerror = msg e.reason.args = (e.reason.errno, msg) raise e # This isn't supposed to happen, but occasionally a socket.timeout gets # through. It's supposed to be caught in `urrlib2` and raised in this # way, but for some reason in mysterious circumstances it doesn't. So # we'll just re-raise it here instead. except socket.timeout as e: # add the requested URL to the message (normally just 'timed out') e.args = ('requested URL {!r} timed out'.format(remote_url),) raise URLError(e) def download_file(remote_url, local_name): """ Download a remote file to local path, unzipping if the URL ends in '.gz'. Parameters ---------- remote_url : str The URL of the file to download local_name : str Absolute path filename of target file. Raises ------ URLError Whenever there's a problem getting the remote file. """ # ensure target directory exists dn = os.path.dirname(local_name) if not os.path.exists(dn): os.makedirs(dn) if remote_url.endswith(".gz"): import io import gzip buf = io.BytesIO() _download_file(remote_url, buf) buf.seek(0) f = gzip.GzipFile(fileobj=buf, mode='rb') with open(local_name, 'wb') as target: target.write(f.read()) f.close() else: try: with open(local_name, 'wb') as target: _download_file(remote_url, target) except: # noqa # in case of error downloading, remove file. if os.path.exists(local_name): os.remove(local_name) raise def download_dir(remote_url, dirname): """ Download a remote tar file to a local directory. Parameters ---------- remote_url : str The URL of the file to download dirname : str Directory in which to place contents of tarfile. Created if it doesn't exist. Raises ------ URLError (from urllib2 on PY2, urllib.request on PY3) Whenever there's a problem getting the remote file. """ import io import tarfile if not os.path.exists(dirname): os.makedirs(dirname) mode = 'r:gz' if remote_url.endswith(".gz") else None # download file to buffer buf = io.BytesIO() _download_file(remote_url, buf) buf.seek(0) # create a tarfile with the buffer and extract tf = tarfile.open(fileobj=buf, mode=mode) # use data_filter if available (py3.12+) tf.extraction_filter = getattr( tarfile, 'data_filter', (lambda member, path: member) ) tf.extractall(path=dirname) tf.close() buf.close() # buf not closed when tf is closed. class DataMirror(object): """Lazy fetcher for remote data. When asked for local absolute path to a file or directory, DataMirror checks if the file or directory exists locally and, if so, returns it. If it doesn't exist, it first determines where to get it from. It first downloads the file ``{remote_root}/redirects.json`` and checks it for a redirect from ``{relative_path}`` to a full URL. If no redirect exists, it uses ``{remote_root}/{relative_path}`` as the URL. It downloads then downloads the URL to ``{rootdir}/{relative_path}``. For directories, ``.tar.gz`` is appended to the ``{relative_path}`` before the above is done and then the directory is unpacked locally. Parameters ---------- rootdir : str or callable The local root directory, or a callable that returns the local root directory given no parameters. (The result of the call is cached.) Using a callable allows one to customize the discovery of the root directory (e.g., from a config file), and to defer that discovery until it is needed. remote_root : str Root URL of the remote server. """ def __init__(self, rootdir, remote_root): if not remote_root.endswith('/'): remote_root = remote_root + '/' self._checked_rootdir = None self._rootdir = rootdir self._remote_root = remote_root self._redirects = None def rootdir(self): """Return the path to the local data directory, ensuring that it exists""" if self._checked_rootdir is None: # If the supplied value is a string, use it. Otherwise # assume it is a callable that returns a string) rootdir = (self._rootdir if isinstance(self._rootdir, str) else self._rootdir()) # Check existance if not os.path.isdir(rootdir): raise Exception("data directory {!r} not an existing " "directory".format(rootdir)) # Cache value for future calls self._checked_rootdir = rootdir return self._checked_rootdir def _fetch_redirects(self): from urllib.request import urlopen import json f = urlopen(self._remote_root + "redirects.json") reader = codecs.getreader("utf-8") self._redirects = json.load(reader(f)) f.close() def _get_url(self, remote_relpath): if self._redirects is None: self._fetch_redirects() if remote_relpath in self._redirects: return self._redirects[remote_relpath] else: return self._remote_root + remote_relpath def abspath(self, relpath, isdir=False): """Return absolute path to file or directory, ensuring that it exists. If ``isdir``, look for ``{relpath}.tar.gz`` on the remote server and unpackage it. Otherwise, just look for ``{relpath}``. If redirect points to a gz, it will be uncompressed.""" abspath = os.path.join(self.rootdir(), relpath) if not os.path.exists(abspath): if isdir: url = self._get_url(relpath + ".tar.gz") # Download and unpack a directory. download_dir(url, os.path.dirname(abspath)) # ensure that tarfile unpacked into the expected directory if not os.path.exists(abspath): raise RuntimeError("Tarfile not unpacked into expected " "subdirectory. Please file an issue.") else: url = self._get_url(relpath) download_file(url, abspath) return abspath def alias_map(aliased, aliases, required=()): """For each key in ``aliases``, find the item in ``aliased`` matching exactly one of the corresponding items in ``aliases``. Parameters ---------- aliased : list of str Input keys, will be values in output map. aliases : dict of sets Dictionary where keys are "canonical name" and values are sets of possible aliases. required : list_like Keys in ``aliases`` that are considered required. An error is raised if no alias is found in ``aliased``. Returns ------- Example:: >>> aliases = {'a':set(['a', 'a_']), 'b':set(['b', 'b_'])} >>> alias_map(['A', 'B_', 'foo'], aliases) {'a': 'A', 'b': 'B_'} """ lowered_to_orig = {key.lower(): key for key in aliased} lowered = set(lowered_to_orig.keys()) mapping = {} for key, key_aliases in aliases.items(): common = lowered & key_aliases if len(common) == 1: mapping[key] = lowered_to_orig[common.pop()] elif len(common) == 0 and key in required: raise ValueError('no alias found for {!r} (possible ' 'case-independent aliases: {})'.format( key, ', '.join(repr(ka) for ka in key_aliases))) elif len(common) > 1: raise ValueError('multiple aliases found for {!r}: {}' .format(key, ', '.join(repr(a) for a in common))) return mapping def integration_grid(low, high, target_spacing): """Divide the range between `start` and `stop` into uniform bins with spacing less than or equal to `target_spacing` and return the bin midpoints and the actual spacing.""" range_diff = high - low spacing = range_diff / int(math.ceil(range_diff / target_spacing)) grid = np.arange(low + 0.5 * spacing, high, spacing) return grid, spacing warned = [] # global used in warn_once def warn_once(name, depver, rmver, extra=None): global warned if name not in warned: msg = ("{} is deprecated in sncosmo {} " "and will be removed in sncosmo {}".format(name, depver, rmver)) if extra is not None: msg += " " + extra warnings.warn(msg, stacklevel=2) warned.append(name) def sine_interp(x_new, fun_x, fun_y): """Sinus interpolation for intrinsic scattering models.""" if len(fun_x) != len(fun_y): raise ValueError('x and y must have the same len') if (x_new > fun_x[-1]).any() or (x_new < fun_x[0]).any(): raise ValueError('x_new is out of range of fun_x') sup_bound = np.vstack([x_new >= x for x in fun_x]) idx_inf = np.sum(sup_bound, axis=0) - 1 idx_inf[idx_inf == len(fun_x) - 1] = -2 x_inf = fun_x[idx_inf] x_sup = fun_x[idx_inf + 1] fun_y_inf = fun_y[idx_inf] fun_y_sup = fun_y[idx_inf + 1] sin_interp = np.sin(np.pi * (x_new - 0.5 * (x_inf + x_sup)) / (x_sup - x_inf)) values = (0.5 * (fun_y_sup + fun_y_inf) + 0.5 * (fun_y_sup - fun_y_inf) * sin_interp) return values sncosmo-2.12.1/tox.ini000066400000000000000000000025641476435666400146250ustar00rootroot00000000000000[tox] envlist = py{38,39,310,311,312}{,-alldeps}{,-oldestdeps}{,-cov} builtins codestyle build_docs isolated_build = true [testenv] description = Run tests # Run the tests in a temporary directory to make sure that we don't import # sncosmo from the source tree changedir = {envtmpdir} # Older versions of astropy need the home directory specified. passenv = HOME # The following indicates which extras_require from setup.cfg will be installed extras = test alldeps: all oldestdeps: oldestdeps cov: test_coverage commands = pip freeze !cov: pytest --pyargs sncosmo cov: pytest --pyargs sncosmo --cov sncosmo --cov-config={toxinidir}/setup.cfg cov: coverage xml -o {toxinidir}/coverage.xml [testenv:builtins] description = Test builtins changedir = {envtmpdir} # Set the HOME directory to a temporary directory to force sncosmo to # redownload all of the builtins. setenv = SNCOSMO_DATA_DIR = {envtmpdir} extras = test commands = pip freeze pytest --pyargs sncosmo.tests.test_download_builtins [testenv:codestyle] description = Check code style changedir = {toxinidir} skip_install = true allowlist_externals = ./checkstyle deps = pycodestyle commands = ./checkstyle [testenv:build_docs] description = Build the docs changedir = docs extras = docs allowlist_externals = make commands = pip freeze make html