pax_global_header00006660000000000000000000000064147275656360014537gustar00rootroot0000000000000052 comment=0bc57222fbaa080f818f87908e84a97a6046ef9d extinction-0.4.7/000077500000000000000000000000001472756563600137335ustar00rootroot00000000000000extinction-0.4.7/.github/000077500000000000000000000000001472756563600152735ustar00rootroot00000000000000extinction-0.4.7/.github/workflows/000077500000000000000000000000001472756563600173305ustar00rootroot00000000000000extinction-0.4.7/.github/workflows/python-package-tox.yml000066400000000000000000000015431472756563600236000ustar00rootroot00000000000000# This workflow will install tox and use it to run tests. # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: Python package on: push: pull_request: jobs: test: strategy: matrix: python: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] fail-fast: false runs-on: ${{ matrix.python <= '3.6' && 'ubuntu-20.04' || 'ubuntu-22.04' }} name: Test on python ${{ matrix.python }} and numpy ${{ matrix.numpyversion }} steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Tox run: pip install tox - name: Run Tox # Run tox using the version of Python in `PATH` run: tox -e py extinction-0.4.7/.github/workflows/upload_to_pypi.yml000066400000000000000000000031741472756563600231070ustar00rootroot00000000000000name: 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@v4 - uses: actions/setup-python@v5 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.20.0 - uses: actions/upload-artifact@v4 with: path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 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: 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 with: name: artifact path: dist - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} extinction-0.4.7/.gitignore000066400000000000000000000015471472756563600157320ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Cython-generated files *.c # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ docs/api/ # PyBuilder target/ #Ipython Notebook .ipynb_checkpoints # emacs *~ # Pipenv Pipfile Pipfile.lock extinction-0.4.7/LICENSE000066400000000000000000000021101472756563600147320ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Kyle Barbary and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. extinction-0.4.7/MANIFEST.in000066400000000000000000000002211472756563600154640ustar00rootroot00000000000000include README.md include LICENSE include setup.py include pyproject.toml include extinction.pyx include extern/bs.* include extern/bsplines.pxi extinction-0.4.7/README.md000066400000000000000000000006661472756563600152220ustar00rootroot00000000000000extinction ========== *Fast interstellar dust extinction laws in Python* ![Build Status](https://github.com/kbarbary/extinction/workflows/Python%20package/badge.svg) [![PyPI](https://img.shields.io/pypi/v/extinction.svg?style=flat-square)](https://pypi.python.org/pypi/extinction) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.804967.svg)](https://doi.org/10.5281/zenodo.804967) documentation: http://extinction.readthedocs.io/ extinction-0.4.7/benchmark.py000077500000000000000000000107161472756563600162470ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function, division import sys import timeit args = sys.argv[1:] if len(args) == 0: args = ['ccm89', 'fitzpatrick99', 'apply'] if 'ccm89' in args: setup = """ import numpy from extinction import {name} wave = numpy.logspace(numpy.log10({minwave}), numpy.log10({maxwave}), {size:d}) """ stmt = "{name}(wave, 1.0, 3.1)" benchmarks = [ {'name': 'ccm89', 'minwave': 1000., 'maxwave': 30000., 'size': 10, 'nloops': 1000000}, {'name': 'ccm89', 'minwave': 1000., 'maxwave': 30000., 'size': 100, 'nloops': 100000}, {'name': 'ccm89', 'minwave': 1000., 'maxwave': 30000., 'size': 1000, 'nloops': 10000}, {'name': 'ccm89', 'minwave': 1000., 'maxwave': 30000., 'size': 10000, 'nloops': 1000}, {'name': 'odonnell94', 'minwave': 1000., 'maxwave': 30000., 'size': 100, 'nloops': 100000}, {'name': 'ccm89', 'minwave': 1000., 'maxwave': 1e4/8, 'size': 100, 'nloops': 100000}, {'name': 'ccm89', 'minwave': 1e4/8, 'maxwave': 1e4/3.3, 'size': 100, 'nloops': 100000}, {'name': 'ccm89', 'minwave': 1e4/3.3, 'maxwave': 1e4/1.1, 'size': 100, 'nloops': 100000}, {'name': 'ccm89', 'minwave': 1e4/1.1, 'maxwave': 1e4/0.3, 'size': 100, 'nloops': 100000}] for b in benchmarks: times = timeit.repeat(stmt.format(**b), setup=setup.format(**b), repeat=3, number=b['nloops']) b['time'] = min(times) / b['nloops'] * 1e6 print('{name:5s} wave=[{minwave: 7.1f}, {maxwave: 7.1f}] size={size:5d} {time:8.2f}us'.format(**b)) print() if 'fitzpatrick99' in args: # ----------------------------------------------------------------------- # Fitzpatrick init setup = """ import numpy from extinction import Fitzpatrick99 """ stmt = "Fitzpatrick99(3.1)" nloops = 10000 times = timeit.repeat(stmt, setup=setup, repeat=3, number=nloops) t = min(times) / nloops * 1e6 print('F99 init {:8.2f}us'.format(t)) #------------------------------------------------------------------------ # Fitzpatrick fast method setup = """ import numpy from extinction import Fitzpatrick99 wave = numpy.logspace(numpy.log10({minwave}), numpy.log10({maxwave}), {size:d}) f = Fitzpatrick99(3.1) """ stmt = "f(wave, 1.0)" benchmarks = [ {'minwave': 1000., 'maxwave': 30000., 'size': 10, 'nloops': 10000}, {'minwave': 1000., 'maxwave': 30000., 'size': 100, 'nloops': 10000}, {'minwave': 1000., 'maxwave': 30000., 'size': 1000, 'nloops': 10000}, {'minwave': 1000., 'maxwave': 30000., 'size': 10000, 'nloops': 1000}, {'minwave': 1000., 'maxwave': 2700., 'size': 1000, 'nloops': 10000}, {'minwave': 2700., 'maxwave': 30000., 'size': 1000, 'nloops': 10000}] for b in benchmarks: times = timeit.repeat(stmt.format(**b), setup=setup.format(**b), repeat=3, number=b['nloops']) b['time'] = min(times) / b['nloops'] * 1e6 print('Fitzpatrick99 call wave=[{minwave: 7.1f}, {maxwave: 7.1f}] size={size:5d} {time:8.2f}us'.format(**b)) if 'apply' in args: setup = """ import numpy from extinction import apply ext = numpy.ones({size:d}) flux = numpy.ones({size:d}) """ stmt = "apply(ext, flux, inplace={inplace!r})" benchmarks = [ {'size': 10, 'nloops': 100000, 'inplace': True}, {'size': 10, 'nloops': 100000, 'inplace': False}, {'size': 100, 'nloops': 100000, 'inplace': True}, {'size': 100, 'nloops': 100000, 'inplace': False}, {'size': 1000, 'nloops': 10000, 'inplace': False}] for b in benchmarks: times = timeit.repeat(stmt.format(**b), setup=setup.format(**b), repeat=3, number=b['nloops']) b['time'] = min(times) / b['nloops'] * 1e6 print('apply size={size:5d} inplace={inplace!r} {time:8.2f}us'.format(**b)) #def benchmark(f, args, repeat=3): # """Time `f(*args)`, repeat `repeat` times and return the best time.""" # # number = 1 # times = [] # # # determine number of times to run the loop. # while number < 10**9: # t0 = time.time() # for i in range(number): # f(*args) # t = time.time() - t0 # # if t > 0.1: # break # # number *= 10 # # # run the loop `number` times extinction-0.4.7/docs/000077500000000000000000000000001472756563600146635ustar00rootroot00000000000000extinction-0.4.7/docs/Makefile000066400000000000000000000151541472756563600163310ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 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 " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @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/* 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/sep.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sep.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/sep" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sep" @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." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @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." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 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." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." extinction-0.4.7/docs/conf.py000066400000000000000000000072261472756563600161710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # build requires sphinx_rtd_theme and numpydoc. import sys import os import sphinx_rtd_theme import matplotlib.sphinxext.plot_directive import extinction # ensure that plot helper is on the path sys.path.insert(0, os.path.abspath(__file__)) # generate api directory if it doesn't already exist if not os.path.exists('api'): os.mkdir('api') # -- General configuration ------------------------------------------------ intersphinx_mapping = { 'python': ('http://docs.python.org/', None), 'numpy': ('http://docs.scipy.org/doc/numpy/', None)} extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', 'numpydoc', matplotlib.sphinxext.plot_directive.__name__] numpydoc_show_class_members = False autosummary_generate = True autoclass_content = "class" autodoc_default_flags = ["members", "no-special-members"] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'extinction' copyright = u'2016, Kyle Barbary and contributors' # 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. # # THe short X.Y version. version = '.'.join(extinction.__version__.split('.')[0:2]) # The full version, including alpha/beta/rc tags. release = extinction.__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 ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. 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 = [] # Output file base name for HTML help builder. htmlhelp_basename = 'extinctiondoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'extinction.tex', u'extinction Documentation', u'Kyle Barbary', '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', 'extinction', u'extinction Documentation', [u'Kyle Barbary'], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'extinction', u'extinction Documentation', u'Kyle Barbary', 'extinction', 'One line description of project.', 'Miscellaneous'), ] extinction-0.4.7/docs/extinction_plot.py000066400000000000000000000034511472756563600204620ustar00rootroot00000000000000"""Plot extinction functions for comparison""" from collections import OrderedDict import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams from mpl_toolkits.axes_grid1 import make_axes_locatable import extinction rcParams['font.family'] = 'serif' def extinction_figure(wave, a_lambda, residual_from, residual_lims=(-0.1, 0.4), title_text='$R_V = 3.1$'): names = list(a_lambda.keys()) # consistent ordering between panels fig = plt.figure(figsize=(8.5, 6.)) ax = plt.axes() for name in names: plt.plot(wave, a_lambda[name], label=name) plt.axvline(x=2700., ls=':', c='k') plt.axvline(x=3030.3030, ls=':', c='k') plt.axvline(x=9090.9091, ls=':', c='k') plt.axvspan(wave[0], 1150., fc='0.8', ec='none', zorder=-1000) plt.axvspan(1150., 1250., fc='0.9', ec='none', zorder=-1000) plt.text(0.65, 0.95, title_text, transform=ax.transAxes, va='top', ha='right', size='x-large') plt.ylabel('Extinction ($A(\lambda)$ / $A_V$)') plt.legend() plt.setp(ax.get_xticklabels(), visible=False) divider = make_axes_locatable(ax) axresid = divider.append_axes("bottom", size=2.0, pad=0.2, sharex=ax) for name in names: plt.plot(wave, a_lambda[name] - a_lambda[residual_from]) plt.axvline(x=2700., ls=':', c='k') plt.axvline(x=3030.3030, ls=':', c='k') plt.axvline(x=9090.9091, ls=':', c='k') plt.axvspan(wave[0], 1150., fc='0.8', ec='none', zorder=-1000) plt.axvspan(1150., 1250., fc='0.9', ec='none', zorder=-1000) plt.xlim(wave[0], wave[-1]) plt.ylim(ymin=residual_lims[0], ymax=residual_lims[1]) plt.ylabel('residual from ' + residual_from) plt.xlabel(r'Wavelength ($\mathrm{\AA}$)') ax.set_xscale('log') axresid.set_xscale('log') plt.tight_layout() return fig extinction-0.4.7/docs/index.rst000066400000000000000000000141471472756563600165330ustar00rootroot00000000000000extinction ========== *Fast interstellar dust extinction laws in Python* Cython-optimized implementations of empirical dust exitinction laws found in the literature. Installation ------------ Using conda:: conda install -c conda-forge extinction Using pip (requires a C compiler):: pip install extinction Extinction depends on numpy and scipy. **Development version / source code:** http://github.com/kbarbary/extinction Usage ----- **Functions:** .. autosummary:: :toctree: api extinction.ccm89 extinction.odonnell94 extinction.calzetti00 extinction.fitzpatrick99 extinction.fm07 extinction.apply extinction.remove **Classes:** .. autosummary:: :toctree: api extinction.Fitzpatrick99 **Examples:** Get extinction in magnitudes at a set of wavelengths for various dust laws:: >>> import numpy as np >>> import extinction >>> wave = np.array([2000., 4000., 8000.]) # wavelength in Angstroms # Cardelli, Clayton & Mathis (1989) with A_V = 1 and R_V = 3.1 >>> extinction.ccm89(wave, 1.0, 3.1) array([ 2.84252644, 1.4645557 , 0.59748901]) # extinction in magnitudes # O'Donnell (1994) >>> extinction.odonnell94(wave, 1.0, 3.1) array([ 2.84252644, 1.42617802, 0.60793495]) # Fitzpatrick (1999) >>> extinction.fitzpatrick99(wave, 1.0, 3.1) array([ 2.76225609, 1.79674653, 1.42325373]) The Fitzpatrick & Massa (2007) function has a fixed :math:`R_V` of 3.1:: >>> extinction.fm07(wave, 1.0) array([ 2.90478329, 1.42645161, 0.54703201]) All extinction laws accept a ``unit`` keyword to change the interpretation of the wavelength array from Angstroms to inverse microns:: >>> wave = np.array([5., 2.5, 1.25]) # wavelength in inverse microns >>> extinction.ccm89(wave, 1.0, 3.1, unit='invum') array([ 2.84252644, 1.4645557 , 0.59748901]) # extinction in magnitudes Redden or deredden .................. To "redden" or "deredden" flux values by some amount, use the ``apply`` and ``remove`` convenience functions:: >>> from extinction import ccm89, apply >>> flux = np.ones(3) # "redden" flux by A_V = 1.0 >>> apply(ccm89(wave, 1.0, 3.1), flux) array([ 0.07294397, 0.25952412, 0.5767723 ]) # "deredden" flux by A_V = 1.0 >>> remove(ccm89(wave, 1.0, 3.1), flux) array([ 13.70915145, 3.85320647, 1.73378645]) Comparison of functions ....................... .. plot:: import numpy as np import extinction from extinction_plot import extinction_figure wave = np.logspace(np.log10(910.), np.log10(30000.), 2000) a_lambda = {'ccm89': extinction.ccm89(wave, 1.0, 3.1), 'odonnell94': extinction.odonnell94(wave, 1.0, 3.1), 'fitzpatrick99': extinction.fitzpatrick99(wave, 1.0), 'fm07': extinction.fm07(wave, 1.0)} extinction_figure(wave, a_lambda, 'fitzpatrick99') R_V dependence of odonnell94 ............................ .. plot:: from collections import OrderedDict import numpy as np import extinction from extinction_plot import extinction_figure wave = np.logspace(np.log10(910.), np.log10(30000.), 2000) a_lambda = OrderedDict([ ('$R_V$ = 2.1', extinction.odonnell94(wave, 1.0, 2.1)), ('$R_V$ = 2.6', extinction.odonnell94(wave, 1.0, 2.6)), ('$R_V$ = 3.1', extinction.odonnell94(wave, 1.0, 3.1)), ('$R_V$ = 3.6', extinction.odonnell94(wave, 1.0, 3.6)), ('$R_V$ = 4.1', extinction.odonnell94(wave, 1.0, 4.1)) ]) extinction_figure(wave, a_lambda, '$R_V$ = 3.1', residual_lims=(-0.2, 0.6), title_text='odonnell94') R_V dependence of Fitzpatrick99 ............................... .. plot:: from collections import OrderedDict import numpy as np import extinction from extinction_plot import extinction_figure wave = np.logspace(np.log10(910.), np.log10(30000.), 2000) a_lambda = OrderedDict([ ('$R_V$ = 2.1', extinction.Fitzpatrick99(2.1)(wave, 1.0)), ('$R_V$ = 2.6', extinction.Fitzpatrick99(2.6)(wave, 1.0)), ('$R_V$ = 3.1', extinction.Fitzpatrick99(3.1)(wave, 1.0)), ('$R_V$ = 3.6', extinction.Fitzpatrick99(3.6)(wave, 1.0)), ('$R_V$ = 4.1', extinction.Fitzpatrick99(4.1)(wave, 1.0)) ]) extinction_figure(wave, a_lambda, '$R_V$ = 3.1', residual_lims=(-0.2, 0.6), title_text='Fitzpatrick99') A note on parameterization .......................... Most extinction laws here have two parameters: :math:`A_V` and :math:`R_V`. :math:`A_V` is a simply a linear scaling parameter; that is: ``ccm89(wave, 2.0, 3.1)`` is the same as ``2.0 * ccm89(wave, 1.0, 3.1)``. :math:`R_V` changes the *shape* of the extinction curve, rather than just the amplitude. Traditionally, the meaning ascribed to these parameters was that :math:`A_V` is the extinction in the *V* band, and :math:`R_V` describes the ratio of total to selective extinction: :math:`R_V = A_V / E(B-V)`, where :math:`E(B-V)` is the difference in extinction between the *B* and *V* bands. While this is approximately correct, the *measured* extinction of a source in actual *B* and *V* bandpasses will generally depend on the source spectrum and the shape of the specific bandpasses. Therefore, the :math:`A_V` and :math:`R_V` that are parameters in our extinction law will not correspond perfectly to measured *B* and *V* extinctions. So, in the context of these extinction laws, it is best to think of :math:`A_V` and :math:`R_V` as simply parameters that describe the amplitude and shape of the wavelength dependence, rather than corresponding directly to measured magnitudes. Finally, for a given shape of the extinction curve (described by :math:`R_V`), one can equally well use :math:`E(B-V)` as a linear scaling parameter in place of :math:`A_V`, with the equivalence :math:`E(B-V) = A_V / R_V`. Note that the above caution applies here: :math:`E(B-V)` should be considered simply a parameter describing amplitude of extinction; it will not correspond exactly to a difference in measured *B* and *V* extinctions. License and Credits ------------------- The license is MIT. Part of this code originated in the specutils package. extinction-0.4.7/docs/rtd-pip-requirements000066400000000000000000000000471472756563600207070ustar00rootroot00000000000000numpy numpydoc scipy Cython matplotlib extinction-0.4.7/extern/000077500000000000000000000000001472756563600152405ustar00rootroot00000000000000extinction-0.4.7/extern/README.md000066400000000000000000000001231472756563600165130ustar00rootroot00000000000000# external code Fetched using `fetch.sh` from https://github.com/kbarbary/bsplinesextinction-0.4.7/extern/bs.c000066400000000000000000001147351472756563600160230ustar00rootroot00000000000000#include #include #include #include #if defined(_MSC_VER) #define INLINE _inline // __inline in newer versions #define RESTRICT __restrict #else #define INLINE inline #define RESTRICT restrict #endif //----------------------------------------------------------------------------- // debug stuff (remove later) #include void print_a_and_b(double first[5], double last[5], double *A, double *b, int M) { int i; printf("\nfirst: [ %f %f %f %f %f ]\n", first[0], first[1], first[2], first[3], first[4]); for (i=0; i= values[i] and x= values[n-1]. //----------------------------------------------------------------------------- // Linear search starting from guess that // values[start] <= x < values[start+1]. static int find_index_from(double *values, int n, double x, int start) { int i; if (start <= -1) { // search down i = 0; while (i < n && x >= values[i]) i++; return i-1; } else if (start >= n-1) { // search down i = n - 1; while (i > -1 && x < values[i]) i--; return i; } else if (x >= values[start]) { // search up i = start + 1; while (i < n && x >= values[i]) i++; return i-1; } else { // search down i = start - 1; while (i > -1 && x < values[i]) i--; return i; } } // find index using binary search static int find_index_binary(double *values, int n, double x) { int lo, hi, mid; lo = 0; hi = n; mid = n/2; if (x < values[0]) return -1; if (x >= values[n-1]) return n-1; while (hi - lo > 1) { if (x >= values[mid]) lo = mid; else hi = mid; mid = lo + (hi - lo) / 2; } return mid; } static int is_monotonic(bs_array x) { int i; int ok; ok = 1; for (i=1; i= x.data[(i-1)*x.stride]); } return ok; } static int min_points(bs_bctype left, bs_bctype right) { // one additional point needed for each not-a-knot condition. return 2 + (left == BS_NOTAKNOT) + (right == BS_NOTAKNOT); } //----------------------------------------------------------------------------- // knots & constants //----------------------------------------------------------------------------- // fill spline knots based on x array (includes padding on either // end of array). static double* alloc_knots(bs_array x) { int N; double *knots; int i; N = x.size; knots = malloc((N + 5) * sizeof(double)); // move pointer past initial two-element padding. knots += 2; // copy x into main part of knots for (i=0; i < N; i++) knots[i] = x.data[i * x.stride]; // fill padded area before beginning knots[-2] = knots[0] - 2.0 * (knots[1] - knots[0]); knots[-1] = knots[0] - 1.0 * (knots[1] - knots[0]); // fill padded area after end. knots[N] = knots[N-1] + 1.0 * (knots[N-1] - knots[N-2]); knots[N+1] = knots[N-1] + 2.0 * (knots[N-1] - knots[N-2]); knots[N+2] = knots[N-1] + 3.0 * (knots[N-1] - knots[N-2]); return knots; } static void free_knots(double *knots) { free(knots - 2); } // constants used when evaluating a spline. // constants + 4*i is a pointer to the four constants used // when evaluating the spline in the range knots[i] <= x < knots[i+1]. static double* alloc_constants(double *knots, int n) { int i; double *constants; constants = malloc(4 * n * sizeof(double)); for (i=0; i0; i--) { b[i] -= b[i+1] * A[3*i+2]; } // first row is different b[0] -= b[1] * A[1] + b[2] * A[2]; } */ //----------------------------------------------------------------------------- // solve() // // Solve A * x = b for x. The solution is stored in b. // // A is a matrix like this: // // | x x x x x | // | x x x | // | x x x | // | ... | // | x x x | // | x x x | // | x x x | // | x x x x x | // // A is stored compactly in 3*n elements, with row i corresponding to A[3*i], // with the exception of the first and last rows which are passed in // separately because they are too large to be stored this way. // // Note that the first 3 and last 3 elements of A are initially empty as // these row values are stored in `first` and `last`. In fact the last 3 // elements of A are not used at all. // //----------------------------------------------------------------------------- typedef struct { double *first; double *rows; // first + 5 double *last; // first + 5 + 3*(M-1) } banded_matrix; // allocate storate for a banded matrix. // M is total number of rows, including first and last. static banded_matrix alloc_banded_matrix(int M) { double* first = malloc((5 + 3*(M-1) + 5) * sizeof(double)); banded_matrix m = {first, first + 5, first + 5 + 3*(M-1)}; return m; } static void free_banded_matrix(banded_matrix A) { free(A.first); A.first = NULL; A.rows = NULL; A.last = NULL; } static void copy_banded_matrix(banded_matrix dst, banded_matrix src, int M) { size_t nbytes = (10 + 3*(M-1)) * sizeof(double); memcpy(dst.first, src.first, nbytes); } static void solve(banded_matrix mat, double* RESTRICT b, int n) { int i; double tmp; double* RESTRICT first = mat.first; double* RESTRICT A = mat.rows; double* RESTRICT last = mat.last; // rows 1, 2, 3: divide by first non-zero // // x x x x x | y x x x x x | y // x x x | y 1 x x | y // x x x | y --> 1 x x | y // x x x | y 1 x x | y for (i=1; i<4; i++) { b[i] /= A[3*i]; A[3*i+2] /= A[3*i]; A[3*i+1] /= A[3*i]; A[3*i] = 1.0; } // eliminate first two elements of first row and divide by first non-zero. // // x x x x x | y 0 0 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 1 x x | y 1 x x | y b[0] -= first[0] * b[1]; first[2] -= first[0] * A[3*1+2]; first[1] -= first[0] * A[3*1+1]; first[0] = 0.0; b[0] -= first[1] * b[2]; first[3] -= first[1] * A[3*2+2]; first[2] -= first[1] * A[3*2+1]; first[1] = 0.0; b[0] /= first[2]; first[4] /= first[2]; first[3] /= first[2]; first[2] = 1.0; // reduce row 3 // // 0 0 1 x x | y 0 0 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 1 x x | y 0 1 x | y b[3] -= A[3*3+0] * b[0]; A[3*3+2] -= A[3*3+0] * first[4]; A[3*3+1] -= A[3*3+0] * first[3]; A[3*3+0] = 0.0; b[3] /= A[3*3+1]; A[3*3+2] /= A[3*3+1]; A[3*3+1] = 1.0; // permute first three rows: // 0 0 1 x x | y 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 0 1 x | y 0 1 x | y tmp = b[0]; b[0] = b[1]; A[3*0+0] = A[3*1+0]; A[3*0+1] = A[3*1+1]; A[3*0+2] = A[3*1+2]; b[1] = b[2]; A[3*1+0] = A[3*2+0]; A[3*1+1] = A[3*2+1]; A[3*1+2] = A[3*2+2]; b[2] = tmp; A[3*2+0] = first[2]; A[3*2+1] = first[3]; A[3*2+2] = first[4]; // reduce rest of the middle rows for (i=4; i=3; i--) { b[i] -= b[i+1] * A[3*i+2]; } // we now have: // 1 x x | y // 1 x x | y // 1 x x | y // 1 | y // 1 | y // ... // // eliminate the remaining elements. b[2] -= b[3] * A[3*2+1] + b[4] * A[3*2+2]; b[1] -= b[2] * A[3*1+1] + b[3] * A[3*1+2]; b[0] -= b[1] * A[3*0+1] + b[2] * A[3*0+2]; } //----------------------------------------------------------------------------- // finding coefficients //----------------------------------------------------------------------------- static void notaknot_row(double *consts, int i, double row[5]) { int j; double buf[4]; d3b3nonzeros(i-1, consts, row); d3b3nonzeros(i, consts, buf); row[4] = 0.0; for (j=0; j<4; j++) { row[j+1] -= buf[j]; } } static void fill_banded_matrix(banded_matrix A, double* RESTRICT knots, double* RESTRICT consts, int N, bs_bctype bctypes[2]) { int i; double* RESTRICT first = A.first; double* RESTRICT rows = A.rows; double* RESTRICT last = A.last; // Left boundary condition switch (bctypes[0]) { case BS_DERIV1: db3nonzeros(knots[0], 0, knots, consts, first); first[3] = first[4] = 0.0; break; case BS_DERIV2: d2b3nonzeros(knots[0], 0, knots, consts, first); first[3] = first[4] = 0.0; break; case BS_NOTAKNOT: notaknot_row(consts, 1, first); } // fill rows 1 through M-1 with values of b_{i-3}, b_{i-2}, b{i-1} // at knot i. for (i=0; i1; i--) last[i] = last[i-2]; last[0] = last[1] = 0.0; break; case BS_DERIV2: d2b3nonzeros(knots[N-1], N-1, knots, consts, last); for (i=4; i>1; i--) last[i] = last[i-2]; last[0] = last[1] = 0.0; break; case BS_NOTAKNOT: notaknot_row(consts, N-2, last); } } // Find spline coefficients along one dimension. // knots and consts are as belong to a spline. // A should be allocated with M = values.size + 2. // coeffs should be size M. static void find_1d_coefficients(banded_matrix A, bs_array values, double bcvalues[2], double* RESTRICT coeffs) { int i; int N = values.size; int M = N+2; coeffs[0] = bcvalues[0]; for (i=0; iknots = alloc_knots(x); spline->n = N; spline->exts = exts; spline->consts = alloc_constants(spline->knots, N); spline->coeffs = malloc(M * sizeof(double)); A = alloc_banded_matrix(M); fill_banded_matrix(A, spline->knots, spline->consts, N, bctypes); bcvalues[0] = (bcs.left.type == BS_NOTAKNOT) ? 0.0 : bcs.left.value; bcvalues[1] = (bcs.right.type == BS_NOTAKNOT) ? 0.0 : bcs.right.value; find_1d_coefficients(A, y, bcvalues, spline->coeffs); free_banded_matrix(A); *out = spline; return BS_OK; } void bs_spline1d_free(bs_spline1d* spline) { if (spline != NULL) { free_knots(spline->knots); free(spline->consts); free(spline->coeffs); free(spline); } } bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out) { int i; int j; double xval; double b3vals[4]; // for first index, it could be anywhere, so use binary search i = find_index_binary(spline->knots, spline->n, x.data[0]); for (j=0; jknots, spline->n, xval, i); // index outside left boundary if (i == -1) { switch (spline->exts.left.type) { case BS_EXTRAPOLATE: i = 0; break; case BS_CONSTANT: i = 0; xval = spline->knots[0]; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i == spline->n - 1) { switch (spline->exts.right.type) { case BS_EXTRAPOLATE: i = spline->n - 2; break; case BS_CONSTANT: i = spline->n-2; xval = spline->knots[spline->n-1]; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // if we get this far, we're either extrapolating or xval is in range. b3nonzeros(xval, i, spline->knots, spline->consts, b3vals); out.data[j*out.stride] = (spline->coeffs[i] * b3vals[0] + spline->coeffs[i+1] * b3vals[1] + spline->coeffs[i+2] * b3vals[2] + spline->coeffs[i+3] * b3vals[3]); } return BS_OK; } //----------------------------------------------------------------------------- // spline2d //----------------------------------------------------------------------------- // get the i-th bc value static double get_bcarray_value(bs_bcarray bc, int i) { return (bc.type == BS_NOTAKNOT) ? 0.0 : bc.data[i * bc.stride]; } static int bcarray_size_match(bs_bcarray bc, int size) { // if not-a-knot, value is not used, so size is OK. return (bc.type == BS_NOTAKNOT) ? 1 : (bc.size == size); } bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out) { int i, j; int nx, mx, ny, my; bs_bctype bctypes[2] = {ybcs.left.type, ybcs.right.type}; double bcvalues[2]; bs_spline2d* spline; double *coeffs; banded_matrix A, Awork; bs_array zslice, coeffs_slice; double *buf; *out = NULL; // In case of error, ensure that output pointer is NULL. if ((x.size != z.sizes[0]) || (y.size != z.sizes[1])) return BS_SIZEMISMATCH; if (!is_monotonic(x) || !is_monotonic(y)) return BS_NOTMONOTONIC; if ((x.size < min_points(xbcs.left.type, xbcs.right.type)) || (y.size < min_points(ybcs.left.type, ybcs.right.type))) return BS_TOOFEWPOINTS; // check if boundary condition sizes match x and y sizes. if (!(bcarray_size_match(xbcs.left, y.size) && bcarray_size_match(xbcs.right, y.size) && bcarray_size_match(ybcs.left, x.size) && bcarray_size_match(ybcs.left, x.size))) return BS_BCSIZEMISMATCH; spline = malloc(sizeof(bs_spline2d)); nx = x.size; mx = nx + 2; ny = y.size; my = ny + 2; spline->xknots = alloc_knots(x); spline->xconsts = alloc_constants(spline->xknots, nx); spline->nx = nx; spline->xexts = xexts; spline->yknots = alloc_knots(y); spline->yconsts = alloc_constants(spline->yknots, ny); spline->ny = ny; spline->yexts = yexts; coeffs = malloc(mx * my * sizeof(double)); // find coefficients along y (fast axis) A = alloc_banded_matrix(my); Awork = alloc_banded_matrix(my); fill_banded_matrix(A, spline->yknots, spline->yconsts, spline->ny, bctypes); for (i=0; ixknots, spline->xconsts, spline->nx, bctypes); buf = malloc(mx * sizeof(double)); for (i=0; icoeffs = coeffs; *out = spline; return BS_OK; } void bs_spline2d_free(bs_spline2d* spline) { if (spline != NULL) { free_knots(spline->xknots); free(spline->xconsts); free_knots(spline->yknots); free(spline->yconsts); free(spline->coeffs); free(spline); } } bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out) { // for first index, it could be anywhere, so use binary search int i = find_index_binary(spline->xknots, spline->nx, x.data[0]); int j0 = find_index_binary(spline->yknots, spline->ny, y.data[0]); int j, k, l; int my = spline->ny + 2; // for indexing coeffs. double xb3vals[4]; double yb3vals[4]; double xval, yval; for (k=0; kxknots, spline->nx, xval, i); // index outside left boundary if (i == -1) { switch (spline->xexts.left.type) { case BS_EXTRAPOLATE: i = 0; break; case BS_CONSTANT: i = 0; xval = spline->xknots[0]; break; case BS_VALUE: for (l=0; lxexts.left.value; } continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i == spline->nx - 1) { switch (spline->xexts.right.type) { case BS_EXTRAPOLATE: i = spline->nx - 2; break; case BS_CONSTANT: i = spline->nx - 2; xval = spline->xknots[spline->nx-1]; break; case BS_VALUE: for (l=0; lxexts.right.value; } continue; case BS_RAISE: return BS_DOMAINERROR; } } // get basis function values for x coordinate b3nonzeros(xval, i, spline->xknots, spline->xconsts, xb3vals); // x value is in range (or extrapolating); loop over y values: j = j0; for (l=0; lyknots, spline->ny, yval, j); // index outside left boundary if (j == -1) { switch (spline->yexts.left.type) { case BS_EXTRAPOLATE: j = 0; break; case BS_CONSTANT: j = 0; yval = spline->yknots[0]; break; case BS_VALUE: out.data[k * out.strides[0] + l * out.strides[1]] = spline->yexts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (j == spline->ny - 1) { switch (spline->yexts.right.type) { case BS_EXTRAPOLATE: j = spline->ny - 2; break; case BS_CONSTANT: j = spline->ny - 2; yval = spline->yknots[spline->ny-1]; break; case BS_VALUE: out.data[k * out.strides[0] + l * out.strides[1]] = spline->yexts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // get basis function values for y coordinate b3nonzeros(yval, j, spline->yknots, spline->yconsts, yb3vals); out.data[k * out.strides[0] + l * out.strides[1]] = (spline->coeffs[(i )*my+j] * xb3vals[0] * yb3vals[0] + spline->coeffs[(i )*my+j+1] * xb3vals[0] * yb3vals[1] + spline->coeffs[(i )*my+j+2] * xb3vals[0] * yb3vals[2] + spline->coeffs[(i )*my+j+3] * xb3vals[0] * yb3vals[3] + spline->coeffs[(i+1)*my+j] * xb3vals[1] * yb3vals[0] + spline->coeffs[(i+1)*my+j+1] * xb3vals[1] * yb3vals[1] + spline->coeffs[(i+1)*my+j+2] * xb3vals[1] * yb3vals[2] + spline->coeffs[(i+1)*my+j+3] * xb3vals[1] * yb3vals[3] + spline->coeffs[(i+2)*my+j] * xb3vals[2] * yb3vals[0] + spline->coeffs[(i+2)*my+j+1] * xb3vals[2] * yb3vals[1] + spline->coeffs[(i+2)*my+j+2] * xb3vals[2] * yb3vals[2] + spline->coeffs[(i+2)*my+j+3] * xb3vals[2] * yb3vals[3] + spline->coeffs[(i+3)*my+j] * xb3vals[3] * yb3vals[0] + spline->coeffs[(i+3)*my+j+1] * xb3vals[3] * yb3vals[1] + spline->coeffs[(i+3)*my+j+2] * xb3vals[3] * yb3vals[2] + spline->coeffs[(i+3)*my+j+3] * xb3vals[3] * yb3vals[3]); } } return BS_OK; } //----------------------------------------------------------------------------- // uspline1d //----------------------------------------------------------------------------- static void fill_banded_matrix_u(banded_matrix A, int N, double didx, bs_bctype bctypes[2]) { int i, j; // values, first and second derivatives of b3_{i-3}, b3_{i-2}, b3_{i-1} // at knot i. const double b3vals[3] = {1.0/6.0, 2.0/3.0, 1.0/6.0}; const double db3vals[3] = {-0.5 * didx, 0.0, 0.5 * didx}; const double d2b3vals[3] = {didx * didx, -2.0 * didx * didx, didx * didx}; const double notaknot_row[5] = {-1.0, 4.0, -6.0, 4.0, -1.0}; // Left boundary condition switch (bctypes[0]) { case BS_DERIV1: for (i=0; i<3; i++) A.first[i] = db3vals[i]; A.first[3] = A.first[4] = 0.0; break; case BS_DERIV2: for (i=0; i<3; i++) A.first[i] = d2b3vals[i]; A.first[3] = A.first[4] = 0.0; break; case BS_NOTAKNOT: for (i=0; i<5; i++) A.first[i] = notaknot_row[i]; } // rows for (i=0; ix = x; spline->didx = didx; spline->n = N; spline->exts = exts; b = malloc(M * sizeof(double)); A = alloc_banded_matrix(M); fill_banded_matrix_u(A, N, didx, bctypes); // fill RHS b[0] = (bcs.left.type == BS_NOTAKNOT)? 0.0: bcs.left.value; b[M-1] = (bcs.right.type == BS_NOTAKNOT)? 0.0: bcs.right.value; for (i=0; icoeffs = b; *out = spline; return BS_OK; } void bs_uspline1d_free(bs_uspline1d* spline) { if (spline != NULL) { free(spline->coeffs); free(spline); } } bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out) { int i, j; double xval; double xfloor; double b3vals[4]; for (j=0; jx.min) * spline->didx; xfloor = floor(xval); i = (int)xfloor; // index outside left boundary if (i < 0) { switch (spline->exts.left.type) { case BS_EXTRAPOLATE: i = 0; xfloor = 0.0; break; case BS_CONSTANT: i = 0; xfloor = 0.0; xval = 0.0; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i >= spline->n-1) { switch (spline->exts.right.type) { case BS_EXTRAPOLATE: i = spline->n - 2; xfloor = i; break; case BS_CONSTANT: i = spline->n - 2; xfloor = i; xval = xfloor+1.0; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // if we get this far, we're evaluating the spline b3unonzeros(xval - xfloor, b3vals); out.data[j*out.stride] = (spline->coeffs[i] * b3vals[0] + spline->coeffs[i+1] * b3vals[1] + spline->coeffs[i+2] * b3vals[2] + spline->coeffs[i+3] * b3vals[3]); } return BS_OK; } extinction-0.4.7/extern/bs.h000066400000000000000000000067341472756563600160270ustar00rootroot00000000000000#ifndef BS_H #define BS_H //----------------------------------------------------------------------------- // Error codes //----------------------------------------------------------------------------- typedef enum { BS_OK = 0, BS_OUTOFMEMORY = 1, BS_DOMAINERROR = 2, BS_NOTMONOTONIC = 3, BS_SIZEMISMATCH = 4, BS_BCSIZEMISMATCH = 5, BS_TOOFEWPOINTS = 6, } bs_errorcode; //----------------------------------------------------------------------------- // Input data types //----------------------------------------------------------------------------- typedef struct { double *data; int size; int stride; } bs_array; typedef struct { double *data; int sizes[2]; int strides[2]; } bs_array2d; typedef struct { double min; // inclusive double max; // inclusive } bs_range; //----------------------------------------------------------------------------- // Boundary conditions //----------------------------------------------------------------------------- typedef enum {BS_DERIV1, BS_DERIV2, BS_NOTAKNOT} bs_bctype; typedef struct { bs_bctype type; double value; } bs_bc; typedef struct { bs_bc left; bs_bc right; } bs_bcs; // array boundary conditions for spline2d. typedef struct { bs_bctype type; double *data; int size; int stride; } bs_bcarray; typedef struct { bs_bcarray left; bs_bcarray right; } bs_bcarray_pair; //----------------------------------------------------------------------------- // out-of-domain behavior ("extension") //----------------------------------------------------------------------------- typedef enum {BS_EXTRAPOLATE, BS_CONSTANT, BS_VALUE, BS_RAISE} bs_exttype; typedef struct { bs_exttype type; double value; } bs_ext; typedef struct { bs_ext left; bs_ext right; } bs_exts; //----------------------------------------------------------------------------- // 1-d splines //----------------------------------------------------------------------------- typedef struct { double *knots; double *consts; double *coeffs; int n; bs_exts exts; } bs_spline1d; bs_errorcode bs_spline1d_create(bs_array x, bs_array y, bs_bcs bcs, bs_exts exts, bs_spline1d **out); bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out); void bs_spline1d_free(bs_spline1d *spline); typedef struct { bs_range x; double didx; double *coeffs; int n; bs_exts exts; } bs_uspline1d; bs_errorcode bs_uspline1d_create(bs_range x, bs_array y, bs_bcs bcs, bs_exts exts, bs_uspline1d **out); bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out); void bs_uspline1d_free(bs_uspline1d *spline); //----------------------------------------------------------------------------- // 2-d splines //----------------------------------------------------------------------------- typedef struct { double *xknots; double *xconsts; double *yknots; double *yconsts; double *coeffs; int nx; int ny; bs_exts xexts; bs_exts yexts; } bs_spline2d; bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out); bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out); void bs_spline2d_free(bs_spline2d *spline); #endif extinction-0.4.7/extern/bsplines.pxi000066400000000000000000000077171472756563600176150ustar00rootroot00000000000000cdef extern from "bs.h": ctypedef enum bs_errorcode: BS_OK = 0 BS_OUTOFMEMORY = 1 BS_DOMAINERROR = 2 BS_NOTMONOTONIC = 3 BS_SIZEMISMATCH = 4 BS_BCSIZEMISMATCH = 5 BS_TOOFEWPOINTS = 6 ctypedef struct bs_array: double *data int size int stride ctypedef struct bs_array2d: double *data int sizes[2] int strides[2] ctypedef struct bs_range: double min double max ctypedef enum bs_bctype: BS_DERIV1 BS_DERIV2 BS_NOTAKNOT ctypedef struct bs_bc: bs_bctype type double value ctypedef struct bs_bcs: bs_bc left bs_bc right ctypedef enum bs_exttype: BS_EXTRAPOLATE BS_CONSTANT BS_VALUE BS_RAISE ctypedef struct bs_ext: bs_exttype type double value ctypedef struct bs_exts: bs_ext left bs_ext right ctypedef struct bs_spline1d: double *knots double *coeffs int n ctypedef struct bs_bcarray: bs_bctype type double *data int size int stride ctypedef struct bs_bcarray_pair: bs_bcarray left bs_bcarray right bs_errorcode bs_spline1d_create(bs_array x, bs_array y, bs_bcs bcs, bs_exts exts, bs_spline1d **out) bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out) void bs_spline1d_free(bs_spline1d *spline) ctypedef struct bs_spline2d: double *xknots double *xconsts double *yknots double *yconsts double *coeffs int nx int ny bs_exts xexts bs_exts yexts bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out) bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out) void bs_spline2d_free(bs_spline2d *spline) ctypedef struct bs_uspline1d: bs_range x double didx double *coeffs int n bs_exts exts bs_errorcode bs_uspline1d_create(bs_range x, bs_array y, bs_bcs bcs, bs_exts exts, bs_uspline1d **out) bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out) void bs_uspline1d_free(bs_uspline1d *spline) #------------------------------------------------------------------------------ # helpers class DomainError(Exception): """Raised when spline input(s) are outside spline boundaries.""" pass cdef int assert_ok(bs_errorcode code) except -1: """raise an exception if the error code is not OK""" if code == BS_OK: return 0 elif code == BS_OUTOFMEMORY: raise MemoryError() elif code == BS_DOMAINERROR: raise DomainError() elif code == BS_NOTMONOTONIC: raise ValueError("input array(s) not monotonically increasing") elif code == BS_SIZEMISMATCH: raise ValueError("input array size mismatch") elif code == BS_BCSIZEMISMATCH: raise ValueError("boundary condition size mismatch") elif code == BS_TOOFEWPOINTS: raise ValueError("Too few points in input array. required: 2 + (1 for each not-a-knot condition)") else: raise Exception("unrecognized error") cdef inline bs_array to_bs_array(double[:] x): return bs_array(&x[0], x.shape[0], x.strides[0]//sizeof(double)) cdef inline bs_array2d to_bs_array2d(double[:, :] x): cdef bs_array2d out out.data = &x[0, 0] out.sizes[0] = x.shape[0] out.sizes[1] = x.shape[1] out.strides[0] = x.strides[0] // sizeof(double) out.strides[1] = x.strides[1] // sizeof(double) return out extinction-0.4.7/extern/fetch.sh000077500000000000000000000003651472756563600166740ustar00rootroot00000000000000#!/bin/sh COMMIT_HASH=2f14aa748f200290400307b40116787a0dbf5d43 URL=https://raw.githubusercontent.com/kbarbary/bsplines wget -nc ${URL}/${COMMIT_HASH}/src/bs.c wget -nc ${URL}/${COMMIT_HASH}/src/bs.h wget -nc ${URL}/${COMMIT_HASH}/bsplines.pxi extinction-0.4.7/extinction.pyx000066400000000000000000000570361472756563600166740ustar00rootroot00000000000000#!python #cython: boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True """Interstellar dust extinction functions.""" import numpy as np cimport numpy as np __version__ = "0.4.7" __all__ = ['ccm89', 'odonnell94', 'Fitzpatrick99', 'fitzpatrick99', 'fm07', 'calzetti00', 'apply', 'remove'] # We use some C code for Cubic splines in the Fitzpatrick99 and fm07 functions. # There are two reasons for using this over something from scipy: # # First, Fitzpatrick99 specifies "natural" boundary conditions on the splines, # but in scipy.interpolate, this can only be achieved using the CubicSpline # class, which is new in scipy v0.18 (a bit too recent). # # Second, this C code is significantly faster than CubicSpline. include "extern/bsplines.pxi" # ------------------------------------------------------------------------------ # Utility functions for converting wavelength units cdef double aa_to_invum(double x): """Convert Angstroms to inverse microns""" return 1e4 / x cdef double noop(double x): return x ctypedef double (*scalar_func)(double) # ----------------------------------------------------------------------------- # Cardelli, Clayton & Mathis (1989) cdef inline void ccm89ab_ir_invum(double x, double *a, double *b): """ccm89 a, b parameters for 0.3 < x < 1.1 (infrared)""" cdef double y y = x**1.61 a[0] = 0.574 * y b[0] = -0.527 * y cdef inline void ccm89ab_opt_invum(double x, double *a, double *b): """ccm89 a, b parameters for 1.1 < x < 3.3 (optical)""" cdef double y y = x - 1.82 a[0] = ((((((0.329990*y - 0.77530)*y + 0.01979)*y + 0.72085)*y - 0.02427)*y - 0.50447)*y + 0.17699)*y + 1.0 b[0] = ((((((-2.09002*y + 5.30260)*y - 0.62251)*y - 5.38434)*y + 1.07233)*y + 2.28305)*y + 1.41338)*y cdef inline void ccm89ab_uv_invum(double x, double *a, double *b): """ccm89 a, b parameters for 3.3 < x < 8.0 (ultraviolet)""" cdef double y, y2, y3 y = x - 4.67 a[0] = 1.752 - 0.316*x - (0.104 / (y*y + 0.341)) y = x - 4.62 b[0] = -3.090 + 1.825*x + (1.206 / (y*y + 0.263)) if x > 5.9: y = x - 5.9 y2 = y * y y3 = y2 * y a[0] += -0.04473*y2 - 0.009779*y3 b[0] += 0.2130*y2 + 0.1207*y3 cdef inline void ccm89ab_fuv_invum(double x, double *a, double *b): """ccm89 a, b parameters for 8 < x < 11 (far-UV)""" cdef double y, y2, y3 y = x - 8. y2 = y * y y3 = y2 * y a[0] = -0.070*y3 + 0.137*y2 - 0.628*y - 1.073 b[0] = 0.374*y3 - 0.420*y2 + 4.257*y + 13.670 cdef inline void ccm89ab_invum(double x, double *a, double *b): """ccm89 a, b parameters for 0.3 < x < 11 in microns^-1""" if x < 1.1: ccm89ab_ir_invum(x, a, b) elif x < 3.3: ccm89ab_opt_invum(x, a, b) elif x < 8.: ccm89ab_uv_invum(x, a, b) else: ccm89ab_fuv_invum(x, a, b) def ccm89(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """ccm89(wave, a_v, r_v, unit='aa', out=None) Cardelli, Clayton & Mathis (1989) extinction function. The parameters given in the original paper [1]_ are used. The claimed validity is 1250 Angstroms to 3.3 microns. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. Notes ----- In Cardelli, Clayton & Mathis (1989) the mean R_V-dependent extinction law, is parameterized as .. math:: = a(x) + b(x) / R_V where the coefficients a(x) and b(x) are functions of wavelength. At a wavelength of approximately 5494.5 angstroms (a characteristic wavelength for the V band), a(x) = 1 and b(x) = 0, so that A(5494.5 angstroms) = A_V. This function returns .. math:: A(\lambda) = A_V (a(x) + b(x) / R_V) References ---------- .. [1] Cardelli, J. A., Clayton, G. C., & Mathis, J. S. 1989, ApJ, 345, 245 """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): ccm89ab_invum(convert_wave(wave[i]), &a, &b) out_view[i] = a_v * (a + b / r_v) return out # ----------------------------------------------------------------------------- # O'Donnell (1994) cdef inline void od94ab_opt_invum(double x, double *a, double *b): """od94 a, b parameters for 1.1 < x < 3.3 (optical)""" cdef double y y = x - 1.82 a[0] = (((((((-0.505*y + 1.647)*y - 0.827)*y - 1.718)*y + 1.137)*y + 0.701)*y - 0.609)*y + 0.104)*y + 1.0 b[0] = (((((((3.347*y - 10.805)*y + 5.491)*y + 11.102)*y - 7.985)*y - 3.989)*y + 2.908)*y + 1.952)*y cdef inline void od94ab_invum(double x, double *a, double *b): """od94 a, b parameters for 0.3 < x < 11 in microns^-1""" if x < 1.1: ccm89ab_ir_invum(x, a, b) elif x < 3.3: od94ab_opt_invum(x, a, b) elif x < 8.: ccm89ab_uv_invum(x, a, b) else: ccm89ab_fuv_invum(x, a, b) def odonnell94(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """odonnell94(wave, a_v, r_v, unit='aa', out=None) O'Donnell (1994) extinction function. Like Cardelli, Clayton, & Mathis (1989) [1]_ but using the O'Donnell (1994) [2]_ optical coefficients between 3030 A and 9091 A. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. Notes ----- This function matches the Goddard IDL astrolib routine CCM_UNRED. From the documentation for that routine: 1. The CCM curve shows good agreement with the Savage & Mathis (1979) [3]_ ultraviolet curve shortward of 1400 A, but is probably preferable between 1200 and 1400 A. 2. Curve is extrapolated between 912 and 1000 A as suggested by Longo et al. (1989) [4]_ 3. Valencic et al. (2004) [5]_ revise the ultraviolet CCM curve (3.3 -- 8.0 um^-1). But since their revised curve does not connect smoothly with longer and shorter wavelengths, it is not included here. References ---------- .. [1] Cardelli, J. A., Clayton, G. C., & Mathis, J. S. 1989, ApJ, 345, 245 .. [2] O'Donnell, J. E. 1994, ApJ, 422, 158O .. [3] Savage & Mathis 1979, ARA&A, 17, 73 .. [4] Longo et al. 1989, ApJ, 339,474 .. [5] Valencic et al. 2004, ApJ, 616, 912 """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): od94ab_invum(convert_wave(wave[i]), &a, &b) out_view[i] = a_v * (a + b / r_v) return out # ----------------------------------------------------------------------------- # Fitzpatrick (1999) DEF F99_X0 = 4.596 DEF F99_GAMMA = 0.99 DEF F99_C3 = 3.23 DEF F99_C4 = 0.41 DEF F99_C5 = 5.9 DEF F99_X02 = F99_X0 * F99_X0 DEF F99_GAMMA2 = F99_GAMMA * F99_GAMMA cdef inline double f99_uv_invum(double x, double a_v, double r_v): """f99 extinction for x < 1e4/2700 microns^-1""" cdef double c1, c2, d, x2, y, y2, rv2, k c2 = -0.824 + 4.717 / r_v c1 = 2.030 - 3.007 * c2 x2 = x * x y = x2 - F99_X02 d = x2 / (y * y + x2 * F99_GAMMA2) k = c1 + c2 * x + F99_C3 * d if x >= F99_C5: y = x - F99_C5 y2 = y * y k += F99_C4 * (0.5392 * y2 + 0.05644 * y2 * y) return a_v * (1. + k / r_v) cdef double *_F99_XKNOTS = [0.0, 1.e4 / 26500., 1.e4 / 12200., 1.e4 / 6000., 1.e4 / 5470., 1.e4 / 4670., 1.e4 / 4110., 1.e4 / 2700., 1.e4 / 2600.] cdef void _f99_kknots(double *xknots, double r_v, double *out): """Fill knot k values in Fitzpatrick 99 for given R_V. xknots and out should be arrays of length 9.""" cdef double c1, c2, d, x, x2, y, rv2 cdef int i c2 = -0.824 + 4.717 / r_v c1 = 2.030 - 3.007 * c2 rv2 = r_v * r_v out[0] = -r_v out[1] = 0.26469 * r_v/3.1 - r_v out[2] = 0.82925 * r_v/3.1 - r_v out[3] = -0.422809 + 1.00270*r_v + 2.13572e-04*rv2 - r_v out[4] = -5.13540e-02 + 1.00216 * r_v - 7.35778e-05*rv2 - r_v out[5] = 0.700127 + 1.00184*r_v - 3.32598e-05*rv2 - r_v out[6] = (1.19456 + 1.01707*r_v - 5.46959e-03*rv2 + 7.97809e-04 * rv2 * r_v - 4.45636e-05 * rv2*rv2 - r_v) for i in range(7,9): x2 = xknots[i] * xknots[i] y = (x2 - F99_X02) d = x2 / (y * y + x2 * F99_GAMMA2) out[i] = c1 + c2*xknots[i] + F99_C3 * d cdef class Fitzpatrick99(object): """Fitzpatrick (1999) dust extinction function for arbitrary R_V. An instance of this class is a callable that can be used as ``f(wave, a_v)`` where ``wave`` is a 1-d array of wavelengths and ``a_v`` is a scalar value. Parameters ---------- r_v : float, optional R_V value. Default is 3.1. Examples -------- Create a callable that gives the extinction law for a given ``r_v`` and use it: >>> f = Fitzpatrick99(3.1) >>> f(np.array([3000., 4000.]), 1.) array([ 1.79939955, 1.42338583]) """ cdef bs_spline1d *spline # pointer to c struct cdef readonly double r_v def __cinit__(self, double r_v=3.1): self.r_v = r_v cdef double *kknots = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] _f99_kknots(_F99_XKNOTS, r_v, kknots) cdef bs_bcs bcs cdef bs_exts exts cdef bs_errorcode code bcs.left.type = BS_DERIV2 bcs.left.value = 0.0 bcs.right.type = BS_DERIV2 bcs.right.value = 0.0 exts.left.type = BS_VALUE exts.left.value = 0.0 exts.right.type = BS_VALUE exts.right.value = 0.0 code = bs_spline1d_create(bs_array(_F99_XKNOTS, 9, 1), bs_array(kknots, 9, 1), bcs, exts, &self.spline) assert_ok(code) def __call__(self, np.ndarray wave not None, double a_v, unit='aa'): cdef double[:] wave_view, out_view cdef double r_v = self.r_v cdef double ebv = a_v / r_v cdef size_t i cdef size_t n cdef bs_errorcode code # translate `wave` to inverse microns if unit == 'invum': pass elif unit == 'aa': wave = 1e4 / wave else: raise ValueError("unrecognized unit") n = wave.shape[0] out = np.empty(n, dtype=np.float64) wave_view = wave out_view = out # Optical/IR spline: evaluate at all wavelengths; we will overwrite # the UV points afterwards. These are outside the spline range, so # they don't actually take too much time to evaluate. # (outview actually holds "k" after this call; see below) code = bs_spline1d_eval(self.spline, to_bs_array(wave_view), to_bs_array(out_view)) assert_ok(code) # Analytic function in the UV (< 2700 Angstroms). for i in range(n): # for optical/IR, `out` is actually k, but we wanted # a_v/r_v * (k+r_v), so we adjust here. if wave_view[i] < 3.7037037037037037: # 1e4 / 2700 out_view[i] = ebv * (out_view[i] + r_v) # for UV, we overwrite the array with the UV function value. else: out_view[i] = f99_uv_invum(wave_view[i], a_v, r_v) return out def __dealloc__(self): bs_spline1d_free(self.spline) # functional interface for Fitzpatrick (1999) with R_V = 3.1 _fitzpatrick99_fixed = Fitzpatrick99(3.1) def fitzpatrick99(wave, a_v, r_v=3.1, unit='aa'): """fitzpatrick99(wave, a_v, r_v=3.1, unit='aa') Fitzpatrick (1999) dust extinction function. Fitzpatrick (1999) [1]_ model which relies on the parametrization of Fitzpatrick & Massa (1990) [2]_ in the UV (below 2700 A) and spline fitting in the optical and IR. This function is defined from 910 A to 6 microns, but note the claimed validity goes down only to 1150 A. The optical spline points are not taken from F99 Table 4, but rather updated versions from E. Fitzpatrick (this matches the Goddard IDL astrolib routine FM_UNRED). Parameters ---------- wave : numpy.ndarray (1-d) Input wavelengths or wavenumbers (see units). a_v : float Total V-band extinction in magnitudes. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Wavelength units: Angstroms or inverse microns. Returns ------- Extinction in magnitudes at each input wavelength. References ---------- .. [1] Fitzpatrick, E. L. 1999, PASP, 111, 63 .. [2] Fitzpatrick, E. L. & Massa, D. 1990, ApJS, 72, 163 """ if r_v == 3.1: return _fitzpatrick99_fixed(wave, a_v, unit=unit) else: return Fitzpatrick99(r_v)(wave, a_v, unit=unit) # ----------------------------------------------------------------------------- # Fitzpatrick & Massa 2007 DEF FM07_X0 = 4.592 DEF FM07_GAMMA = 0.922 DEF FM07_C1 = -0.175 DEF FM07_C2 = 0.807 DEF FM07_C3 = 2.991 DEF FM07_C4 = 0.319 DEF FM07_C5 = 6.097 DEF FM07_X02 = FM07_X0 * FM07_X0 DEF FM07_GAMMA2 = FM07_GAMMA * FM07_GAMMA DEF FM07_R_V = 3.1 # Fixed for the time being (used in fm07kknots) # Used for wave < 2700. cdef inline double fm07_uv_invum(double x, double a_v): """fm07 for x < 1e4/2700 microns^-1 (ultraviolet)""" cdef double d, x2, y, k x2 = x * x y = x2 - FM07_X02 d = x2 / (y*y + x2 * FM07_GAMMA2) k = FM07_C1 + FM07_C2 * x + FM07_C3 * d if x > FM07_C5: y = x - FM07_C5 k += FM07_C4 * y * y return a_v * (1. + k / FM07_R_V) cdef void _fm07_kknots(double *xknots, double *out): """Return k knots given xknots for fm07. (Could be a static array)""" cdef double d cdef int i for i in range(0, 5): out[i] = (-0.83 + 0.63*FM07_R_V) * xknots[i]**1.84 - FM07_R_V out[5] = 0.0 out[6] = 1.322 out[7] = 2.055 for i in range(8, 10): d = xknots[i]**2 / ((xknots[i]**2 - FM07_X02)**2 + xknots[i]**2 * FM07_GAMMA2) out[i] = FM07_C1 + FM07_C2 * xknots[i] + FM07_C3 * d cdef double *_FM07_XKNOTS = [0., 0.25, 0.50, 0.75, 1., 1.e4 / 5530., 1.e4 / 4000., 1.e4 / 3300., 1.e4 / 2700., 1.e4 / 2600.] cdef double *_FM07_KKNOTS = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.] _fm07_kknots(_FM07_XKNOTS, _FM07_KKNOTS) def fm07(np.ndarray wave not None, double a_v, unit='aa'): """fm07(wave, a_v, unit='aa') Fitzpatrick & Massa (2007) extinction model for R_V = 3.1. The Fitzpatrick & Massa (2007) [1]_ model, which has a slightly different functional form from that of Fitzpatrick (1999) [3]_ (`extinction_f99`). Fitzpatrick & Massa (2007) claim it is preferable, although it is unclear if signficantly so (Gordon et al. 2009 [2]_). Defined from 910 A to 6 microns. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). References ---------- .. [1] Fitzpatrick, E. L. & Massa, D. 2007, ApJ, 663, 320 .. [2] Gordon, K. D., Cartledge, S., & Clayton, G. C. 2009, ApJ, 705, 1320 .. [3] Fitzpatrick, E. L. 1999, PASP, 111, 63 """ cdef double[:] wave_view, out_view cdef double ebv = a_v / FM07_R_V cdef size_t i cdef size_t n # translate `wave` to inverse microns if unit == 'invum': pass elif unit == 'aa': wave = 1e4 / wave else: raise ValueError("unrecognized unit") # create the spline (we do this here rather than globally so we can # deallocate it.) cdef bs_spline1d* spline cdef bs_bcs bcs cdef bs_exts exts cdef bs_errorcode code bcs.left.type = BS_DERIV2 bcs.left.value = 0.0 bcs.right.type = BS_DERIV2 bcs.right.value = 0.0 exts.left.type = BS_VALUE exts.left.value = 0.0 exts.right.type = BS_VALUE exts.right.value = 0.0 code = bs_spline1d_create(bs_array(_FM07_XKNOTS, 10, 1), bs_array(_FM07_KKNOTS, 10, 1), bcs, exts, &spline) assert_ok(code) n = wave.shape[0] out = np.empty(n, dtype=np.float64) wave_view = wave out_view = out # Optical/IR spline: evaluate at all wavelengths; we will overwrite # the UV points afterwards. These are outside the spline range, so # they don't actually take too much time to evaluate. # (outview actually holds "k" after this call; see below) code = bs_spline1d_eval(spline, to_bs_array(wave_view), to_bs_array(out_view)) assert_ok(code) bs_spline1d_free(spline) # Analytic function in the UV (< 2700 Angstroms). for i in range(n): # for optical/IR, `out` is actually k, but we wanted # a_v/r_v * (k+r_v), so we adjust here. if wave_view[i] < 3.7037037037037037: # 1e4 / 2700 out_view[i] = ebv * (out_view[i] + FM07_R_V) # for UV, we overwrite the array with the UV function value. else: out_view[i] = fm07_uv_invum(wave_view[i], a_v) return out # ----------------------------------------------------------------------------- # Calzetti 2000 # http://adsabs.harvard.edu/abs/2000ApJ...533..682C cdef inline double calzetti00k_uv_invum(double x): """calzetti00 `k` for 0.12 microns < wave < 0.63 microns (UV/optical), x in microns^-1""" return 2.659 * (((0.011*x - 0.198)*x + 1.509)*x - 2.156) cdef inline double calzetti00k_ir_invum(double x): """calzetti00 `k` for 0.63 microns < wave < 2.2 microns (optical/IR), x in microns^-1""" return 2.659 * (1.040*x - 1.857) cdef inline double calzetti00_invum(double x, double r_v): cdef double k if x > 1.5873015873015872: # 1. / 0.63 k = calzetti00k_uv_invum(x) else: k = calzetti00k_ir_invum(x) return 1.0 + k / r_v def calzetti00(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """calzetti00(wave, a_v, r_v, unit='aa', out=None) Calzetti (2000) extinction function. Calzetti et al. (2000, ApJ 533, 682) developed a recipe for dereddening the spectra of galaxies where massive stars dominate the radiation output, valid between 0.12 to 2.2 microns. They estimate :math:`R_V = 4.05 \pm 0.80` from optical-IR observations of 4 starburst galaxies. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): out_view[i] = a_v * calzetti00_invum(convert_wave(wave[i]), r_v) return out # ------------------------------------------------------------------------------ # convenience functions for applying/removing extinction to/from flux # values, optionally in-place. It turns out that pure Python is about as # fast as C here. def apply(extinction, flux, inplace=False): """apply(extinction, flux, inplace=False) Apply extinction to flux values. This is a convenience function to perform "reddening" of flux values. It simply performs ``flux * 10**(-0.4 * extinction)``: flux is decreased (for positive extinction values). Parameters ---------- extinction : numpy.ndarray (1-d) Extinction in magnitudes. (Positive extinction values decrease flux values.) flux : numpy.ndarray Flux values. inplace : bool, optional Whether to perform the operation in-place on the flux array. If True, the return value is a reference to the input flux array. Returns ------- new_flux : numpy.ndarray (1-d) Flux values with extinction applied. """ trans = 10.**(-0.4 * extinction) if inplace: flux *= trans return flux else: return flux * trans # ------------------------------------------------------------------------------ # convenience function for removing extinction from flux values, optionally # in-place. (It turns out that this isn't really faster than just doing it # in pure python...) def remove(extinction, flux, inplace=False): """remove(extinction, flux, inplace=False) Remove extinction from flux values. This is a convenience function to "deredden" fluxes. It simply performs ``flux * 10**(0.4 * extinction)``: flux is increased (for positive extinction values). Parameters ---------- extinction : numpy.ndarray (1-d) Extinction in magnitudes. flux : numpy.ndarray Flux values. inplace : bool, optional Whether to perform the operation in-place on the flux array. If True, the return value is a reference to the input flux array. Returns ------- new_flux : numpy.ndarray (1-d) Flux values with extinction removed. """ trans = 10.**(0.4 * extinction) if inplace: flux *= trans return flux else: return flux * trans extinction-0.4.7/pyproject.toml000066400000000000000000000003261472756563600166500ustar00rootroot00000000000000[build-system] requires = [ "wheel", "setuptools", "Cython>=0.29.2", "oldest-supported-numpy; python_version<'3.9'", "numpy>=2; python_version>='3.9'", ] build-backend = 'setuptools.build_meta' extinction-0.4.7/setup.py000077500000000000000000000025401472756563600154510ustar00rootroot00000000000000#!/usr/bin/env python import os import re import sys import numpy from setuptools import setup from setuptools.extension import Extension classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Topic :: Scientific/Engineering :: Astronomy", "Intended Audience :: Science/Research", ] # Synchronize version from code. fname = "extinction.pyx" version = re.findall(r"__version__ = \"(.*?)\"", open(fname).read())[0] # Build Cython extension source_files = [fname, os.path.join("extern", "bs.c")] depends_files = [ os.path.join("extern", "bs.h"), os.path.join("extern", "bsplines.pxi") ] include_dirs = [numpy.get_include(), "extern"] extensions = [ Extension( "extinction", source_files, include_dirs=include_dirs, depends=depends_files, extra_compile_args=["-std=c11"], ) ] setup( name="extinction", version=version, description="Fast interstellar dust extinction laws", long_description="documentation: http://extinction.readthedocs.io", license="MIT", classifiers=classifiers, url="http://github.com/kbarbary/extinction", author="Kyle Barbary", author_email="kylebarbary@gmail.com", ext_modules=extensions, install_requires=["numpy>=1.13.3"], python_requires=">=3.6", ) extinction-0.4.7/test.py000077500000000000000000000153171472756563600152760ustar00rootroot00000000000000#!/usr/bin/env py.test import os import numpy as np from numpy.testing import assert_allclose import extinction def test_ccm89(): # NOTE: Test is only to precision of 0.016 because there is a discrepancy # of 0.014 for the B band wavelength of unknown origin (and up to 0.002 in # other bands). # # Note that a and b can be obtained with: # b = ccm89(wave, 0.) # a = ccm89(wave, 1.) - b # # These differ from the values tablulated in the original paper. # Could be due to floating point errors in the original paper? # # U, B, V, R, I, J, H, K band effective wavelengths from CCM '89 table 3 x_inv_microns = np.array([2.78, 2.27, 1.82, 1.43, 1.11, 0.80, 0.63, 0.46]) wave = 1.e4 / x_inv_microns # A(lambda)/A(V) for R_V = 3.1 from Table 3 of CCM '89 ref_values = np.array([1.569, 1.337, 1.000, 0.751, 0.479, 0.282, 0.190, 0.114]) assert_allclose(extinction.ccm89(wave, 1.0, 3.1), ref_values, rtol=0.016, atol=0.) def test_odonnell94(): # NOTE: The tabulated values go to 0.001, but the test is only for matching # at the 0.005 level, because there is currently a discrepancy up to 0.0047 # of unknown origin. # # Tests od94() at Rv = 3.1 against the widely used values tabulated in # Schlegel, Finkbeiner and Davis (1998) # http://adsabs.harvard.edu/abs/1998ApJ...500..525S # # This is tested by evaluating the extinction curve at a (given) # effective wavelength, since these effective wavelengths: # "... represent(s) that wavelength on the extinction curve # with the same extinction as the full passband." # # The test does not include UKIRT L' (which, at 3.8 microns) is # beyond the range of wavelengths allowed by the function # or the APM b_J filter which is defined in a non-standard way. # # The SFD98 tabulated values go to 1e-3, so we should be able to match at # that level. wave = np.array([3372., 4404., 5428., 6509., 8090., 3683., 4393., 5519., 6602., 8046., 12660., 16732., 22152., 5244., 6707., 7985., 9055., 6993., 3502., 4676., 4127., 4861., 5479., 3546., 4925., 6335., 7799., 9294., 3047., 4711., 5498., 6042., 7068., 8066., 4814., 6571., 8183.]) ref_values = np.array([1.664, 1.321, 1.015, 0.819, 0.594, 1.521, 1.324, 0.992, 0.807, 0.601, 0.276, 0.176, 0.112, 1.065, 0.793, 0.610, 0.472, 0.755, 1.602, 1.240, 1.394, 1.182, 1.004, 1.579, 1.161, 0.843, 0.639, 0.453, 1.791, 1.229, 0.996, 0.885, 0.746, 0.597, 1.197, 0.811, 0.580]) assert_allclose(extinction.odonnell94(wave, 1.0, 3.1), ref_values, rtol=0.0051, atol=0.) def test_fitzpatrick99_knots(): """Test that knots match values in Fitzpatrick (1999) Table 3 for fitzpatrick99 function (with R_V = 3.1)""" wave = np.array([np.inf, 26500., 12200., 6000., 5470., 4670., 4110., 2700., 2600.]) x = np.array([0.0, 0.377, 0.820, 1.667, 1.828, 2.141, 2.433, 3.704, 3.846]) # A(lambda) values for E(B-V) = 1 or A_V = 3.1 ref_values = np.array([0.0, 0.265, 0.829, 2.688, 3.055, 3.806, 4.315, 6.265, 6.591]) assert_allclose(extinction.fitzpatrick99(wave, 3.1, unit='aa'), ref_values, rtol=0., atol=0.001) # atol = 0.002 because the input values are less precise (e.g., 0.377 # rather than 1.e4 / 26500.) assert_allclose(extinction.fitzpatrick99(x, 3.1, unit='invum'), ref_values, rtol=0., atol=0.002) def test_fitzpatrick99_rv_knots(): """Test that Fitzpatrick function has the right R_V dependence at the knots.""" wave = np.array([26500., 12200., 6000., 5470., 4670., 4110.]) # "the IR points at 1/lambda < 1 invum are simply scaled by R/3.1" # "the optical points are vertically offset by an amount R - 3.1 , with # slight corrections made to preserve the normalization" for r in [1., 2., 3., 4., 5., 6.]: # `expected` gives expected A(lambda) for E(B-V) = 1 (A_V = R) expected = [r / 3.1 * 0.265, # Table 3 scaled by R/3.1 r / 3.1 * 0.829, # Table 3 scaled by R/3.1 -0.426 + 1.0044 * r, # Table 4 -0.050 + 1.0016 * r, # Table 4 0.701 + 1.0016 * r, # Table 4 1.208 + 1.0032 * r - 0.00033 * r**2] # Table 4 result = extinction.Fitzpatrick99(r)(wave, r) # Note that we don't expect an exact match because, as noted in the # code, "the optical spline points are not taken from F99 table 4, # but rather updated versions from E. Fitzpatrick (matching the IDL # astrolib routine FM_UNRED). assert_allclose(result, expected, rtol=0.003) def test_fitzpatrick99_idl(): """Test that result matches implementation in IDL procedure FM_UNRED""" for r_v in (2.3, 3.1, 4.0, 5.3): fname = os.path.join('testdata', 'fm_unred_{:3.1f}.dat'.format(r_v)) wave, a_lambda_ref = np.loadtxt(fname, unpack=True) a_lambda = extinction.Fitzpatrick99(r_v)(wave, 1.0) assert_allclose(a_lambda, a_lambda_ref, rtol=0.00005) def test_fitzpatrick99_r_v(): """Test that we can access the `r_v` attribute.""" f = extinction.Fitzpatrick99() assert f.r_v == 3.1 f = extinction.Fitzpatrick99(2.1) assert f.r_v == 2.1 def test_fitzpatrick99_func(): """Check passing R_V.""" wave = np.array([2000., 30000.]) for r_v in (3.1, 4.0): assert np.all(extinction.Fitzpatrick99(r_v)(wave, 1.0) == extinction.fitzpatrick99(wave, 1.0, r_v)) def test_fm07(): wave = np.arange(3000., 9000., 1000) # from running the code; we're just checking that results don't change! ref_values = [1.84192286, 1.42645161, 1.13842341, 0.88843179, 0.69226384, 0.54709373] assert_allclose(extinction.fm07(wave, 1.), ref_values) def test_calzetti00(): """Test calzetti against another translation of the same base code""" wave = np.array([2000., 4000., 8000.]) flux = np.ones(3) new_flux = extinction.apply(extinction.calzetti00(wave, -1., 3.1), flux) # derived using Julia version of IDL calz_unred ref_values = np.array([10.5288, 3.88153, 1.61769]) assert_allclose(new_flux, ref_values, atol=0.0001) extinction-0.4.7/testdata/000077500000000000000000000000001472756563600155445ustar00rootroot00000000000000extinction-0.4.7/testdata/README.md000066400000000000000000000005001472756563600170160ustar00rootroot00000000000000Test data --------- This directory contains comparison files used in the tests. These outputs from running the IDL procedure `FM_UNRED` from the IDL Astro library: http://idlastro.gsfc.nasa.gov/ftp/pro/astro/ (`FM_UNRED` also requires the `CSPLINE` procedure.) The script used to generate the output is included here. extinction-0.4.7/testdata/fm_unred_2.3.dat000066400000000000000000000124301472756563600204170ustar00rootroot00000000000000 1000.00 7.94421 1019.85 7.59781 1040.10 7.27403 1060.75 6.97145 1081.81 6.68871 1103.28 6.42455 1125.19 6.17778 1147.53 5.94731 1170.31 5.73211 1193.54 5.53122 1217.24 5.34375 1241.40 5.16887 1266.05 5.00581 1291.18 4.85387 1316.82 4.71239 1342.96 4.58078 1369.62 4.45850 1396.81 4.34507 1424.54 4.24006 1452.82 4.14313 1481.67 4.05396 1511.08 3.97234 1541.08 3.89814 1571.68 3.83131 1602.88 3.77192 1634.70 3.72020 1667.16 3.67655 1700.25 3.64155 1734.01 3.61454 1768.43 3.59610 1803.54 3.58819 1839.35 3.59330 1875.86 3.61449 1913.10 3.65526 1951.09 3.71893 1989.82 3.80714 2029.32 3.91664 2069.61 4.03432 2110.70 4.13307 2152.60 4.17544 2195.34 4.13146 2238.92 3.99958 2283.37 3.80789 2328.70 3.59403 2374.93 3.38638 2422.08 3.19915 2470.17 3.03622 2519.21 2.89621 2569.22 2.77581 2620.23 2.67149 2672.25 2.58009 2725.30 2.49643 2779.41 2.41715 2834.58 2.34391 2890.86 2.27609 2948.25 2.21313 3006.78 2.15452 3066.48 2.09980 3127.35 2.04854 3189.44 2.00033 3252.76 1.95481 3317.34 1.91167 3383.20 1.87059 3450.36 1.83130 3518.86 1.79356 3588.72 1.75714 3659.97 1.72184 3732.63 1.68747 3806.73 1.65386 3882.31 1.62087 3959.38 1.58836 4037.99 1.55622 4118.16 1.52434 4199.91 1.49253 4283.29 1.46040 4368.33 1.42754 4455.05 1.39361 4543.50 1.35830 4633.70 1.32134 4725.69 1.28255 4819.51 1.24218 4915.19 1.20085 5012.77 1.15908 5112.29 1.11735 5213.78 1.07609 5317.29 1.03569 5422.86 0.996474 5530.52 0.958739 5640.31 0.922606 5752.29 0.888073 5866.49 0.855133 5982.96 0.823770 6101.73 0.793961 6222.87 0.765628 6346.41 0.738682 6472.41 0.713036 6600.90 0.688611 6731.95 0.665332 6865.60 0.643129 7001.90 0.621937 7140.91 0.601695 7282.68 0.582344 7427.26 0.563831 7574.72 0.546106 7725.09 0.529122 7878.46 0.512833 8034.87 0.497201 8194.39 0.482184 8357.07 0.467748 8522.98 0.453859 8692.18 0.440485 8864.75 0.427595 9040.74 0.415164 9220.23 0.403164 9403.28 0.391572 9589.96 0.380365 9780.35 0.369522 9974.52 0.359023 10172.5 0.348849 10374.5 0.338983 10580.5 0.329409 10790.5 0.320112 11004.7 0.311078 11223.2 0.302292 11446.0 0.293743 11673.3 0.285420 11905.0 0.277312 12141.4 0.269407 12382.4 0.261698 12628.2 0.254182 12878.9 0.246855 13134.6 0.239718 13395.4 0.232768 13661.3 0.226003 13932.5 0.219421 14209.1 0.213019 14491.2 0.206796 14778.9 0.200747 15072.3 0.194871 15371.5 0.189163 15676.7 0.183622 15987.9 0.178243 16305.4 0.173024 16629.1 0.167961 16959.2 0.163051 17295.9 0.158291 17639.3 0.153676 17989.4 0.149205 18346.6 0.144872 18710.8 0.140676 19082.3 0.136612 19461.1 0.132678 19847.5 0.128869 20241.5 0.125183 20643.4 0.121617 21053.2 0.118166 21471.2 0.114829 21897.4 0.111601 22332.2 0.108480 22775.5 0.105463 23227.7 0.102547 23688.8 0.0997283 24159.1 0.0970047 24638.8 0.0943733 25127.9 0.0918315 25626.7 0.0893764 26135.5 0.0870052 26654.4 0.0847156 27183.5 0.0825048 27723.2 0.0803691 28273.6 0.0783053 28834.9 0.0763102 29407.4 0.0743810 29991.2 0.0725147 30586.6 0.0707087 31193.9 0.0689605 31813.1 0.0672676 32444.7 0.0656278 33088.8 0.0640387 33745.8 0.0624984 34415.7 0.0610050 35098.9 0.0595565 35795.8 0.0581510 36506.4 0.0567870 37231.2 0.0554630 37970.3 0.0541771 38724.1 0.0529281 39492.9 0.0517146 40277.0 0.0505352 41076.6 0.0493886 41892.1 0.0482737 42723.8 0.0471893 43571.9 0.0461343 44437.0 0.0451076 45319.2 0.0441083 46218.9 0.0431353 47136.5 0.0421879 48072.3 0.0412650 49026.6 0.0403660 50000.0 0.0394899 extinction-0.4.7/testdata/fm_unred_3.1.dat000066400000000000000000000124301472756563600204160ustar00rootroot00000000000000 1000.00 4.95826 1019.85 4.73448 1040.10 4.52685 1060.75 4.33431 1081.81 4.15586 1103.28 3.99059 1125.19 3.83763 1147.53 3.69617 1170.31 3.56547 1193.54 3.44482 1217.24 3.33358 1241.40 3.23113 1266.05 3.13692 1291.18 3.05044 1316.82 2.97121 1342.96 2.89880 1369.62 2.83283 1396.81 2.77293 1424.54 2.71882 1452.82 2.67023 1481.67 2.62695 1511.08 2.58883 1541.08 2.55577 1571.68 2.52775 1602.88 2.50483 1634.70 2.48719 1667.16 2.47514 1700.25 2.46910 1734.01 2.46861 1768.43 2.47410 1803.54 2.48702 1839.35 2.50924 1875.86 2.54303 1913.10 2.59100 1951.09 2.65561 1989.82 2.73809 2029.32 2.83603 2069.61 2.93973 2110.70 3.02905 2152.60 3.07623 2195.34 3.05904 2238.92 2.97633 2283.37 2.84895 2328.70 2.70483 2374.93 2.56504 2422.08 2.44012 2470.17 2.33296 2519.21 2.24254 2569.22 2.16641 2620.23 2.10194 2672.25 2.04681 2725.30 1.99695 2779.41 1.94947 2834.58 1.90573 2890.86 1.86530 2948.25 1.82779 3006.78 1.79285 3066.48 1.76015 3127.35 1.72939 3189.44 1.70031 3252.76 1.67266 3317.34 1.64621 3383.20 1.62077 3450.36 1.59615 3518.86 1.57219 3588.72 1.54874 3659.97 1.52567 3732.63 1.50286 3806.73 1.48021 3882.31 1.45762 3959.38 1.43502 4037.99 1.41233 4118.16 1.38948 4199.91 1.36638 4283.29 1.34278 4368.33 1.31843 4455.05 1.29314 4543.50 1.26670 4633.70 1.23898 4725.69 1.20985 4819.51 1.17958 4915.19 1.14863 5012.77 1.11746 5112.29 1.08643 5213.78 1.05591 5317.29 1.02620 5422.86 0.997567 5530.52 0.970241 5640.31 0.944145 5752.29 0.918987 5866.49 0.894497 5982.96 0.870432 6101.73 0.846604 6222.87 0.822994 6346.41 0.799644 6472.41 0.776590 6600.90 0.753864 6731.95 0.731495 6865.60 0.709507 7001.90 0.687921 7140.91 0.666757 7282.68 0.646028 7427.26 0.625750 7574.72 0.605930 7725.09 0.586580 7878.46 0.567704 8034.87 0.549309 8194.39 0.531394 8357.07 0.513965 8522.98 0.497019 8692.18 0.480557 8864.75 0.464575 9040.74 0.449072 9220.23 0.434041 9403.28 0.419480 9589.96 0.405382 9780.35 0.391742 9974.52 0.378552 10172.5 0.365806 10374.5 0.353495 10580.5 0.341611 10790.5 0.330148 11004.7 0.319096 11223.2 0.308445 11446.0 0.298187 11673.3 0.288314 11905.0 0.278816 12141.4 0.269684 12382.4 0.260907 12628.2 0.252474 12878.9 0.244368 13134.6 0.236574 13395.4 0.229081 13661.3 0.221874 13932.5 0.214941 14209.1 0.208271 14491.2 0.201852 14778.9 0.195674 15072.3 0.189726 15371.5 0.183998 15676.7 0.178482 15987.9 0.173168 16305.4 0.168049 16629.1 0.163115 16959.2 0.158359 17295.9 0.153774 17639.3 0.149353 17989.4 0.145090 18346.6 0.140977 18710.8 0.137009 19082.3 0.133179 19461.1 0.129483 19847.5 0.125915 20241.5 0.122469 20643.4 0.119142 21053.2 0.115928 21471.2 0.112822 21897.4 0.109822 22332.2 0.106922 22775.5 0.104118 23227.7 0.101408 23688.8 0.0987867 24159.1 0.0962515 24638.8 0.0937989 25127.9 0.0914260 25626.7 0.0891297 26135.5 0.0869070 26654.4 0.0847552 27183.5 0.0826718 27723.2 0.0806538 28273.6 0.0786983 28834.9 0.0768030 29407.4 0.0749655 29991.2 0.0731833 30586.6 0.0714542 31193.9 0.0697763 31813.1 0.0681475 32444.7 0.0665660 33088.8 0.0650297 33745.8 0.0635371 34415.7 0.0620866 35098.9 0.0606766 35795.8 0.0593055 36506.4 0.0579720 37231.2 0.0566748 37970.3 0.0554124 38724.1 0.0541837 39492.9 0.0529875 40277.0 0.0518228 41076.6 0.0506882 41892.1 0.0495831 42723.8 0.0485062 43571.9 0.0474566 44437.0 0.0464336 45319.2 0.0454362 46218.9 0.0444634 47136.5 0.0435147 48072.3 0.0425892 49026.6 0.0416863 50000.0 0.0408051 extinction-0.4.7/testdata/fm_unred_4.0.dat000066400000000000000000000124301472756563600204160ustar00rootroot00000000000000 1000.00 3.46911 1019.85 3.31235 1040.10 3.16777 1060.75 3.03457 1081.81 2.91198 1103.28 2.79930 1125.19 2.69585 1147.53 2.60103 1170.31 2.51426 1193.54 2.43499 1217.24 2.36274 1241.40 2.29703 1266.05 2.23744 1291.18 2.18358 1316.82 2.13508 1342.96 2.09161 1369.62 2.05289 1396.81 2.01864 1424.54 1.98863 1452.82 1.96267 1481.67 1.94059 1511.08 1.92229 1541.08 1.90770 1571.68 1.89679 1602.88 1.88964 1634.70 1.88636 1667.16 1.88721 1700.25 1.89253 1734.01 1.90194 1768.43 1.91580 1803.54 1.93524 1839.35 1.96170 1875.86 1.99695 1913.10 2.04300 1951.09 2.10179 1989.82 2.17425 2029.32 2.25852 2069.61 2.34710 2110.70 2.42437 2152.60 2.46883 2195.34 2.46325 2238.92 2.40674 2283.37 2.31546 2328.70 2.21107 2374.93 2.10989 2422.08 2.02009 2470.17 1.94392 2519.21 1.88059 2569.22 1.82820 2620.23 1.78472 2672.25 1.74835 2725.30 1.71583 2779.41 1.68470 2834.58 1.65609 2890.86 1.62968 2948.25 1.60518 3006.78 1.58234 3066.48 1.56092 3127.35 1.54069 3189.44 1.52147 3252.76 1.50307 3317.34 1.48534 3383.20 1.46812 3450.36 1.45130 3518.86 1.43475 3588.72 1.41837 3659.97 1.40207 3732.63 1.38576 3806.73 1.36939 3882.31 1.35287 3959.38 1.33616 4037.99 1.31922 4118.16 1.30199 4199.91 1.28442 4283.29 1.26632 4368.33 1.24754 4455.05 1.22791 4543.50 1.20732 4633.70 1.18566 4725.69 1.16285 4819.51 1.13913 4915.19 1.11491 5012.77 1.09057 5112.29 1.06645 5213.78 1.04285 5317.29 1.02004 5422.86 0.998245 5530.52 0.977658 5640.31 0.958074 5752.29 0.939005 5866.49 0.920010 5982.96 0.900695 6101.73 0.880760 6222.87 0.860225 6346.41 0.839217 6472.41 0.817852 6600.90 0.796235 6731.95 0.774461 6865.60 0.752616 7001.90 0.730777 7140.91 0.709017 7282.68 0.687396 7427.26 0.665972 7574.72 0.644794 7725.09 0.623909 7878.46 0.603352 8034.87 0.583163 8194.39 0.563367 8357.07 0.543993 8522.98 0.525062 8692.18 0.506594 8864.75 0.488603 9040.74 0.471103 9220.23 0.454104 9403.28 0.437614 9589.96 0.421638 9780.35 0.406180 9974.52 0.391242 10172.5 0.376824 10374.5 0.362925 10580.5 0.349541 10790.5 0.336670 11004.7 0.324306 11223.2 0.312443 11446.0 0.301075 11673.3 0.290195 11905.0 0.279794 12141.4 0.269863 12382.4 0.260393 12628.2 0.251364 12878.9 0.242751 13134.6 0.234532 13395.4 0.226685 13661.3 0.219191 13932.5 0.212031 14209.1 0.205186 14491.2 0.198640 14778.9 0.192377 15072.3 0.186383 15371.5 0.180642 15676.7 0.175142 15987.9 0.169871 16305.4 0.164815 16629.1 0.159965 16959.2 0.155310 17295.9 0.150840 17639.3 0.146544 17989.4 0.142416 18346.6 0.138445 18710.8 0.134625 19082.3 0.130948 19461.1 0.127407 19847.5 0.123995 20241.5 0.120706 20643.4 0.117534 21053.2 0.114473 21471.2 0.111519 21897.4 0.108666 22332.2 0.105909 22775.5 0.103244 23227.7 0.100668 23688.8 0.0981748 24159.1 0.0957619 24638.8 0.0934256 25127.9 0.0911625 25626.7 0.0889694 26135.5 0.0868431 26654.4 0.0847810 27183.5 0.0827804 27723.2 0.0808387 28273.6 0.0789537 28834.9 0.0771232 29407.4 0.0753452 29991.2 0.0736178 30586.6 0.0719387 31193.9 0.0703065 31813.1 0.0687193 32444.7 0.0671756 33088.8 0.0656737 33745.8 0.0642121 34415.7 0.0627895 35098.9 0.0614045 35795.8 0.0600558 36506.4 0.0587421 37231.2 0.0574622 37970.3 0.0562150 38724.1 0.0549996 39492.9 0.0538147 40277.0 0.0526595 41076.6 0.0515328 41892.1 0.0504340 42723.8 0.0493619 43571.9 0.0483160 44437.0 0.0472953 45319.2 0.0462990 46218.9 0.0453264 47136.5 0.0443770 48072.3 0.0434497 49026.6 0.0425442 50000.0 0.0416597 extinction-0.4.7/testdata/fm_unred_5.3.dat000066400000000000000000000124301472756563600204220ustar00rootroot00000000000000 1000.00 2.48184 1019.85 2.37415 1040.10 2.27545 1060.75 2.18513 1081.81 2.10263 1103.28 2.02740 1125.19 1.95896 1147.53 1.89684 1170.31 1.84061 1193.54 1.78987 1217.24 1.74423 1241.40 1.70337 1266.05 1.66695 1291.18 1.63469 1316.82 1.60632 1342.96 1.58159 1369.62 1.56027 1396.81 1.54218 1424.54 1.52714 1452.82 1.51500 1481.67 1.50565 1511.08 1.49901 1541.08 1.49503 1571.68 1.49369 1602.88 1.49505 1634.70 1.49920 1667.16 1.50634 1700.25 1.51673 1734.01 1.53008 1768.43 1.54667 1803.54 1.56735 1839.35 1.59321 1875.86 1.62559 1913.10 1.66601 1951.09 1.71593 1989.82 1.77606 2029.32 1.84500 2069.61 1.91709 2110.70 1.98054 2152.60 2.01913 2195.34 2.01985 2238.92 1.98204 2283.37 1.91790 2328.70 1.84376 2374.93 1.77196 2422.08 1.70866 2470.17 1.65556 2519.21 1.61206 2569.22 1.57674 2620.23 1.54807 2672.25 1.52467 2725.30 1.50402 2779.41 1.48412 2834.58 1.46587 2890.86 1.44904 2948.25 1.43344 3006.78 1.41886 3066.48 1.40516 3127.35 1.39216 3189.44 1.37973 3252.76 1.36775 3317.34 1.35610 3383.20 1.34469 3450.36 1.33342 3518.86 1.32221 3588.72 1.31100 3659.97 1.29971 3732.63 1.28831 3806.73 1.27673 3882.31 1.26494 3959.38 1.25290 4037.99 1.24058 4118.16 1.22796 4199.91 1.21498 4283.29 1.20153 4368.33 1.18746 4455.05 1.17267 4543.50 1.15707 4633.70 1.14057 4725.69 1.12313 4819.51 1.10495 4915.19 1.08641 5012.77 1.06784 5112.29 1.04954 5213.78 1.03177 5317.29 1.01478 5422.86 0.998771 5530.52 0.983897 5640.31 0.969843 5752.29 0.955955 5866.49 0.941640 5982.96 0.926373 6101.73 0.909758 6222.87 0.891846 6346.41 0.872838 6472.41 0.852916 6600.90 0.832247 6731.95 0.810983 6865.60 0.789264 7001.90 0.767214 7140.91 0.744950 7282.68 0.722573 7427.26 0.700177 7574.72 0.677845 7725.09 0.655655 7878.46 0.633672 8034.87 0.611957 8194.39 0.590561 8357.07 0.569535 8522.98 0.548915 8692.18 0.528742 8864.75 0.509042 9040.74 0.489845 9220.23 0.471172 9403.28 0.453041 9589.96 0.435467 9780.35 0.418464 9974.52 0.402038 10172.5 0.386198 10374.5 0.370947 10580.5 0.356286 10790.5 0.342218 11004.7 0.328739 11223.2 0.315844 11446.0 0.303532 11673.3 0.291795 11905.0 0.280626 12141.4 0.270016 12382.4 0.259956 12628.2 0.250420 12878.9 0.241376 13134.6 0.232794 13395.4 0.224646 13661.3 0.216908 13932.5 0.209554 14209.1 0.202560 14491.2 0.195907 14778.9 0.189572 15072.3 0.183538 15371.5 0.177786 15676.7 0.172300 15987.9 0.167065 16305.4 0.162064 16629.1 0.157286 16959.2 0.152716 17295.9 0.148343 17639.3 0.144154 17989.4 0.140141 18346.6 0.136292 18710.8 0.132598 19082.3 0.129050 19461.1 0.125641 19847.5 0.122361 20241.5 0.119205 20643.4 0.116165 21053.2 0.113236 21471.2 0.110409 21897.4 0.107682 22332.2 0.105047 22775.5 0.102501 23227.7 0.100038 23688.8 0.0976542 24159.1 0.0953455 24638.8 0.0931080 25127.9 0.0909383 25626.7 0.0888330 26135.5 0.0867889 26654.4 0.0848029 27183.5 0.0828727 27723.2 0.0809961 28273.6 0.0791710 28834.9 0.0773957 29407.4 0.0756684 29991.2 0.0739874 30586.6 0.0723509 31193.9 0.0707575 31813.1 0.0692058 32444.7 0.0676944 33088.8 0.0662216 33745.8 0.0647864 34415.7 0.0633876 35098.9 0.0620239 35795.8 0.0606941 36506.4 0.0593972 37231.2 0.0581322 37970.3 0.0568980 38724.1 0.0556938 39492.9 0.0545185 40277.0 0.0533713 41076.6 0.0522514 41892.1 0.0511579 42723.8 0.0500900 43571.9 0.0490471 44437.0 0.0480283 45319.2 0.0470331 46218.9 0.0460607 47136.5 0.0451105 48072.3 0.0441818 49026.6 0.0432741 50000.0 0.0423868 extinction-0.4.7/testdata/run.pro000066400000000000000000000016251472756563600170760ustar00rootroot00000000000000 pro run minwave = 1000 maxwave = 50000 n = 200 ; create log-spaced wavelength vector minexp = alog10(minwave) maxexp = alog10(maxwave) step = (maxexp - minexp) / (n-1) exps = minexp + step * findgen(n) wave = 10.^exps ; create a "flat" flux vector flux = wave * 0.0 + 1.0 r_vs = [2.3, 3.1, 4.0, 5.3] fnames = ['fm_unred_2.3.dat', 'fm_unred_3.1.dat', 'fm_unred_4.0.dat', 'fm_unred_5.3.dat'] for i = 0, n_elements(r_vs) - 1 do begin a_v = 1.0 r_v = r_vs(i) ebv = a_v / r_v fm_unred, wave, flux, -ebv, flux_new, R_V=r_v ;Redden (negative E(B-V)) ; fm_unred does `result = flux * 10.^(0.4*curve)` where is scaled by input ; undo it to get the curve a_lambda = -2.5 * alog10(flux_new) data = fltarr(2, n_elements(wave)) data(0,*) = wave data(1,*) = a_lambda openw, 1, fnames(i) printf, 1, data close, 1 endfor end extinction-0.4.7/tox.ini000066400000000000000000000001751472756563600152510ustar00rootroot00000000000000[tox] minversion = 3.3 envlist = py3 isolated_build = true [testenv] passenv = HOME deps = pytest commands = pytest test.py