├── pdspy ├── constants │ ├── math.py │ ├── __init__.py │ ├── time.py │ ├── astronomy.py │ ├── NumberUnit.py │ └── physics.py ├── gas │ ├── __init__.py │ └── data │ │ ├── test.hdf5 │ │ └── test.py ├── mcmc │ ├── __init__.py │ ├── change_params.py │ ├── ml2d.py │ ├── ml.py │ ├── mcmc.py │ └── mcmc2d.py ├── table │ ├── __init__.py │ ├── column.py │ └── table.py ├── dust │ ├── data │ │ ├── c2d.hdf5 │ │ ├── diana.hdf5 │ │ ├── test.hdf5 │ │ ├── PAH_MRN.hdf5 │ │ ├── draine.hdf5 │ │ ├── pollack.hdf5 │ │ ├── diana_10cm.hdf5 │ │ ├── diana_10um.hdf5 │ │ ├── diana_1cm.hdf5 │ │ ├── diana_1mm.hdf5 │ │ ├── diana_1um.hdf5 │ │ ├── diana_wice.hdf5 │ │ ├── draine_1cm.hdf5 │ │ ├── draine_1mm.hdf5 │ │ ├── draine_1um.hdf5 │ │ ├── draine_2mm.hdf5 │ │ ├── draine_3mm.hdf5 │ │ ├── ricci_1um.hdf5 │ │ ├── diana_100um.hdf5 │ │ ├── draine_100um.hdf5 │ │ ├── draine_10um.hdf5 │ │ ├── pollack_100um.hdf5 │ │ ├── pollack_10cm.hdf5 │ │ ├── pollack_10um.hdf5 │ │ ├── pollack_1cm.hdf5 │ │ ├── pollack_1mm.hdf5 │ │ ├── pollack_1um.hdf5 │ │ ├── pollack_new.hdf5 │ │ ├── diana_wice_10cm.hdf5 │ │ ├── diana_wice_10um.hdf5 │ │ ├── diana_wice_1cm.hdf5 │ │ ├── diana_wice_1mm.hdf5 │ │ ├── diana_wice_1um.hdf5 │ │ ├── diana_wice_100um.hdf5 │ │ ├── diana_wice_singlesize.hdf5 │ │ ├── make_PAH_MRN.py │ │ ├── make_dsharp.py │ │ ├── make_diana.py │ │ ├── make_diana_wice.py │ │ ├── make_diana_wice_singlesize.py │ │ ├── make_diana_10cm.py │ │ ├── make_diana_10um.py │ │ ├── make_diana_1cm.py │ │ ├── make_diana_1mm.py │ │ ├── make_diana_1um.py │ │ ├── make_diana_wice_1cm.py │ │ ├── make_diana_wice_1mm.py │ │ ├── make_diana_wice_1um.py │ │ ├── make_ricci_1um.py │ │ ├── make_diana_100um.py │ │ ├── make_diana_wice_100um.py │ │ ├── make_diana_wice_10cm.py │ │ ├── make_diana_wice_10um.py │ │ ├── make_pollack_new.py │ │ ├── optical_constants │ │ │ ├── extrapolate_as.py │ │ │ ├── extrapolate_acz96.py │ │ │ ├── PAH_qion.txt │ │ │ └── graphite.txt │ │ ├── make_draine.py │ │ ├── make_draine_1cm.py │ │ ├── make_draine_100um.py │ │ ├── make_draine_10um.py │ │ ├── make_draine_1mm.py │ │ ├── make_draine_1um.py │ │ ├── make_draine_2mm.py │ │ ├── make_draine_3mm.py │ │ ├── plot_kabs.py │ │ ├── test.py │ │ ├── make_pollack_1um.py │ │ ├── make_pollack.py │ │ ├── plot_beta.py │ │ ├── make_pollack_1cm.py │ │ ├── make_pollack_1mm.py │ │ ├── make_pollack_100um.py │ │ ├── make_pollack_10cm.py │ │ ├── make_pollack_10um.py │ │ ├── plot_draine.py │ │ ├── compare_c2d.py │ │ ├── plot_diana.py │ │ ├── plot_diana_wice.py │ │ └── plot_pollack.py │ ├── __init__.py │ ├── reddening │ │ └── steenman_the.dat │ ├── run_opacity_tool.py │ ├── mix_dust.py │ └── redden.py ├── radmc3d │ └── __init__.py ├── misc │ ├── gaussian.py │ ├── __init__.py │ ├── B_nu.py │ ├── dB_nu.py │ └── gaussian2d.py ├── statistics │ ├── binomial.py │ ├── __init__.py │ ├── fisher_exact.py │ ├── linear_regression.py │ ├── Ftest.py │ ├── ttest.py │ └── leastsq.py ├── modeling │ ├── DartoisPringleDisk.py │ ├── __init__.py │ ├── Star.py │ ├── check_parameters.py │ ├── get_surrogate_model.py │ ├── PringleDisk.py │ └── SettledPringleDisk.py ├── utils │ ├── __init__.py │ └── propose_point_emcee.py ├── spectroscopy │ ├── __init__.py │ ├── read_spectrum.py │ ├── freefree.py │ ├── spectroscopy.py │ ├── btsettl_photometry.py │ └── find_lines.py ├── plotting │ ├── Transform.py │ ├── __init__.py │ ├── colormaps.py │ ├── cubeshow.py │ ├── plot_scattered_light.py │ ├── plot_2D_visibilities.py │ └── plot_SED.py ├── stars │ ├── __init__.py │ └── Teff_from_SpT.py ├── imaging │ ├── __init__.py │ ├── imtovis.py │ ├── update_catalog.py │ ├── extract_pv_diagram.py │ ├── readpvfits.py │ ├── match_source_lists.py │ ├── readimfits.py │ └── imaging.py ├── interferometry │ ├── rotate.py │ ├── __init__.py │ ├── center.py │ ├── rmlimage.py │ ├── readvis.py │ ├── concatenate.py │ ├── interpolate_model.py │ ├── invert.py │ └── readuvfits.py └── __init__.py ├── tests ├── testdata.hdf5 ├── test.py └── test_dartois_disk.py ├── pyproject.toml ├── MANIFEST.in ├── docs ├── interferometry.rst ├── requirements.txt ├── modeling.rst ├── utils.rst ├── Makefile ├── plotting.rst ├── make.bat ├── installation.rst ├── upgrading.rst └── conf.py ├── .gitignore ├── .readthedocs.yaml ├── compile ├── meta.yaml ├── README.md ├── .github └── workflows │ └── python-build-test-publish.yml ├── setup.py └── examples └── run_radiative_transfer_model.py /pdspy/constants/math.py: -------------------------------------------------------------------------------- 1 | pi = 3.14159265 2 | -------------------------------------------------------------------------------- /pdspy/gas/__init__.py: -------------------------------------------------------------------------------- 1 | from .Gas import Gas 2 | -------------------------------------------------------------------------------- /pdspy/mcmc/__init__.py: -------------------------------------------------------------------------------- 1 | from .mcmc import mcmc 2 | from .mcmc2d import mcmc2d 3 | -------------------------------------------------------------------------------- /tests/testdata.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/tests/testdata.hdf5 -------------------------------------------------------------------------------- /pdspy/table/__init__.py: -------------------------------------------------------------------------------- 1 | from .column import MaskedColumn 2 | from .table import Table 3 | -------------------------------------------------------------------------------- /pdspy/dust/data/c2d.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/c2d.hdf5 -------------------------------------------------------------------------------- /pdspy/gas/data/test.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/gas/data/test.hdf5 -------------------------------------------------------------------------------- /pdspy/radmc3d/__init__.py: -------------------------------------------------------------------------------- 1 | from . import read 2 | from . import write 3 | from . import run 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools","wheel","Cython", "numpy<2.0.0"] 3 | -------------------------------------------------------------------------------- /pdspy/dust/data/diana.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/test.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/test.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/PAH_MRN.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/PAH_MRN.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_10cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_10cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_10um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_10um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_1cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_1cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_1mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_1mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_1um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_1um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_1cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_1cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_1mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_1mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_1um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_1um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_2mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_2mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_3mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_3mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/ricci_1um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/ricci_1um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_100um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_100um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_100um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_100um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/draine_10um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/draine_10um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_100um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_100um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_10cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_10cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_10um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_10um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_1cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_1cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_1mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_1mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_1um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_1um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/pollack_new.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/pollack_new.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_10cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_10cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_10um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_10um.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_1cm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_1cm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_1mm.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_1mm.hdf5 -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_1um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_1um.hdf5 -------------------------------------------------------------------------------- /pdspy/constants/__init__.py: -------------------------------------------------------------------------------- 1 | from . import astronomy 2 | from . import math 3 | from . import physics 4 | from . import time 5 | -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_100um.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_100um.hdf5 -------------------------------------------------------------------------------- /pdspy/constants/time.py: -------------------------------------------------------------------------------- 1 | hour = 3.6e3 # s 2 | day = 8.64e4 # s 3 | year = 3.1557600e7 # s 4 | -------------------------------------------------------------------------------- /pdspy/dust/data/diana_wice_singlesize.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psheehan/pdspy/HEAD/pdspy/dust/data/diana_wice_singlesize.hdf5 -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml 2 | 3 | # Include the README 4 | include *.md 5 | 6 | # Include the license file 7 | include LICENSE 8 | -------------------------------------------------------------------------------- /docs/interferometry.rst: -------------------------------------------------------------------------------- 1 | pdspy.interferometry 2 | =============================== 3 | 4 | .. autofunction:: pdspy.interferometry.readms 5 | -------------------------------------------------------------------------------- /pdspy/misc/gaussian.py: -------------------------------------------------------------------------------- 1 | from numpy import exp 2 | 3 | def gaussian(x,x0,sigma,f0): 4 | 5 | return f0*exp(-1*(x-x0)**2/(2*sigma**2)) -------------------------------------------------------------------------------- /pdspy/misc/__init__.py: -------------------------------------------------------------------------------- 1 | from .B_nu import B_nu 2 | from .dB_nu import dB_nu 3 | from .gaussian import gaussian 4 | from .gaussian2d import gaussian2d 5 | -------------------------------------------------------------------------------- /pdspy/statistics/binomial.py: -------------------------------------------------------------------------------- 1 | from math import factorial 2 | 3 | def binomial(N, m, p): 4 | 5 | return factorial(N)/(factorial(m)*factorial(N-m))*p**m*(1-p)**(N-m) 6 | -------------------------------------------------------------------------------- /pdspy/misc/B_nu.py: -------------------------------------------------------------------------------- 1 | from numpy import exp 2 | from ..constants.physics import h, c, k 3 | 4 | def B_nu(nu,T): 5 | 6 | return (2*h*nu**3/c**2)/(exp(h*nu/(k*T))-1.0) 7 | -------------------------------------------------------------------------------- /pdspy/modeling/DartoisPringleDisk.py: -------------------------------------------------------------------------------- 1 | from .DartoisDisk import DartoisDisk 2 | from .PringleDisk import PringleDisk 3 | 4 | class DartoisPringleDisk(DartoisDisk,PringleDisk): 5 | pass 6 | -------------------------------------------------------------------------------- /pdspy/misc/dB_nu.py: -------------------------------------------------------------------------------- 1 | from numpy import exp 2 | from ..constants.physics import h, c, k 3 | 4 | def dB_nu(nu,T): 5 | 6 | return (-2*h**2*nu**4/(c**2*k*T**2))/(exp(h*nu/(k*T))-1)/ \ 7 | (1.-exp(-h*nu/(k*T))) 8 | -------------------------------------------------------------------------------- /pdspy/dust/__init__.py: -------------------------------------------------------------------------------- 1 | from .Dust import Dust 2 | from .DustGenerator import DustGenerator 3 | from .PAH import PAH 4 | from .mix_dust import mix_dust 5 | from .redden import redden 6 | from .run_opacity_tool import run_opacity_tool 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.pyc 3 | __pycache__ 4 | *.swp 5 | *.pdf 6 | *.dSYM 7 | pdspy/dust/reddening/*.py 8 | pdspy/dust/reddening/*.pdf 9 | test 10 | *.c 11 | *.cover 12 | _build 13 | _static 14 | _templates 15 | build 16 | pdspy.egg-info 17 | -------------------------------------------------------------------------------- /pdspy/statistics/__init__.py: -------------------------------------------------------------------------------- 1 | from .binomial import binomial 2 | from .fisher_exact import fisher_exact 3 | from .leastsq import leastsq 4 | from .linear_regression import linear_regression 5 | from .ttest import ttest 6 | from .Ftest import Ftest 7 | -------------------------------------------------------------------------------- /pdspy/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .load_config import load_config 2 | from .load_data import load_data 3 | from .load_results import load_results 4 | from .propose_point_emcee import propose_point_emcee 5 | 6 | from . import emcee 7 | from . import dynesty 8 | -------------------------------------------------------------------------------- /pdspy/misc/gaussian2d.py: -------------------------------------------------------------------------------- 1 | from numpy import exp,cos,sin 2 | 3 | def gaussian2d(x,y,x0,y0,sigmax,sigmay,pa,f0): 4 | 5 | xp=(x-x0)*cos(pa)-(y-y0)*sin(pa) 6 | yp=(x-x0)*sin(pa)+(y-y0)*cos(pa) 7 | 8 | return f0*exp(-1*xp**2/(2*sigmax**2))*exp(-1*yp**2/(2*sigmay**2)) -------------------------------------------------------------------------------- /pdspy/spectroscopy/__init__.py: -------------------------------------------------------------------------------- 1 | from .spectroscopy import Spectrum 2 | from .find_lines import find_lines 3 | from .line_flux import line_flux 4 | from .read_spectrum import read_spectrum 5 | from .btsettl_photometry import btsettl_photometry 6 | from .freefree import freefree 7 | -------------------------------------------------------------------------------- /pdspy/plotting/Transform.py: -------------------------------------------------------------------------------- 1 | class Transform: 2 | def __init__(self, xmin, xmax, dx, fmt): 3 | self.xmin = xmin 4 | self.xmax = xmax 5 | self.dx = dx 6 | self.fmt = fmt 7 | 8 | def __call__(self, x, p): 9 | return self.fmt% ((x-(self.xmax-self.xmin+1)/2)*self.dx) 10 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # File: .readthedocs.yaml 2 | 3 | version: 2 4 | 5 | # Build from the docs/ directory with Sphinx 6 | sphinx: 7 | configuration: docs/conf.py 8 | 9 | # Explicitly set the version of Python and its requirements 10 | python: 11 | version: 3.7 12 | install: 13 | - requirements: docs/requirements.txt 14 | -------------------------------------------------------------------------------- /pdspy/stars/__init__.py: -------------------------------------------------------------------------------- 1 | from .pms_evolutionary_tracks import pms_get_mstar 2 | from .pms_evolutionary_tracks import pms_get_age 3 | from .pms_evolutionary_tracks import pms_get_teff 4 | from .pms_evolutionary_tracks import pms_get_luminosity 5 | from .pms_evolutionary_tracks import pms_get_radius 6 | from .Teff_from_SpT import Teff_from_SpT 7 | -------------------------------------------------------------------------------- /pdspy/table/column.py: -------------------------------------------------------------------------------- 1 | import astropy.table 2 | 3 | class MaskedColumn(astropy.table.MaskedColumn): 4 | 5 | def __getitem__(self, item): 6 | x = super(MaskedColumn, self).__getitem__(item) 7 | 8 | try: 9 | if x.mask: 10 | return '--' 11 | except: 12 | return x 13 | -------------------------------------------------------------------------------- /pdspy/statistics/fisher_exact.py: -------------------------------------------------------------------------------- 1 | from math import factorial 2 | 3 | def fisher_exact(a,b,c,d): 4 | 5 | return float(factorial(a+b))*float(factorial(c+d))*float(factorial(a+c))* \ 6 | float(factorial(b+d))/(float(factorial(a))*float(factorial(b))* \ 7 | float(factorial(c))*float(factorial(d))*float(factorial(a+b+c+d))) 8 | -------------------------------------------------------------------------------- /pdspy/spectroscopy/read_spectrum.py: -------------------------------------------------------------------------------- 1 | from numpy import loadtxt 2 | from .spectroscopy import Spectrum 3 | 4 | def read_spectrum(file): 5 | 6 | spectrum = loadtxt(file) 7 | wave = spectrum[:,0] 8 | flux = spectrum[:,1] 9 | unc = spectrum[:,2] 10 | 11 | data = Spectrum(wave,flux,unc) 12 | 13 | return data 14 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | command=python 2 | 3 | while [[ $# -gt 0 ]]; do 4 | case "$1" in 5 | -v|--version) 6 | command="${2#*=}" 7 | shift 2 8 | ;; 9 | esac 10 | done 11 | 12 | $command setup.py build_ext --inplace 13 | 14 | rm -r build pdspy/interferometry/libinterferometry.c 15 | rm -r build pdspy/imaging/libimaging.c 16 | rm -r build pdspy/radmc3d/read.c 17 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # File: docs/requirements.txt 2 | 3 | # Defining the exact version will make sure things don't break 4 | sphinx==4.1.2 5 | sphinx_rtd_theme==0.5.2 6 | readthedocs-sphinx-search==0.3.2 7 | docutils==0.16 8 | numpy 9 | scipy 10 | matplotlib 11 | emcee 12 | corner 13 | hyperion 14 | h5py 15 | Cython 16 | astropy 17 | schwimmbad 18 | dynesty 19 | scikit-learn 20 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pdspy.interferometry as uv 4 | import matplotlib.pyplot as plt 5 | 6 | data = uv.Visibilities() 7 | data.read("testdata.hdf5") 8 | 9 | image = uv.invert(data, imsize=512, pixel_size=0.5, convolution="expsinc") 10 | 11 | plt.imshow(image.image[:,:,0], origin="lower", interpolation="none") 12 | plt.colorbar() 13 | plt.savefig("test.pdf") 14 | -------------------------------------------------------------------------------- /docs/modeling.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | pdspy.modeling 5 | =============================== 6 | 7 | .. automodule:: pdspy.modeling 8 | .. currentmodule:: pdspy.modeling 9 | 10 | Model 11 | ---------------------- 12 | 13 | .. autoclass:: Model 14 | :members: 15 | 16 | YSOModel 17 | ---------------------- 18 | 19 | .. autoclass:: YSOModel 20 | :members: 21 | -------------------------------------------------------------------------------- /pdspy/imaging/__init__.py: -------------------------------------------------------------------------------- 1 | from .libimaging import Image 2 | from .libimaging import UnstructuredImage 3 | from .imtovis import imtovis 4 | from .readimfits import readimfits 5 | from .readpvfits import readpvfits 6 | from .find import find 7 | from .match_source_lists import match_source_lists 8 | from .update_catalog import update_catalog 9 | from .extract_pv_diagram import extract_pv_diagram 10 | -------------------------------------------------------------------------------- /pdspy/interferometry/rotate.py: -------------------------------------------------------------------------------- 1 | from .libinterferometry import Visibilities 2 | import numpy 3 | 4 | def rotate(data, pa=0): 5 | 6 | newu = data.u * numpy.cos(pa) + data.v * numpy.sin(pa) 7 | newv = -data.u * numpy.sin(pa) + data.v * numpy.cos(pa) 8 | 9 | return Visibilities(newu, newv, data.freq.copy(), data.real.copy(), \ 10 | data.imag.copy(), data.weights.copy()) 11 | -------------------------------------------------------------------------------- /pdspy/constants/astronomy.py: -------------------------------------------------------------------------------- 1 | AU = 1.495978707e13 # cm 2 | pc = 3.085677581e18 # cm 3 | ly = 9.463e17 # cm 4 | M_sun = 1.99e33 # g 5 | R_sun = 6.96e10 # cm 6 | L_sun = 3.9e33 # erg s^-1 7 | T_sun = 5.78e3 # K 8 | Jy = 1.0e-23 # erg s^-1 cm^-2 Hz^-1 9 | arcsec = 4.84813681e-6 # radians 10 | kms = 1e5 11 | M_jupiter = 1.89813e30 12 | M_earth = 5.97219e27 13 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_PAH_MRN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | dust = PAH() 7 | dust.set_properties_from_draine("optical_constants/PAHneu_30.txt", \ 8 | "optical_constants/PAHneu_30.txt","optical_constants/PAH_qion.txt") 9 | dust.set_density(3.0) 10 | 11 | amin = dust.a.min() 12 | amax = dust.a.max() 13 | pl = 3.5 14 | 15 | dust.calculate_size_distribution_opacity(amin, amax, pl) 16 | 17 | dust.write('PAH_MRN.hdf5') 18 | -------------------------------------------------------------------------------- /pdspy/plotting/__init__.py: -------------------------------------------------------------------------------- 1 | from .plot_SED import plot_SED 2 | from .plot_continuum_image import plot_continuum_image 3 | from .plot_1D_visibilities import plot_1D_visibilities 4 | from .plot_2D_visibilities import plot_2D_visibilities 5 | from .plot_scattered_light import plot_scattered_light 6 | from .plot_channel_maps import plot_channel_maps 7 | from .plot_pvdiagram import plot_pvdiagram 8 | from . import colormaps 9 | from .Transform import Transform 10 | from .cubeshow import cubeshow 11 | -------------------------------------------------------------------------------- /pdspy/dust/reddening/steenman_the.dat: -------------------------------------------------------------------------------- 1 | # steenman_the.dat 2 | # 3 | # Josh Eisner 4 | # April 2, 2003 5 | # 6 | # Hold Extinction Coefficients from Steenman & The 1991, Ap&SS,184,9S 7 | # 8 | # Filter Wvlngth(um) A_lambda/A_v 9 | JohnU 0.36 1.65 10 | JohnB 0.44 1.33 11 | JohnV 0.55 1.00 12 | CousR 0.64 0.82 13 | JohnR 0.70 0.72 14 | CousI 0.79 0.60 15 | JohnI 0.90 0.48 16 | J 1.25 0.26 17 | H 1.65 0.15 18 | K 2.20 0.08 19 | L 3.60 0.03 20 | M 4.80 0.02 21 | -------------------------------------------------------------------------------- /docs/utils.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | pdspy.utils 5 | =============================== 6 | 7 | .. automodule:: pdspy.utils 8 | .. currentmodule:: pdspy.utils 9 | 10 | utils.load_config 11 | --------------------------------- 12 | 13 | .. autofunction:: load_config 14 | 15 | utils.load_data 16 | --------------------------------- 17 | 18 | .. autofunction:: load_data 19 | 20 | utils.load_results 21 | --------------------------------- 22 | 23 | .. autofunction:: load_results 24 | 25 | -------------------------------------------------------------------------------- /pdspy/mcmc/change_params.py: -------------------------------------------------------------------------------- 1 | from numpy import zeros 2 | from numpy.random import randint, normal 3 | 4 | def change_params(params, sigma_params, change_param=None): 5 | 6 | new_params = zeros(params.size) 7 | 8 | if change_param == None: 9 | change_param = randint(low=0, high=params.size, size=1)[0] 10 | 11 | new_params = params.copy() 12 | new_params[change_param] = params[change_param] + \ 13 | normal(loc=0., scale=sigma_params[change_param], size=1)[0] 14 | 15 | return new_params 16 | -------------------------------------------------------------------------------- /pdspy/interferometry/__init__.py: -------------------------------------------------------------------------------- 1 | from .libinterferometry import Visibilities, average, grid, freqcorrect, chisq 2 | from .readuvfits import readuvfits 3 | from .readvis import readvis 4 | from .center import center 5 | from .clean import clean 6 | from .concatenate import concatenate 7 | from .fit_model import fit_model 8 | from .invert import invert 9 | from .model import model 10 | from .rotate import rotate 11 | from .interpolate_model import interpolate_model 12 | 13 | try: 14 | from .readms import readms 15 | except: 16 | pass 17 | 18 | from .rmlimage import rmlimage 19 | -------------------------------------------------------------------------------- /pdspy/spectroscopy/freefree.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def freefree(nu, F_nu_ff, nu_turn, pl_turn): 4 | if type(nu) == numpy.ndarray: 5 | flux = numpy.where(nu < nu_turn, F_nu_ff * (nu / nu_turn)**pl_turn, \ 6 | F_nu_ff * (nu / nu_turn)**-0.1) 7 | 8 | flux = numpy.where(nu > 1.0e13, 0., flux) 9 | 10 | return flux 11 | else: 12 | if nu < nu_turn: 13 | return F_nu_ff * (nu / nu_turn)**pl_turn 14 | elif nu > 1.0e13: 15 | return 0. 16 | else: 17 | return F_nu_ff * (nu / nu_turn)**-0.1 18 | -------------------------------------------------------------------------------- /pdspy/statistics/linear_regression.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def linear_regression(_x, _y, sigma_y=None): 4 | 5 | x = numpy.mat(_x).T 6 | y = numpy.mat(_y).T 7 | 8 | A = numpy.concatenate((x, numpy.ones(x.shape)), axis=1) 9 | 10 | if type(sigma_y) != type(None): 11 | C = numpy.diag(sigma_y**2) 12 | else: 13 | C = numpy.diag(numpy.ones(_y.shape)) 14 | 15 | Xa = numpy.linalg.inv(A.T * numpy.linalg.inv(C) * A) * \ 16 | (A.T * numpy.linalg.inv(C) * y) 17 | 18 | Cov = numpy.linalg.inv(A.T * numpy.linalg.inv(C) * A) 19 | 20 | return Xa[0,0], Xa[1,0], Cov[0,0]**0.5, Cov[1,1]**0.5 21 | -------------------------------------------------------------------------------- /pdspy/constants/NumberUnit.py: -------------------------------------------------------------------------------- 1 | class NumberUnit: 2 | 3 | def __init__(self,cgs): 4 | self.cgs = cgs 5 | 6 | def __add__(self,a): 7 | return self.cgs + a 8 | 9 | def __radd__(self,a): 10 | return self.cgs + a 11 | 12 | def __sub__(self,a): 13 | return self.cgs - a 14 | 15 | def __rsub__(self,a): 16 | return a - self.cgs 17 | 18 | def __mul__(self,a): 19 | return self.cgs * a 20 | 21 | def __rmul__(self,a): 22 | return self.cgs * a 23 | 24 | def __div__(self,a): 25 | return self.cgs / a 26 | 27 | def __rdiv__(self,a): 28 | return a / self.cgs 29 | -------------------------------------------------------------------------------- /pdspy/gas/data/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.constants.math import pi 4 | from pdspy.modeling import YSOModel 5 | from pdspy.gas import Gas 6 | import numpy 7 | import matplotlib.pyplot as plt 8 | import matplotlib 9 | 10 | # Change a few of the plotting parameters. 11 | 12 | matplotlib.rcParams['font.family'] = 'serif' 13 | 14 | # Read in the gas. 15 | 16 | g = Gas() 17 | g.set_properties_from_lambda('co.dat') 18 | 19 | # Make the model. 20 | 21 | model = YSOModel() 22 | model.set_spherical_grid(0.1,20.,100,2,2) 23 | model.add_star() 24 | model.add_disk(rmax=20., gas=g, abundance=1.0e-5) 25 | 26 | # Save the model. 27 | 28 | model.write_yso("test.hdf5") 29 | 30 | -------------------------------------------------------------------------------- /pdspy/constants/physics.py: -------------------------------------------------------------------------------- 1 | c = 2.99792458e10 # cm s^-1 2 | h = 6.6260755e-27 # erg s 3 | hbar = 1.05457266e-27 # erg s 4 | G = 6.67259e-8 # cm^3 g^-1 s^-2 5 | e = 4.8032068e-10 # esu 6 | m_e = 9.1093897e-28 # g 7 | m_p = 1.6726231e-24 # g 8 | m_n = 1.6749286e-24 # g 9 | m_H = 1.6733e-24 # g 10 | amu = 1.6605402e-24 # g 11 | N_A = 6.0221367e23 # 12 | k = 1.380658e-16 # erg K^-1 13 | eV = 1.6021772e-12 # erg 14 | a = 7.5646e-15 # erg cm^-3 K^-4 15 | sigma = 5.67051e-5 # erg cm^-2 K^-4 s^-1 16 | alpha = 7.29735308e-3 # 17 | R = 2.1798741e-11 # erg 18 | -------------------------------------------------------------------------------- /pdspy/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | text = warnings.warn("pdspy v2.0.0 represents a major update to the pdspy code, and is not backwards compatible with the results of versions < 2.0.0. *Do not use v2.0.0 to work with results from earlier versions.* For more information, see pdspy.readthedocs.io.", stacklevel=2) 4 | 5 | from . import constants 6 | from . import dust 7 | from . import gas 8 | from . import imaging 9 | from . import interferometry 10 | from . import mcmc 11 | from . import misc 12 | from . import modeling 13 | from . import plotting 14 | from . import radmc3d 15 | from . import spectroscopy 16 | from . import stars 17 | from . import statistics 18 | from . import table 19 | from . import utils 20 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /pdspy/statistics/Ftest.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def Ftest(data1, data2, tailed=2): 4 | 5 | N = len(data1) 6 | M = len(data2) 7 | 8 | mean1 = numpy.mean(data1) 9 | mean2 = numpy.mean(data2) 10 | 11 | S1 = numpy.sum((data1 - mean1)**2) / (N - 1) 12 | S2 = numpy.sum((data2 - mean2)**2) / (M - 1) 13 | 14 | F = S1 / S2 15 | f = numpy.random.f(N-1, M-1, 100000) 16 | 17 | if tailed == 1: 18 | if F > 1: 19 | p = len(f[f > F]) / len(f) 20 | elif F < 1: 21 | p = len(f[f < F]) / len(f) 22 | elif tailed == 2: 23 | if F > 1: 24 | p = len(f[(f > F) | (f < 1./F)]) / len(f) 25 | elif F < 1: 26 | p = len(f[(f > 1./F) | (f < F)]) / len(f) 27 | 28 | return F, p 29 | -------------------------------------------------------------------------------- /docs/plotting.rst: -------------------------------------------------------------------------------- 1 | .. role:: hidden 2 | :class: hidden-section 3 | 4 | pdspy.plotting 5 | =============================== 6 | 7 | .. automodule:: pdspy.plotting 8 | .. currentmodule:: pdspy.plotting 9 | 10 | plotting.plot_1D_visibilities 11 | --------------------------------- 12 | 13 | .. autofunction:: plot_1D_visibilities 14 | 15 | plotting.plot_continuum_image 16 | --------------------------------- 17 | 18 | .. autofunction:: plot_continuum_image 19 | 20 | plotting.plot_channel_maps 21 | --------------------------------- 22 | 23 | .. autofunction:: plot_channel_maps 24 | 25 | plotting.plot_SED 26 | --------------------------------- 27 | 28 | .. autofunction:: plot_SED 29 | 30 | plotting.plot_pvdiagram 31 | --------------------------------- 32 | 33 | .. autofunction:: plot_pvdiagram 34 | -------------------------------------------------------------------------------- /pdspy/modeling/__init__.py: -------------------------------------------------------------------------------- 1 | from .Disk import Disk 2 | from .DartoisDisk import DartoisDisk 3 | from .SettledDisk import SettledDisk 4 | 5 | from .PringleDisk import PringleDisk 6 | from .DartoisPringleDisk import DartoisPringleDisk 7 | from .SettledPringleDisk import SettledPringleDisk 8 | 9 | from .Grid import Grid 10 | from .Model import Model 11 | from .Star import Star 12 | from .UlrichEnvelope import UlrichEnvelope 13 | from .UlrichEnvelopeExtended import UlrichEnvelopeExtended 14 | from .TaperedUlrichEnvelope import TaperedUlrichEnvelope 15 | from .TaperedUlrichEnvelopeExtended import TaperedUlrichEnvelopeExtended 16 | from .YSOModel import YSOModel 17 | 18 | from .run_disk_model import run_disk_model 19 | from .run_flared_model import run_flared_model 20 | from .check_parameters import check_parameters 21 | from .get_surrogate_model import get_surrogate_model 22 | -------------------------------------------------------------------------------- /pdspy/statistics/ttest.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def ttest(data1, data2, tailed=2): 4 | 5 | n = len(data1) 6 | m = len(data2) 7 | nu = n + m - 2 8 | 9 | mean1 = numpy.mean(data1) 10 | mean2 = numpy.mean(data2) 11 | 12 | S1 = numpy.sum((data1 - mean1)**2) / n 13 | S2 = numpy.sum((data2 - mean2)**2) / m 14 | 15 | s = (n*S1 + m*S2) / nu 16 | 17 | t = (mean1 - mean2) / (numpy.sqrt(s) * numpy.sqrt(m**-1 + n**-1)) 18 | 19 | T = numpy.random.standard_t(nu, size=100000) 20 | 21 | if tailed == 1: 22 | if t < 0: 23 | p = len(T[T < t]) / len(T) 24 | elif t > 0: 25 | p = len(T[T > t]) / len(T) 26 | elif tailed == 2: 27 | if t < 0: 28 | p = len(T[(T < t) | (T > -t)]) / len(T) 29 | elif t > 0: 30 | p = len(T[(T > t) | (T < -t)]) / len(T) 31 | 32 | return t, p 33 | -------------------------------------------------------------------------------- /pdspy/mcmc/ml2d.py: -------------------------------------------------------------------------------- 1 | from numpy import sqrt, exp, pi 2 | 3 | def ml2d(x, y, z, sigma_z, params, model, args=None, limits=None): 4 | 5 | if (type(limits) != type(None)): 6 | for i in range(params.size): 7 | if ((limits[i]["limited"][0] == True) and \ 8 | (params[i] < limits[i]["limits"][0])): 9 | ml = 0.0 10 | chisq = 1.0e300 11 | return ml, chisq 12 | elif ((limits[i]["limited"][1] == True) and \ 13 | (params[i] > limits[i]["limits"][1])): 14 | ml = 0.0 15 | chisq = 1.0e300 16 | return ml, chisq 17 | 18 | m = model(x, y, params, **args) 19 | 20 | mlarr = 1./sqrt(2*pi*sigma_z**2)*\ 21 | exp(-(z-m)**2/(2*sigma_z**2)) 22 | 23 | ml = (mlarr**(1./(z.size-params.size))).prod() 24 | chisq = ((z-m)**2/sigma_z**2).sum() 25 | 26 | return ml, chisq 27 | -------------------------------------------------------------------------------- /pdspy/mcmc/ml.py: -------------------------------------------------------------------------------- 1 | from numpy import sqrt, exp, pi 2 | 3 | def ml(x, y, sigma_y, params, model, args=None, limits=None): 4 | 5 | if (type(limits) != type(None)): 6 | for i in range(params.size): 7 | if ((limits[i]["limited"][0] == True) and \ 8 | (params[i] < limits[i]["limits"][0])): 9 | ml = 0.0 10 | chisq = 1.0e300 11 | return ml, chisq 12 | elif ((limits[i]["limited"][1] == True) and \ 13 | (params[i] > limits[i]["limits"][1])): 14 | ml = 0.0 15 | chisq = 1.0e300 16 | return ml, chisq 17 | 18 | m = model(x, params, **args) 19 | 20 | mlarr = 1./sqrt(2*pi*sigma_y**2)*\ 21 | exp(-(y-m)**2/(2*sigma_y**2)) 22 | 23 | #ml = (mlarr**(1./(y.size-params.size))).prod() 24 | ml = 1. 25 | chisq = ((y-m)**2/sigma_y**2).sum() 26 | 27 | return ml, chisq 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /pdspy/interferometry/center.py: -------------------------------------------------------------------------------- 1 | from .model import model 2 | from .libinterferometry import Visibilities 3 | import numpy 4 | 5 | def center(data, params): 6 | 7 | if type(params) == list: 8 | params = numpy.array(params) 9 | elif type(params) == numpy.ndarray: 10 | pass 11 | 12 | centering_params = [params[0], params[1], 1.] 13 | 14 | data_complex = data.real+1j*data.imag 15 | 16 | model_complex = numpy.empty(data.real.shape, dtype=complex) 17 | for i in range(len(data.freq)): 18 | model_complex[:,i] = model(data.u*data.freq[i]/data.freq.mean(), \ 19 | data.v*data.freq[i]/data.freq.mean(), centering_params, \ 20 | funct="point", return_type="complex")[:,0] 21 | 22 | centered_data = data_complex * model_complex.conj() 23 | 24 | return Visibilities(data.u.copy(), data.v.copy(), data.freq.copy(), \ 25 | centered_data.real, centered_data.imag, data.weights.copy()) 26 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_dsharp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | troilite = Dust() 7 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 8 | troilite.set_density(4.83) 9 | 10 | water_ice = Dust() 11 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 12 | water_ice.set_density(0.92) 13 | 14 | silicates = Dust() 15 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 16 | silicates.set_density(3.3) 17 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 18 | 19 | organics = Dust() 20 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 21 | organics.set_density(1.5) 22 | 23 | species = [silicates,troilite,organics,water_ice] 24 | abundances = [0.1670,0.0258,0.4430,0.3642] 25 | 26 | dust = mix_dust(species, abundances) 27 | 28 | # Create the dust generator class. 29 | 30 | dust_gen = DustGenerator(dust) 31 | 32 | dust_gen.write("dsharp.hdf5") 33 | -------------------------------------------------------------------------------- /pdspy/interferometry/rmlimage.py: -------------------------------------------------------------------------------- 1 | from scipy.optimize import minimize 2 | from ..imaging import Image 3 | from . import interpolate_model 4 | import numpy 5 | 6 | def rmlimage(data, imsize=512, pixelsize=0.01): 7 | 8 | def neg_ln_like(p): 9 | if numpy.any(p <= 0): 10 | return numpy.inf 11 | else: 12 | model = Image(numpy.array(p).reshape((imsize,imsize,1,1)), \ 13 | x=numpy.arange(imsize)*pixelsize, y=numpy.arange(imsize)*\ 14 | pixelsize, freq=data.freq) 15 | 16 | model_vis = interpolate_model(data.u, data.v, data.freq, model) 17 | 18 | return ((data.real - model_vis.real)**2 * data.weights + \ 19 | (data.imag - model_vis.imag)**2 * data.weights).sum() + \ 20 | (model.image * numpy.log(model.image)).sum() 21 | 22 | result = minimize(neg_ln_like, numpy.ones(imsize**2)) 23 | 24 | return Image(numpy.array(result.x).reshape((imsize,imsize,1,1)), \ 25 | x=numpy.arange(imsize)*pixelsize, y=numpy.arange(imsize)*\ 26 | pixelsize, freq=data.freq) 27 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | dust_gen = DustGenerator(dust, with_dhs=True) 28 | 29 | dust_gen.write('diana.hdf5') 30 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | dust_gen = DustGenerator(dust, with_dhs=True) 29 | 30 | dust_gen.write('diana_wice.hdf5') 31 | -------------------------------------------------------------------------------- /pdspy/imaging/imtovis.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | from ..interferometry import Visibilities 3 | from ..constants.astronomy import pc, arcsec 4 | from scipy.fftpack import fft2, fftshift, fftfreq, ifftshift 5 | 6 | def imtovis(image): 7 | 8 | ##### Some natural constants 9 | 10 | real = numpy.empty((image.x.size*image.y.size,image.freq.size)) 11 | imag = numpy.empty((image.x.size*image.y.size,image.freq.size)) 12 | weights = numpy.ones(real.shape) 13 | for i in range(image.freq.size): 14 | vis = fftshift(fft2(ifftshift(image.image[:,:,i,0]))) 15 | real[:,i] = vis.real.reshape((image.x.size*image.y.size,)) 16 | imag[:,i] = vis.imag.reshape((image.x.size*image.y.size,)) 17 | 18 | uu = fftshift(fftfreq(image.x.size, (image.x[1] - image.x[0]) * arcsec)) 19 | vv = fftshift(fftfreq(image.y.size, (image.y[1] - image.y[0]) * arcsec)) 20 | 21 | u, v = numpy.meshgrid(uu, vv) 22 | u = u.reshape((image.x.size*image.y.size,)) 23 | v = v.reshape((image.y.size*image.y.size,)) 24 | 25 | freq = image.freq 26 | 27 | return Visibilities(u, v, freq, real, imag, weights) 28 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_singlesize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | dust_gen = DustGenerator(dust, with_dhs=True, singlesize=True) 29 | 30 | dust_gen.write('diana_wice_singlesize.hdf5') 31 | -------------------------------------------------------------------------------- /pdspy/mcmc/mcmc.py: -------------------------------------------------------------------------------- 1 | from numpy import array, arange, exp 2 | from numpy.random import uniform 3 | from .change_params import change_params 4 | from .ml import ml 5 | 6 | def mcmc(x, y, sigma_y, params, sigma_params, model, args={}, \ 7 | nsteps=1e5, change_param=None, limits=None): 8 | 9 | MLold, chisq_old = ml(x, y, sigma_y, params, model, args=args, \ 10 | limits=limits) 11 | 12 | accepted_params = [] 13 | 14 | for i in arange(nsteps): 15 | new_params = change_params(params, sigma_params, \ 16 | change_param=change_param) 17 | 18 | MLnew, chisq_new = ml(x, y, sigma_y, new_params, model, \ 19 | args=args, limits=limits) 20 | 21 | if chisq_new < chisq_old: 22 | accepted_params.append(params) 23 | params = new_params 24 | MLold = MLnew 25 | chisq_old = chisq_new 26 | else: 27 | if uniform(low=0., high=1., size=1)[0] <= \ 28 | exp(0.5*(chisq_old-chisq_new)): 29 | accepted_params.append(params) 30 | params = new_params 31 | MLold = MLnew 32 | chisq_old = chisq_new 33 | 34 | return array(accepted_params) 35 | -------------------------------------------------------------------------------- /pdspy/statistics/leastsq.py: -------------------------------------------------------------------------------- 1 | import scipy.optimize 2 | import numpy 3 | 4 | def leastsq(func, x0, args=(), limits=None, Dfun=None, full_output=0, \ 5 | col_deriv=0, ftol=1.49012e-08, xtol=1.49012e-08, gtol=0.0, maxfev=0, \ 6 | epsfcn=None, factor=100, diag=None): 7 | 8 | def within_bounds(p): 9 | if (type(limits) != type(None)): 10 | for i in range(len(p)): 11 | if ((limits[i]["limited"][0] == True) and \ 12 | (p[i] < limits[i]["limits"][0])): 13 | return False 14 | elif ((limits[i]["limited"][1] == True) and \ 15 | (p[i] > limits[i]["limits"][1])): 16 | return False 17 | 18 | return True 19 | else: 20 | return True 21 | 22 | def residuals(p): 23 | if within_bounds(p): 24 | return func(p, *args) 25 | else: 26 | return numpy.repeat(1e20, len(func(p, *args))) 27 | 28 | return scipy.optimize.leastsq(residuals, x0, args=(), \ 29 | full_output=full_output, col_deriv=col_deriv, ftol=ftol, \ 30 | xtol=xtol, gtol=gtol, maxfev=maxfev, epsfcn=epsfcn, factor=factor, \ 31 | diag=diag) 32 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_10cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e1 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_10cm.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_10um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e-3 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_10um.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_1cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e0 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_1cm.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_1mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e-1 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_1mm.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_1um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e-4 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_1um.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_1cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e0 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_1cm.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_1mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e-1 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_1mm.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_1um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e-4 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_1um.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_ricci_1um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | organics = Dust() 11 | organics.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | organics.set_density(2.24) 13 | organics.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | silicates = Dust() 16 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 17 | silicates.set_density(3.3) 18 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 19 | 20 | species = [silicates,organics,water_ice] 21 | mass_fraction = numpy.array([2.64e-3,3.53e-3,5.55e-3]) 22 | rho = numpy.array([silicates.rho,organics.rho,water_ice.rho]) 23 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.6) 27 | 28 | amin = 0.1e-4 29 | amax = 1.000e-4 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0) 34 | 35 | dust.write('ricci_1um.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_100um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | amorphous_carbon = Dust() 11 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 12 | #amorphous_carbon.set_density(2.24) 13 | amorphous_carbon.set_density(1.0) 14 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | silicates = Dust() 17 | silicates.set_optical_constants_from_henn("optical_constants/amorphous_silicates_extrapolated.txt") 18 | silicates.set_density(3.3) 19 | silicates.calculate_optical_constants_on_wavelength_grid(amorphous_carbon.lam) 20 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 21 | 22 | species = [silicates,amorphous_carbon] 23 | abundances = numpy.array([0.8,0.2]) 24 | 25 | dust = mix_dust(species, abundances, filling=0.75) 26 | 27 | amin = 0.05e-4 28 | amax = 1.000e-2 29 | pl = 3.5 30 | 31 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 32 | coat_volume_fraction=0.0, nang=1) 33 | 34 | dust.write('diana_100um.hdf5') 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_100um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e-2 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_100um.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_10cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e1 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_10cm.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_diana_wice_10um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | silicates = Dust() 11 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 12 | silicates.set_density(3.3) 13 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 14 | 15 | amorphous_carbon = Dust() 16 | amorphous_carbon.set_optical_constants_from_henn("optical_constants/amorphous_carbon_zubko1996_extrapolated.txt") 17 | #amorphous_carbon.set_density(2.24) 18 | amorphous_carbon.set_density(1.0) 19 | amorphous_carbon.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 20 | 21 | species = [silicates,amorphous_carbon,water_ice] 22 | abundances = numpy.array([0.8,0.2,0.5]) 23 | abundances = abundances / abundances.sum() 24 | print(abundances) 25 | 26 | dust = mix_dust(species, abundances, filling=0.75) 27 | 28 | amin = 0.05e-4 29 | amax = 1.000e-3 30 | pl = 3.5 31 | 32 | dust.calculate_size_distribution_opacity(amin, amax, pl, with_dhs=True, \ 33 | coat_volume_fraction=0.0, nang=1) 34 | 35 | dust.write('diana_wice_10um.hdf5') 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_new.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | troilite = Dust() 7 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 8 | troilite.set_density(4.83) 9 | 10 | organics = Dust() 11 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 12 | organics.set_density(1.5) 13 | 14 | water_ice = Dust() 15 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 16 | water_ice.set_density(0.92) 17 | 18 | silicates = Dust() 19 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 20 | silicates.set_density(3.3) 21 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 22 | 23 | species = [silicates,troilite,organics,water_ice] 24 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 25 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 26 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 27 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 28 | 29 | dust = mix_dust(species, abundances) 30 | 31 | # Create the dust generator class. 32 | 33 | dust_gen = DustGenerator(dust) 34 | 35 | dust_gen.write("pollack_new.hdf5") 36 | -------------------------------------------------------------------------------- /pdspy/mcmc/mcmc2d.py: -------------------------------------------------------------------------------- 1 | from numpy import array, arange, exp 2 | from numpy.random import uniform 3 | from .change_params import change_params 4 | from .ml2d import ml2d 5 | 6 | def mcmc2d(x, y, z, sigma_z, params, sigma_params, model, args={}, \ 7 | nsteps=1e5, change_param=None, limits=None): 8 | 9 | MLold, chisq_old = ml2d(x, y, z, sigma_z, params, model, args=args, \ 10 | limits=limits) 11 | 12 | accepted_params = [] 13 | 14 | for i in arange(nsteps): 15 | new_params = change_params(params, sigma_params, \ 16 | change_param=change_param) 17 | 18 | MLnew, chisq_new = ml2d(x, y, z, sigma_z, new_params, model, \ 19 | args=args, limits=limits) 20 | 21 | if chisq_new < chisq_old: 22 | accepted_params.append(params) 23 | params = new_params 24 | MLold = MLnew 25 | chisq_old = chisq_new 26 | else: 27 | if uniform(low=0., high=1., size=1)[0] <= \ 28 | exp(0.5*(chisq_old-chisq_new)): 29 | accepted_params.append(params) 30 | params = new_params 31 | MLold = MLnew 32 | chisq_old = chisq_new 33 | 34 | return array(accepted_params) 35 | -------------------------------------------------------------------------------- /pdspy/dust/data/optical_constants/extrapolate_as.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import matplotlib.pyplot as plt 4 | import pdspy.dust as dust 5 | import numpy 6 | 7 | d = dust.Dust() 8 | d.set_optical_constants_from_henn("amorphous_silicates.txt") 9 | 10 | new_lam = numpy.logspace(numpy.log10(d.lam[-1]),numpy.log10(d.lam[-1]*10),50) 11 | 12 | lognpoly = numpy.polyfit(numpy.log10(d.lam[d.lam > 1.0e-1]), \ 13 | numpy.log10(d.n[d.lam > 1.0e-1]), deg=1) 14 | 15 | logkpoly = numpy.polyfit(numpy.log10(d.lam[d.lam > 1.0e-1]), \ 16 | numpy.log10(d.k[d.lam > 1.0e-1]), deg=1) 17 | 18 | new_n = 10.**numpy.polyval(lognpoly, numpy.log10(new_lam)) 19 | new_k = 10.**numpy.polyval(logkpoly, numpy.log10(new_lam)) 20 | 21 | new_lam = numpy.concatenate((d.lam, new_lam[1:])) 22 | new_n = numpy.concatenate((d.n, new_n[1:])) 23 | new_k = numpy.concatenate((d.k, new_k[1:])) 24 | 25 | plt.loglog(d.lam, d.n, "b-") 26 | plt.loglog(new_lam, new_n, "b--") 27 | 28 | plt.loglog(d.lam, d.k, "r-") 29 | plt.loglog(new_lam, new_k, "r--") 30 | 31 | plt.show() 32 | 33 | f = open("amorphous_silicates_extrapolated.txt", "w") 34 | 35 | for i in range(len(new_lam)): 36 | f.write("{0:f} {1:f} {2:f}\n".format(new_lam[i]/1.0e-4, new_n[i], \ 37 | new_k[i])) 38 | 39 | f.close() 40 | -------------------------------------------------------------------------------- /pdspy/stars/Teff_from_SpT.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def Teff_from_SpT(SpT, relation="HH14"): 4 | 5 | # Set up the data. 6 | 7 | SpT_list = ['F5','F8','G0',"G2", "G5", "G8", "K0", "K2", "K5", "K7", "M0", \ 8 | "M1", "M2", "M3", "M4", "M5", "M6", "M7"] 9 | SpT_numbers = [float(test[0].replace("F","4").replace("G","3").\ 10 | replace("K","2").replace("M","1") + str(9-float(test[1:]))) \ 11 | for test in SpT_list] 12 | 13 | if relation == 'HH14': 14 | Teff_list = [6600, 6130, 5930, 5690, 5430, 5180, 4870, 4710, 4210, \ 15 | 4020, 3900, 3720, 3560, 3410, 3190, 2980, 2860, 2770] 16 | elif relation == 'PM13': 17 | Teff_list = [6420, 6100, 6050, 5870, 5500, 5210, 5030, 4760, 4140, \ 18 | 3970, 3770, 3630, 3490, 3360, 3160, 2880] 19 | 20 | SpT_list = SpT_list[0:-2] 21 | SpT_numbers = SpT_numbers[0:-2] 22 | 23 | # Now turn the provided spectral type into a number. 24 | 25 | SpT_number = float(SpT[0].replace("F","4").replace("G","3").\ 26 | replace("K","2").replace("M","1") + str(9-float(SpT[1:]))) 27 | 28 | # Finally, interpolate to a temperature. 29 | 30 | Teff = numpy.interp(SpT_number, SpT_numbers[::-1], Teff_list[::-1]) 31 | 32 | return Teff 33 | -------------------------------------------------------------------------------- /pdspy/dust/data/optical_constants/extrapolate_acz96.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import matplotlib.pyplot as plt 4 | import pdspy.dust as dust 5 | import numpy 6 | 7 | d = dust.Dust() 8 | d.set_optical_constants_from_henn("amorphous_carbon_zubko1996.txt") 9 | 10 | new_lam = numpy.logspace(numpy.log10(d.lam[-1]),numpy.log10(d.lam[-1]*10),50) 11 | 12 | lognpoly = numpy.polyfit(numpy.log10(d.lam[d.lam > 1.0e-1]), \ 13 | numpy.log10(d.n[d.lam > 1.0e-1]), deg=1) 14 | 15 | logkpoly = numpy.polyfit(numpy.log10(d.lam[d.lam > 1.0e-1]), \ 16 | numpy.log10(d.k[d.lam > 1.0e-1]), deg=1) 17 | 18 | new_n = 10.**numpy.polyval(lognpoly, numpy.log10(new_lam)) 19 | new_k = 10.**numpy.polyval(logkpoly, numpy.log10(new_lam)) 20 | 21 | new_lam = numpy.concatenate((d.lam, new_lam[1:])) 22 | new_n = numpy.concatenate((d.n, new_n[1:])) 23 | new_k = numpy.concatenate((d.k, new_k[1:])) 24 | 25 | plt.loglog(d.lam, d.n, "b-") 26 | plt.loglog(new_lam, new_n, "b--") 27 | 28 | plt.loglog(d.lam, d.k, "r-") 29 | plt.loglog(new_lam, new_k, "r--") 30 | 31 | plt.show() 32 | 33 | f = open("amorphous_carbon_zubko1996_extrapolated.txt", "w") 34 | 35 | for i in range(len(new_lam)): 36 | f.write("{0:f} {1:f} {2:f}\n".format(new_lam[i]/1.0e-4, new_n[i], \ 37 | new_k[i])) 38 | 39 | f.close() 40 | -------------------------------------------------------------------------------- /meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "pdspy" %} 2 | {% set version = "2.0.8" %} 3 | 4 | package: 5 | name: {{ name|lower }} 6 | version: {{ version }} 7 | 8 | source: 9 | path: ../pdspy 10 | 11 | build: 12 | number: 0 13 | script: "{{ PYTHON }} -m pip install . -vv --no-deps" 14 | skip: True # [win] 15 | 16 | requirements: 17 | build: 18 | - {{ compiler('c') }} 19 | - {{ compiler('fortran') }} 20 | host: 21 | - python 22 | - numpy >=1.16 23 | - cython 24 | - pip 25 | run: 26 | - python 27 | - {{ pin_compatible('numpy>=1.16') }} 28 | - scipy 29 | - matplotlib-base 30 | - astropy 31 | - h5py 32 | - mpi4py 33 | - emcee 34 | - dynesty 35 | - corner 36 | - galario 37 | - schwimmbad 38 | - scikit-learn 39 | 40 | test: 41 | imports: 42 | - pdspy 43 | commands: 44 | - test -d ${PREFIX} 45 | - test -f ${PREFIX}/bin/disk_model.py 46 | 47 | about: 48 | home: https://github.com/psheehan/pdspy 49 | license: GPL-3.0-only 50 | license_family: GPL 51 | license_file: LICENSE 52 | summary: 'Radiative transfer modeling of protoplanetary disks' 53 | 54 | extra: 55 | recipe-maintainers: 56 | - psheehan 57 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | # Create the dust generator class. 35 | 36 | dust_gen = DustGenerator(dust) 37 | 38 | dust_gen.write("draine.hdf5") 39 | -------------------------------------------------------------------------------- /pdspy/dust/data/optical_constants/PAH_qion.txt: -------------------------------------------------------------------------------- 1 | 3.5443603241341353, 0.19099992790714426 2 | 3.948168451911216, 0.2679335784490422 3 | 4.485109834840719, 0.3091207074712229 4 | 5.045342937893085, 0.3393151178718189 5 | 5.9608377769061995, 0.3777377261913345 6 | 6.973713698650947, 0.41066589767620687 7 | 8.118784695255469, 0.4449705620839641 8 | 9.35961822288943, 0.47515536010381365 9 | 10.42595729839427, 0.49710619277629586 10 | 11.332461379712768, 0.5163174969360537 11 | 12.936939791522699, 0.5451315694614663 12 | 15.061161573520614, 0.5780616634224882 13 | 17.109443206462625, 0.6068776584240503 14 | 19.3411986305669, 0.6384447167952323 15 | 22.40682877100203, 0.6727513036791388 16 | 25.70499980895651, 0.7084363059620793 17 | 31.123162362342114, 0.7537221541345254 18 | 36.4116642382865, 0.7935231778530748 19 | 40.5600364749332, 0.8195977218657632 20 | 45.6263728481819, 0.8470429913728883 21 | 50.08227535091069, 0.8717487323672891 22 | 54.17045171926874, 0.8675942614086944 23 | 57.73659415947844, 0.8661946987720184 24 | 62.14406622756642, 0.8661658616297792 25 | 66.23512666637937, 0.8702645807800448 26 | 70.94257957284145, 0.8757359479008964 27 | 77.48991458126022, 0.8853233364573572 28 | 82.5912210002754, 0.8949203373945642 29 | 88.8960352160657, 0.9058880638262082 30 | 92.00081914295062, 0.9347405858746066 31 | 96.15254798160309, 0.9677129743109125 32 | 99.51077295504223, 0.9979400668060463 33 | -------------------------------------------------------------------------------- /pdspy/imaging/update_catalog.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import astropy 3 | import astropy.coordinates 4 | 5 | #def update_catalog(catalog, *lists, tol=0.3, column_names=['1']): 6 | def update_catalog(catalog, tol=0.3, column_names=['1'], *lists): 7 | 8 | coord_catalog = astropy.coordinates.SkyCoord(catalog["ra"].tolist(), \ 9 | catalog["dec"].tolist(), frame='icrs') 10 | catalog["id"] = numpy.arange(len(catalog)) 11 | 12 | for i, list1 in enumerate(lists): 13 | coord1 = astropy.coordinates.SkyCoord(list1["ra"].tolist(), \ 14 | list1['dec'].tolist(), frame='icrs') 15 | 16 | idx, d2d, d3d = astropy.coordinates.match_coordinates_sky(coord1, \ 17 | coord_catalog) 18 | 19 | ids, counts = numpy.unique(idx[d2d.arcsec < tol], return_counts=True) 20 | for j in range(len(ids)): 21 | if counts[j] > 1: 22 | min_d2d = min(d2d[idx == ids[j]].arcsec) 23 | d2d[(d2d.arcsec != min_d2d) & (idx == ids[j])] = \ 24 | astropy.coordinates.Angle("0d00m{0:f}s".format(2*tol)) 25 | 26 | for j, k in enumerate(idx): 27 | if d2d.arcsec[j] < tol: 28 | for key in list1.colnames: 29 | catalog[key+"_{0}".format(column_names[i])][k] = \ 30 | list1[key][j] 31 | 32 | del catalog["id"] 33 | 34 | return catalog 35 | -------------------------------------------------------------------------------- /pdspy/interferometry/readvis.py: -------------------------------------------------------------------------------- 1 | from .readuvfits import readuvfits 2 | from .libinterferometry import Visibilities 3 | from glob import glob 4 | import numpy 5 | 6 | def readvis(filename, fmt="casa"): 7 | 8 | filenames = numpy.array(glob(filename+"/*.uv.fits")) 9 | 10 | for i in range(filenames.size): 11 | if i == 0: 12 | vis = readuvfits(filenames[i],fmt=fmt) 13 | 14 | vis.u /= vis.freq.mean() 15 | vis.v /= vis.freq.mean() 16 | else: 17 | new = readuvfits(filenames[i],fmt=fmt) 18 | 19 | vis.freq = numpy.concatenate((vis.freq,new.freq)) 20 | vis.real = numpy.concatenate((vis.real,new.real),axis=1) 21 | vis.imag = numpy.concatenate((vis.imag,new.imag),axis=1) 22 | vis.amp = numpy.concatenate((vis.amp,new.amp),axis=1) 23 | vis.weights = numpy.concatenate((vis.weights,new.weights),axis=1) 24 | 25 | vis.real = vis.real[:,numpy.argsort(vis.freq)][:,::-1] 26 | vis.imag = vis.imag[:,numpy.argsort(vis.freq)][:,::-1] 27 | vis.amp = vis.amp[:,numpy.argsort(vis.freq)][:,::-1] 28 | vis.weights = vis.weights[:,numpy.argsort(vis.freq)][:,::-1] 29 | vis.freq = vis.freq[numpy.argsort(vis.freq)][::-1] 30 | 31 | vis.u *= vis.freq.mean() 32 | vis.v *= vis.freq.mean() 33 | 34 | return Visibilities(vis.u, vis.v, vis.freq, vis.real, vis.imag, \ 35 | vis.weights, baseline=vis.baseline) 36 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_1cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 1.000e0 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_1cm.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_100um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 1.000e-2 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_100um.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_10um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 1.000e-3 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_10um.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_1mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 1.000e-1 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_1mm.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_1um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 1.000e-4 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_1um.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_2mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 2.000e-1 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_2mm.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_draine_3mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | water_ice = Dust() 7 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 8 | water_ice.set_density(0.92) 9 | 10 | #1/3 11 | graphite_parallel = Dust() 12 | graphite_parallel.set_optical_constants_from_draine("optical_constants/graphite_parallel_0.01.txt") 13 | graphite_parallel.set_density(2.24) 14 | graphite_parallel.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 15 | 16 | #2/3 17 | graphite_perpendicular = Dust() 18 | graphite_perpendicular.set_optical_constants_from_draine("optical_constants/graphite_perpendicular_0.01.txt") 19 | graphite_perpendicular.set_density(2.24) 20 | graphite_perpendicular.calculate_optical_constants_on_wavelength_grid(\ 21 | water_ice.lam) 22 | 23 | silicates = Dust() 24 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 25 | silicates.set_density(3.3) 26 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 27 | 28 | species = [silicates,graphite_parallel,graphite_perpendicular] 29 | abundances = numpy.array([0.65,0.35*1./3,0.35*2./3]) 30 | print(abundances) 31 | 32 | dust = mix_dust(species, abundances) 33 | 34 | amin = 0.005e-4 35 | amax = 3.000e-1 36 | pl = 3.5 37 | 38 | dust.calculate_size_distribution_opacity(amin, amax, pl, nang=1, \ 39 | coat_volume_fraction=0.0) 40 | 41 | dust.write('draine_3mm.hdf5') 42 | -------------------------------------------------------------------------------- /pdspy/interferometry/concatenate.py: -------------------------------------------------------------------------------- 1 | from .libinterferometry import Visibilities 2 | import numpy 3 | 4 | def concatenate(visibilities): 5 | 6 | for i, vis in enumerate(visibilities): 7 | if i == 0: 8 | u = vis.u.copy() 9 | v = vis.v.copy() 10 | real = vis.real.copy() 11 | imag = vis.imag.copy() 12 | amp = vis.amp.copy() 13 | weights = vis.weights.copy() 14 | freq = vis.freq.copy() 15 | if type(vis.baseline) != type(None): 16 | baseline = vis.baseline.copy() 17 | incl_baselines = True 18 | else: 19 | incl_baselines = False 20 | else: 21 | u = numpy.concatenate((u, vis.u.copy())) 22 | v = numpy.concatenate((v, vis.v.copy())) 23 | real = numpy.concatenate((real, vis.real.copy())) 24 | imag = numpy.concatenate((imag, vis.imag.copy())) 25 | amp = numpy.concatenate((amp, vis.amp.copy())) 26 | weights = numpy.concatenate((weights, vis.weights.copy())) 27 | if incl_baselines: 28 | if type(vis.baseline) != type(None): 29 | baseline = numpy.concatenate((baseline,vis.baseline.copy())) 30 | else: 31 | incl_baselines = False 32 | 33 | if incl_baselines: 34 | return Visibilities(u, v, freq, real, imag, weights, baseline=baseline) 35 | else: 36 | return Visibilities(u, v, freq, real, imag, weights) 37 | -------------------------------------------------------------------------------- /pdspy/dust/run_opacity_tool.py: -------------------------------------------------------------------------------- 1 | from .Dust import Dust 2 | import os 3 | import numpy 4 | 5 | def run_opacity_tool(amin=0.05, amax=3000., apow=3.5, na=50, fcarbon=0.13, \ 6 | Vcarbon=0.15, porosity=0.25, fmax=0.8, lmin=0.05, lmax=5000, nlam=300, \ 7 | filename=None, verbose=False): 8 | 9 | original_dir = os.environ["PWD"] 10 | os.mkdir("/tmp/temp_opacitytool") 11 | os.chdir("/tmp/temp_opacitytool") 12 | 13 | try: 14 | command = "OpacityTool -amin {0:f} -amax {1:f} -apow {2:f} -na {3:d} "\ 15 | "-fcarbon {4:f} -Vcarbon {5:f} -porosity {6:f} -fmax {7:f} "\ 16 | "-lmin {8:f} -lmax {9:f} -nlam {10:d}".format(amin, amax, apow,\ 17 | na, fcarbon, Vcarbon, porosity, fmax, lmin, lmax, nlam) 18 | 19 | if not verbose: 20 | os.system(command + " > log.txt") 21 | else: 22 | os.system(command) 23 | 24 | data = numpy.loadtxt("particle.dat") 25 | 26 | d = Dust() 27 | d.set_properties(data[:,0], data[:,1], data[:,2]) 28 | 29 | if not verbose: 30 | os.system("rm particle.dat log.txt") 31 | else: 32 | os.system("rm particle.dat") 33 | os.chdir(original_dir) 34 | os.rmdir("/tmp/temp_opacitytool") 35 | 36 | if filename != None: 37 | d.write(filename) 38 | else: 39 | return d 40 | except: 41 | os.system("rm particle.dat log.txt") 42 | os.chdir(original_dir) 43 | os.rmdir("/tmp/temp_opacitytool") 44 | -------------------------------------------------------------------------------- /pdspy/table/table.py: -------------------------------------------------------------------------------- 1 | from .column import MaskedColumn 2 | import astropy.table 3 | import astropy.utils 4 | import h5py 5 | import os 6 | 7 | class Table(astropy.table.Table): 8 | 9 | def __init__(self, *args, **kwargs): 10 | super(Table, self).__init__(*args, **kwargs) 11 | 12 | self.MaskedColumn = MaskedColumn 13 | if self._masked: 14 | self._column_class = MaskedColumn 15 | 16 | @staticmethod 17 | def read_hdf5(*args, **kwargs): 18 | try: 19 | self = Table(astropy.table.Table.read(*args, path="table", \ 20 | **kwargs), masked=True) 21 | self.mask = astropy.table.Table.read(*args, path="mask", **kwargs) 22 | except OSError: 23 | self = Table(astropy.table.Table.read(*args, path="table",**kwargs)) 24 | 25 | for col in self.colnames: 26 | if 'S' in self[col].dtype.str: 27 | self.replace_column(col, self[col].astype(str)) 28 | 29 | return self 30 | 31 | def write_hdf5(self, *args, **kwargs): 32 | for col in self.colnames: 33 | if 'U' in self[col].dtype.str: 34 | self.replace_column(col, self[col].astype(bytes)) 35 | 36 | self.write(*args, path='table', overwrite=True, **kwargs) 37 | 38 | for col in self.colnames: 39 | if 'S' in self[col].dtype.str: 40 | self.replace_column(col, self[col].astype(str)) 41 | 42 | if self.masked: 43 | self.mask.write(*args, path='mask', append=True, **kwargs) 44 | 45 | return 46 | -------------------------------------------------------------------------------- /pdspy/imaging/extract_pv_diagram.py: -------------------------------------------------------------------------------- 1 | from ..constants.astronomy import arcsec 2 | from ..constants.physics import c 3 | from . import Image 4 | try: 5 | import pvextractor 6 | except: 7 | pass 8 | import numpy 9 | 10 | def extract_pv_diagram(image, xy=(0.,0.), pa=0., length=100, width=1): 11 | 12 | ny, nx, nfreq, npol = image.image.shape 13 | 14 | # Create a data cube that is appropriately shaped. 15 | 16 | cube = numpy.empty((nfreq, ny, nx)) 17 | for i in range(nfreq): 18 | cube[i,:,:] = image.image[:,:,i,0] 19 | 20 | # Create the Path object. 21 | 22 | x0, y0 = xy 23 | 24 | line = [(x0-length/2*numpy.sin(pa),y0-length/2*numpy.cos(pa)), \ 25 | (x0+length/2*numpy.sin(pa), y0+length/2*numpy.cos(pa))] 26 | 27 | path = pvextractor.Path(line, width=width) 28 | 29 | # Extract the PV diagram along the Path 30 | 31 | pv = pvextractor.extract_pv_slice(cube, path) 32 | 33 | # Convert back to my Image class. 34 | 35 | pvdiagram = numpy.empty((1, pv.data.shape[1], pv.data.shape[0], 1)) 36 | for i in range(pv.data.shape[0]): 37 | pvdiagram[0,:,i,0] = pv.data[i,:] 38 | 39 | # Get the velocity. 40 | 41 | velocity = c * (image.header["RESTFRQ"] - image.freq) / \ 42 | image.header["RESTFRQ"] 43 | 44 | # Get the x coordinates. 45 | 46 | x0 = 0. 47 | dx = image.header["CDELT2"] * numpy.pi/180 / arcsec 48 | nx0 = int(pv.data.shape[1] / 2) + 1 49 | 50 | x = (numpy.arange(pv.data.shape[1]) - (nx0-1))*dx + x0 51 | 52 | return Image(pvdiagram, x=x, velocity=velocity, freq=image.freq) 53 | -------------------------------------------------------------------------------- /pdspy/modeling/Star.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import h5py 3 | from ..constants.physics import sigma 4 | from ..constants.astronomy import L_sun, R_sun 5 | 6 | class Star: 7 | def __init__(self, mass=0.5, luminosity=1.0, temperature=4000., x=0.0, \ 8 | y=0.0, z=0.0): 9 | self.mass = mass 10 | self.luminosity = luminosity 11 | self.temperature = temperature 12 | self.radius = (luminosity*L_sun/ \ 13 | (4*numpy.pi*sigma*temperature**4))**(1./2)/R_sun 14 | self.x = x 15 | self.y = y 16 | self.z = z 17 | 18 | def read(self, filename=None, usefile=None): 19 | if (usefile == None): 20 | f = h5py.File(filename, "r") 21 | else: 22 | f = usefile 23 | 24 | self.mass = f['mass'][()] 25 | self.luminosity = f['luminosity'][()] 26 | self.temperature = f['temperature'][()] 27 | self.radius = f['radius'][()] 28 | self.x = f['x'][()] 29 | self.y = f['y'][()] 30 | self.z = f['z'][()] 31 | 32 | if (usefile == None): 33 | f.close() 34 | 35 | def write(self, filename=None, usefile=None): 36 | if (usefile == None): 37 | f = h5py.File(filename, "w") 38 | else: 39 | f = usefile 40 | 41 | f['mass'] = self.mass 42 | f['luminosity'] = self.luminosity 43 | f['temperature'] = self.temperature 44 | f['radius'] = self.radius 45 | 46 | f['x'] = self.x 47 | f['y'] = self.y 48 | f['z'] = self.z 49 | 50 | if (usefile == None): 51 | f.close() 52 | -------------------------------------------------------------------------------- /pdspy/imaging/readpvfits.py: -------------------------------------------------------------------------------- 1 | from ..constants.astronomy import arcsec 2 | from ..constants.physics import c 3 | from .libimaging import Image 4 | from astropy.utils.exceptions import AstropyWarning 5 | import astropy.io.fits as fits 6 | import astropy.wcs as wcs 7 | import warnings 8 | import numpy 9 | 10 | def readpvfits(filename): 11 | 12 | # Open the fits file. 13 | 14 | data = fits.open(filename) 15 | 16 | # Figure out the dimensions of each axis and create an array to put the data 17 | # into, and put the data into that array. 18 | 19 | npol, nv, nx = data[0].data.shape 20 | 21 | image = numpy.zeros((1,nx,nv,npol)) 22 | 23 | for i in range(npol): 24 | for j in range(nv): 25 | image[0,:,j,i] = data[0].data[i,j,:] 26 | 27 | # Read in the x and y coordinate information, including the WCS info if it 28 | # is available. 29 | 30 | header = data[0].header 31 | 32 | #x, y = numpy.meshgrid(numpy.linspace(0,nx-1,nx), numpy.linspace(0,ny-1,ny)) 33 | x, y = None, None 34 | 35 | x0 = data[0].header["CRVAL1"] 36 | dx = data[0].header["CDELT1"] 37 | nx0 = data[0].header["CRPIX1"] 38 | 39 | x = (numpy.arange(nx) - (nx0-1))*dx + x0 40 | 41 | nu0 = data[0].header["CRVAL2"] 42 | dnu = data[0].header["CDELT2"] 43 | n0 = data[0].header["CRPIX2"] 44 | freq = (numpy.arange(nv)-(n0-1))*dnu + nu0 45 | restfreq = data[0].header["RESTFRQ"] 46 | 47 | velocity = c * (restfreq - freq) / restfreq 48 | 49 | data.close() 50 | 51 | return Image(image, x=x, y=y, header=header, wcs=None, velocity=velocity, \ 52 | freq=freq) 53 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_kabs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import Dust, DustGenerator 4 | import numpy 5 | import matplotlib 6 | import matplotlib.pyplot as plt 7 | 8 | # Maximum dust grain sizes. 9 | 10 | a_max = numpy.logspace(0.,5.,1000) 11 | 12 | # Grain size distribution power-law 13 | 14 | p = numpy.linspace(2.5, 4.5, 10) 15 | 16 | # Read in the dust generator class. 17 | 18 | dust_gen = DustGenerator("diana_wice.hdf5") 19 | 20 | # Change a few of the parameters to make the plot look nice. 21 | 22 | matplotlib.rcParams["font.family"] = "serif" 23 | matplotlib.rcParams["font.size"] = 14 24 | matplotlib.rcParams["text.usetex"] = "True" 25 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 26 | matplotlib.rcParams["legend.fontsize"] = 14 27 | 28 | # Start the plotting. 29 | 30 | fig, ax = plt.subplots(nrows=1, ncols=1) 31 | 32 | # Loop through each of the values for p. 33 | 34 | for i in range(p.size): 35 | # Set up a list to put beta in. 36 | 37 | kabs = [] 38 | 39 | # Loop through and calculate beta. 40 | 41 | for j in range(a_max.size): 42 | # Get the dust generator properties. 43 | 44 | dust = dust_gen(a_max[j] / 1.0e4, p[i]) 45 | 46 | # Add to the array. 47 | 48 | kabs.append(dust.kabs[103]) 49 | 50 | # Plot beta. 51 | 52 | ax.semilogx(a_max, kabs, "-", label="$p = {0:3.1f}$".format(p[i])) 53 | 54 | # Add a legend. 55 | 56 | plt.legend(loc="lower left") 57 | 58 | # Adjust the figure. 59 | 60 | ax.set_xlabel("$a_{max}$ [$\mu$m]") 61 | ax.set_ylabel(r"$\kappa_{abs}$") 62 | 63 | # Save the figure. 64 | 65 | fig.savefig("kabs_plot.pdf") 66 | -------------------------------------------------------------------------------- /pdspy/dust/data/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import matplotlib.pyplot as plt 5 | import numpy 6 | import time 7 | 8 | troilite = Dust() 9 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 10 | troilite.set_density(4.83) 11 | 12 | organics = Dust() 13 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 14 | organics.set_density(1.5) 15 | 16 | water_ice = Dust() 17 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 18 | water_ice.set_density(0.92) 19 | 20 | silicates = Dust() 21 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 22 | silicates.set_density(3.3) 23 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 24 | 25 | species = [silicates,troilite,organics,water_ice] 26 | mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 27 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 28 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 29 | 30 | dust1 = mix_dust(species, abundances) 31 | dust2 = mix_dust(species, abundances) 32 | 33 | amin = 0.005e-4 34 | amax = 1.000e0 35 | pl = 3.5 36 | 37 | starttime = time.time() 38 | dust1.calculate_size_distribution_opacity(amin, amax, pl, \ 39 | coat_volume_fraction=0.0, nang=2) 40 | endtime = time.time() 41 | print(endtime - starttime) 42 | 43 | pl = 3.0 44 | 45 | starttime = time.time() 46 | dust2.calculate_size_distribution_opacity(amin, amax, pl, \ 47 | coat_volume_fraction=0.0, nang=2) 48 | endtime = time.time() 49 | print(endtime - starttime) 50 | 51 | plt.loglog(dust1.lam, dust1.ksca, 'b-') 52 | plt.loglog(dust2.lam, dust2.ksca, 'r-') 53 | plt.show() 54 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_1um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | species = [silicates,troilite,organics,water_ice] 36 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 37 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 38 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 39 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 40 | 41 | dust = mix_dust(species, abundances) 42 | 43 | amin = 0.005e-4 44 | amax = 1.000e-4 45 | pl = 3.5 46 | 47 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 48 | coat_volume_fraction=0.0) 49 | 50 | dust.write('pollack_1um.hdf5') 51 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | # Create the dust generator class. 48 | 49 | dust_gen = DustGenerator(dust) 50 | 51 | dust_gen.write("pollack.hdf5") 52 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_beta.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import Dust, DustGenerator 4 | import numpy 5 | import matplotlib 6 | import matplotlib.pyplot as plt 7 | 8 | # Maximum dust grain sizes. 9 | 10 | a_max = numpy.logspace(0.,5.,1000) 11 | 12 | # Grain size distribution power-law 13 | 14 | p = numpy.linspace(2.5, 4.5, 10) 15 | 16 | # Read in the dust generator class. 17 | 18 | dust_gen = DustGenerator("diana_wice.hdf5") 19 | 20 | # Change a few of the parameters to make the plot look nice. 21 | 22 | matplotlib.rcParams["font.family"] = "serif" 23 | matplotlib.rcParams["font.size"] = 14 24 | matplotlib.rcParams["text.usetex"] = "True" 25 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 26 | matplotlib.rcParams["legend.fontsize"] = 14 27 | 28 | # Start the plotting. 29 | 30 | fig, ax = plt.subplots(nrows=1, ncols=1) 31 | 32 | # Loop through each of the values for p. 33 | 34 | for i in range(p.size): 35 | # Set up a list to put beta in. 36 | 37 | beta = [] 38 | 39 | # Loop through and calculate beta. 40 | 41 | for j in range(a_max.size): 42 | # Get the dust generator properties. 43 | 44 | dust = dust_gen(a_max[j] / 1.0e4, p[i]) 45 | 46 | # Calculate beta. 47 | 48 | tmp_beta = -(numpy.log10(dust.kabs[108])-numpy.log10(dust.kabs[102]))/\ 49 | (numpy.log10(dust.lam[108]) - numpy.log10(dust.lam[102])) 50 | 51 | # Add to the array. 52 | 53 | beta.append(tmp_beta) 54 | 55 | # Plot beta. 56 | 57 | ax.semilogx(a_max, beta, "-", label="$p = {0:3.1f}$".format(p[i])) 58 | 59 | # Add a legend. 60 | 61 | plt.legend(loc="lower left") 62 | 63 | # Adjust the figure. 64 | 65 | ax.set_xlabel("$a_{max}$ [$\mu$m]") 66 | ax.set_ylabel(r"$\beta$") 67 | 68 | # Save the figure. 69 | 70 | fig.savefig("beta_plot.pdf") 71 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_1cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | amin = 0.005e-4 48 | amax = 1.000e0 49 | pl = 3.5 50 | 51 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 52 | coat_volume_fraction=0.0) 53 | 54 | dust.write('pollack_1cm.hdf5') 55 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_1mm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | amin = 0.005e-4 48 | amax = 1.000e-1 49 | pl = 3.5 50 | 51 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 52 | coat_volume_fraction=0.0) 53 | 54 | dust.write('pollack_1mm.hdf5') 55 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_100um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | amin = 0.005e-4 48 | amax = 1.000e-2 49 | pl = 3.5 50 | 51 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 52 | coat_volume_fraction=0.0) 53 | 54 | dust.write('pollack_100um.hdf5') 55 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_10cm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | amin = 0.005e-4 48 | amax = 1.000e1 49 | pl = 3.5 50 | 51 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 52 | coat_volume_fraction=0.0) 53 | 54 | dust.write('pollack_10cm.hdf5') 55 | -------------------------------------------------------------------------------- /pdspy/dust/data/make_pollack_10um.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import * 4 | import numpy 5 | 6 | iron = Dust() 7 | iron.set_optical_constants_from_henn("optical_constants/iron.txt") 8 | iron.set_density(7.87) 9 | 10 | olivine = Dust() 11 | olivine.set_optical_constants_from_henn("optical_constants/olivine.txt") 12 | olivine.set_density(3.49) 13 | 14 | orthopyroxene = Dust() 15 | orthopyroxene.set_optical_constants_from_henn("optical_constants/orthopyroxene.txt") 16 | orthopyroxene.set_density(3.4) 17 | 18 | troilite = Dust() 19 | troilite.set_optical_constants_from_henn("optical_constants/troilite.txt") 20 | troilite.set_density(4.83) 21 | 22 | organics = Dust() 23 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 24 | organics.set_density(1.5) 25 | 26 | water_ice = Dust() 27 | water_ice.set_optical_constants_from_henn("optical_constants/water_ice.txt") 28 | water_ice.set_density(0.92) 29 | 30 | silicates = Dust() 31 | silicates.set_optical_constants_from_draine("optical_constants/astronomical_silicates.txt") 32 | silicates.set_density(3.3) 33 | silicates.calculate_optical_constants_on_wavelength_grid(water_ice.lam) 34 | 35 | organics = Dust() 36 | organics.set_optical_constants_from_henn("optical_constants/organics.txt") 37 | organics.set_density(1.5) 38 | 39 | species = [silicates,troilite,organics,water_ice] 40 | #mass_fraction = numpy.array([6.4e-3,7.68e-4,2.13e-3,1.4e-3]) 41 | mass_fraction = numpy.array([3.41e-3,7.68e-4,4.13e-3,5.55e-3]) 42 | rho = numpy.array([silicates.rho,troilite.rho,organics.rho,water_ice.rho]) 43 | abundances = (mass_fraction/rho)/(mass_fraction/rho).sum() 44 | 45 | dust = mix_dust(species, abundances) 46 | 47 | amin = 0.005e-4 48 | amax = 1.000e-3 49 | pl = 3.5 50 | 51 | dust.calculate_size_distribution_opacity(amin, amax, pl, \ 52 | coat_volume_fraction=0.0) 53 | 54 | dust.write('pollack_10um.hdf5') 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/pdspy/badges/installer/conda.svg)](https://conda.anaconda.org/conda-forge) 2 | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/pdspy/badges/version.svg)](https://anaconda.org/conda-forge/pdspy) 3 | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/pdspy/badges/platforms.svg)](https://anaconda.org/conda-forge/pdspy) 4 | [![Documentation Status](https://readthedocs.org/projects/pdspy/badge/?version=latest)](https://pdspy.readthedocs.io/en/latest/?badge=latest) 5 | 6 | # pdspy: A MCMC Tool for Continuum and Spectral Line Radiative Transfer Modeling 7 | 8 | Welcome to the GitHub page for pdspy! This code is meant to fit Monte Carlo Radiative Transfer models for protostellar/protoplanetary disks to ALMA continuum and spectral line datasets using Markov Chain Monte Carlo fitting. 9 | 10 | Further capabilities (e.g. fitting spectral line data with a radiative equilibrium calculation) are being developed. If you are interested in new features, do let me know and I would be happy to either add them myself, or to work with you to add them. The documentation is currently included below, but will be ported to a more extensive, better laid out format soon. 11 | 12 | If you are interested in making use of the code, please check out the [documentation](http://pdspy.readthedocs.io). 13 | 14 | For more extensive details on what the code does, please see these papers: 15 | 16 | + [Disk Masses for Embedded Class I Protostars in the Taurus Molecular Cloud](https://ui.adsabs.harvard.edu/abs/2017ApJ...851...45S/abstract) 17 | + [High-precision Dynamical Masses of Pre-main-sequence Stars with ALMA and Gaia](https://ui.adsabs.harvard.edu/abs/2019ApJ...874..136S/abstract) 18 | 19 | If you have any questions about using the code (or this documentation), requests for features, or suggestions for improvement, please don't hesitate to send me an e-mail. 20 | -------------------------------------------------------------------------------- /pdspy/modeling/check_parameters.py: -------------------------------------------------------------------------------- 1 | from .base_parameters import base_parameters 2 | 3 | def check_parameters(parameters, nvis=3): 4 | # Make sure the code is backwards compatible to a time when only a single 5 | # gas file was being supplied. 6 | 7 | if "gas_file" in parameters: 8 | parameters["gas_file1"] = parameters["gas_file"] 9 | parameters["logabundance1"] = parameters["logabundance"] 10 | 11 | # Make sure that the envelope dust is the same as the disk dust, if it is 12 | # not specified. 13 | 14 | if "dust_file" in parameters and not "envelope_dust" in parameters: 15 | parameters["envelope_dust"] = parameters["dust_file"] 16 | 17 | # Now loop through and make sure all of the parameters are present and 18 | # accounted for. 19 | 20 | for key in base_parameters: 21 | if not key in parameters: 22 | parameters[key] = base_parameters[key] 23 | 24 | # Make sure there are enough instances of freezeout for all of the gas 25 | # files provided. 26 | 27 | index = 1 28 | while index > 0: 29 | if "gas_file"+str(index) in parameters: 30 | if not "logabundance{0:d}".format(index) in parameters: 31 | parameters["logabundance"+str(index)] = \ 32 | base_parameters["logabundance1"] 33 | if not "freezeout{0:d}".format(index) in parameters: 34 | parameters["freezeout"+str(index)] = \ 35 | base_parameters["freezeout1"] 36 | 37 | index += 1 38 | else: 39 | index = -1 40 | 41 | # Make sure there are enough instances of flux_unc for all of the 42 | # data files that are being fit. 43 | 44 | for i in range(nvis): 45 | if not "flux_unc{0:d}".format(i+1) in parameters: 46 | parameters["flux_unc{0:d}".format(i+1)] = parameters["flux_unc1"] 47 | 48 | return parameters 49 | -------------------------------------------------------------------------------- /pdspy/dust/mix_dust.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import scipy 3 | from .Dust import Dust 4 | from ..constants.math import pi 5 | 6 | def mix_dust(dust, abundance, medium=None, rule="Bruggeman", filling=1.): 7 | 8 | if rule == "Bruggeman": 9 | meff = numpy.zeros(dust[0].lam.size,dtype=complex) 10 | rho = 0.0 11 | 12 | for i in range(dust[0].lam.size): 13 | temp = scipy.optimize.fsolve(bruggeman,numpy.array([1.0,0.0]),\ 14 | args=(dust,abundance,i, filling)) 15 | meff[i] = temp[0]+1j*temp[1] 16 | 17 | for i in range(len(dust)): 18 | rho += dust[i].rho*abundance[i] 19 | 20 | rho *= filling 21 | 22 | elif rule == "MaxGarn": 23 | numerator = 0.0+1j*0.0 24 | denominator = 0.0+1j*0.0 25 | rho = 0.0 26 | 27 | for i in range(len(dust)): 28 | gamma = 3. / (dust[i].m**2 + 2) 29 | 30 | numerator += abundance[i] * gamma * dust[i].m**2 31 | denominator += abundance[i] * gamma 32 | 33 | rho += dust[i].rho*abundance[i] 34 | 35 | mmix = numpy.sqrt(numerator / denominator) 36 | 37 | F = (mmix**2 - 1.) / (mmix**2 + 2.) 38 | 39 | meff = numpy.sqrt((1. + 2.*filling*F) / (1. - filling*F)) 40 | 41 | rho *= filling 42 | 43 | new = Dust() 44 | new.set_density(rho) 45 | new.set_optical_constants(dust[0].lam, meff.real, meff.imag) 46 | 47 | return new 48 | 49 | def bruggeman(meff, dust, abundance, index, filling): 50 | 51 | m_eff = meff[0]+1j*meff[1] 52 | tot = 0+0j 53 | 54 | for j in range(len(dust)): 55 | tot += filling * abundance[j]*(dust[j].m[index]**2-m_eff**2)/ \ 56 | (dust[j].m[index]**2+2*m_eff**2) 57 | 58 | # Add in the void. 59 | 60 | tot += (1 - filling) * (1. - m_eff**2) / (1. + 2*m_eff**2) 61 | 62 | return numpy.array([tot.real,tot.imag]) 63 | -------------------------------------------------------------------------------- /tests/test_dartois_disk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pdspy.modeling as modeling 4 | import pdspy.interferometry as uv 5 | import pdspy.dust as dust 6 | import pdspy.gas as gas 7 | 8 | import matplotlib.pyplot as plt 9 | 10 | import numpy 11 | 12 | # Set up a radiative transfer model: 13 | 14 | m = modeling.YSOModel() 15 | 16 | # Add a grid. Spherical typically makes the most sense. 17 | 18 | nr, ntheta, nphi = 100, 100, 2 19 | rmin, rmax = 0.1, 300 20 | 21 | m.set_spherical_grid(rmin, rmax, nr, ntheta, nphi, code="radmc3d") 22 | 23 | # Add a star to the model. 24 | 25 | m.add_star(mass=0.5, luminosity=1., temperature=4000.) 26 | 27 | #Set up dust properties for the disk. 28 | 29 | dust_gen = dust.DustGenerator(dust.__path__[0]+"/data/diana_wice.hdf5") 30 | 31 | a_max = 100 # microns 32 | p = 3.5 33 | 34 | d = dust_gen(a_max / 1e4, p) # dust_gen wants units of cm 35 | 36 | # Also set up the gas properties of the disk. 37 | 38 | g = gas.Gas() 39 | g.set_properties_from_lambda(gas.__path__[0]+"/data/co.dat") 40 | 41 | gases = [g] 42 | 43 | # Add a disk to the model. 44 | 45 | m.add_dartois_pringle_disk(mass=0.0003, rmin=0.1, rmax=50., plrho=1., h0=5., \ 46 | plh=1., dust=d, tmid0=20., tatm0=100, zq0=0.1, pltgas=0.25, delta=1, \ 47 | gas=gases, abundance=[1.0e-4], freezeout=[20.], aturb=0.1) 48 | 49 | # Finally, we need to set the wavelength grid. 50 | 51 | m.grid.set_wavelength_grid(0.1, 1.0e5, 500, log=True) 52 | 53 | # Now generate a plot of the number density. 54 | 55 | r, theta = numpy.meshgrid(m.grid.r, m.grid.theta[::-1], indexing='ij') 56 | 57 | with numpy.errstate(divide="ignore"): 58 | z = numpy.log10(m.grid.number_density[0][:,:,0]) 59 | vmin = numpy.nanmax(z) - 10. 60 | vmax = numpy.nanmax(z) 61 | 62 | fig, ax = plt.subplots(nrows=1, ncols=1, subplot_kw={'projection':'polar'}) 63 | 64 | ax.pcolor(theta, r, z, shading="auto", vmin=vmin, vmax=vmax) 65 | 66 | ax.set_thetalim(0.,numpy.pi/2) 67 | 68 | plt.show() 69 | 70 | -------------------------------------------------------------------------------- /pdspy/spectroscopy/spectroscopy.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import astropy 3 | import h5py 4 | from ..constants.physics import c 5 | 6 | class Spectrum: 7 | 8 | def __init__(self, wave=None, flux=None, unc=None): 9 | if (type(wave) != type(None)): 10 | self.wave = wave 11 | self.freq = c / wave / 1.0e-4 12 | self.flux = flux 13 | if type(unc) == type(None): 14 | unc = numpy.zeros(wave.size) 15 | self.unc = unc 16 | else: 17 | self.unc = unc 18 | 19 | def asFITS(self): 20 | hdulist = astropy.io.fits.HDUList([]) 21 | 22 | hdu = pyfits.PrimaryHDU(numpy.concatenate(( \ 23 | self.wave.reshape((1,self.wave.size)), \ 24 | self.flux.reshape((1,self.wave.size)), \ 25 | self.unc.reshape((1,self.wave.size))))) 26 | hdulist.append(hdu) 27 | 28 | return hdulist 29 | 30 | def read(self, filename=None, usefile=None): 31 | if (usefile == None): 32 | f = h5py.File(filename, "r") 33 | else: 34 | f = usefile 35 | 36 | wave = f['wave'][...] 37 | flux = f['flux'][...] 38 | unc = f['unc'][...] 39 | 40 | self.__init__(wave, flux, unc) 41 | 42 | if (usefile == None): 43 | f.close() 44 | 45 | def write(self, filename=None, usefile=None): 46 | if (usefile == None): 47 | f = h5py.File(filename, "w") 48 | else: 49 | f = usefile 50 | 51 | if hasattr(self, "wave"): 52 | wave_dset = f.create_dataset("wave", (self.wave.size,), dtype='f') 53 | wave_dset[...] = self.wave 54 | flux_dset = f.create_dataset("flux", (self.flux.size,), dtype='f') 55 | flux_dset[...] = self.flux 56 | unc_dset = f.create_dataset("unc", (self.unc.size,), dtype='f') 57 | unc_dset[...] = self.unc 58 | 59 | if (usefile == None): 60 | f.close() 61 | -------------------------------------------------------------------------------- /pdspy/interferometry/interpolate_model.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import scipy.interpolate 3 | from ..constants.astronomy import arcsec 4 | from .libinterferometry import Visibilities 5 | from galario import double 6 | try: 7 | import trift 8 | except: 9 | pass 10 | 11 | def interpolate_model(u, v, freq, model, nthreads=1, dRA=0., dDec=0., \ 12 | code="galario", nxy=1024, dxy=0.01): 13 | 14 | if code == "galario": 15 | real = [] 16 | imag = [] 17 | 18 | double.threads(nthreads) 19 | 20 | dxy = (model.x[1] - model.x[0])*arcsec 21 | 22 | for i in range(len(model.freq)): 23 | vis = double.sampleImage(model.image[::-1,:,i,0].copy(order='C'), \ 24 | dxy, u, v, dRA=dRA*arcsec, dDec=dDec*arcsec) 25 | 26 | real.append(vis.real.reshape((u.size,1))) 27 | imag.append(-vis.imag.reshape((u.size,1))) 28 | 29 | real = numpy.concatenate(real, axis=1) 30 | imag = numpy.concatenate(imag, axis=1) 31 | 32 | elif code == "galario-unstructured": 33 | real = [] 34 | imag = [] 35 | 36 | double.threads(nthreads) 37 | 38 | vol = None 39 | for i in range(len(model.freq)): 40 | vis = double.sampleUnstructuredImage(model.x*arcsec, \ 41 | -model.y*arcsec, model.image[:,i].copy(order='C'), nxy, \ 42 | dxy*arcsec, u, v, dRA=dRA*arcsec, dDec=dDec*arcsec) 43 | 44 | real.append(vis.real.reshape((u.size,1))) 45 | imag.append(-vis.imag.reshape((u.size,1))) 46 | 47 | real = numpy.concatenate(real, axis=1) 48 | imag = numpy.concatenate(imag, axis=1) 49 | 50 | elif code == "trift": 51 | vis = trift.cpu.trift(model.x*arcsec, model.y*arcsec, \ 52 | model.image, u, v, dRA*arcsec, dDec*arcsec, \ 53 | nthreads=nthreads, mode="extended") 54 | 55 | real, imag = vis.real, vis.imag 56 | 57 | return Visibilities(u, v, freq, real, imag, numpy.ones(real.shape)) 58 | -------------------------------------------------------------------------------- /pdspy/dust/redden.py: -------------------------------------------------------------------------------- 1 | from . import __file__ 2 | from scipy.interpolate import interp1d 3 | import numpy 4 | import os 5 | 6 | def redden(wave, flux, Av, law="steenman", Rv='3.1', magnitudes=False): 7 | 8 | # Read in the extinction coefficients. 9 | 10 | if law == 'steenman': 11 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 12 | "/reddening/steenman_the.dat", usecols=[1,2]) 13 | elif law == 'mcclure': 14 | if Av <= 1.: 15 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 16 | "/reddening/mcclure.dat", usecols=[0,1]) 17 | else: 18 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 19 | "/reddening/mcclure.dat", usecols=[0,2]) 20 | elif law == 'draine': 21 | if Rv == '3.1': 22 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 23 | "/reddening/draine_3.1.dat", skiprows=80, \ 24 | usecols=(0,3)) 25 | 26 | data = data[::-1,:] 27 | data[:,1] /= data[605,1] 28 | 29 | elif Rv == '4.0': 30 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 31 | "/reddening/draine_4.0.dat", skiprows=80, \ 32 | usecols=(0,3)) 33 | 34 | data = data[::-1,:] 35 | data[:,1] /= data[605,1] 36 | 37 | elif Rv == '5.1': 38 | data = numpy.loadtxt(os.path.dirname(__file__)+ \ 39 | "/reddening/draine_5.1.dat", skiprows=80, \ 40 | usecols=(0,3)) 41 | 42 | data = data[::-1,:] 43 | data[:,1] /= data[605,1] 44 | 45 | waves = data[:,0] 46 | coeffs = data[:,1] 47 | 48 | # Interpolate the data to the wavelength range supplied. 49 | 50 | coeffs = interp1d(numpy.log10(waves), numpy.log10(coeffs), \ 51 | fill_value="extrapolate") 52 | 53 | if magnitudes: 54 | return flux + Av * 10.**coeffs(numpy.log10(wave)) 55 | else: 56 | coeff = 10.**coeffs(numpy.log10(wave)) 57 | 58 | # Now adjust the provided flux. 59 | 60 | return flux / 10.**(Av * coeff / 2.5) 61 | -------------------------------------------------------------------------------- /pdspy/plotting/colormaps.py: -------------------------------------------------------------------------------- 1 | import matplotlib.colors as colors 2 | 3 | # Rainbow1 colormap. 4 | 5 | rainbow1_cdict = {'red': ((0, 1, 1), 6 | (0.15, 0, 0), 7 | (0.45, 0, 0), 8 | (0.5, 0, 0), 9 | (0.55, 1, 1), 10 | (0.75, 1, 1), 11 | (0.875, 1, 1), 12 | (1, 0, 0)), 13 | 'green': ((0, 1, 1), 14 | (0.15, 0, 0), 15 | (0.45, 1, 1), 16 | (0.5, 1, 1), 17 | (0.55, 1, 1), 18 | (0.75, 0, 0), 19 | (0.875, 0, 0), 20 | (1, 0, 0)), 21 | 'blue': ((0, 1, 1), 22 | (0.15, 1, 1), 23 | (0.45, 1, 1), 24 | (0.5, 0, 0), 25 | (0.55, 0, 0), 26 | (0.75, 0, 0), 27 | (0.875, 1, 1), 28 | (1, 0, 0))} 29 | 30 | rainbow1 = colors.LinearSegmentedColormap('rainbow3',rainbow1_cdict,256) 31 | 32 | # Rainbow3 colormap. 33 | 34 | rainbow3_cdict = {'red': ((0, 0, 0), 35 | (0.15, 0, 0), 36 | (0.45, 0, 0), 37 | (0.5, 0, 0), 38 | (0.55, 1, 1), 39 | (0.75, 1, 1), 40 | (0.875, 1, 1), 41 | (1, 1, 1)), 42 | 'green': ((0, 0, 0), 43 | (0.15, 0, 0), 44 | (0.45, 1, 1), 45 | (0.5, 1, 1), 46 | (0.55, 1, 1), 47 | (0.75, 0, 0), 48 | (0.875, 0, 0), 49 | (1, 1, 1)), 50 | 'blue': ((0, 0, 0), 51 | (0.15, 1, 1), 52 | (0.45, 1, 1), 53 | (0.5, 0, 0), 54 | (0.55, 0, 0), 55 | (0.75, 0, 0), 56 | (0.875, 1, 1), 57 | (1, 1, 1))} 58 | 59 | rainbow3 = colors.LinearSegmentedColormap('rainbow3',rainbow3_cdict,256) 60 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_draine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import Dust 4 | import matplotlib 5 | import matplotlib.pyplot as plt 6 | 7 | # List of the files to be plotted. 8 | 9 | species_list = ["draine_1um.hdf5", "draine_10um.hdf5", "draine_100um.hdf5", \ 10 | "draine_1mm.hdf5", "draine_3mm.hdf5", "draine_1cm.hdf5"] 11 | 12 | # Change a few of the parameters to make the plot look nice. 13 | 14 | matplotlib.rcParams["font.family"] = "serif" 15 | matplotlib.rcParams["font.size"] = 14 16 | matplotlib.rcParams["text.usetex"] = "True" 17 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 18 | matplotlib.rcParams["legend.fontsize"] = 14 19 | 20 | # Start the plotting. 21 | 22 | fig, ax = plt.subplots(nrows=2, ncols=2) 23 | 24 | for species in species_list: 25 | dust = Dust() 26 | dust.set_properties_from_file(species) 27 | 28 | # Make a label for each line. 29 | 30 | size = species.split('_')[1].split('.')[0] 31 | if (len(size.split('um')) == 2): 32 | label = r"$a_{max} = %s$ $\upmu$m" % size.split('um')[0] 33 | elif (len(size.split('mm')) == 2): 34 | label = "$a_{max} = %s$ mm" % size.split('mm')[0] 35 | elif (len(size.split('cm')) == 2): 36 | label = "$a_{max} = %s$ cm" % size.split('cm')[0] 37 | 38 | # Plot the opacities. 39 | 40 | ax[0,0].loglog(dust.lam*1e4, dust.kabs, label=label) 41 | ax[0,1].loglog(dust.lam*1e4, dust.ksca) 42 | ax[1,0].loglog(dust.lam*1e4, dust.kext) 43 | ax[1,1].semilogx(dust.lam*1e4, dust.albedo) 44 | 45 | ax[0,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 46 | ax[0,0].set_ylabel("$\kappa_{abs}$ [cm$^2$ g$^{-1}$]") 47 | ax[0,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 48 | ax[0,1].set_ylabel("$\kappa_{sca}$ [cm$^2$ g$^{-1}$]") 49 | ax[1,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 50 | ax[1,0].set_ylabel("$\kappa_{ext}$ [cm$^2$ g$^{-1}$]") 51 | ax[1,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 52 | ax[1,1].set_ylabel("Albedo") 53 | 54 | ax[0,0].legend(loc="lower left") 55 | 56 | ax[0,0].axis([1e-1,1e5,1e-4,1e5]) 57 | ax[0,1].axis([1e-1,1e5,1e-4,1e5]) 58 | ax[1,0].axis([1e-1,1e5,1e-4,1e5]) 59 | ax[1,1].axis([1e-1,1e5,0,1]) 60 | 61 | fig.set_size_inches((10,10)) 62 | fig.subplots_adjust(wspace=0.25, left=0.08, top=0.97, right=0.98, bottom=0.06) 63 | fig.savefig("draine_plot.pdf") 64 | -------------------------------------------------------------------------------- /pdspy/plotting/cubeshow.py: -------------------------------------------------------------------------------- 1 | from matplotlib.widgets import Slider, Button, RadioButtons 2 | import matplotlib.pyplot as plt 3 | 4 | """ 5 | Display a 3d ndarray with a slider to move along the third dimension. 6 | 7 | Extra keyword arguments are passed to imshow 8 | 9 | Original from here: http://nbarbey.github.io/2011/07/08/matplotlib-slider.html 10 | """ 11 | 12 | def cubeshow(cube, axis=2, **kwargs): 13 | # check dim 14 | if not cube.ndim == 3: 15 | raise ValueError("cube should be an ndarray with ndim == 3") 16 | 17 | # Generate a figure with a set of axes. 18 | 19 | fig = plt.figure() 20 | 21 | ax = plt.subplot(111) 22 | 23 | fig.subplots_adjust(bottom=0.17, top=0.98) 24 | 25 | # Pick out just the first image to show. 26 | 27 | s = [slice(0, 1) if i == axis else slice(None) for i in range(3)] 28 | im = cube[tuple(s)].squeeze() 29 | 30 | # Show the image. 31 | 32 | l = ax.imshow(im, **kwargs) 33 | 34 | # Create the slider 35 | 36 | axslide = fig.add_axes([0.3, 0.02, 0.4, 0.075]) 37 | 38 | slider = Slider(axslide, '', 0, cube.shape[axis] - 1, valinit=0, \ 39 | valfmt=' %i', valstep=1) 40 | 41 | def update(val): 42 | ind = int(slider.val) 43 | s = [slice(ind, ind + 1) if i == axis else slice(None) 44 | for i in range(3)] 45 | im = cube[tuple(s)].squeeze() 46 | l.set_data(im) 47 | fig.canvas.draw() 48 | 49 | slider.on_changed(update) 50 | 51 | # Create a button to go to the next frame. 52 | 53 | axnext = fig.add_axes([0.81, 0.02, 0.1, 0.075]) 54 | 55 | bnext = Button(axnext, 'Next') 56 | 57 | def next_image(event): 58 | if slider.val < cube.shape[axis]-1: 59 | slider.set_val(slider.val+1) 60 | elif slider.val == cube.shape[axis]-1: 61 | slider.set_val(0) 62 | 63 | bnext.on_clicked(next_image) 64 | 65 | # Create a button to go to the previous frame. 66 | 67 | axprev = fig.add_axes([0.09, 0.02, 0.1, 0.075]) 68 | 69 | bprev = Button(axprev, 'Prev.') 70 | 71 | def prev_image(event): 72 | if slider.val > 0: 73 | slider.set_val(slider.val-1) 74 | elif slider.val == 0: 75 | slider.set_val(cube.shape[axis]-1) 76 | 77 | bprev.on_clicked(prev_image) 78 | 79 | # Show the plot. 80 | 81 | plt.show(block=True) 82 | -------------------------------------------------------------------------------- /pdspy/spectroscopy/btsettl_photometry.py: -------------------------------------------------------------------------------- 1 | from pdspy.constants.astronomy import R_sun, pc 2 | import scipy.interpolate 3 | import astropy.table 4 | import numpy 5 | 6 | def btsettl_photometry(Teff=5770., Logg=4.4, Rstar=1.0, dpc=140., \ 7 | system="Johnson", filters="B", AB=False): 8 | 9 | # Create the filename. 10 | 11 | path = '/'.join(__file__.split("/")[0:-1])+"/btsettl_data/" 12 | 13 | basename = "colmag.BT-Settl.server." 14 | 15 | filename = basename + system + "." 16 | 17 | if AB: 18 | filename += "AB.txt" 19 | else: 20 | filename += "Vega.txt" 21 | 22 | # Get the column names. 23 | 24 | f = open(path+filename, "r") 25 | lines = f.readlines() 26 | f.close() 27 | 28 | for i, line in enumerate(lines): 29 | if line[0] != "!": 30 | break 31 | 32 | colnames = lines[i-1].split()[1:] 33 | 34 | # Get the data. 35 | 36 | data = numpy.loadtxt(path+filename, comments='!') 37 | 38 | # Make the data into a table. 39 | 40 | table = astropy.table.Table(data[1:,0:16], names=colnames) 41 | 42 | # Make sure we only include the Teff's with all the log(g)'s. 43 | 44 | """ 45 | teff = numpy.unique(table["Teff"]) 46 | logg = numpy.unique(table["Logg"]) 47 | 48 | for T in teff: 49 | if len(table["Teff"][table["Teff"] == T]) < len(logg): 50 | table.remove_rows(numpy.where(table["Teff"] == T)[0]) 51 | """ 52 | 53 | # Now loop through the filters and get the photometry. 54 | 55 | """ 56 | teff = numpy.array(numpy.unique(table["Teff"])) 57 | logg = numpy.array(numpy.unique(table["Logg"])) 58 | """ 59 | 60 | mag = [] 61 | 62 | for filter in filters.split(','): 63 | #data = numpy.array(table[filter]).reshape((teff.size, logg.size)) 64 | 65 | # Now do the 2D interpolation. 66 | 67 | #f = scipy.interpolate.interp2d(logg, teff, data) 68 | 69 | f = scipy.interpolate.LinearNDInterpolator((table["Teff"], \ 70 | table["Logg"]), table[filter], rescale=False) 71 | 72 | # Calculate the photometry now. 73 | 74 | mag.append(f([[Teff,Logg]])[0]) 75 | 76 | # Turn the photometry into an array. 77 | 78 | mag = numpy.array(mag) 79 | 80 | # Turn into an absolute magnitude. 81 | 82 | M = mag - 5 * numpy.log10(Rstar*R_sun / pc) + 5. 83 | 84 | # Now get the apparent magnitude. 85 | 86 | photometry = M + 5*(numpy.log10(dpc) - 1.) 87 | 88 | return photometry 89 | -------------------------------------------------------------------------------- /pdspy/dust/data/compare_c2d.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import Dust, DustGenerator 4 | import matplotlib 5 | import matplotlib.pyplot as plt 6 | 7 | # List of the files to be plotted. 8 | 9 | species_list = ["pollack_1um.hdf5", "pollack_10um.hdf5", "draine_1um.hdf5", \ 10 | "c2d.hdf5"] 11 | 12 | # Change a few of the parameters to make the plot look nice. 13 | 14 | matplotlib.rcParams["font.family"] = "serif" 15 | matplotlib.rcParams["font.size"] = 14 16 | matplotlib.rcParams["text.usetex"] = "True" 17 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 18 | matplotlib.rcParams["legend.fontsize"] = 14 19 | 20 | # Start the plotting. 21 | 22 | fig, ax = plt.subplots(nrows=2, ncols=2) 23 | 24 | for i, species in enumerate(species_list): 25 | dust = Dust() 26 | dust.set_properties_from_file(species) 27 | 28 | # If we're using the Draine opacities, multiply by 100. 29 | 30 | if species == "draine_1um.hdf5": 31 | dust.kabs *= 100 32 | dust.ksca *= 100 33 | dust.kext *= 100 34 | 35 | # Make a label for each line. 36 | 37 | if species == "pollack_1um.hdf5": 38 | label = "Pollack et al. 1994, $a_{max} = 1$ $\mu$m" 39 | if species == "pollack_10um.hdf5": 40 | label = "Pollack et al. 1994, $a_{max} = 10$ $\mu$m" 41 | elif species == "draine_1um.hdf5": 42 | label = "70\% astronomical silicate, 30\% graphite" 43 | elif species == "c2d.hdf5": 44 | label = "c2d opacities" 45 | 46 | # Plot the opacities. 47 | 48 | ax[0,0].loglog(dust.lam*1e4, dust.kabs, label=label) 49 | ax[0,1].loglog(dust.lam*1e4, dust.ksca) 50 | ax[1,0].loglog(dust.lam*1e4, dust.kext) 51 | ax[1,1].semilogx(dust.lam*1e4, dust.albedo) 52 | 53 | ax[0,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 54 | ax[0,0].set_ylabel("$\kappa_{abs}$ [cm$^2$ g$^{-1}$]") 55 | ax[0,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 56 | ax[0,1].set_ylabel("$\kappa_{sca}$ [cm$^2$ g$^{-1}$]") 57 | ax[1,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 58 | ax[1,0].set_ylabel("$\kappa_{ext}$ [cm$^2$ g$^{-1}$]") 59 | ax[1,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 60 | ax[1,1].set_ylabel("Albedo") 61 | 62 | ax[0,0].legend(loc="lower left", fontsize="small") 63 | 64 | ax[0,0].axis([1e-1,1e5,1e-4,1e5]) 65 | ax[0,1].axis([1e-1,1e5,1e-4,1e5]) 66 | ax[1,0].axis([1e-1,1e5,1e-4,1e5]) 67 | ax[1,1].axis([1e-1,1e5,0,1]) 68 | 69 | fig.set_size_inches((10,10)) 70 | fig.subplots_adjust(wspace=0.25, left=0.08, top=0.97, right=0.98, bottom=0.06) 71 | fig.savefig("c2d_plot.pdf") 72 | -------------------------------------------------------------------------------- /pdspy/plotting/plot_scattered_light.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import matplotlib.ticker as ticker 3 | import numpy 4 | 5 | def plot_scattered_light(images, model, parameters, params, index=0, \ 6 | fig=None): 7 | 8 | # If no axes are provided, create them. 9 | 10 | if fig == None: 11 | fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(4.5,4)) 12 | else: 13 | fig, ax = fig 14 | 15 | # Scale the image appropriately. 16 | 17 | c = scale_image(images["data"][index], mode="arcsinh") 18 | 19 | # Show the image. 20 | 21 | ax.imshow(c[:,:,0,0], origin="lower", interpolation="nearest", cmap="gray") 22 | 23 | # Scale the model image. 24 | 25 | c = scale_image(model.images[images["lam"][index]], \ 26 | mode=images["plot_mode"][index]) 27 | 28 | # Contour the model image over the data. 29 | 30 | levels = numpy.array([0.05,0.25,0.45,0.65,0.85,1.0]) * \ 31 | (c.max() - c.min()) + c.min() 32 | 33 | ax.contour(c[:,:,0,0], colors='gray', levels=levels) 34 | 35 | # Adjust the ticks. 36 | 37 | transform3 = ticker.FuncFormatter(Transform(0, images["npix"][index], \ 38 | images["pixelsize"][index], '%.1f"')) 39 | 40 | ticks = images["ticks"][index] 41 | 42 | ax.set_xticks(images["npix"][index]/2+ticks/images["pixelsize"][index]) 43 | ax.set_yticks(images["npix"][index]/2+ticks/images["pixelsize"][index]) 44 | ax.get_xaxis().set_major_formatter(transform3) 45 | ax.get_yaxis().set_major_formatter(transform3) 46 | 47 | # Adjust the axes labels. 48 | 49 | ax.set_xlabel("$\Delta$RA") 50 | ax.set_ylabel("$\Delta$Dec") 51 | 52 | # Return the figure and axes. 53 | 54 | return fig, ax 55 | 56 | # Define a useful class for plotting. 57 | 58 | class Transform: 59 | def __init__(self, xmin, xmax, dx, fmt): 60 | self.xmin = xmin 61 | self.xmax = xmax 62 | self.dx = dx 63 | self.fmt = fmt 64 | 65 | def __call__(self, x, p): 66 | return self.fmt% ((x-(self.xmax-self.xmin)/2)*self.dx) 67 | 68 | # Define a function to scale an image to look nice. 69 | 70 | def scale_image(image, mode="linear"): 71 | vmin = image.image.min() 72 | vmax = numpy.percentile(image.image, 95) 73 | 74 | a = 1000. 75 | b = (image.image - vmin) / (vmax - vmin) 76 | 77 | if mode == "linear": 78 | c = b 79 | elif mode == "arcsinh": 80 | c = numpy.arcsinh(10*b)/3. 81 | elif mode == "log": 82 | c = numpy.log10(a*b+1)/numpy.log10(a) 83 | else: 84 | print("Not a valid mode!") 85 | 86 | return c 87 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Installing the code with Anaconda 6 | """"""""""""""""""""""""""""""""" 7 | 8 | Anaconda is probably the easiest way to install pdspy because the dependency GALARIO is easily conda-installable. pdspy is available on the conda-forge channel and can be installed from the command line with: 9 | 10 | :: 11 | 12 | conda install pdspy -c conda-forge 13 | 14 | Installing the code with pip 15 | """""""""""""""""""""""""""" 16 | 17 | 1. In a terminal, run: 18 | :: 19 | 20 | pip install pdspy 21 | 22 | 2. Install GALARIO. Unfortunately, GALARIO is not pip-installable by default, so you will need to follow the instructions `here `_. Alternatively, a pip-installable version of GALARIO is in the works and can be installed like so: 23 | :: 24 | 25 | pip install git+https://github.com/psheehan/galario.git@add_unstructured 26 | 27 | but note that this is a fork of GALARIO and is not yet completely merged. 28 | 29 | Installing the code manually 30 | """""""""""""""""""""""""""" 31 | 32 | If you would like the most cutting-edge version of pdspy, with updates that go beyond the pre-packaged versions, you can download and compile the code yourself following these instructions: 33 | 34 | 1. Download the code from this webpage. Git clone is recommended if you would like to be able to pull updates: 35 | :: 36 | 37 | git clone https://github.com/psheehan/pdspy.git 38 | 39 | 2. Install the Python dependencies: 40 | 41 | * numpy 42 | * scipy 43 | * matplotlib 44 | * emcee 45 | * corner 46 | * hyperion 47 | * h5py 48 | * mpi4py 49 | * galario 50 | * Cython 51 | * astropy < 4.0 52 | * schwimmbad 53 | * dynesty 54 | 55 | 3. In a terminal, go to the directory where the code was downloaded, and into the code directory. Run: 56 | :: 57 | 58 | python setup.py install 59 | 60 | or 61 | 62 | :: 63 | 64 | pip install -e . 65 | 66 | or 67 | 68 | :: 69 | 70 | conda build pdspy -c conda-forge 71 | conda install pdspy -c conda-forge --use-local 72 | 73 | depending on what method you prefer best. 74 | 75 | Other dependencies 76 | """""""""""""""""" 77 | 78 | The other codes that are needed to run pdspy are `Hyperion `_ and `RADMC-3D `_. If you are a `Homebrew `_ user, you can do this with: 79 | 80 | :: 81 | 82 | brew tap psheehan/science 83 | brew install hyperion 84 | brew install radmc3d 85 | 86 | -------------------------------------------------------------------------------- /pdspy/spectroscopy/find_lines.py: -------------------------------------------------------------------------------- 1 | from numpy import arange,array,where 2 | from numpy import abs as absv 3 | from .line_flux import line_flux 4 | import matplotlib.pyplot as plt 5 | 6 | def find_lines(data,thresh=3): 7 | 8 | nside = 8 9 | nleft = 8 10 | nright = 8 11 | 12 | index = 8 13 | 14 | B = 1.0/1200 15 | 16 | while index < data.wave.size-8: 17 | 18 | lines = array([data.wave[index]]) 19 | 20 | results,chisq = line_flux(data,lines,nleft=nleft,nright=nright, \ 21 | quiet=True) 22 | results_fw,chisq_fw = line_flux(data,lines,nleft=nleft,nright=nright,\ 23 | quiet=True,fixed_width=True) 24 | 25 | chisq_test = (chisq <= thresh) & (chisq_fw <= thresh) 26 | SN_test = (results[0,1]/results[0,2] >= 5) & \ 27 | (results_fw[0,1]/results_fw[0,2] >= 5) 28 | width_test = (results[0,3] >= lines[0]*B/2) & (results[0,3] <= \ 29 | lines[0]*3*B) 30 | flux_test = absv(results[0,1]-results_fw[0,1])/results[0,1] <= 0.2 31 | 32 | #if (chisq_test & flux_test) & (SN_test & width_test): 33 | if (chisq_test & SN_test): 34 | print(" {0:f} {1:e} {2:e} {3:f} {4:f}".format( \ 35 | results[0,0],results[0,1],results[0,2],results[0,3],chisq)) 36 | 37 | results,chisq = line_flux(data,lines,nleft=nleft,nright=nright, \ 38 | plotout="{0:6.3f}.pdf".format(results[0,0]),quiet=True) 39 | 40 | index = where(absv(data.wave-data.wave[index]-0.05) == \ 41 | absv(data.wave-data.wave[index]-0.05).min())[0][0] 42 | nside = 6 43 | else: 44 | index = where(absv(data.wave-data.wave[index]-0.05) == \ 45 | absv(data.wave-data.wave[index]-0.05).min())[0][0] 46 | # else: 47 | # if (nleft > 3) & (nright == nside): 48 | # nleft = nleft - 1 49 | # else: 50 | # nleft = nside 51 | # if (nright > 3) & (nleft == nside): 52 | # nright = nright-1 53 | # else: 54 | # nright = nside 55 | # if nside > 3: 56 | # nside = nside - 1 57 | # nleft = nside 58 | # nright = nside 59 | # else: 60 | # nside = 8 61 | # nleft = nside 62 | # nright = nside 63 | # index = where(absv(data.wave-data.wave[index]-0.05) ==\ 64 | # absv(data.wave-data.wave[index]-0.05).min())[0][0] 65 | -------------------------------------------------------------------------------- /docs/upgrading.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Upgrading to v2 from v1 3 | ======================= 4 | 5 | **tl;dr -** pdspy v2.0.0 represents a major upgrade to the pdspy infrastructure, and is not backwards compatible with models that were run using earlier versions. Use the :code:`upgrade_to_pdspy2.py` script to upgrade your pre-v2 models. 6 | 7 | What's new? 8 | """"""""""" 9 | 10 | Version 2 of the pdspy code fixes longstanding issues with the orientation parameters (x0, y0, pa, etc.). Previously x0 and y0 were entirely backwards from their traditional definitions, and pa was even worse. Moreover, for models with density reductions, e.g. gaps, outflow cavities, etc., the mass of the component with the density reduction was not being calculated accurately. Now the values that are reported from the modeling are entirely accurate. 11 | 12 | Why is it not backwards compatible? 13 | =================================== 14 | 15 | The definition of a number of parameters fundamentally changed and so if you try to run models with the results of v1.* fits, they will not give correct results using v2.* code. 16 | 17 | What needs to be updated? 18 | ========================= 19 | 20 | There are a few places in particular that you need to look to make sure that your files are up-to-date and ready for v2: 21 | 22 | * config.py 23 | 24 | * *x0/y0*: These are now defined such that in a CASA image x0 is positive to the left and y0 is positive up. 25 | 26 | * Visibility HDF5 files: These files *must* be recreated from their UVFITS or CAASA MS origins. In order to get images oriented correctly when made from the visibility data, pdspy now takes the complex conjugate of the data when reading from UVFITS or MS files. The HDF5 visibility files store and read the data exactly as-is, which means that they need to be recreated in order to have the proper orientation. 27 | 28 | This sounds like a pain... 29 | ========================== 30 | 31 | You're telling me... and I'm sorry for the difficulty, but hope that these changes will make pdspy a more accurate tool. The safest thing to do would be to update your config.py file and your data files as described above and restart running your models, or to stick with v1 until you are done with your current modeling tasks and are ready to start fresh. If you do need to upgrade and can't wait for your models to finish, there is a helper script :code:`upgrade_to_pdspy2` that *should* take care of the transition for you in most cases. To use, simply go to the directory of an existing model and run the script. *Note:* The script will make permanent changes to files in that directory, and though it makes a backup copy of everything, it would perhaps be wise to make your own backup beforehand. 32 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath("../")) 16 | 17 | 18 | # -- Mock imports ------------------------------------------------------------ 19 | 20 | autodoc_mock_imports = ["numpy","hyperion","scipy","scikit-learn","h5py", 21 | "matplotlib","emcee","corner","mpi4py","astropy","schwimmbad","dynesty", 22 | "pdspy.dust.bhmie","pdspy.dust.bhcoat","pdspy.dust.dmilay", 23 | "pdspy.interferometry.libinterferometry","pdspy.imaging.libimaging", 24 | "pdspy.radmc3d.read","galario","mpl_toolkits","sklearn","casatools"] 25 | 26 | # -- Project information ----------------------------------------------------- 27 | 28 | project = 'pdspy' 29 | copyright = '2020, Patrick Sheehan' 30 | author = 'Patrick Sheehan' 31 | 32 | # The full version, including alpha/beta/rc tags 33 | release = '2.0.0' 34 | 35 | 36 | # -- General configuration --------------------------------------------------- 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | 'recommonmark', 43 | 'sphinx.ext.autodoc', 44 | ] 45 | 46 | # Add any paths that contain templates here, relative to this directory. 47 | templates_path = ['_templates'] 48 | 49 | # List of patterns, relative to source directory, that match files and 50 | # directories to ignore when looking for source files. 51 | # This pattern also affects html_static_path and html_extra_path. 52 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 53 | 54 | 55 | # -- Options for HTML output ------------------------------------------------- 56 | 57 | # The theme to use for HTML and HTML Help pages. See the documentation for 58 | # a list of builtin themes. 59 | # 60 | html_theme = 'sphinx_rtd_theme' 61 | 62 | # Add any paths that contain custom static files (such as style sheets) here, 63 | # relative to this directory. They are copied after the builtin static files, 64 | # so a file named "default.css" will overwrite the builtin "default.css". 65 | html_static_path = ['_static'] 66 | 67 | master_doc = "index" 68 | -------------------------------------------------------------------------------- /pdspy/modeling/get_surrogate_model.py: -------------------------------------------------------------------------------- 1 | import schwimmbad 2 | import pickle 3 | import numpy 4 | import time 5 | import os 6 | 7 | def get_surrogate_model(params, model="pringle+ulrich+diana", \ 8 | quantity="temperature", nthreads=1): 9 | 10 | # Load the keys for the parameters of the surrogate model. 11 | 12 | keys = list(numpy.loadtxt(os.path.dirname(os.path.abspath(__file__))+\ 13 | "/surrogate_models/{0:s}/{1:s}/keys.txt".format(model, quantity), \ 14 | dtype=str)) 15 | 16 | # Load in the PCA that was found. 17 | 18 | pca = pickle.load(open(os.path.dirname(os.path.abspath(__file__))+\ 19 | "/surrogate_models/{0:s}/{1:s}/pca.pkl".format(model, quantity), \ 20 | "rb")) 21 | 22 | # Also the transformed data 23 | 24 | y_grid = numpy.load(os.path.dirname(os.path.abspath(__file__))+\ 25 | "/surrogate_models/{0:s}/{1:s}/transformed_data.npy".\ 26 | format(model, quantity)) 27 | 28 | # Also load in the Gaussian process fits. 29 | 30 | gps = pickle.load(open(os.path.dirname(os.path.abspath(__file__))+\ 31 | "/surrogate_models/{0:s}/{1:s}/gps.pkl".format(model, quantity), \ 32 | "rb")) 33 | 34 | # Load the samples and make sure the best fit values are used for the 35 | # hyperparameters. 36 | 37 | ncomponents = y_grid.shape[1] 38 | ncomponents = 9 39 | 40 | samples = [] 41 | for i in range(ncomponents): 42 | samples += [numpy.load(os.path.dirname(os.path.abspath(__file__))+\ 43 | "/surrogate_models/{0:s}/{1:s}/gp_samples_component{2:d}.pkl." 44 | "npy".format(model, quantity, i))] 45 | 46 | # Load in the data. 47 | 48 | x = [] 49 | data = [] 50 | 51 | x.append([(params[k] - -9.)**3 if k == "logM_env" else params[k] for k in \ 52 | keys]) 53 | 54 | x = numpy.array(x) 55 | shape = (99,100,1) 56 | 57 | # Set the parameter vector for the GPs randomly from the posteriors. 58 | 59 | for i in range(ncomponents): 60 | w = numpy.random.randint(samples[i].shape[0]) 61 | 62 | gps[i].set_parameter_vector(samples[i][w]) 63 | 64 | # Reconstruct the data from the PCA + GP fit. 65 | 66 | sample = lambda i: gps[i].sample_conditional(y_grid[:,i], x)[0] if \ 67 | i < ncomponents else 0. 68 | 69 | t1 = time.time() 70 | with schwimmbad.JoblibPool(nthreads) as pool: 71 | components = [list(pool.map(sample, range(15)))] 72 | 73 | projected = pca.inverse_transform(components) 74 | t2 = time.time() 75 | print("Time to reconstruct = {0:f} seconds".format(t2 - t1)) 76 | 77 | # Return the projected quantity. 78 | 79 | return 10.**projected[0].reshape(shape) 80 | -------------------------------------------------------------------------------- /pdspy/imaging/match_source_lists.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import astropy 3 | import astropy.coordinates 4 | 5 | #def match_source_lists(*lists, tol=0.3, table_names=['1', '2']): 6 | def match_source_lists(tol=0.3, table_names=['1', '2'], *lists): 7 | 8 | for i in range(len(lists)-1): 9 | if i == 0: 10 | list1 = lists[i] 11 | else: 12 | list1 = joined_list 13 | list2 = lists[i+1] 14 | 15 | coord1 = astropy.coordinates.SkyCoord(list1["ra"].tolist(), \ 16 | list1['dec'].tolist(), frame='icrs') 17 | coord2 = astropy.coordinates.SkyCoord(list2["ra"].tolist(), \ 18 | list2["dec"].tolist(), frame='icrs') 19 | 20 | idx, d2d, d3d = astropy.coordinates.match_coordinates_sky(coord1,coord2) 21 | 22 | ids, counts = numpy.unique(idx[d2d.arcsec < tol], return_counts=True) 23 | for j in range(len(ids)): 24 | if counts[j] > 1: 25 | min_d2d = min(d2d[idx == ids[j]].arcsec) 26 | d2d[(d2d.arcsec != min_d2d) & (idx == ids[j])] = \ 27 | astropy.coordinates.Angle("0d00m{0:f}s".format(2*tol)) 28 | 29 | list1["id"] = numpy.arange(len(list1)) + len(list2) 30 | list2["id"] = numpy.arange(len(list2)) 31 | list1["id"][d2d.arcsec < tol] = idx[d2d.arcsec < tol] 32 | 33 | if i == 0: 34 | names = table_names[0:2] 35 | else: 36 | names = ["",table_names[i+1]] 37 | 38 | joined_list = astropy.table.join(list1, list2, join_type='outer', \ 39 | keys='id', table_names=names, \ 40 | uniq_col_name='{col_name}_{table_name}') 41 | 42 | del list1["id"] 43 | del list2["id"] 44 | del joined_list["id"] 45 | 46 | if i == 0: 47 | joined_list["ra"] = numpy.where(\ 48 | joined_list["ra_"+names[0]].mask, \ 49 | joined_list["ra_"+names[1]], joined_list["ra_"+names[0]]) 50 | joined_list["dec"] = numpy.where(\ 51 | joined_list["dec_"+names[0]].mask, \ 52 | joined_list["dec_"+names[1]], joined_list["dec_"+names[0]]) 53 | else: 54 | for name in list2.colnames: 55 | if name in joined_list.colnames: 56 | joined_list.rename_column(name, name+"_"+names[1]) 57 | 58 | joined_list["ra"] = numpy.where(\ 59 | joined_list["ra_"].mask, joined_list["ra_"+names[1]], \ 60 | joined_list["ra_"]) 61 | joined_list["dec"] = numpy.where(\ 62 | joined_list["dec_"].mask, joined_list["dec_"+names[1]], \ 63 | joined_list["dec_"]) 64 | 65 | del joined_list["ra_"] 66 | del joined_list["dec_"] 67 | 68 | del joined_list["ra"] 69 | del joined_list["dec"] 70 | 71 | return joined_list 72 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_diana.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pdspy.dust 4 | import matplotlib 5 | import matplotlib.pyplot as plt 6 | 7 | # List of the files to be plotted. 8 | 9 | species_list = ["diana_1um.hdf5", "diana_10um.hdf5", "diana_100um.hdf5", \ 10 | "diana_1mm.hdf5", "diana_1cm.hdf5", "diana_10cm.hdf5"] 11 | 12 | # Maximum dust grain sizes. 13 | 14 | a_max = [1., 10., 100., 1000., 10000., 100000.] 15 | 16 | # Read in the dust generator class. 17 | 18 | dust_gen = pdspy.dust.DustGenerator("diana.hdf5") 19 | 20 | # Change a few of the parameters to make the plot look nice. 21 | 22 | matplotlib.rcParams["font.family"] = "serif" 23 | matplotlib.rcParams["font.size"] = 14 24 | matplotlib.rcParams["text.usetex"] = "True" 25 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 26 | matplotlib.rcParams["legend.fontsize"] = 14 27 | 28 | # Start the plotting. 29 | 30 | fig, ax = plt.subplots(nrows=2, ncols=2) 31 | 32 | for i, species in enumerate(species_list): 33 | dust = pdspy.dust.Dust() 34 | dust.set_properties_from_file(species) 35 | 36 | # Get the dust generator properties. 37 | 38 | dust1 = dust_gen(a_max[i] / 1.0e4, 3.5) 39 | """ 40 | dust1 = pdspy.dust.run_opacity_tool(amax=a_max[i], fmax=0.8) 41 | """ 42 | 43 | # Make a label for each line. 44 | 45 | size = species.split('_')[1].split('.')[0] 46 | if (len(size.split('um')) == 2): 47 | label = r"$a_{max} = %s$ $\upmu$m" % size.split('um')[0] 48 | elif (len(size.split('mm')) == 2): 49 | label = "$a_{max} = %s$ mm" % size.split('mm')[0] 50 | elif (len(size.split('cm')) == 2): 51 | label = "$a_{max} = %s$ cm" % size.split('cm')[0] 52 | 53 | # Plot the opacities. 54 | 55 | ax[0,0].loglog(dust.lam*1e4, dust.kabs, label=label) 56 | ax[0,1].loglog(dust.lam*1e4, dust.ksca) 57 | ax[1,0].loglog(dust.lam*1e4, dust.kext) 58 | ax[1,1].semilogx(dust.lam*1e4, dust.albedo) 59 | 60 | ax[0,0].loglog(dust1.lam*1e4, dust1.kabs, '--') 61 | ax[0,1].loglog(dust1.lam*1e4, dust1.ksca, '--') 62 | ax[1,0].loglog(dust1.lam*1e4, dust1.kext, '--') 63 | ax[1,1].semilogx(dust1.lam*1e4, dust1.albedo, '--') 64 | 65 | ax[0,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 66 | ax[0,0].set_ylabel("$\kappa_{abs}$ [cm$^2$ g$^{-1}$]") 67 | ax[0,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 68 | ax[0,1].set_ylabel("$\kappa_{sca}$ [cm$^2$ g$^{-1}$]") 69 | ax[1,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 70 | ax[1,0].set_ylabel("$\kappa_{ext}$ [cm$^2$ g$^{-1}$]") 71 | ax[1,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 72 | ax[1,1].set_ylabel("Albedo") 73 | 74 | ax[0,0].legend(loc="lower left") 75 | 76 | ax[0,0].axis([1e-1,1e5,1e-4,1e5]) 77 | ax[0,1].axis([1e-1,1e5,1e-4,1e5]) 78 | ax[1,0].axis([1e-1,1e5,1e-4,1e5]) 79 | ax[1,1].axis([1e-1,1e5,0,1]) 80 | 81 | fig.set_size_inches((10,10)) 82 | fig.subplots_adjust(wspace=0.25, left=0.08, top=0.97, right=0.98, bottom=0.06) 83 | fig.savefig("diana_plot.pdf") 84 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_diana_wice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pdspy.dust 4 | import matplotlib 5 | import matplotlib.pyplot as plt 6 | 7 | # List of the files to be plotted. 8 | 9 | species_list = ["diana_wice_1um.hdf5", "diana_wice_10um.hdf5", \ 10 | "diana_wice_100um.hdf5", "diana_wice_1mm.hdf5", "diana_wice_1cm.hdf5", \ 11 | "diana_wice_10cm.hdf5"] 12 | 13 | # Maximum dust grain sizes. 14 | 15 | a_max = [1., 10., 100., 1000., 10000., 100000.] 16 | 17 | # Read in the dust generator class. 18 | 19 | dust_gen = pdspy.dust.DustGenerator("diana_wice.hdf5") 20 | 21 | # Change a few of the parameters to make the plot look nice. 22 | 23 | matplotlib.rcParams["font.family"] = "serif" 24 | matplotlib.rcParams["font.size"] = 14 25 | matplotlib.rcParams["text.usetex"] = "True" 26 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 27 | matplotlib.rcParams["legend.fontsize"] = 14 28 | 29 | # Start the plotting. 30 | 31 | fig, ax = plt.subplots(nrows=2, ncols=2) 32 | 33 | for i, species in enumerate(species_list): 34 | dust = pdspy.dust.Dust() 35 | dust.set_properties_from_file(species) 36 | 37 | # Get the dust generator properties. 38 | 39 | dust1 = dust_gen(a_max[i] / 1.0e4, 3.5) 40 | """ 41 | dust1 = pdspy.dust.run_opacity_tool(amax=a_max[i], fmax=0.8) 42 | """ 43 | 44 | # Make a label for each line. 45 | 46 | size = species.split('_')[2].split('.')[0] 47 | if (len(size.split('um')) == 2): 48 | label = r"$a_{max} = %s$ $\upmu$m" % size.split('um')[0] 49 | elif (len(size.split('mm')) == 2): 50 | label = "$a_{max} = %s$ mm" % size.split('mm')[0] 51 | elif (len(size.split('cm')) == 2): 52 | label = "$a_{max} = %s$ cm" % size.split('cm')[0] 53 | 54 | # Plot the opacities. 55 | 56 | ax[0,0].loglog(dust.lam*1e4, dust.kabs, label=label) 57 | ax[0,1].loglog(dust.lam*1e4, dust.ksca) 58 | ax[1,0].loglog(dust.lam*1e4, dust.kext) 59 | ax[1,1].semilogx(dust.lam*1e4, dust.albedo) 60 | 61 | ax[0,0].loglog(dust1.lam*1e4, dust1.kabs, '--') 62 | ax[0,1].loglog(dust1.lam*1e4, dust1.ksca, '--') 63 | ax[1,0].loglog(dust1.lam*1e4, dust1.kext, '--') 64 | ax[1,1].semilogx(dust1.lam*1e4, dust1.albedo, '--') 65 | 66 | ax[0,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 67 | ax[0,0].set_ylabel("$\kappa_{abs}$ [cm$^2$ g$^{-1}$]") 68 | ax[0,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 69 | ax[0,1].set_ylabel("$\kappa_{sca}$ [cm$^2$ g$^{-1}$]") 70 | ax[1,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 71 | ax[1,0].set_ylabel("$\kappa_{ext}$ [cm$^2$ g$^{-1}$]") 72 | ax[1,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 73 | ax[1,1].set_ylabel("Albedo") 74 | 75 | ax[0,0].legend(loc="lower left") 76 | 77 | ax[0,0].axis([1e-1,1e5,1e-4,1e5]) 78 | ax[0,1].axis([1e-1,1e5,1e-4,1e5]) 79 | ax[1,0].axis([1e-1,1e5,1e-4,1e5]) 80 | ax[1,1].axis([1e-1,1e5,0,1]) 81 | 82 | fig.set_size_inches((10,10)) 83 | fig.subplots_adjust(wspace=0.25, left=0.08, top=0.97, right=0.98, bottom=0.06) 84 | fig.savefig("diana_wice_plot.pdf") 85 | -------------------------------------------------------------------------------- /pdspy/dust/data/plot_pollack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pdspy.dust import Dust, DustGenerator 4 | import matplotlib 5 | import matplotlib.pyplot as plt 6 | 7 | # List of the files to be plotted. 8 | 9 | #species_list = ["pollack_1um.hdf5", "pollack_10um.hdf5", "pollack_100um.hdf5", \ 10 | # "pollack_1mm.hdf5", "pollack_1cm.hdf5", "pollack_10cm.hdf5"] 11 | species_list = ["pollack_1um.hdf5", "pollack_10um.hdf5", "pollack_100um.hdf5", \ 12 | "pollack_1mm.hdf5", "pollack_1cm.hdf5", "pollack_10cm.hdf5"] 13 | 14 | # Maximum dust grain sizes. 15 | 16 | a_max = [1., 10., 100., 1000., 10000., 100000.] 17 | 18 | # Read in the dust generator class. 19 | 20 | dust_gen = DustGenerator("pollack_new.hdf5") 21 | 22 | # Change a few of the parameters to make the plot look nice. 23 | 24 | matplotlib.rcParams["font.family"] = "serif" 25 | matplotlib.rcParams["font.size"] = 14 26 | matplotlib.rcParams["text.usetex"] = "True" 27 | matplotlib.rcParams["text.latex.preamble"] = r"\usepackage{upgreek}" 28 | matplotlib.rcParams["legend.fontsize"] = 14 29 | 30 | # Start the plotting. 31 | 32 | fig, ax = plt.subplots(nrows=2, ncols=2) 33 | 34 | for i, species in enumerate(species_list): 35 | dust = Dust() 36 | dust.set_properties_from_file(species) 37 | 38 | # Get the dust generator properties. 39 | 40 | dust1 = dust_gen(a_max[i] / 1.0e4, 2.5) 41 | 42 | # Make a label for each line. 43 | 44 | size = species.split('_')[1].split('.')[0] 45 | if (len(size.split('um')) == 2): 46 | label = r"$a_{max} = %s$ $\upmu$m" % size.split('um')[0] 47 | elif (len(size.split('mm')) == 2): 48 | label = "$a_{max} = %s$ mm" % size.split('mm')[0] 49 | elif (len(size.split('cm')) == 2): 50 | label = "$a_{max} = %s$ cm" % size.split('cm')[0] 51 | 52 | # Plot the opacities. 53 | 54 | ax[0,0].loglog(dust.lam*1e4, dust.kabs, label=label) 55 | ax[0,1].loglog(dust.lam*1e4, dust.ksca) 56 | ax[1,0].loglog(dust.lam*1e4, dust.kext) 57 | ax[1,1].semilogx(dust.lam*1e4, dust.albedo) 58 | 59 | ax[0,0].loglog(dust1.lam*1e4, dust1.kabs, '--') 60 | ax[0,1].loglog(dust1.lam*1e4, dust1.ksca, '--') 61 | ax[1,0].loglog(dust1.lam*1e4, dust1.kext, '--') 62 | ax[1,1].semilogx(dust1.lam*1e4, dust1.albedo, '--') 63 | 64 | ax[0,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 65 | ax[0,0].set_ylabel("$\kappa_{abs}$ [cm$^2$ g$^{-1}$]") 66 | ax[0,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 67 | ax[0,1].set_ylabel("$\kappa_{sca}$ [cm$^2$ g$^{-1}$]") 68 | ax[1,0].set_xlabel(r"$\lambda$ [$\upmu$m]") 69 | ax[1,0].set_ylabel("$\kappa_{ext}$ [cm$^2$ g$^{-1}$]") 70 | ax[1,1].set_xlabel(r"$\lambda$ [$\upmu$m]") 71 | ax[1,1].set_ylabel("Albedo") 72 | 73 | ax[0,0].legend(loc="lower left") 74 | 75 | ax[0,0].axis([1e-1,1e5,1e-4,1e5]) 76 | ax[0,1].axis([1e-1,1e5,1e-4,1e5]) 77 | ax[1,0].axis([1e-1,1e5,1e-4,1e5]) 78 | ax[1,1].axis([1e-1,1e5,0,1]) 79 | 80 | fig.set_size_inches((10,10)) 81 | fig.subplots_adjust(wspace=0.25, left=0.08, top=0.97, right=0.98, bottom=0.06) 82 | fig.savefig("pollack_plot.pdf") 83 | -------------------------------------------------------------------------------- /.github/workflows/python-build-test-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Test build pdspy package across platforms and versions 10 | 11 | on: 12 | push: 13 | branches: [master] 14 | paths-ignore: 15 | - 'docs/*' 16 | - '.readthedocs.yaml' 17 | release: 18 | branches: [master] 19 | 20 | jobs: 21 | build_wheels: 22 | name: Test build status 23 | 24 | runs-on: ${{ matrix.os }} 25 | 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, macos-latest] 29 | 30 | steps: 31 | - uses: actions/checkout@v2 32 | 33 | #- name: Symlink gfortran (macOS) 34 | # if: runner.os == 'macOS' 35 | # run: | 36 | # make sure gfortran is available 37 | # https://github.com/actions/virtual-environments/issues/2524 38 | # https://github.com/cbg-ethz/dce/blob/master/.github/workflows/pkgdown.yaml 39 | # sudo ln -s /usr/local/bin/gfortran-11 /usr/local/bin/gfortran 40 | # sudo mkdir /usr/local/gfortran 41 | # sudo ln -s /usr/local/Cellar/gcc@11/*/lib/gcc/11 /usr/local/gfortran/lib 42 | # gfortran --version 43 | 44 | - name: Build wheels 45 | uses: pypa/cibuildwheel@v2.19.2 46 | 47 | - uses: actions/upload-artifact@v2 48 | with: 49 | path: ./wheelhouse/*.whl 50 | 51 | build_sdist: 52 | name: Build source distribution 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v2 56 | 57 | - uses: actions/setup-python@v2 58 | name: Install Python 59 | with: 60 | python-version: '3.8' 61 | 62 | - name: Install numpy and Cython 63 | run: 64 | pip install numpy 65 | pip install cython 66 | 67 | - name: Build sdist 68 | run: python setup.py sdist 69 | 70 | - uses: actions/upload-artifact@v2 71 | with: 72 | path: dist/*.tar.gz 73 | 74 | upload_pypi: 75 | needs: [build_wheels, build_sdist] 76 | runs-on: ubuntu-latest 77 | # upload to PyPI on every tag starting with 'v' 78 | #if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') 79 | # alternatively, to publish when a GitHub Release is created, use the following rule: 80 | if: github.event_name == 'release' && github.event.action == 'published' 81 | steps: 82 | - uses: actions/download-artifact@v2 83 | with: 84 | name: artifact 85 | path: dist 86 | 87 | - uses: pypa/gh-action-pypi-publish@v1.4.2 88 | with: 89 | user: __token__ 90 | password: ${{ secrets.PYPI_API_TOKEN }} 91 | -------------------------------------------------------------------------------- /pdspy/imaging/readimfits.py: -------------------------------------------------------------------------------- 1 | from ..constants.physics import c 2 | from ..constants.astronomy import arcsec 3 | from .libimaging import Image 4 | from astropy.utils.exceptions import AstropyWarning 5 | import astropy.io.fits as fits 6 | import astropy.wcs as wcs 7 | import warnings 8 | import numpy 9 | 10 | def readimfits(filename): 11 | 12 | # Open the fits file. 13 | 14 | data = fits.open(filename) 15 | 16 | # Figure out the dimensions of each axis and create an array to put the data 17 | # into, and put the data into that array. 18 | 19 | if len(data[0].data.shape) == 4: 20 | npol, nfreq, ny, nx = data[0].data.shape 21 | elif len(data[0].data.shape) == 3: 22 | nfreq, ny, nx = data[0].data.shape 23 | npol = 1 24 | elif len(data[0].data.shape) == 2: 25 | ny, nx = data[0].data.shape 26 | npol, nfreq = 1, 1 27 | 28 | image = numpy.zeros((ny,nx,nfreq,npol)) 29 | 30 | for i in range(npol): 31 | for j in range(nfreq): 32 | if len(data[0].data.shape) == 4: 33 | image[:,:,j,i] = data[0].data[i,j,:,:].reshape(ny,nx) 34 | if len(data[0].data.shape) == 3: 35 | image[:,:,j,i] = data[0].data[j,:,:].reshape(ny,nx) 36 | if len(data[0].data.shape) == 2: 37 | image[:,:,j,i] = data[0].data[:,:].reshape(ny,nx) 38 | 39 | # Read in the x and y coordinate information, including the WCS info if it 40 | # is available. 41 | 42 | header = data[0].header 43 | 44 | # Check whether there is a CASA beam table. 45 | 46 | if len(data) > 1: 47 | if data[1].columns[0].name == 'BMAJ': 48 | header["BMAJ"] = data[1].data["BMAJ"].mean()*arcsec * 180./numpy.pi 49 | header["BMIN"] = data[1].data["BMIN"].mean()*arcsec * 180./numpy.pi 50 | header["BPA"] = data[1].data["BPA"].mean() 51 | 52 | # Turn off the WCS warnings that come from the PCX_Y values because they 53 | # are annoying... 54 | with warnings.catch_warnings(): 55 | warnings.simplefilter('ignore', category=AstropyWarning) 56 | 57 | w = wcs.WCS(header) 58 | try: 59 | w = w.dropaxis(2) 60 | except: 61 | pass 62 | try: 63 | w = w.dropaxis(3) 64 | except: 65 | pass 66 | 67 | #x, y = numpy.meshgrid(numpy.linspace(0,nx-1,nx), numpy.linspace(0,ny-1,ny)) 68 | x, y = None, None 69 | 70 | if len(data[0].data.shape) >= 3: 71 | if header["CTYPE3"] in ["VELOCITY","VRAD"]: 72 | v0 = data[0].header["CRVAL3"] 73 | dv = data[0].header["CDELT3"] 74 | n0 = data[0].header["CRPIX3"] 75 | velocity = (numpy.arange(nfreq)-(n0-1))*dv/1000.+v0/1000. 76 | 77 | nu0 = data[0].header["RESTFREQ"] 78 | freq = nu0 - velocity*1e5 * nu0 / c 79 | elif header["CTYPE3"] == "FREQ": 80 | nu0 = data[0].header["CRVAL3"] 81 | dnu = data[0].header["CDELT3"] 82 | n0 = data[0].header["CRPIX3"] 83 | freq = (numpy.arange(nfreq)-(n0-1))*dnu + nu0 84 | 85 | velocity = None 86 | else: 87 | velocity, freq = None, None 88 | 89 | data.close() 90 | 91 | return Image(image, x=x, y=y, header=header, wcs=w, velocity=velocity, \ 92 | freq=freq) 93 | -------------------------------------------------------------------------------- /pdspy/imaging/imaging.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import astropy 3 | import h5py 4 | from ..constants.physics import c 5 | 6 | class Image: 7 | 8 | def __init__(self,image=None,x=None,y=None,header=None,wave=None, \ 9 | freq=None,unc=None,velocity=None): 10 | 11 | if (type(image) != type(None)): 12 | self.image = image 13 | 14 | if (type(x) != type(None)): 15 | self.x = x 16 | self.y = y 17 | if (type(header) != type(None)): 18 | self.header = header 19 | if (type(unc) != type(None)): 20 | self.unc = unc 21 | if (type(velocity) != type(None)): 22 | self.velocity = velocity 23 | 24 | if (type(wave) == type(None)) and (type(freq) != type(None)): 25 | self.freq = freq 26 | self.wave = c / freq 27 | elif (type(wave) != type(None)) and (type(freq) == type(None)): 28 | self.wave = wave 29 | self.freq = c / wave 30 | elif (type(wave) != type(None)) and (type(freq) != type(None)): 31 | self.wave = wave 32 | self.freq = freq 33 | 34 | def asFITS(self): 35 | hdulist = astropy.io.fits.HDUList([]) 36 | for i in range(self.image[0,0,:].size): 37 | hdu = astropy.io.fits.PrimaryHDU(self.image[:,:,i]) 38 | 39 | if hasattr(self, "header"): 40 | hdu.header = self.header[i] 41 | 42 | hdulist.append(hdu) 43 | 44 | return hdulist 45 | 46 | def read(self, filename=None, usefile=None): 47 | if (usefile == None): 48 | f = h5py.File(filename, "r") 49 | else: 50 | f = usefile 51 | 52 | if ('x' in f): 53 | x = f['x'][...] 54 | y = f['y'][...] 55 | else: 56 | x = None 57 | y = None 58 | 59 | if ('freq' in f): 60 | freq = f['freq'][...] 61 | else: 62 | freq = None 63 | 64 | image = f['image'][...] 65 | 66 | if ('unc' in f): 67 | unc = f['unc'][...] 68 | else: 69 | unc = None 70 | 71 | self.__init__(image, x=x, y=y, unc=unc, freq=freq) 72 | 73 | if (usefile == None): 74 | f.close() 75 | 76 | def write(self, filename=None, usefile=None): 77 | if (usefile == None): 78 | f = h5py.File(filename, "w") 79 | else: 80 | f = usefile 81 | 82 | if hasattr(self, "x"): 83 | x_dset = f.create_dataset("x", (self.x.size,), dtype='f') 84 | x_dset[...] = self.x 85 | y_dset = f.create_dataset("y", (self.y.size,), dtype='f') 86 | y_dset[...] = self.y 87 | 88 | if hasattr(self, "freq"): 89 | freq_dset = f.create_dataset("freq", (self.freq.size,), dtype='f') 90 | freq_dset[...] = self.freq 91 | 92 | image_dset = f.create_dataset("image", self.image.shape, dtype='f') 93 | image_dset[...] = self.image 94 | 95 | if hasattr(self, "unc"): 96 | unc_dset = f.create_dataset("uncertainty", self.unc.shape, \ 97 | dtype='f') 98 | unc_dset[...] = self.unc 99 | 100 | if (usefile == None): 101 | f.close() 102 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools.extension import Extension 3 | from Cython.Build import cythonize 4 | import numpy as np 5 | 6 | # Set up the extension modules. 7 | 8 | libinterferometry = cythonize([\ 9 | Extension('pdspy.interferometry.libinterferometry',\ 10 | ["pdspy/interferometry/libinterferometry.pyx"],\ 11 | libraries=["m"], extra_compile_args=['-ffast-math'], \ 12 | include_dirs=[np.get_include()], \ 13 | define_macros=[('NPY_NO_DEPRECATED_API', 0)])])[0] 14 | 15 | libimaging = cythonize([Extension('pdspy.imaging.libimaging',\ 16 | ["pdspy/imaging/libimaging.pyx"], libraries=[], \ 17 | extra_compile_args=[], include_dirs=[np.get_include()])])[0] 18 | 19 | bhmie = Extension('pdspy.dust.bhmie', sources=['pdspy/dust/bhmie.f90']) 20 | 21 | bhcoat = Extension('pdspy.dust.bhcoat', sources=['pdspy/dust/bhcoat.f90']) 22 | 23 | dmilay = Extension('pdspy.dust.dmilay', sources=['pdspy/dust/DMiLay.f90']) 24 | 25 | read = cythonize([Extension('pdspy.radmc3d.read', ["pdspy/radmc3d/read.pyx"], \ 26 | libraries=[], extra_compile_args=[], \ 27 | include_dirs=[np.get_include()])])[0] 28 | 29 | # Now define the setup for the package. 30 | 31 | setup(name="pdspy", \ 32 | version="2.0.8", \ 33 | author="Patrick Sheehan", \ 34 | author_email="psheehan@northwestern.edu", \ 35 | description="Radiative transfer modeling of protoplanetary disks", \ 36 | long_description=open("README.md","r").read(), \ 37 | long_description_content_type="text/markdown", \ 38 | url="https://github.com/psheehan/pdspy", \ 39 | packages=[\ 40 | "pdspy",\ 41 | "pdspy.constants", \ 42 | "pdspy.dust",\ 43 | "pdspy.gas",\ 44 | "pdspy.imaging",\ 45 | "pdspy.interferometry", \ 46 | "pdspy.mcmc",\ 47 | "pdspy.misc",\ 48 | "pdspy.modeling",\ 49 | "pdspy.plotting", \ 50 | "pdspy.radmc3d",\ 51 | "pdspy.spectroscopy",\ 52 | "pdspy.stars",\ 53 | "pdspy.statistics", \ 54 | "pdspy.table", \ 55 | "pdspy.utils"], \ 56 | package_dir={\ 57 | "pdspy.dust": 'pdspy/dust', \ 58 | "pdspy.gas": 'pdspy/gas', \ 59 | "pdspy.spectroscopy": 'pdspy/spectroscopy', \ 60 | "pdspy.stars": 'pdspy/stars'}, \ 61 | package_data={\ 62 | 'pdspy.dust': ['data/*','reddening/*.dat'], \ 63 | 'pdspy.imaging': ['*.pyx'], \ 64 | 'pdspy.interferometry': ['*.pyx'], \ 65 | 'pdspy.gas': ['data/*.dat'], \ 66 | 'pdspy.radmc3d': ['*.pyx'], \ 67 | 'pdspy.spectroscopy': ['btsettle_data/*.txt']}, \ 68 | #ext_modules=[libinterferometry, libimaging, bhmie, \ 69 | #bhcoat, dmilay, read], \ 70 | ext_modules=[libinterferometry, libimaging, read], \ 71 | scripts=[\ 72 | 'bin/config_template.py',\ 73 | 'bin/upgrade_to_pdspy2.py',\ 74 | 'bin/generate_surrogate_model.py',\ 75 | 'bin/disk_model_emcee3.py',\ 76 | 'bin/disk_model_nested.py',\ 77 | 'bin/disk_model_dynesty.py',\ 78 | 'bin/disk_model_powerlaw.py',\ 79 | 'bin/flared_model_emcee3.py',\ 80 | 'bin/flared_model_nested.py', \ 81 | 'bin/flared_model_dynesty.py'], \ 82 | install_requires=['numpy','scipy','matplotlib','emcee','corner',\ 83 | 'hyperion','h5py','mpi4py','Cython','astropy','schwimmbad','dynesty',\ 84 | 'scikit-learn']) 85 | -------------------------------------------------------------------------------- /pdspy/modeling/PringleDisk.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import h5py 3 | from scipy.integrate import trapz 4 | from ..constants.physics import G, m_p 5 | from ..constants.astronomy import AU, M_sun 6 | from ..constants.math import pi 7 | from ..dust import Dust 8 | from ..gas import Gas 9 | from .Disk import Disk 10 | 11 | class PringleDisk(Disk): 12 | def surface_density(self, r, normalize=True): 13 | # Get the disk parameters. 14 | 15 | rr = r * AU 16 | rin = self.rmin * AU 17 | rout = self.rmax * AU 18 | mass = self.mass * M_sun 19 | gamma = self.plrho - self.plh 20 | if self.gamma_taper != None: 21 | gamma_taper = self.gamma_taper 22 | else: 23 | gamma_taper = gamma 24 | 25 | # Set up the surface density. 26 | 27 | Sigma0 = (2-gamma)*mass/(2*pi*rout**2)*numpy.exp((rin/rout)**(2-gamma)) 28 | 29 | Sigma = Sigma0 * (rr/rout)**(-gamma) * \ 30 | numpy.exp(-(rr/rout)**(2-gamma_taper)) 31 | 32 | Sigma[r <= rin/AU] = 0e0 33 | 34 | # In case of r == 0 (a singularity), get the value from slightly off 0. 35 | 36 | dr = rr[r > 0].min() 37 | Sigma[r == 0] = Sigma0 * (0.7*dr/rout)**(-gamma) * \ 38 | numpy.exp(-(0.7*dr/rout)**(2-gamma)) 39 | 40 | # Add gaps to the disk. 41 | 42 | for i in range(len(self.gap_rin)): 43 | if self.gaussian_gaps: 44 | gap_r = (self.gap_rin[i] + self.gap_rout[i])/2 45 | gap_w = self.gap_rout[i] - self.gap_rin[i] 46 | 47 | Sigma /= 1 + 1./self.gap_delta[i] * numpy.exp(-4*numpy.log(2.)*\ 48 | (r - gap_r)**2 / gap_w**2) 49 | else: 50 | Sigma[(r >= self.gap_rin[i]) & \ 51 | (r <= self.gap_rout[i])] *= self.gap_delta[i] 52 | 53 | ##### Normalize the surface density correctly. 54 | 55 | if normalize: 56 | r_high = numpy.logspace(numpy.log10(self.rmin), \ 57 | numpy.log10(10*self.rmax), 1000) 58 | Sigma_high = self.surface_density(r_high, normalize=False) 59 | 60 | scale = mass / (2*numpy.pi*trapz(r_high*AU*Sigma_high, r_high*AU)) 61 | 62 | Sigma *= scale 63 | 64 | return Sigma 65 | 66 | def scale_height(self, r): 67 | return self.h0 * AU * (r / self.rmax)**self.plh 68 | 69 | def temperature(self, r, theta, phi): 70 | ##### Disk Parameters 71 | 72 | rin = self.rmin * AU 73 | rout = self.rmax * AU 74 | t0 = self.t0 75 | plt = self.plt 76 | 77 | ##### Set up the coordinates 78 | 79 | rt, tt, pp = numpy.meshgrid(r*AU, theta, phi,indexing='ij') 80 | 81 | rr = rt*numpy.sin(tt) 82 | zz = rt*numpy.cos(tt) 83 | 84 | ##### Make the dust density model for a protoplanetary disk. 85 | 86 | t = t0 * (rr / (1*AU))**(-plt) 87 | 88 | t[rr <= rin] = 0e0 89 | 90 | t[t > 10000] = 10000 91 | 92 | return t 93 | 94 | def temperature_1d(self, r): 95 | rin = self.rmin * AU 96 | rout = self.rmax * AU 97 | t0 = self.t0 98 | plt = self.plt 99 | 100 | T = t0 * r**(-plt) 101 | 102 | T[r <= rin/AU] = 0.0 103 | 104 | dr = r[r > 0].min() 105 | T[r == 0] = t0 * (0.7*dr)**(-plt) 106 | 107 | return T 108 | -------------------------------------------------------------------------------- /pdspy/utils/propose_point_emcee.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | def propose_point_emcee(parameters, model="disk"): 4 | m_env = numpy.random.uniform(-6., parameters["logM_env"]["limits"][1],1)[0] 5 | 6 | # Set up R_env, R_disk, and R_in as they depend on eachother. 7 | 8 | if model == "disk": 9 | r_env = numpy.random.uniform(max(parameters["logR_env"]["limits"][0], \ 10 | 0.5*m_env+4.), parameters["logR_env"]["limits"][1],1)[0] 11 | elif model == "flared": 12 | r_env = numpy.random.uniform(parameters["logR_env"]["limits"][0],\ 13 | parameters["logR_env"]["limits"][1],1)[0] 14 | 15 | r_disk = numpy.random.uniform(max(numpy.log10(5.), \ 16 | parameters["logR_disk"]["limits"][0]), min(numpy.log10(500.), \ 17 | r_env, parameters["logR_disk"]["limits"][1]),1)[0] 18 | r_in = numpy.random.uniform(parameters["logR_in"]["limits"][0],\ 19 | min(parameters["logR_in"]["limits"][1], \ 20 | numpy.log10((10.**r_disk)/2)),1)[0] 21 | 22 | # Also set up R_cav as it depends on those as well. 23 | 24 | r_cav = numpy.random.uniform(max(r_in,parameters["logR_cav"]["limits"][0]),\ 25 | min(numpy.log10(0.75*10.**r_disk),\ 26 | parameters["logR_cav"]["limits"][1]),1)[0] 27 | 28 | # Same thing for R_gap and w_gap. 29 | 30 | r_gap1 = numpy.random.uniform(numpy.log10(10.**r_in+\ 31 | parameters["w_gap1"]["limits"][0]/2), \ 32 | numpy.log10(0.75*10.**r_disk),1)[0] 33 | 34 | w_gap1 = numpy.random.uniform(parameters["w_gap1"]["limits"][0],\ 35 | min(parameters["w_gap1"]["limits"][1],\ 36 | 2*(10.**r_gap1-10.**r_in)), 1)[0] 37 | 38 | # If we are using logTatm0 and logTmid0, they depend on eachother. 39 | 40 | if "logTatm0" in parameters: 41 | tatm0 = numpy.random.uniform(parameters["logTatm0"]\ 42 | ["limits"][0],parameters["logTatm0"]["limits"][1],1)[0] 43 | tmid0 = numpy.random.uniform(parameters["logTmid0"]["limits"][0],\ 44 | min(parameters["logTatm0"]["limits"][1], tatm0),1)[0] 45 | 46 | # Loop through and generate the point proposal. 47 | 48 | p = [] 49 | 50 | for key in sorted(parameters.keys()): 51 | if parameters[key]["fixed"]: 52 | pass 53 | elif key == "logR_in": 54 | p.append(r_in) 55 | elif key == "logR_disk": 56 | p.append(r_disk) 57 | elif key == "logR_env": 58 | p.append(r_env) 59 | elif key == "logR_cav": 60 | p.append(r_cav) 61 | elif key == "logR_gap1": 62 | p.append(r_gap1) 63 | elif key == "w_gap1": 64 | p.append(w_gap1) 65 | elif key == "logM_disk" and model == "disk": 66 | p.append(numpy.random.uniform(-6.,parameters[key]["limits"][1], \ 67 | 1)[0]) 68 | elif key == "logM_env": 69 | p.append(m_env) 70 | elif key == "h_0": 71 | p.append(numpy.random.uniform(parameters[key]["limits"][0], \ 72 | 0.2, 1)[0]) 73 | elif key == "logTatm0": 74 | p.append(tatm0) 75 | elif key == "logTmid0": 76 | p.append(tmid0) 77 | elif key[0:8] == "flux_unc": 78 | p.append(numpy.random.normal(parameters[key]["value"], 0.001, 1)[0]) 79 | else: 80 | p.append(numpy.random.uniform(parameters[key]["limits"][0], \ 81 | parameters[key]["limits"][1], 1)[0]) 82 | 83 | # Return the proposed point. 84 | 85 | return p 86 | -------------------------------------------------------------------------------- /examples/run_radiative_transfer_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pdspy.modeling as modeling 4 | import pdspy.interferometry as uv 5 | import pdspy.dust as dust 6 | 7 | import matplotlib.pyplot as plt 8 | 9 | import numpy 10 | 11 | # Set up a radiative transfer model: 12 | 13 | m = modeling.YSOModel() 14 | 15 | # Add a grid. Spherical typically makes the most sense. 16 | 17 | nr, ntheta, nphi = 100, 100, 2 18 | rmin, rmax = 0.1, 300 19 | 20 | m.set_spherical_grid(rmin, rmax, nr, ntheta, nphi, code="radmc3d") 21 | 22 | # Add a star to the model. 23 | 24 | m.add_star(mass=0.5, luminosity=1., temperature=4000.) 25 | 26 | #Set up dust properties for the disk. 27 | 28 | dust_gen = dust.DustGenerator(dust.__path__[0]+"/data/diana_wice.hdf5") 29 | 30 | a_max = 100 # microns 31 | p = 3.5 32 | 33 | d = dust_gen(a_max / 1e4, p) # dust_gen wants units of cm 34 | 35 | # Add a disk to the model. 36 | 37 | m.add_disk(mass=0.01, rmin=0.1, rmax=50., plrho=1., h0=0.1, plh=1., dust=d) 38 | 39 | # Finally, we need to set the wavelength grid. 40 | 41 | m.grid.set_wavelength_grid(0.1, 1.0e5, 500, log=True) 42 | 43 | # Now that we have the model set up, we need to run the radiative transfer 44 | # temperature calculation. 45 | 46 | m.run_thermal(nphot=1e6, modified_random_walk=True, verbose=True, setthreads=1,\ 47 | code="radmc3d") 48 | 49 | # With that, we can now run an image. 50 | 51 | m.run_image(name="870um", nphot=1e5, npix=256, pixelsize=0.01, lam="870", \ 52 | incl=45, pa=30, dpc=140, code="radmc3d", verbose=True, \ 53 | setthreads=2) 54 | 55 | # Or visibilities: 56 | 57 | m.run_visibilities(name="870um", nphot=1e5, npix=256, pixelsize=0.01, \ 58 | lam="870", incl=45, pa=30, dpc=140, code="radmc3d", verbose=True, \ 59 | setthreads=2) 60 | 61 | # Or a spectrum: 62 | 63 | m.set_camera_wavelength(numpy.logspace(-1, 4, 50)) 64 | 65 | m.run_sed(name="SED", nphot=1e4, loadlambda=True, incl=45, pa=30, dpc=140, \ 66 | code="radmc3d", verbose=True, setthreads=2) 67 | 68 | # You can access the synthetic observations through: 69 | 70 | m.images 71 | m.visibilities 72 | m.spectra 73 | 74 | # For example: 75 | 76 | plt.loglog(m.spectra["SED"].wave, m.spectra["SED"].flux, "b-") 77 | plt.show() 78 | 79 | # Or an image. Note that an image is actually a 4D array - the last two dimensions are for frequency (in case of image cubes) and polarization. 80 | 81 | plt.imshow(m.images["870um"].image[:,:,0,0], origin="lower", \ 82 | interpolation="nearest") 83 | plt.show() 84 | 85 | # Image objects also have a few other parts that may be of use: 86 | 87 | m.images["870um"].x 88 | m.images["870um"].y 89 | m.images["870um"].freq 90 | 91 | # Finally, lets average the visibility data azimuthally and plot it. Binsize is 92 | # in units of klambda. As is m1d.uvdist 93 | 94 | m1d = uv.average(m.visibilities["870um"], gridsize=10000, binsize=3500, \ 95 | radial=True) 96 | 97 | plt.semilogx(m1d.uvdist, m1d.amp, "-") 98 | 99 | plt.show() 100 | 101 | # Visibility classes also have a few other parts that may be of use: 102 | 103 | m.visibilities["870um"].u 104 | m.visibilities["870um"].v 105 | m.visibilities["870um"].uvdist 106 | m.visibilities["870um"].real 107 | m.visibilities["870um"].imag 108 | m.visibilities["870um"].amp 109 | m.visibilities["870um"].weights 110 | m.visibilities["870um"].freq 111 | 112 | # Finally, if you want to access the actual density and temperature structures, 113 | # you can find them here: 114 | 115 | m.grid.r 116 | m.grid.theta 117 | m.grid.phi 118 | 119 | m.grid.density[0] # should be (r, theta, phi) 120 | m.grid.temperature[0] 121 | -------------------------------------------------------------------------------- /pdspy/dust/data/optical_constants/graphite.txt: -------------------------------------------------------------------------------- 1 | 5.1825018009E+02 1.7900000000E+00 2.5554500000E-02 2 | 4.5767637703E+02 1.7900000000E+00 2.5756600000E-02 3 | 4.0418248031E+02 1.7900000000E+00 2.6165700000E-02 4 | 3.5916572984E+02 1.7900000000E+00 2.6372700000E-02 5 | 3.2315502716E+02 1.7900000000E+00 2.6791500000E-02 6 | 2.8538487004E+02 1.7900000000E+00 2.7432300000E-02 7 | 2.5202819691E+02 1.7900000000E+00 2.8310600000E-02 8 | 2.2535465188E+02 1.7900000000E+00 2.8987700000E-02 9 | 2.0150402605E+02 1.7900000000E+00 2.9681100000E-02 10 | 1.8017758303E+02 1.7900000000E+00 3.1117800000E-02 11 | 1.5813251821E+02 1.7900000000E+00 3.2624200000E-02 12 | 1.4125371144E+02 1.7900000000E+00 3.4185000000E-02 13 | 1.2589257838E+02 1.7900000000E+00 3.5619000000E-02 14 | 1.1220183764E+02 1.7900000000E+00 3.6350000000E-02 15 | 1.0000000000E+02 1.7900000000E+00 3.7655000000E-02 16 | 8.9124971034E+01 1.7870000000E+00 3.7642000000E-02 17 | 7.9432533977E+01 1.7840000000E+00 3.5711000000E-02 18 | 7.0794455378E+01 1.7810000000E+00 3.4111000000E-02 19 | 6.3095861542E+01 1.7780000000E+00 3.2733000000E-02 20 | 5.6234113863E+01 1.7740000000E+00 3.2593000000E-02 21 | 5.0118781512E+01 1.7710000000E+00 3.3774000000E-02 22 | 4.4668381933E+01 1.7670000000E+00 3.5587000000E-02 23 | 3.9360471066E+01 1.7630000000E+00 3.7998300000E-02 24 | 3.4622801885E+01 1.7590000000E+00 4.0870900000E-02 25 | 3.1046837259E+01 1.7560000000E+00 4.3761000000E-02 26 | 2.7485515133E+01 1.7530000000E+00 4.6642500000E-02 27 | 2.4646811196E+01 1.7500000000E+00 4.9487800000E-02 28 | 2.2101254688E+01 1.7480000000E+00 5.2506600000E-02 29 | 1.9818619990E+01 1.7460000000E+00 5.5456400000E-02 30 | 1.7321656227E+01 1.7440000000E+00 5.8572000000E-02 31 | 1.5124016939E+01 1.7420000000E+00 6.3214000000E-02 32 | 1.3663050057E+01 1.7410000000E+00 6.8382900000E-02 33 | 1.2416190713E+01 1.7380000000E+00 7.4041000000E-02 34 | 1.1044842059E+01 1.7340000000E+00 8.2863000000E-02 35 | 1.0044194456E+01 1.7280000000E+00 9.1025000000E-02 36 | 8.9126559715E+00 1.7160000000E+00 1.0090000000E-01 37 | 7.7760497667E+00 1.6970000000E+00 1.0680000000E-01 38 | 6.9204152249E+00 1.6810000000E+00 1.0400000000E-01 39 | 6.1538461538E+00 1.6700000000E+00 9.6063000000E-02 40 | 5.3966540745E+00 1.6660000000E+00 8.8005000000E-02 41 | 4.8007681229E+00 1.6670000000E+00 8.5660000000E-02 42 | 4.4286979628E+00 1.6680000000E+00 8.7206000000E-02 43 | 3.9401103231E+00 1.6680000000E+00 9.3402000000E-02 44 | 3.5486160397E+00 1.6640000000E+00 1.0050000000E-01 45 | 3.1625553447E+00 1.6550000000E+00 1.0480000000E-01 46 | 2.8153153153E+00 1.6500000000E+00 1.0460000000E-01 47 | 2.5233409034E+00 1.6490000000E+00 1.0530000000E-01 48 | 2.2609088854E+00 1.6510000000E+00 1.0960000000E-01 49 | 1.9968051118E+00 1.6540000000E+00 1.1880000000E-01 50 | 1.7636684303E+00 1.6550000000E+00 1.3190000000E-01 51 | 1.5790304753E+00 1.6560000000E+00 1.4640000000E-01 52 | 1.4042971493E+00 1.6560000000E+00 1.6490000000E-01 53 | 1.2642225032E+00 1.6550000000E+00 1.8420000000E-01 54 | 1.1270145385E+00 1.6530000000E+00 2.0890000000E-01 55 | 1.0032102729E+00 1.6480000000E+00 2.3840000000E-01 56 | 8.9325591782E-01 1.6410000000E+00 2.7310000000E-01 57 | 7.9434426881E-01 1.6280000000E+00 3.1480000000E-01 58 | 7.0796460177E-01 1.6070000000E+00 3.6250000000E-01 59 | 6.3095463436E-01 1.5730000000E+00 4.1570000000E-01 60 | 5.6433408578E-01 1.5240000000E+00 4.6630000000E-01 61 | 4.9895220038E-01 1.4520000000E+00 5.1260000000E-01 62 | 4.4668780989E-01 1.3750000000E+00 5.4220000000E-01 63 | 3.9810502010E-01 1.2840000000E+00 5.5570000000E-01 64 | 3.5481124042E-01 1.1900000000E+00 5.4510000000E-01 65 | 3.1622553205E-01 1.1070000000E+00 5.1100000000E-01 66 | 2.8184098532E-01 1.0440000000E+00 4.6380000000E-01 67 | 2.5118685790E-01 9.9920000000E-01 4.1830000000E-01 68 | 2.2387391421E-01 9.6090000000E-01 3.8240000000E-01 69 | 1.9952513019E-01 9.1460000000E-01 3.4690000000E-01 70 | -------------------------------------------------------------------------------- /pdspy/plotting/plot_2D_visibilities.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import matplotlib.ticker as ticker 3 | 4 | def plot_2D_visibilities(visibilities, model, parameters, params, index=0, \ 5 | fig=None): 6 | 7 | # Generate a figure and axes if none is provided. 8 | 9 | if fig == None: 10 | fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(9.,4.)) 11 | else: 12 | fig, ax = fig 13 | 14 | # Calculate the pixel range to show. 15 | 16 | ticks = visibilities["ticks"][index] 17 | 18 | xmin, xmax = int(round(visibilities["npix"][index]/2+ticks[0]/\ 19 | (visibilities["binsize"][index]/1000))), \ 20 | int(round(visibilities["npix"][index]/2+ticks[-1]/\ 21 | (visibilities["binsize"][index]/1000))) 22 | ymin, ymax = int(round(visibilities["npix"][index]/2+ticks[0]/\ 23 | (visibilities["binsize"][index]/1000))), \ 24 | int(round(visibilities["npix"][index]/2+ticks[-1]/\ 25 | (visibilities["binsize"][index]/1000))) 26 | 27 | # How to scale the real part. 28 | 29 | vmin = min(0, visibilities["data1d"][index].real.min()) 30 | vmax = visibilities["data1d"][index].real.max() 31 | 32 | # Show the real component. 33 | 34 | ax[0].imshow(visibilities["data2d"][index].real.reshape(\ 35 | (visibilities["npix"][index],visibilities["npix"][index]))\ 36 | [xmin:xmax,xmin:xmax][:,::-1], origin="lower", \ 37 | interpolation="nearest", vmin=vmin, vmax=vmax, cmap="jet") 38 | 39 | ax[0].contour(model.visibilities[visibilities["lam"][index]+"_2d"].real.\ 40 | reshape((visibilities["npix"][index],visibilities["npix"][index]))\ 41 | [xmin:xmax,xmin:xmax][:,::-1], cmap="jet") 42 | 43 | # How to scale the imaginary part. 44 | 45 | vmin = -visibilities["data1d"][index].real.max() 46 | vmax = visibilities["data1d"][index].real.max() 47 | 48 | # Show the imaginary component. 49 | 50 | ax[1].imshow(visibilities["data2d"][index].imag.reshape(\ 51 | (visibilities["npix"][index],visibilities["npix"][index]))\ 52 | [xmin:xmax,xmin:xmax][:,::-1], origin="lower", \ 53 | interpolation="nearest", vmin=vmin, vmax=vmax, cmap="jet") 54 | 55 | ax[1].contour(model.visibilities[visibilities["lam"][index]+"_2d"].imag.\ 56 | reshape((visibilities["npix"][index],visibilities["npix"][index]))\ 57 | [xmin:xmax,xmin:xmax][:,::-1], cmap="jet") 58 | 59 | # Adjust the axes ticks. 60 | 61 | transform1 = ticker.FuncFormatter(Transform(xmin, xmax, \ 62 | visibilities["binsize"][index]/1000, '%.0f')) 63 | 64 | ax[0].set_xticks(visibilities["npix"][index]/2+ticks[1:-1]/\ 65 | (visibilities["binsize"][index]/1000)-xmin) 66 | ax[0].set_yticks(visibilities["npix"][index]/2+ticks[1:-1]/\ 67 | (visibilities["binsize"][index]/1000)-ymin) 68 | ax[0].get_xaxis().set_major_formatter(transform1) 69 | ax[0].get_yaxis().set_major_formatter(transform1) 70 | 71 | ax[1].set_xticks(visibilities["npix"][index]/2+ticks[1:-1]/\ 72 | (visibilities["binsize"][index]/1000)-xmin) 73 | ax[1].set_yticks(visibilities["npix"][index]/2+ticks[1:-1]/\ 74 | (visibilities["binsize"][index]/1000)-ymin) 75 | ax[1].get_xaxis().set_major_formatter(transform1) 76 | ax[1].get_yaxis().set_major_formatter(transform1) 77 | 78 | # Adjust the plot and save it. 79 | 80 | ax[0].set_xlabel("U [k$\lambda$]") 81 | ax[0].set_ylabel("V [k$\lambda$]") 82 | 83 | ax[1].set_xlabel("U [k$\lambda$]") 84 | ax[1].set_ylabel("V [k$\lambda$]") 85 | 86 | # Return the figure and axes. 87 | 88 | return fig, ax 89 | 90 | # Define a useful class for plotting. 91 | 92 | class Transform: 93 | def __init__(self, xmin, xmax, dx, fmt): 94 | self.xmin = xmin 95 | self.xmax = xmax 96 | self.dx = dx 97 | self.fmt = fmt 98 | 99 | def __call__(self, x, p): 100 | return self.fmt% ((x-(self.xmax-self.xmin+1)/2)*self.dx) 101 | -------------------------------------------------------------------------------- /pdspy/plotting/plot_SED.py: -------------------------------------------------------------------------------- 1 | from pdspy.constants.astronomy import Jy 2 | from pdspy.constants.physics import c as c_l 3 | import matplotlib.pyplot as plt 4 | 5 | def plot_SED(spectra, model, SED=False, fig=None, model_color="g", 6 | linewidth=1, fontsize="medium"): 7 | r""" 8 | Plot the SED generated by a radiative transfer modeling run with pdspy, typically generated by the output of `modeling.run_disk_model`. 9 | 10 | Args: 11 | :attr:`spectra` (list): 12 | List of `Spectrum` objects with data for the object you are studying. 13 | :attr:`model` (`modeling.Model`): 14 | The radiative transfer model that you would like to plot the SED of. The `Model.spectra` dictionary must include a `"SED"` key. Typically this is the output of modeling.run_disk_model. 15 | :attr:`SED` (bool, optional): 16 | Whether to plot as a traditional SED (`True`), i.e. as :math:`\nu F_{\nu}`, or as a spectrum, i.e. :math:`F_{\nu}`. Default: `False` 17 | :attr:`fig` (`tuple`, `(matplotlib.Figure, matplotlib.Axes)`, optional): 18 | If you've already created a figure and axes to put the plot in, you can supply them here. Otherwise, `plot_SED` will generate them for you. Default: `None` 19 | :attr:`model_color` (str, optional): 20 | The color to use for plotting the model SED. Default: `"g"` 21 | :attr:`linewidth` (int, optional): 22 | What linewidth to use for plotting the model. Default: 1 23 | :attr:`fontsize` (`str` or `int`): 24 | What fontsize to use for labels, ticks, etc. Default: `"medium"` 25 | 26 | Returns: 27 | :attr:`fig` (`matplotlib.Figure`): 28 | The matplotlib figure that was used for the plot. 29 | :attr:`ax` (`matplotlib.Axes`): 30 | The matplotlib axes that were used for the plot. 31 | """ 32 | 33 | # If no axes are provided, create them. 34 | 35 | if fig == None: 36 | fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(4.5,4)) 37 | else: 38 | fig, ax = fig 39 | 40 | # Plot the SED. 41 | 42 | for j in range(len(spectra["file"])): 43 | if spectra["bin?"][j]: 44 | if SED: 45 | ax.plot(spectra["data"][j].wave, \ 46 | c_l / spectra["data"][j].wave / 1.0e-4 * \ 47 | spectra["data"][j].flux * Jy, "k-") 48 | else: 49 | ax.plot(spectra["data"][j].wave, spectra["data"][j].flux, \ 50 | "k-") 51 | else: 52 | if SED: 53 | ax.errorbar(spectra["data"][j].wave, \ 54 | c_l / spectra["data"][j].wave / 1.0e-4 * \ 55 | spectra["data"][j].flux * Jy, fmt="ko", \ 56 | yerr=c_l / spectra["data"][j].wave / 1.0e-4 * \ 57 | spectra["data"][j].unc * Jy, markeredgecolor="k") 58 | else: 59 | ax.errorbar(spectra["data"][j].wave, \ 60 | spectra["data"][j].flux, fmt="ko", \ 61 | yerr=spectra["data"][j].unc, markeredgecolor="k") 62 | 63 | if len(spectra["file"]) > 0: 64 | if SED: 65 | ax.plot(model.spectra["SED"].wave, c_l/model.spectra["SED"].wave / \ 66 | 1.0e-4 * model.spectra["SED"].flux * Jy, "-", \ 67 | color=model_color, linewidth=linewidth) 68 | else: 69 | ax.plot(model.spectra["SED"].wave, model.spectra["SED"].flux, "-", \ 70 | color=model_color, linewidth=linewidth) 71 | 72 | # Add axes labels and adjust the plot. 73 | 74 | if SED: 75 | ax.axis([0.1,1.0e4,1e-13,1e-8]) 76 | else: 77 | ax.axis([0.1,1.0e4,1e-6,1e3]) 78 | 79 | ax.set_xscale("log", nonpositive='clip') 80 | ax.set_yscale("log", nonpositive='clip') 81 | 82 | ax.set_xlabel("$\lambda$ [$\mu$m]", fontsize=fontsize) 83 | if SED: 84 | ax.set_ylabel(r"$\nu F_{\nu}$ [ergs s$^{-1}$ cm$^{-2}$]", \ 85 | fontsize=fontsize) 86 | else: 87 | ax.set_ylabel(r"$F_{\nu}$ [Jy]", fontsize=fontsize) 88 | 89 | # Return the figure and axes. 90 | 91 | return fig, ax 92 | -------------------------------------------------------------------------------- /pdspy/interferometry/invert.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | from . import center 3 | from .libinterferometry import grid 4 | from ..imaging import Image 5 | from ..constants.astronomy import arcsec 6 | from scipy.fftpack import ifft2, fftshift, ifftshift, fftfreq 7 | 8 | def invert(data, imsize=256, pixel_size=0.25, convolution="pillbox", mfs=False,\ 9 | weighting="natural", robust=2, npixels=0, centering=None, \ 10 | mode='continuum', beam=False, uvtaper=None): 11 | 12 | # If we are calculating the beam, set all of the real values to 1 and the 13 | # imaginary data to 0. 14 | 15 | if beam: 16 | real = data.real.copy() 17 | imag = data.imag.copy() 18 | 19 | data.real[:,:] = 1. 20 | data.imag[:,:] = 0. 21 | 22 | # Apply a uv-taper to the data, if requested. 23 | 24 | if type(uvtaper) != type(None): 25 | taper = numpy.exp(-0.5*data.uvdist**2/(uvtaper*1e3)**2) 26 | 27 | weights = data.weights.copy() 28 | for i in range(data.freq.size): 29 | data.weights[:,i] *= taper 30 | 31 | # Grid the data before imaging. 32 | 33 | binsize = 1.0 / (pixel_size * imsize * arcsec) 34 | gridded_data = grid(data, gridsize=imsize, binsize=binsize, \ 35 | convolution=convolution, mfs=mfs, imaging=True, \ 36 | weighting=weighting, robust=robust, npixels=npixels, mode=mode) 37 | 38 | # If making an image of the beam, restore the data to what it was before. 39 | 40 | if beam: 41 | data.real = real 42 | data.imag = imag 43 | 44 | # If using uvtaper, restore the weights. 45 | 46 | if type(uvtaper) != type(None): 47 | data.weights = weights 48 | 49 | # Center the data, if requested. 50 | 51 | if type(centering) != type(None): 52 | gridded_data = center(gridded_data, centering) 53 | 54 | # Set up information for the final image. 55 | 56 | x = fftshift(fftfreq(imsize, binsize)) / arcsec 57 | y = fftshift(fftfreq(imsize, binsize)) / arcsec 58 | xx, yy = numpy.meshgrid(x, y) 59 | r = numpy.sqrt(xx**2 + yy**2) 60 | 61 | image = numpy.zeros((imsize,imsize,gridded_data.real.shape[1],1)) 62 | 63 | # Loop through frequency space and do the Fourier transform. 64 | 65 | for i in range(gridded_data.real.shape[1]): 66 | u = gridded_data.u.reshape((imsize, imsize)) 67 | v = gridded_data.v.reshape((imsize, imsize)) 68 | real = gridded_data.real[:,i].reshape((imsize, imsize)) 69 | imag = gridded_data.imag[:,i].reshape((imsize, imsize)) 70 | weights = gridded_data.weights[:,i].reshape((imsize, imsize)) 71 | 72 | comp = real + 1j*imag 73 | 74 | if convolution == "pillbox": 75 | conv_func = pillbox 76 | elif convolution == "expsinc": 77 | conv_func = exp_sinc 78 | 79 | im = fftshift(ifft2(ifftshift(comp))).real * imsize**2 80 | 81 | convolve = fftshift(ifft2(ifftshift(conv_func(u, v, binsize, \ 82 | binsize)))).real 83 | 84 | image[:,:,i,0] = (im/convolve)[:,::-1] 85 | 86 | # Make sure the beam peaks at exactly 1. Should be just a small 87 | # correction. 88 | 89 | if beam: 90 | image[:,:,i,0] /= image[:,:,i,0].max() 91 | 92 | return Image(image, x=x, y=y, freq=gridded_data.freq) 93 | 94 | def pillbox(u, v, delta_u, delta_v): 95 | 96 | m = 1 97 | 98 | arr = numpy.ones(u.shape, dtype=float)*u.size 99 | 100 | arr[numpy.abs(u) >= m * delta_u / 2] = 0 101 | arr[numpy.abs(v) >= m * delta_v / 2] = 0 102 | 103 | return arr 104 | 105 | def exp_sinc(u, v, delta_u, delta_v): 106 | 107 | alpha1 = 1.55 108 | alpha2 = 2.52 109 | m = 6 110 | 111 | arr = numpy.sinc(u / (alpha1 * delta_u)) * \ 112 | numpy.exp(-1 * (u / (alpha2 * delta_u))**2)* \ 113 | numpy.sinc(v / (alpha1 * delta_v)) * \ 114 | numpy.exp(-1 * (v / (alpha2 * delta_v))**2) 115 | 116 | arr[numpy.abs(u) >= m * delta_u / 2] = 0 117 | arr[numpy.abs(v) >= m * delta_v / 2] = 0 118 | 119 | arr = arr/arr.sum() * arr.size 120 | 121 | return arr 122 | -------------------------------------------------------------------------------- /pdspy/interferometry/readuvfits.py: -------------------------------------------------------------------------------- 1 | from astropy.io.fits import open 2 | from ..constants.physics import c 3 | from .libinterferometry import Visibilities 4 | import numpy 5 | 6 | def readuvfits(filename, fmt="casa", fast=False): 7 | 8 | data = open(filename) 9 | 10 | header = data[0].header 11 | u = data[0].data.field(0).astype(numpy.float64) 12 | v = data[0].data.field(1).astype(numpy.float64) 13 | 14 | order = numpy.argsort(u) 15 | 16 | u = u[order] 17 | v = v[order] 18 | 19 | if fmt in ["casa","noema"]: 20 | arr = data[0].data.field("data").astype(numpy.float) 21 | 22 | for i in range(min(2,data[0].data.field("data").shape[5])): 23 | if i == 0: 24 | real = [arr[:,0,0,j,:,0,0] for j in range(arr.shape[3])] 25 | imag = [arr[:,0,0,j,:,0,1] for j in range(arr.shape[3])] 26 | weights = [arr[:,0,0,j,:,0,2] for j in range(arr.shape[3])] 27 | baselines = data[0].data.field(5) 28 | 29 | real = numpy.concatenate(real, axis=1) 30 | imag = numpy.concatenate(imag, axis=1) 31 | weights = numpy.concatenate(weights, axis=1) 32 | 33 | real = real[order,:] 34 | imag = imag[order,:] 35 | weights = weights[order,:] 36 | baselines = baselines[order] 37 | else: 38 | new_real = [arr[:,0,0,j,:,1,0] for j in range(arr.shape[3])] 39 | new_imag = [arr[:,0,0,j,:,1,1] for j in range(arr.shape[3])] 40 | new_weights = [arr[:,0,0,j,:,1,2] for j in range(arr.shape[3])] 41 | 42 | new_real = numpy.concatenate(new_real, axis=1) 43 | new_imag = numpy.concatenate(new_imag, axis=1) 44 | new_weights = numpy.concatenate(new_weights, axis=1) 45 | 46 | new_real = new_real[order,:] 47 | new_imag = new_imag[order,:] 48 | new_weights = new_weights[order,:] 49 | 50 | real = real*weights + new_real*new_weights 51 | imag = imag*weights + new_imag*new_weights 52 | weights += new_weights 53 | real[weights != 0] /= weights[weights != 0] 54 | imag[weights != 0] /= weights[weights != 0] 55 | elif fmt == "miriad": 56 | real = (data[0].data.field("data"))[:,0,0,:,0,0].astype(numpy.float64) 57 | imag = (data[0].data.field("data"))[:,0,0,:,0,1].astype(numpy.float64) 58 | weights = (data[0].data.field("data"))[:,0,0,:,0,2].\ 59 | astype(numpy.float64) 60 | baselines = data[0].data.field(3) 61 | ant2 = numpy.mod(baselines,256) 62 | ant1 = (baselines-ant2)/256 63 | baseline = numpy.repeat(" 6.1-6.1",u.size) 64 | baseline[((ant1 < 7) & (ant2 >= 7)) ^ ((ant1 >= 7) & (ant2 < 7))] = \ 65 | " 6.1-10.4" 66 | baseline[(ant1 < 7) & (ant2 < 7)] = "10.4-10.4" 67 | 68 | u = numpy.concatenate((u, -u)) 69 | v = numpy.concatenate((v, -v)) 70 | real = numpy.concatenate((real, real)) 71 | imag = numpy.concatenate((imag, -imag)) 72 | weights = numpy.concatenate((weights, weights)) 73 | baseline = numpy.concatenate((baseline, baseline)) 74 | 75 | if fmt == "casa": 76 | IF = data[1].data.field('if freq')[0] 77 | delta_freq = data[1].data.field('ch width')[0] 78 | else: 79 | IF = 0. 80 | delta_freq = header["CDELT4"] 81 | freq0 = header["CRVAL4"] 82 | pix0 = header["CRPIX4"] 83 | nfreq = header["NAXIS4"] 84 | 85 | if isinstance(IF, float): 86 | IF = numpy.array([IF]) 87 | delta_freq = numpy.array([delta_freq]) 88 | 89 | freq = [freq0 + IF[i] + (numpy.arange(nfreq)-(pix0-1))*delta_freq[i] \ 90 | for i in range(IF.size)] 91 | 92 | freq = numpy.concatenate(freq) 93 | nfreq = header["NAXIS4"]*IF.size 94 | 95 | u *= freq.mean() 96 | v *= freq.mean() 97 | 98 | weights *= nfreq 99 | 100 | data.close() 101 | 102 | uvdata = Visibilities(u, v, freq, real, -imag, weights, baseline=baseline) 103 | 104 | uvdata.set_header(header) 105 | 106 | return uvdata 107 | -------------------------------------------------------------------------------- /pdspy/modeling/SettledPringleDisk.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import h5py 3 | from ..constants.physics import G, m_p 4 | from ..constants.astronomy import AU, M_sun 5 | from ..constants.math import pi 6 | from ..dust import Dust 7 | from ..gas import Gas 8 | from .SettledDisk import SettledDisk 9 | 10 | class SettledPringleDisk(SettledDisk): 11 | def surface_density(self, r, normalize=True): 12 | # Get the disk parameters. 13 | 14 | rr = r * AU 15 | rin = self.rmin * AU 16 | rout = self.rmax * AU 17 | mass = self.mass * M_sun 18 | gamma = self.plrho - self.plh 19 | if self.gamma_taper != None: 20 | gamma_taper = self.gamma_taper 21 | else: 22 | gamma_taper = gamma 23 | 24 | # Set up the surface density. 25 | 26 | Sigma0 = (2-gamma)*mass/(2*pi*rout**2)*numpy.exp((rin/rout)**(2-gamma)) 27 | 28 | Sigma = Sigma0 * (rr/rout)**(-gamma) * \ 29 | numpy.exp(-(rr/rout)**(2-gamma_taper)) 30 | 31 | Sigma[r <= rin/AU] = 0e0 32 | 33 | # In case of r == 0 (a singularity), get the value from slightly off 0. 34 | 35 | dr = rr[r > 0].min() 36 | Sigma[r == 0] = Sigma0 * (0.7*dr/rout)**(-gamma) * \ 37 | numpy.exp(-(0.7*dr/rout)**(2-gamma)) 38 | 39 | # Add gaps to the disk. 40 | 41 | for i in range(len(self.gap_rin)): 42 | if self.gaussian_gaps: 43 | gap_r = (self.gap_rin[i] + self.gap_rout[i])/2 44 | gap_w = self.gap_rout[i] - self.gap_rin[i] 45 | 46 | Sigma /= 1 + 1./self.gap_delta[i] * numpy.exp(-4*numpy.log(2.)*\ 47 | (r - gap_r)**2 / gap_w**2) 48 | else: 49 | Sigma[(r >= self.gap_rin[i]) & \ 50 | (r <= self.gap_rout[i])] *= self.gap_delta[i] 51 | 52 | ##### Normalize the surface density correctly. 53 | 54 | if normalize: 55 | r_high = numpy.logspace(numpy.log10(self.rmin), \ 56 | numpy.log10(self.rmax), 1000) 57 | Sigma_high = self.surface_density(r_high, normalize=False) 58 | 59 | scale = mass / (2*numpy.pi*trapz(r_high*AU*Sigma_high, r_high*AU)) 60 | 61 | Sigma *= scale 62 | 63 | return Sigma 64 | 65 | def scale_height(self, r): 66 | return self.h0 * AU * (r / self.rmax)**self.plh 67 | 68 | def temperature(self, r, theta, phi): 69 | ##### Disk Parameters 70 | 71 | rin = self.rmin * AU 72 | rout = self.rmax * AU 73 | t0 = self.t0 74 | plt = self.plt 75 | 76 | ##### Set up the coordinates 77 | 78 | rt, tt, pp = numpy.meshgrid(r*AU, theta, phi,indexing='ij') 79 | 80 | rr = rt*numpy.sin(tt) 81 | zz = rt*numpy.cos(tt) 82 | 83 | ##### Make the dust density model for a protoplanetary disk. 84 | 85 | t = t0 * (rr / (1*AU))**(-plt) 86 | 87 | t[rr <= rin] = 0e0 88 | 89 | t[t > 10000] = 10000 90 | 91 | return t 92 | 93 | def temperature_1d(self, r): 94 | rin = self.rmin * AU 95 | rout = self.rmax * AU 96 | t0 = self.t0 97 | plt = self.plt 98 | 99 | T = t0 * r**(-plt) 100 | 101 | T[r <= rin/AU] = 0.0 102 | 103 | dr = r[r > 0].min() 104 | T[r == 0] = t0 * (0.7*dr)**(-plt) 105 | 106 | return T 107 | 108 | def gas_temperature(self, r, theta, phi): 109 | ##### Disk Parameters 110 | 111 | rin = self.rmin * AU 112 | rout = self.rmax * AU 113 | pltgas = self.pltgas 114 | tmid0 = self.tmid0 115 | tatm0 = self.tatm0 116 | zq0 = self.zq0 117 | delta = self.delta 118 | 119 | ##### Set up the coordinates 120 | 121 | rt, tt, pp = numpy.meshgrid(r*AU, theta, phi,indexing='ij') 122 | 123 | rr = rt*numpy.sin(tt) 124 | zz = rt*numpy.cos(tt) 125 | 126 | ##### Make the dust density model for a protoplanetary disk. 127 | 128 | zq = zq0 * (rt / rin)**1.3 129 | 130 | tmid = tmid0 * (rr / rin)**(-pltgas) 131 | tatm = tatm0 * (rr / rin)**(-pltgas) 132 | 133 | t = numpy.zeros(tatm.shape) 134 | t[zz >= zq] = tatm[zz >= zq] 135 | t[zz < zq] = tatm[zz < zq] + (tmid[zz < zq] - tatm[zz < zq]) * \ 136 | (numpy.cos(numpy.pi * zz[zz < zq] / (2*zq[zz < zq])))**2*delta 137 | 138 | return t 139 | --------------------------------------------------------------------------------