From 30a9943f79a588e7df166692b88fc06b5a109fb6 Mon Sep 17 00:00:00 2001 From: Tomas Stolker Date: Sat, 11 Nov 2023 22:40:24 +0100 Subject: [PATCH] Fixed issues with new package structure --- Makefile | 3 + README.rst | 2 +- docs/index.rst | 2 +- docs/species.data.companion_data.rst | 21 + docs/species.data.filter_data.rst | 21 + docs/species.data.isochrone_data.rst | 101 ++ docs/species.data.misc_data.rst | 29 + docs/species.data.model_data.rst | 29 + docs/species.data.phot_data.rst | 29 + docs/species.data.rst | 132 +-- docs/species.data.spec_data.rst | 69 ++ docs/tutorials.rst | 2 +- species/core/box.py | 350 +++---- species/core/constants.py | 33 +- species/core/species_init.py | 6 +- species/data/__init__.py | 1 + species/data/companion_data/__init__.py | 0 .../{ => companion_data}/companion_data.json | 0 .../companion_spectra.json | 0 .../{ => companion_data}/companion_spectra.py | 0 species/data/database.py | 513 ++++------ species/data/filter_data/__init__.py | 0 .../filter_data.py} | 135 ++- species/data/isochrone_data/__init__.py | 0 species/data/isochrone_data/add_isochrone.py | 100 ++ species/data/isochrone_data/iso_ames.py | 59 ++ species/data/isochrone_data/iso_atmo.py | 135 +++ .../data/isochrone_data/iso_baraffe2015.py | 75 ++ species/data/isochrone_data/iso_btsettl.py | 49 + species/data/isochrone_data/iso_linder2019.py | 166 ++++ species/data/isochrone_data/iso_manual.py | 125 +++ species/data/isochrone_data/iso_marleau.py | 54 ++ species/data/isochrone_data/iso_nextgen.py | 49 + species/data/isochrone_data/iso_saumon2008.py | 127 +++ species/data/isochrone_data/iso_sonora.py | 112 +++ species/data/isochrones.py | 910 ------------------ species/data/misc_data/__init__.py | 0 .../accretion_data.py} | 0 .../data/{dust.py => misc_data/dust_data.py} | 0 species/data/model_data/__init__.py | 0 species/data/{ => model_data}/custom_model.py | 0 species/data/{ => model_data}/model_data.json | 0 .../data/{ => model_data}/model_spectra.py | 0 species/data/phot_data/__init__.py | 0 .../{leggett.py => phot_data/phot_leggett.py} | 0 .../{vlm_plx.py => phot_data/phot_vlm_plx.py} | 0 species/data/spec_data/__init__.py | 0 species/data/spec_data/add_spec_data.py | 70 ++ .../spec_allers2013.py} | 2 - .../spec_bonnefoy2014.py} | 2 - .../data/{irtf.py => spec_data/spec_irtf.py} | 2 - .../spec_kesseli2017.py} | 2 - .../data/{spex.py => spec_data/spec_spex.py} | 2 - .../data/{vega.py => spec_data/spec_vega.py} | 0 species/fit/fit_model.py | 13 +- species/phot/syn_phot.py | 2 +- species/plot/plot_color.py | 6 +- species/plot/plot_mcmc.py | 4 +- species/read/__init__.py | 11 + species/read/read_calibration.py | 4 +- species/read/read_color.py | 10 +- species/read/read_filter.py | 39 +- species/read/read_isochrone.py | 40 +- species/read/read_model.py | 18 +- species/read/read_object.py | 4 +- species/read/read_planck.py | 4 +- species/read/read_spectrum.py | 149 ++- species/util/box_util.py | 8 +- species/util/dust_util.py | 2 +- species/util/fit_util.py | 2 +- species/util/model_util.py | 9 +- species/util/plot_util.py | 2 +- species/util/radtrans_util.py | 2 +- species/util/test_util.py | 3 +- 74 files changed, 2102 insertions(+), 1749 deletions(-) create mode 100644 docs/species.data.companion_data.rst create mode 100644 docs/species.data.filter_data.rst create mode 100644 docs/species.data.isochrone_data.rst create mode 100644 docs/species.data.misc_data.rst create mode 100644 docs/species.data.model_data.rst create mode 100644 docs/species.data.phot_data.rst create mode 100644 docs/species.data.spec_data.rst create mode 100644 species/data/companion_data/__init__.py rename species/data/{ => companion_data}/companion_data.json (100%) rename species/data/{ => companion_data}/companion_spectra.json (100%) rename species/data/{ => companion_data}/companion_spectra.py (100%) create mode 100644 species/data/filter_data/__init__.py rename species/data/{filters.py => filter_data/filter_data.py} (56%) create mode 100644 species/data/isochrone_data/__init__.py create mode 100644 species/data/isochrone_data/add_isochrone.py create mode 100644 species/data/isochrone_data/iso_ames.py create mode 100644 species/data/isochrone_data/iso_atmo.py create mode 100644 species/data/isochrone_data/iso_baraffe2015.py create mode 100644 species/data/isochrone_data/iso_btsettl.py create mode 100644 species/data/isochrone_data/iso_linder2019.py create mode 100644 species/data/isochrone_data/iso_manual.py create mode 100644 species/data/isochrone_data/iso_marleau.py create mode 100644 species/data/isochrone_data/iso_nextgen.py create mode 100644 species/data/isochrone_data/iso_saumon2008.py create mode 100644 species/data/isochrone_data/iso_sonora.py delete mode 100644 species/data/isochrones.py create mode 100644 species/data/misc_data/__init__.py rename species/data/{accretion.py => misc_data/accretion_data.py} (100%) rename species/data/{dust.py => misc_data/dust_data.py} (100%) create mode 100644 species/data/model_data/__init__.py rename species/data/{ => model_data}/custom_model.py (100%) rename species/data/{ => model_data}/model_data.json (100%) rename species/data/{ => model_data}/model_spectra.py (100%) create mode 100644 species/data/phot_data/__init__.py rename species/data/{leggett.py => phot_data/phot_leggett.py} (100%) rename species/data/{vlm_plx.py => phot_data/phot_vlm_plx.py} (100%) create mode 100644 species/data/spec_data/__init__.py create mode 100644 species/data/spec_data/add_spec_data.py rename species/data/{allers2013.py => spec_data/spec_allers2013.py} (99%) rename species/data/{bonnefoy2014.py => spec_data/spec_bonnefoy2014.py} (99%) rename species/data/{irtf.py => spec_data/spec_irtf.py} (99%) rename species/data/{kesseli2017.py => spec_data/spec_kesseli2017.py} (99%) rename species/data/{spex.py => spec_data/spec_spex.py} (99%) rename species/data/{vega.py => spec_data/spec_vega.py} (100%) diff --git a/Makefile b/Makefile index 3b5ea15c..c66fe3e5 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ pypi-test: docs: rm -f docs/species.core.rst rm -f docs/species.data.rst + rm -f docs/species.data.*.rst rm -f docs/species.fit.rst rm -f docs/species.phot.rst rm -f docs/species.plot.rst @@ -28,6 +29,8 @@ docs: cd docs/ $(MAKE) -C docs clean $(MAKE) -C docs html + rm -f docs/species_config.ini + rm -f docs/species_database.hdf5 coverage: coverage run --source=species -m pytest diff --git a/README.rst b/README.rst index e4191fa3..1df0c885 100644 --- a/README.rst +++ b/README.rst @@ -28,7 +28,7 @@ There are tools available for grid and free retrievals using Bayesian inference, synthetic photometry, interpolating a variety atmospheric and evolutionary model grids (including the possibility to add a custom grid), color-magnitude and color-color diagrams, empirical spectral analysis, spectral and photometric calibration, and analysis of emission lines. The package has been released on `PyPI `_ and is actively developed and maintained on Github. -**Important:** Importing the *species* package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. In the latest version, any functionalities should be explicitly imported from the modules that they are part of. +**Important:** Importing the *species* package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. Any functionalities should now be explicitly imported from the modules that they are part of. Documentation ------------- diff --git a/docs/index.rst b/docs/index.rst index 4c2d20c1..4ea4f913 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ Documentation for *species* *species* is a toolkit for atmospheric characterization of directly imaged exoplanets. .. important:: - Importing the ``species`` package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. In the latest version, any functionalities should be explicitly imported from the modules that they are part of. + Importing the ``species`` package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. Any functionalities should now be explicitly imported from the modules that they are part of. .. toctree:: :maxdepth: 2 diff --git a/docs/species.data.companion_data.rst b/docs/species.data.companion_data.rst new file mode 100644 index 00000000..7d6a289f --- /dev/null +++ b/docs/species.data.companion_data.rst @@ -0,0 +1,21 @@ +species.data.companion\_data package +==================================== + +Submodules +---------- + +species.data.companion\_data.companion\_spectra module +------------------------------------------------------ + +.. automodule:: species.data.companion_data.companion_spectra + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.companion_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.filter_data.rst b/docs/species.data.filter_data.rst new file mode 100644 index 00000000..dc306905 --- /dev/null +++ b/docs/species.data.filter_data.rst @@ -0,0 +1,21 @@ +species.data.filter\_data package +================================= + +Submodules +---------- + +species.data.filter\_data.filter\_data module +--------------------------------------------- + +.. automodule:: species.data.filter_data.filter_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.filter_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.isochrone_data.rst b/docs/species.data.isochrone_data.rst new file mode 100644 index 00000000..25f11481 --- /dev/null +++ b/docs/species.data.isochrone_data.rst @@ -0,0 +1,101 @@ +species.data.isochrone\_data package +==================================== + +Submodules +---------- + +species.data.isochrone\_data.add\_isochrone module +-------------------------------------------------- + +.. automodule:: species.data.isochrone_data.add_isochrone + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_ames module +--------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_ames + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_atmo module +--------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_atmo + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_baraffe2015 module +---------------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_baraffe2015 + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_btsettl module +------------------------------------------------ + +.. automodule:: species.data.isochrone_data.iso_btsettl + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_linder2019 module +--------------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_linder2019 + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_manual module +----------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_manual + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_marleau module +------------------------------------------------ + +.. automodule:: species.data.isochrone_data.iso_marleau + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_nextgen module +------------------------------------------------ + +.. automodule:: species.data.isochrone_data.iso_nextgen + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_saumon2008 module +--------------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_saumon2008 + :members: + :undoc-members: + :show-inheritance: + +species.data.isochrone\_data.iso\_sonora module +----------------------------------------------- + +.. automodule:: species.data.isochrone_data.iso_sonora + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.isochrone_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.misc_data.rst b/docs/species.data.misc_data.rst new file mode 100644 index 00000000..4830ce71 --- /dev/null +++ b/docs/species.data.misc_data.rst @@ -0,0 +1,29 @@ +species.data.misc\_data package +=============================== + +Submodules +---------- + +species.data.misc\_data.accretion\_data module +---------------------------------------------- + +.. automodule:: species.data.misc_data.accretion_data + :members: + :undoc-members: + :show-inheritance: + +species.data.misc\_data.dust\_data module +----------------------------------------- + +.. automodule:: species.data.misc_data.dust_data + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.misc_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.model_data.rst b/docs/species.data.model_data.rst new file mode 100644 index 00000000..4f9d1803 --- /dev/null +++ b/docs/species.data.model_data.rst @@ -0,0 +1,29 @@ +species.data.model\_data package +================================ + +Submodules +---------- + +species.data.model\_data.custom\_model module +--------------------------------------------- + +.. automodule:: species.data.model_data.custom_model + :members: + :undoc-members: + :show-inheritance: + +species.data.model\_data.model\_spectra module +---------------------------------------------- + +.. automodule:: species.data.model_data.model_spectra + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.model_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.phot_data.rst b/docs/species.data.phot_data.rst new file mode 100644 index 00000000..08365ad6 --- /dev/null +++ b/docs/species.data.phot_data.rst @@ -0,0 +1,29 @@ +species.data.phot\_data package +=============================== + +Submodules +---------- + +species.data.phot\_data.phot\_leggett module +-------------------------------------------- + +.. automodule:: species.data.phot_data.phot_leggett + :members: + :undoc-members: + :show-inheritance: + +species.data.phot\_data.phot\_vlm\_plx module +--------------------------------------------- + +.. automodule:: species.data.phot_data.phot_vlm_plx + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.phot_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/species.data.rst b/docs/species.data.rst index 42609c71..f496db06 100644 --- a/docs/species.data.rst +++ b/docs/species.data.rst @@ -1,48 +1,22 @@ species.data package ==================== -Submodules ----------- - -species.data.accretion module ------------------------------ - -.. automodule:: species.data.accretion - :members: - :undoc-members: - :show-inheritance: - -species.data.allers2013 module ------------------------------- - -.. automodule:: species.data.allers2013 - :members: - :undoc-members: - :show-inheritance: - -species.data.bonnefoy2014 module --------------------------------- - -.. automodule:: species.data.bonnefoy2014 - :members: - :undoc-members: - :show-inheritance: +Subpackages +----------- -species.data.companion\_spectra module --------------------------------------- +.. toctree:: + :maxdepth: 4 -.. automodule:: species.data.companion_spectra - :members: - :undoc-members: - :show-inheritance: + species.data.companion_data + species.data.filter_data + species.data.isochrone_data + species.data.misc_data + species.data.model_data + species.data.phot_data + species.data.spec_data -species.data.custom\_model module ---------------------------------- - -.. automodule:: species.data.custom_model - :members: - :undoc-members: - :show-inheritance: +Submodules +---------- species.data.database module ---------------------------- @@ -52,86 +26,6 @@ species.data.database module :undoc-members: :show-inheritance: -species.data.dust module ------------------------- - -.. automodule:: species.data.dust - :members: - :undoc-members: - :show-inheritance: - -species.data.filters module ---------------------------- - -.. automodule:: species.data.filters - :members: - :undoc-members: - :show-inheritance: - -species.data.irtf module ------------------------- - -.. automodule:: species.data.irtf - :members: - :undoc-members: - :show-inheritance: - -species.data.isochrones module ------------------------------- - -.. automodule:: species.data.isochrones - :members: - :undoc-members: - :show-inheritance: - -species.data.kesseli2017 module -------------------------------- - -.. automodule:: species.data.kesseli2017 - :members: - :undoc-members: - :show-inheritance: - -species.data.leggett module ---------------------------- - -.. automodule:: species.data.leggett - :members: - :undoc-members: - :show-inheritance: - -species.data.model\_spectra module ----------------------------------- - -.. automodule:: species.data.model_spectra - :members: - :undoc-members: - :show-inheritance: - -species.data.spex module ------------------------- - -.. automodule:: species.data.spex - :members: - :undoc-members: - :show-inheritance: - -species.data.vega module ------------------------- - -.. automodule:: species.data.vega - :members: - :undoc-members: - :show-inheritance: - -species.data.vlm\_plx module ----------------------------- - -.. automodule:: species.data.vlm_plx - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/docs/species.data.spec_data.rst b/docs/species.data.spec_data.rst new file mode 100644 index 00000000..e7d887ba --- /dev/null +++ b/docs/species.data.spec_data.rst @@ -0,0 +1,69 @@ +species.data.spec\_data package +=============================== + +Submodules +---------- + +species.data.spec\_data.add\_spec\_data module +---------------------------------------------- + +.. automodule:: species.data.spec_data.add_spec_data + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_allers2013 module +----------------------------------------------- + +.. automodule:: species.data.spec_data.spec_allers2013 + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_bonnefoy2014 module +------------------------------------------------- + +.. automodule:: species.data.spec_data.spec_bonnefoy2014 + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_irtf module +----------------------------------------- + +.. automodule:: species.data.spec_data.spec_irtf + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_kesseli2017 module +------------------------------------------------ + +.. automodule:: species.data.spec_data.spec_kesseli2017 + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_spex module +----------------------------------------- + +.. automodule:: species.data.spec_data.spec_spex + :members: + :undoc-members: + :show-inheritance: + +species.data.spec\_data.spec\_vega module +----------------------------------------- + +.. automodule:: species.data.spec_data.spec_vega + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: species.data.spec_data + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/tutorials.rst b/docs/tutorials.rst index a8107516..2ba03a05 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -9,7 +9,7 @@ This page contains a list of tutorials which highlight some of the functionaliti Details on the various classes, functions, and parameters can be found in the `API documentation `_. .. important:: - Importing the ``species`` package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. In the latest version, any functionalities should be explicitly imported from the modules that they are part of. + Importing the ``species`` package had become slow because of the many classes and functions that were implicitly imported. The initialization of the package has therefore been adjusted. Any functionalities should now be explicitly imported from the modules that they are part of. **Spectral retrievals** diff --git a/species/core/box.py b/species/core/box.py index c575c423..cdae8320 100644 --- a/species/core/box.py +++ b/species/core/box.py @@ -1,5 +1,5 @@ """ -Module with classes for storing data in ``Box`` objects. +Module with the ``Box`` classes and ``create_box`` function. """ from typing import List, Union @@ -12,180 +12,6 @@ from species.util.spec_util import smooth_spectrum -def create_box(boxtype, **kwargs): - """ - Function for creating a :class:`~species.core.box.Box`. - - Returns - ------- - species.core.box - Box with the data and parameters. - """ - - if boxtype == "colormag": - box = ColorMagBox() - box.library = kwargs["library"] - box.object_type = kwargs["object_type"] - box.filters_color = kwargs["filters_color"] - box.filter_mag = kwargs["filter_mag"] - box.color = kwargs["color"] - box.magnitude = kwargs["magnitude"] - if "names" in kwargs: - box.names = kwargs["names"] - if "sptype" in kwargs: - box.sptype = kwargs["sptype"] - if "mass" in kwargs: - box.mass = kwargs["mass"] - if "radius" in kwargs: - box.radius = kwargs["radius"] - if "iso_tag" in kwargs: - box.iso_tag = kwargs["iso_tag"] - - if boxtype == "colorcolor": - box = ColorColorBox() - box.library = kwargs["library"] - box.object_type = kwargs["object_type"] - box.filters = kwargs["filters"] - box.color1 = kwargs["color1"] - box.color2 = kwargs["color2"] - if "names" in kwargs: - box.names = kwargs["names"] - if "sptype" in kwargs: - box.sptype = kwargs["sptype"] - if "mass" in kwargs: - box.mass = kwargs["mass"] - if "radius" in kwargs: - box.radius = kwargs["radius"] - if "iso_tag" in kwargs: - box.iso_tag = kwargs["iso_tag"] - - elif boxtype == "cooling": - box = CoolingBox() - box.model = kwargs["model"] - box.mass = kwargs["mass"] - if "age" in kwargs: - box.age = kwargs["age"] - else: - box.age = kwargs["ages"] - box.teff = kwargs["teff"] - box.log_lum = kwargs["log_lum"] - box.logg = kwargs["logg"] - box.radius = kwargs["radius"] - box.filter_mag = kwargs["filter_mag"] - box.magnitude = kwargs["magnitude"] - box.filters_color = kwargs["filters_color"] - box.color = kwargs["color"] - - elif boxtype == "isochrone": - box = IsochroneBox() - box.model = kwargs["model"] - box.age = kwargs["age"] - if "mass" in kwargs: - box.mass = kwargs["mass"] - else: - box.mass = kwargs["masses"] - box.teff = kwargs["teff"] - box.log_lum = kwargs["log_lum"] - box.logg = kwargs["logg"] - box.radius = kwargs["radius"] - box.filter_mag = kwargs["filter_mag"] - box.magnitude = kwargs["magnitude"] - box.filters_color = kwargs["filters_color"] - box.color = kwargs["color"] - - elif boxtype == "model": - box = ModelBox() - box.model = kwargs["model"] - box.wavelength = kwargs["wavelength"] - box.flux = kwargs["flux"] - box.parameters = kwargs["parameters"] - box.quantity = kwargs["quantity"] - if "contribution" in kwargs: - box.contribution = kwargs["contribution"] - if "bol_flux" in kwargs: - box.bol_flux = kwargs["bol_flux"] - - elif boxtype == "object": - box = ObjectBox() - box.name = kwargs["name"] - box.filters = kwargs["filters"] - box.mean_wavel = kwargs["mean_wavel"] - box.magnitude = kwargs["magnitude"] - box.flux = kwargs["flux"] - box.spectrum = kwargs["spectrum"] - if "parallax" in kwargs: - box.parallax = kwargs["parallax"] - if "distance" in kwargs: - box.distance = kwargs["distance"] - - elif boxtype == "photometry": - box = PhotometryBox() - if "name" in kwargs: - box.name = kwargs["name"] - if "sptype" in kwargs: - box.sptype = kwargs["sptype"] - if "wavelength" in kwargs: - box.wavelength = kwargs["wavelength"] - if "flux" in kwargs: - box.flux = kwargs["flux"] - if "app_mag" in kwargs: - box.app_mag = kwargs["app_mag"] - if "abs_mag" in kwargs: - box.abs_mag = kwargs["abs_mag"] - if "filter_name" in kwargs: - box.filter_name = kwargs["filter_name"] - - elif boxtype == "residuals": - box = ResidualsBox() - box.name = kwargs["name"] - box.photometry = kwargs["photometry"] - box.spectrum = kwargs["spectrum"] - if "chi2_red" in kwargs: - box.chi2_red = kwargs["chi2_red"] - - elif boxtype == "samples": - box = SamplesBox() - box.spectrum = kwargs["spectrum"] - box.parameters = kwargs["parameters"] - box.samples = kwargs["samples"] - box.ln_prob = kwargs["ln_prob"] - box.ln_evidence = kwargs["ln_evidence"] - box.prob_sample = kwargs["prob_sample"] - box.median_sample = kwargs["median_sample"] - box.attributes = kwargs["attributes"] - - elif boxtype == "spectrum": - box = SpectrumBox() - box.spectrum = kwargs["spectrum"] - box.wavelength = kwargs["wavelength"] - box.flux = kwargs["flux"] - if "error" in kwargs: - box.error = kwargs["error"] - if "name" in kwargs: - box.name = kwargs["name"] - if "simbad" in kwargs: - box.simbad = kwargs["simbad"] - if "sptype" in kwargs: - box.sptype = kwargs["sptype"] - if "distance" in kwargs: - box.distance = kwargs["distance"] - if "spec_res" in kwargs: - box.spec_res = kwargs["spec_res"] - - elif boxtype == "synphot": - box = SynphotBox() - box.name = kwargs["name"] - box.flux = kwargs["flux"] - if "wavelength" in kwargs: - box.wavelength = kwargs["wavelength"] - if "app_mag" in kwargs: - box.app_mag = kwargs["app_mag"] - if "abs_mag" in kwargs: - box.abs_mag = kwargs["abs_mag"] - - return box - - class Box: """ Class for generic methods that can be applied on all `Box` object. @@ -585,3 +411,177 @@ def __init__(self): self.flux = None self.app_mag = None self.abs_mag = None + + +def create_box(boxtype, **kwargs): + """ + Function for creating a :class:`~species.core.box.Box`. + + Returns + ------- + species.core.box + Box with the data and parameters. + """ + + if boxtype == "colormag": + box = ColorMagBox() + box.library = kwargs["library"] + box.object_type = kwargs["object_type"] + box.filters_color = kwargs["filters_color"] + box.filter_mag = kwargs["filter_mag"] + box.color = kwargs["color"] + box.magnitude = kwargs["magnitude"] + if "names" in kwargs: + box.names = kwargs["names"] + if "sptype" in kwargs: + box.sptype = kwargs["sptype"] + if "mass" in kwargs: + box.mass = kwargs["mass"] + if "radius" in kwargs: + box.radius = kwargs["radius"] + if "iso_tag" in kwargs: + box.iso_tag = kwargs["iso_tag"] + + if boxtype == "colorcolor": + box = ColorColorBox() + box.library = kwargs["library"] + box.object_type = kwargs["object_type"] + box.filters = kwargs["filters"] + box.color1 = kwargs["color1"] + box.color2 = kwargs["color2"] + if "names" in kwargs: + box.names = kwargs["names"] + if "sptype" in kwargs: + box.sptype = kwargs["sptype"] + if "mass" in kwargs: + box.mass = kwargs["mass"] + if "radius" in kwargs: + box.radius = kwargs["radius"] + if "iso_tag" in kwargs: + box.iso_tag = kwargs["iso_tag"] + + elif boxtype == "cooling": + box = CoolingBox() + box.model = kwargs["model"] + box.mass = kwargs["mass"] + if "age" in kwargs: + box.age = kwargs["age"] + else: + box.age = kwargs["ages"] + box.teff = kwargs["teff"] + box.log_lum = kwargs["log_lum"] + box.logg = kwargs["logg"] + box.radius = kwargs["radius"] + box.filter_mag = kwargs["filter_mag"] + box.magnitude = kwargs["magnitude"] + box.filters_color = kwargs["filters_color"] + box.color = kwargs["color"] + + elif boxtype == "isochrone": + box = IsochroneBox() + box.model = kwargs["model"] + box.age = kwargs["age"] + if "mass" in kwargs: + box.mass = kwargs["mass"] + else: + box.mass = kwargs["masses"] + box.teff = kwargs["teff"] + box.log_lum = kwargs["log_lum"] + box.logg = kwargs["logg"] + box.radius = kwargs["radius"] + box.filter_mag = kwargs["filter_mag"] + box.magnitude = kwargs["magnitude"] + box.filters_color = kwargs["filters_color"] + box.color = kwargs["color"] + + elif boxtype == "model": + box = ModelBox() + box.model = kwargs["model"] + box.wavelength = kwargs["wavelength"] + box.flux = kwargs["flux"] + box.parameters = kwargs["parameters"] + box.quantity = kwargs["quantity"] + if "contribution" in kwargs: + box.contribution = kwargs["contribution"] + if "bol_flux" in kwargs: + box.bol_flux = kwargs["bol_flux"] + + elif boxtype == "object": + box = ObjectBox() + box.name = kwargs["name"] + box.filters = kwargs["filters"] + box.mean_wavel = kwargs["mean_wavel"] + box.magnitude = kwargs["magnitude"] + box.flux = kwargs["flux"] + box.spectrum = kwargs["spectrum"] + if "parallax" in kwargs: + box.parallax = kwargs["parallax"] + if "distance" in kwargs: + box.distance = kwargs["distance"] + + elif boxtype == "photometry": + box = PhotometryBox() + if "name" in kwargs: + box.name = kwargs["name"] + if "sptype" in kwargs: + box.sptype = kwargs["sptype"] + if "wavelength" in kwargs: + box.wavelength = kwargs["wavelength"] + if "flux" in kwargs: + box.flux = kwargs["flux"] + if "app_mag" in kwargs: + box.app_mag = kwargs["app_mag"] + if "abs_mag" in kwargs: + box.abs_mag = kwargs["abs_mag"] + if "filter_name" in kwargs: + box.filter_name = kwargs["filter_name"] + + elif boxtype == "residuals": + box = ResidualsBox() + box.name = kwargs["name"] + box.photometry = kwargs["photometry"] + box.spectrum = kwargs["spectrum"] + if "chi2_red" in kwargs: + box.chi2_red = kwargs["chi2_red"] + + elif boxtype == "samples": + box = SamplesBox() + box.spectrum = kwargs["spectrum"] + box.parameters = kwargs["parameters"] + box.samples = kwargs["samples"] + box.ln_prob = kwargs["ln_prob"] + box.ln_evidence = kwargs["ln_evidence"] + box.prob_sample = kwargs["prob_sample"] + box.median_sample = kwargs["median_sample"] + box.attributes = kwargs["attributes"] + + elif boxtype == "spectrum": + box = SpectrumBox() + box.spectrum = kwargs["spectrum"] + box.wavelength = kwargs["wavelength"] + box.flux = kwargs["flux"] + if "error" in kwargs: + box.error = kwargs["error"] + if "name" in kwargs: + box.name = kwargs["name"] + if "simbad" in kwargs: + box.simbad = kwargs["simbad"] + if "sptype" in kwargs: + box.sptype = kwargs["sptype"] + if "distance" in kwargs: + box.distance = kwargs["distance"] + if "spec_res" in kwargs: + box.spec_res = kwargs["spec_res"] + + elif boxtype == "synphot": + box = SynphotBox() + box.name = kwargs["name"] + box.flux = kwargs["flux"] + if "wavelength" in kwargs: + box.wavelength = kwargs["wavelength"] + if "app_mag" in kwargs: + box.app_mag = kwargs["app_mag"] + if "abs_mag" in kwargs: + box.abs_mag = kwargs["abs_mag"] + + return box diff --git a/species/core/constants.py b/species/core/constants.py index 730f3dea..3b31b3de 100644 --- a/species/core/constants.py +++ b/species/core/constants.py @@ -2,21 +2,22 @@ Physical constants in the International System of Units (SI). """ +from typing import Final from astropy import constants -PLANCK = constants.h.value # (m2 kg s-1) -LIGHT = constants.c.value # (m s-1) -BOLTZMANN = constants.k_B.value # (J K-1) -GRAVITY = constants.G.value # (m3 kg−1 s−2) -PARSEC = constants.pc.value # (m) -AU = constants.au.value # (m) -R_JUP = constants.R_jup.value # (m) -M_JUP = constants.M_jup.value # (kg) -L_SUN = constants.L_sun.value # (W) -R_SUN = constants.R_sun.value # (m) -M_SUN = constants.M_sun.value # (kg) -R_EARTH = constants.R_earth.value # (m) -M_EARTH = constants.M_earth.value # (kg) -SIGMA_SB = constants.sigma_sb.value # (W m−2 K−4) -ATOMIC_MASS = constants.u.value # (kg) -RYDBERG = constants.Ryd.value # (m-1) +PLANCK: Final = constants.h.value # (m2 kg s-1) +LIGHT: Final = constants.c.value # (m s-1) +BOLTZMANN: Final = constants.k_B.value # (J K-1) +GRAVITY: Final = constants.G.value # (m3 kg−1 s−2) +PARSEC: Final = constants.pc.value # (m) +AU: Final = constants.au.value # (m) +R_JUP: Final = constants.R_jup.value # (m) +M_JUP: Final = constants.M_jup.value # (kg) +L_SUN: Final = constants.L_sun.value # (W) +R_SUN: Final = constants.R_sun.value # (m) +M_SUN: Final = constants.M_sun.value # (kg) +R_EARTH: Final = constants.R_earth.value # (m) +M_EARTH: Final = constants.M_earth.value # (kg) +SIGMA_SB: Final = constants.sigma_sb.value # (W m−2 K−4) +ATOMIC_MASS: Final = constants.u.value # (kg) +RYDBERG: Final = constants.Ryd.value # (m-1) diff --git a/species/core/species_init.py b/species/core/species_init.py index fea97666..2b9f5f31 100644 --- a/species/core/species_init.py +++ b/species/core/species_init.py @@ -2,7 +2,6 @@ Module for setting up species in the working folder. """ -import configparser import json import os import socket @@ -13,6 +12,7 @@ import h5py import species +from configparser import ConfigParser from typeguard import typechecked @@ -40,7 +40,7 @@ def __init__(self) -> None: print(len(species_msg) * "=") working_folder = os.path.abspath(os.getcwd()) - print(f"Working folder: {working_folder}") + print(f"\nWorking folder: {working_folder}") config_file = os.path.join(working_folder, "species_config.ini") @@ -80,7 +80,7 @@ def __init__(self) -> None: print(" [DONE]") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) if "database" in config["species"]: diff --git a/species/data/__init__.py b/species/data/__init__.py index e69de29b..45118301 100644 --- a/species/data/__init__.py +++ b/species/data/__init__.py @@ -0,0 +1 @@ +__all__ = ["database"] diff --git a/species/data/companion_data/__init__.py b/species/data/companion_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/companion_data.json b/species/data/companion_data/companion_data.json similarity index 100% rename from species/data/companion_data.json rename to species/data/companion_data/companion_data.json diff --git a/species/data/companion_spectra.json b/species/data/companion_data/companion_spectra.json similarity index 100% rename from species/data/companion_spectra.json rename to species/data/companion_data/companion_spectra.json diff --git a/species/data/companion_spectra.py b/species/data/companion_data/companion_spectra.py similarity index 100% rename from species/data/companion_spectra.py rename to species/data/companion_data/companion_spectra.py diff --git a/species/data/database.py b/species/data/database.py index fe835c64..5f2dc19a 100644 --- a/species/data/database.py +++ b/species/data/database.py @@ -11,9 +11,8 @@ from configparser import ConfigParser from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union -import emcee import h5py import numpy as np @@ -25,59 +24,7 @@ from typeguard import typechecked from species.core import constants -from species.core.box import ( - ModelBox, - ObjectBox, - SamplesBox, - SpectrumBox, - create_box, -) -from species.data.accretion import add_accretion_relation -from species.data.allers2013 import add_allers2013 -from species.data.bonnefoy2014 import add_bonnefoy2014 -from species.data.companion_spectra import companion_spectra -from species.data.custom_model import add_custom_model_grid -from species.data.dust import add_cross_sections, add_optical_constants -from species.data.filters import download_filter -from species.data.irtf import add_irtf -from species.data.isochrones import ( - add_ames, - add_atmo, - add_baraffe2015, - add_btsettl, - add_linder2019, - add_manual, - add_marleau, - add_nextgen, - add_saumon, - add_sonora, -) -from species.data.kesseli2017 import add_kesseli2017 -from species.data.leggett import add_leggett -from species.data.model_spectra import add_model_grid -from species.data.spex import add_spex -from species.data.vega import add_vega -from species.data.vlm_plx import add_vlm_plx -from species.phot.syn_phot import SyntheticPhotometry -from species.read.read_calibration import ReadCalibration -from species.read.read_filter import ReadFilter -from species.read.read_model import ReadModel -from species.read.read_object import ReadObject -from species.read.read_planck import ReadPlanck -from species.read.read_radtrans import ReadRadtrans -from species.util.data_util import convert_units, correlation_to_covariance -from species.util.dust_util import ism_extinction -from species.util.model_util import binary_to_single, powerlaw_spectrum -from species.util.radtrans_util import retrieval_spectrum -from species.util.retrieval_util import ( - find_cloud_deck, - list_to_dict, - log_x_cloud_base, - pt_ret_model, - pt_spline_interp, - quench_pressure, - scale_cloud_abund, -) +from species.core.box import ObjectBox, ModelBox, SamplesBox, SpectrumBox, create_box class Database: @@ -100,7 +47,7 @@ def __init__(self) -> None: config.read(config_file) self.database = config["species"]["database"] - self.input_path = config["species"]["data_folder"] + self.data_folder = config["species"]["data_folder"] @typechecked def list_content(self) -> None: @@ -169,7 +116,7 @@ def list_companions() -> List[str]: List with the object names that are stored in the database. """ - data_file = Path(__file__).parent.resolve() / "companion_data.json" + data_file = Path(__file__).parent.resolve() / "companion_data/companion_data.json" with open(data_file, "r", encoding="utf-8") as json_file: comp_data = json.load(json_file) @@ -312,8 +259,8 @@ def add_companion( """ Function for adding the magnitudes and spectra of directly imaged planets and brown dwarfs from - `data/companion_data.json` and - :func:`~species.data.companion_spectra` to the database. + `data/companion_data/companion_data.json` and + :func:`~species.data.companion_data.companion_spectra` to the database. Parameters ---------- @@ -332,10 +279,12 @@ def add_companion( None """ + from species.data.companion_data.companion_spectra import companion_spectra + if isinstance(name, str): name = list((name,)) - data_file = Path(__file__).parent.resolve() / "companion_data.json" + data_file = Path(__file__).parent.resolve() / "companion_data/companion_data.json" with open(data_file, "r", encoding="utf-8") as json_file: comp_data = json.load(json_file) @@ -344,7 +293,7 @@ def add_companion( name = list(comp_data.keys()) for item in name: - spec_dict = companion_spectra(self.input_path, item, verbose=verbose) + spec_dict = companion_spectra(self.data_folder, item, verbose=verbose) parallax = None @@ -420,12 +369,14 @@ def add_dust(self) -> None: None """ + from species.data.misc_data.dust_data import add_cross_sections, add_optical_constants + with h5py.File(self.database, "a") as hdf5_file: if "dust" in hdf5_file: del hdf5_file["dust"] - add_optical_constants(self.input_path, hdf5_file) - add_cross_sections(self.input_path, hdf5_file) + add_optical_constants(self.data_folder, hdf5_file) + add_cross_sections(self.data_folder, hdf5_file) @typechecked def add_accretion(self) -> None: @@ -445,12 +396,14 @@ def add_accretion(self) -> None: NoneType None """ + + from species.data.misc_data.accretion_data import add_accretion_relation with h5py.File(self.database, "a") as hdf5_file: if "accretion" in hdf5_file: del hdf5_file["accretion"] - add_accretion_relation(self.input_path, hdf5_file) + add_accretion_relation(self.data_folder, hdf5_file) @typechecked def add_filter( @@ -473,7 +426,7 @@ def add_filter( Filter name from the SVO Filter Profile Service (e.g., 'Paranal/NACO.Lp') or a user-defined name if a ``filename`` is specified. - filename : str + filename : str, None Filename of the filter profile. The first column should contain the wavelength (um) and the second column the fractional transmission. The profile is downloaded from @@ -499,41 +452,35 @@ def add_filter( if verbose: print(f"Adding filter: {filter_name}...", end="", flush=True) - filter_split = filter_name.split("/") + # filter_split = filter_name.split("/") - hdf5_file = h5py.File(self.database, "a") - - if f"filters/{filter_name}" in hdf5_file: - del hdf5_file[f"filters/{filter_name}"] + if filename is not None: + filter_profile = np.loadtxt(filename) - if f"filters/{filter_split[0]}" not in hdf5_file: - hdf5_file.create_group(f"filters/{filter_split[0]}") + wavelength = filter_profile[:, 0] + transmission = filter_profile[:, 1] - if filename is not None: - data = np.loadtxt(filename) - wavelength = data[:, 0] - transmission = data[:, 1] + with h5py.File(self.database, "a") as hdf5_file: + if f"filters/{filter_name}" in hdf5_file: + del hdf5_file[f"filters/{filter_name}"] - else: - wavelength, transmission, detector_type = download_filter(filter_name) + dset = hdf5_file.create_dataset( + f"filters/{filter_name}", data=np.column_stack((wavelength, transmission)) + ) - if wavelength is not None and transmission is not None: - wavel_new = [wavelength[0]] - transm_new = [transmission[0]] + dset.attrs["det_type"] = str(detector_type) - for i in range(wavelength.size - 1): - if wavelength[i + 1] > wavel_new[-1]: - # Required for the issue with the Keck/NIRC2.J filter on SVO - wavel_new.append(wavelength[i + 1]) - transm_new.append(transmission[i + 1]) + else: + from species.data.filter_data.filter_data import add_filter_profile - dset = hdf5_file.create_dataset( - f"filters/{filter_name}", data=np.column_stack((wavel_new, transm_new)) - ) + with h5py.File(self.database, "a") as hdf5_file: + if f"filters/{filter_name}" in hdf5_file: + del hdf5_file[f"filters/{filter_name}"] - dset.attrs["det_type"] = str(detector_type) + # if f"filters/{filter_split[0]}" not in hdf5_file: + # hdf5_file.create_group(f"filters/{filter_split[0]}") - hdf5_file.close() + add_filter_profile(self.data_folder, hdf5_file, filter_name) if verbose: print(" [DONE]") @@ -564,7 +511,7 @@ def add_isochrones( Filename with the isochrone data. Setting the argument is only required when ``model='manual'``. Otherwise, the argument can be set to ``None``. - tag : str + tag : str, None Database tag name where the isochrone that will be stored. Setting the argument is only required when ``model='manual'``. Otherwise, the argument can be @@ -576,6 +523,8 @@ def add_isochrones( None """ + from species.data.isochrone_data.add_isochrone import add_isochrone_grid + if model == "phoenix": warnings.warn( "Please set model='manual' instead of " @@ -584,107 +533,67 @@ def add_isochrones( DeprecationWarning, ) - hdf5_file = h5py.File(self.database, "a") - - if "isochrones" not in hdf5_file: - hdf5_file.create_group("isochrones") - - if model in ["manual", "marleau", "phoenix"]: - if f"isochrones/{tag}" in hdf5_file: - del hdf5_file[f"isochrones/{tag}"] - - elif model == "ames": - if "isochrones/ames-cond" in hdf5_file: - del hdf5_file["isochrones/ames-cond"] - if "isochrones/ames-dusty" in hdf5_file: - del hdf5_file["isochrones/ames-dusty"] - - elif model == "atmo": - if "isochrones/atmo-ceq" in hdf5_file: - del hdf5_file["isochrones/atmo-ceq"] - if "isochrones/atmo-neq-weak" in hdf5_file: - del hdf5_file["isochrones/atmo-neq-weak"] - if "isochrones/atmo-neq-strong" in hdf5_file: - del hdf5_file["isochrones/atmo-neq-strong"] - - elif model == "baraffe2015": - if "isochrones/baraffe2015" in hdf5_file: - del hdf5_file["isochrones/baraffe2015"] - - elif model == "bt-settl": - if "isochrones/bt-settl" in hdf5_file: - del hdf5_file["isochrones/bt-settl"] - - elif model == "linder2019": - if "isochrones" in hdf5_file: - for iso_item in list(hdf5_file["isochrones"]): - if iso_item[:10] == "linder2019": - del hdf5_file[f"isochrones/{iso_item}"] - - elif model == "nextgen": - if "isochrones/nextgen" in hdf5_file: - del hdf5_file["isochrones/nextgen"] - - elif model == "saumon2008": - if "isochrones/saumon2008-nc_solar" in hdf5_file: - del hdf5_file["isochrones/saumon2008-nc_solar"] - if "isochrones/saumon2008-nc_-0.3" in hdf5_file: - del hdf5_file["isochrones/saumon2008-nc_-0.3"] - if "isochrones/saumon2008-nc_+0.3" in hdf5_file: - del hdf5_file["isochrones/saumon2008-nc_+0.3"] - if "isochrones/saumon2008-f2_solar" in hdf5_file: - del hdf5_file["isochrones/saumon2008-f2_solar"] - if "isochrones/saumon2008-hybrid_solar" in hdf5_file: - del hdf5_file["isochrones/saumon2008-hybrid_solar"] - - elif model == "sonora": - if "isochrones/sonora+0.0" in hdf5_file: - del hdf5_file["isochrones/sonora+0.0"] - if "isochrones/sonora+0.5" in hdf5_file: - del hdf5_file["isochrones/sonora+0.5"] - if "isochrones/sonora-0.5" in hdf5_file: - del hdf5_file["isochrones/sonora-0.5"] - - if model == "ames": - add_ames(hdf5_file, self.input_path) - - elif model == "atmo": - add_atmo(hdf5_file, self.input_path) - - elif model == "baraffe2015": - add_baraffe2015(hdf5_file, self.input_path) - - elif model == "bt-settl": - add_btsettl(hdf5_file, self.input_path) - - elif model == "linder2019": - add_linder2019(hdf5_file, self.input_path) - - elif model == "manual": - add_manual(hdf5_file, tag, filename) - - elif model == "marleau": - add_marleau(hdf5_file, tag, filename) - - elif model == "nextgen": - add_nextgen(hdf5_file, self.input_path) - - elif model == "saumon2008": - add_saumon(hdf5_file, self.input_path) - - elif model == "sonora": - add_sonora(hdf5_file, self.input_path) - - else: - raise ValueError( - f"The evolutionary model '{model}' is " - "not supported. Please choose another " - "argument for 'model'. Have a look " - "at the documentation of add_isochrones " - "for details on the supported models." - ) - - hdf5_file.close() + with h5py.File(self.database, "a") as hdf5_file: + if "isochrones" not in hdf5_file: + hdf5_file.create_group("isochrones") + + if model in ["manual", "marleau", "phoenix"]: + if f"isochrones/{tag}" in hdf5_file: + del hdf5_file[f"isochrones/{tag}"] + + elif model == "ames": + if "isochrones/ames-cond" in hdf5_file: + del hdf5_file["isochrones/ames-cond"] + if "isochrones/ames-dusty" in hdf5_file: + del hdf5_file["isochrones/ames-dusty"] + + elif model == "atmo": + if "isochrones/atmo-ceq" in hdf5_file: + del hdf5_file["isochrones/atmo-ceq"] + if "isochrones/atmo-neq-weak" in hdf5_file: + del hdf5_file["isochrones/atmo-neq-weak"] + if "isochrones/atmo-neq-strong" in hdf5_file: + del hdf5_file["isochrones/atmo-neq-strong"] + + elif model == "baraffe2015": + if "isochrones/baraffe2015" in hdf5_file: + del hdf5_file["isochrones/baraffe2015"] + + elif model == "bt-settl": + if "isochrones/bt-settl" in hdf5_file: + del hdf5_file["isochrones/bt-settl"] + + elif model == "linder2019": + if "isochrones" in hdf5_file: + for iso_item in list(hdf5_file["isochrones"]): + if iso_item[:10] == "linder2019": + del hdf5_file[f"isochrones/{iso_item}"] + + elif model == "nextgen": + if "isochrones/nextgen" in hdf5_file: + del hdf5_file["isochrones/nextgen"] + + elif model == "saumon2008": + if "isochrones/saumon2008-nc_solar" in hdf5_file: + del hdf5_file["isochrones/saumon2008-nc_solar"] + if "isochrones/saumon2008-nc_-0.3" in hdf5_file: + del hdf5_file["isochrones/saumon2008-nc_-0.3"] + if "isochrones/saumon2008-nc_+0.3" in hdf5_file: + del hdf5_file["isochrones/saumon2008-nc_+0.3"] + if "isochrones/saumon2008-f2_solar" in hdf5_file: + del hdf5_file["isochrones/saumon2008-f2_solar"] + if "isochrones/saumon2008-hybrid_solar" in hdf5_file: + del hdf5_file["isochrones/saumon2008-hybrid_solar"] + + elif model == "sonora": + if "isochrones/sonora+0.0" in hdf5_file: + del hdf5_file["isochrones/sonora+0.0"] + if "isochrones/sonora+0.5" in hdf5_file: + del hdf5_file["isochrones/sonora+0.5"] + if "isochrones/sonora-0.5" in hdf5_file: + del hdf5_file["isochrones/sonora-0.5"] + + add_isochrone_grid(self.data_folder, hdf5_file, model, filename=filename, tag=tag) @typechecked def add_model( @@ -739,12 +648,14 @@ def add_model( None """ + from species.data.model_data.model_spectra import add_model_grid + with h5py.File(self.database, "a") as hdf5_file: if "models" not in hdf5_file: hdf5_file.create_group("models") add_model_grid( - model, self.input_path, hdf5_file, wavel_range, teff_range, spec_res + model, self.data_folder, hdf5_file, wavel_range, teff_range, spec_res ) @typechecked @@ -815,6 +726,8 @@ def add_custom_model( None """ + from species.data.model_data.custom_model import add_custom_model_grid + with h5py.File(self.database, "a") as hdf5_file: if "models" not in hdf5_file: hdf5_file.create_group("models") @@ -940,25 +853,30 @@ def add_object( # will also open the HDF5 database if app_mag is not None: + from species.read.read_filter import ReadFilter for mag_item in app_mag: read_filt = ReadFilter(mag_item) - hdf5_file = h5py.File(self.database, "a") - if deredden is None: deredden = {} if app_mag is not None: - if "spectra/calibration/vega" not in hdf5_file: - self.add_spectra("vega") + from species.data.spec_data.spec_vega import add_vega - for item in app_mag: - if f"filters/{item}" not in hdf5_file: - self.add_filter(item, verbose=verbose) + with h5py.File(self.database, "a") as hdf5_file: + if "spectra/calibration/vega" not in hdf5_file: + add_vega(self.data_folder, hdf5_file) + + for item in app_mag: + if f"filters/{item}" not in hdf5_file: + self.add_filter(item, verbose=verbose) if flux_density is not None: - if "spectra/calibration/vega" not in hdf5_file: - self.add_spectra("vega") + from species.data.spec_data.spec_vega import add_vega + + with h5py.File(self.database, "a") as hdf5_file: + if "spectra/calibration/vega" not in hdf5_file: + add_vega(self.data_folder, hdf5_file) for item in flux_density: if f"filters/{item}" not in hdf5_file: @@ -969,6 +887,8 @@ def add_object( else: print(f"Adding object: {object_name}", end="", flush=True) + hdf5_file = h5py.File(self.database, "a") + if "objects" not in hdf5_file: hdf5_file.create_group("objects") @@ -1002,11 +922,14 @@ def add_object( dered_phot = {} if app_mag is not None: + from species.read.read_filter import ReadFilter for mag_item in app_mag: read_filt = ReadFilter(mag_item) mean_wavel = read_filt.mean_wavelength() if isinstance(deredden, float) or mag_item in deredden: + from species.util.dust_util import ism_extinction + filter_profile = read_filt.get_filter() if isinstance(deredden, float): @@ -1017,6 +940,7 @@ def add_object( deredden[mag_item], 3.1, filter_profile[:, 0] ) + from species.phot.syn_phot import SyntheticPhotometry synphot = SyntheticPhotometry(mag_item) dered_phot[mag_item], _ = synphot.spectrum_to_flux( @@ -1028,6 +952,7 @@ def add_object( if isinstance(app_mag[mag_item], tuple): try: + from species.phot.syn_phot import SyntheticPhotometry synphot = SyntheticPhotometry(mag_item) flux[mag_item], error[mag_item] = synphot.magnitude_to_flux( @@ -1054,6 +979,7 @@ def add_object( for i, dupl_item in enumerate(app_mag[mag_item]): try: + from species.phot.syn_phot import SyntheticPhotometry synphot = SyntheticPhotometry(mag_item) flux_dupl, error_dupl = synphot.magnitude_to_flux( @@ -1179,6 +1105,7 @@ def add_object( dset.attrs["n_phot"] = n_phot if flux_density is not None: + from species.read.read_filter import ReadFilter for flux_item in flux_density: read_filt = ReadFilter(flux_item) mean_wavel = read_filt.mean_wavelength() @@ -1205,6 +1132,7 @@ def add_object( ) if flux_item in units: + from species.util.data_util import convert_units flux_in = np.array([[mean_wavel, data[2], data[3]]]) flux_out = convert_units(flux_in, ("um", units[flux_item])) @@ -1272,6 +1200,7 @@ def add_object( and spec_item not in read_spec ): if spec_item in units: + from species.util.data_util import convert_units data = convert_units(data, units[spec_item]) read_spec[spec_item] = data @@ -1304,15 +1233,20 @@ def add_object( print(" - Spectrum:") if spec_item in units: + from species.util.data_util import convert_units data = convert_units(data, units[spec_item]) read_spec[spec_item] = data if isinstance(deredden, float): + from species.util.dust_util import ism_extinction + ext_mag = ism_extinction(deredden, 3.1, read_spec[spec_item][:, 0]) read_spec[spec_item][:, 1] *= 10.0 ** (0.4 * ext_mag) elif spec_item in deredden: + from species.util.dust_util import ism_extinction + ext_mag = ism_extinction( deredden[spec_item], 3.1, read_spec[spec_item][:, 0] ) @@ -1419,6 +1353,8 @@ def add_object( == read_spec[spec_item].shape[0] ): if np.all(np.diag(data) == 1.0): + from species.util.data_util import correlation_to_covariance + warnings.warn(corr_warn) read_cov[ @@ -1467,6 +1403,8 @@ def add_object( f"matrix." ) + from species.util.data_util import correlation_to_covariance + read_cov[spec_item] = correlation_to_covariance( data, read_spec[spec_item][:, 2] ) @@ -1535,21 +1473,21 @@ def add_photometry(self, phot_library: str) -> None: None """ - hdf5_file = h5py.File(self.database, "a") - if "photometry" not in hdf5_file: - hdf5_file.create_group("photometry") + with h5py.File(self.database, "a") as hdf5_file: + if "photometry" not in hdf5_file: + hdf5_file.create_group("photometry") - if "photometry/" + phot_library in hdf5_file: - del hdf5_file["photometry/" + phot_library] + if f"photometry/{phot_library}" in hdf5_file: + del hdf5_file[f"photometry/{phot_library}"] - if phot_library[0:7] == "vlm-plx": - add_vlm_plx(self.input_path, hdf5_file) + if phot_library[0:7] == "vlm-plx": + from species.data.phot_data.phot_vlm_plx import add_vlm_plx + add_vlm_plx(self.data_folder, hdf5_file) - elif phot_library[0:7] == "leggett": - add_leggett(self.input_path, hdf5_file) - - hdf5_file.close() + elif phot_library[0:7] == "leggett": + from species.data.phot_data.phot_leggett import add_leggett + add_leggett(self.data_folder, hdf5_file) @typechecked def add_calibration( @@ -1705,64 +1643,6 @@ def add_calibration( print(" [DONE]") - @typechecked - def add_spectrum( - self, spec_library: str, sptypes: Optional[List[str]] = None - ) -> None: - """ - DEPRECATION: This method is deprecated and will be - removed in a future release. Please use the - :func:`~species.data.database.Database.add_spectra` - method instead. - - Parameters - ---------- - spec_library : str - Spectral library ('irtf', 'spex', 'kesseli+2017', - 'bonnefoy+2014', 'allers+2013'). - sptypes : list(str) - Spectral types ('F', 'G', 'K', 'M', 'L', 'T'). - Currently only implemented for 'irtf'. - - Returns - ------- - NoneType - None - """ - - warnings.warn( - "This method is deprecated and will be removed in a future release. Please " - "use the add_spectra method instead." - ) - - hdf5_file = h5py.File(self.database, "a") - - if "spectra" not in hdf5_file: - hdf5_file.create_group("spectra") - - if "spectra/" + spec_library in hdf5_file: - del hdf5_file["spectra/" + spec_library] - - if spec_library[0:5] == "vega": - add_vega(self.input_path, hdf5_file) - - elif spec_library[0:5] == "irtf": - add_irtf(self.input_path, hdf5_file, sptypes) - - elif spec_library[0:5] == "spex": - add_spex(self.input_path, hdf5_file) - - elif spec_library[0:12] == "kesseli+2017": - add_kesseli2017(self.input_path, hdf5_file) - - elif spec_library[0:13] == "bonnefoy+2014": - add_bonnefoy2014(self.input_path, hdf5_file) - - elif spec_library[0:11] == "allers+2013": - add_allers2013(self.input_path, hdf5_file) - - hdf5_file.close() - @typechecked def add_spectra( self, spec_library: str, sptypes: Optional[List[str]] = None @@ -1789,33 +1669,13 @@ def add_spectra( None """ - hdf5_file = h5py.File(self.database, "a") - - if "spectra" not in hdf5_file: - hdf5_file.create_group("spectra") - - if f"spectra/{spec_library}" in hdf5_file: - del hdf5_file["spectra/" + spec_library] - - if spec_library[0:5] == "vega": - add_vega(self.input_path, hdf5_file) - - elif spec_library[0:5] == "irtf": - add_irtf(self.input_path, hdf5_file, sptypes) - - elif spec_library[0:5] == "spex": - add_spex(self.input_path, hdf5_file) - - elif spec_library[0:12] == "kesseli+2017": - add_kesseli2017(self.input_path, hdf5_file) + from species.data.spec_data.add_spec_data import add_spec_library - elif spec_library[0:13] == "bonnefoy+2014": - add_bonnefoy2014(self.input_path, hdf5_file) - - elif spec_library[0:11] == "allers+2013": - add_allers2013(self.input_path, hdf5_file) + with h5py.File(self.database, "a") as hdf5_file: + if f"spectra/{spec_library}" in hdf5_file: + del hdf5_file["spectra/" + spec_library] - hdf5_file.close() + add_spec_library(self.data_folder, hdf5_file, spec_library, sptypes) @typechecked def add_samples( @@ -1936,8 +1796,10 @@ def add_samples( print("Integrated autocorrelation time:") + from emcee.autocorr import integrated_time + for i, item in enumerate(modelpar): - auto_corr = emcee.autocorr.integrated_time(samples[:, i], quiet=True)[0] + auto_corr = integrated_time(samples[:, i], quiet=True)[0] if np.allclose(samples[:, i], np.mean(samples[:, i])): print(f" - {item}: fixed") @@ -2289,15 +2151,18 @@ def get_mcmc_spectra( if spectrum_type == "model": if spectrum_name == "planck": + from species.read.read_planck import ReadPlanck readmodel = ReadPlanck(wavel_range) elif spectrum_name == "powerlaw": pass else: + from species.read.read_model import ReadModel readmodel = ReadModel(spectrum_name, wavel_range=wavel_range) elif spectrum_type == "calibration": + from species.read.read_calibration import ReadCalibration readcalib = ReadCalibration(spectrum_name, filter_name=None) boxes = [] @@ -2330,9 +2195,12 @@ def get_mcmc_spectra( "'powerlaw' model so the argument will be ignored." ) + from species.util.model_util import powerlaw_spectrum specbox = powerlaw_spectrum(wavel_range, model_param) else: + from species.util.model_util import binary_to_single + if binary: param_0 = binary_to_single(model_param, 0) @@ -2472,13 +2340,16 @@ def get_mcmc_photometry( if spectrum_type == "model": if spectrum_name == "powerlaw": + from species.phot.syn_phot import SyntheticPhotometry synphot = SyntheticPhotometry(filter_name) synphot.zero_point() # Set the wavel_range attribute else: + from species.read.read_model import ReadModel readmodel = ReadModel(spectrum_name, filter_name=filter_name) elif spectrum_type == "calibration": + from species.read.read_calibration import ReadCalibration readcalib = ReadCalibration(spectrum_name, filter_name=filter_name) mcmc_phot = np.zeros((samples.shape[0])) @@ -2497,6 +2368,7 @@ def get_mcmc_photometry( if spectrum_type == "model": if spectrum_name == "powerlaw": + from species.util.model_util import powerlaw_spectrum pl_box = powerlaw_spectrum(synphot.wavel_range, model_param) if phot_type == "magnitude": @@ -2513,6 +2385,8 @@ def get_mcmc_photometry( else: if phot_type == "magnitude": if binary: + from species.util.model_util import binary_to_single + param_0 = binary_to_single(model_param, 0) mcmc_phot_0, _ = readmodel.get_magnitude(param_0) @@ -2529,6 +2403,8 @@ def get_mcmc_photometry( elif phot_type == "flux": if binary: + from species.util.model_util import binary_to_single + param_0 = binary_to_single(model_param, 0) mcmc_phot_0, _ = readmodel.get_flux(param_0) @@ -2608,6 +2484,8 @@ def get_object( flux = {} mean_wavel = {} + from species.read.read_filter import ReadFilter + for observatory in dset.keys(): if observatory not in ["parallax", "distance", "spectrum"]: for filter_name in dset[observatory]: @@ -2617,9 +2495,6 @@ def get_object( magnitude[name] = dset[name][0:2] flux[name] = dset[name][2:4] - read_filt = ReadFilter(name) - mean_wavel[name] = read_filt.mean_wavelength() - phot_filters = list(magnitude.keys()) else: @@ -2656,6 +2531,11 @@ def get_object( hdf5_file.close() + if magnitude is not None: + for filter_name in magnitude.keys(): + read_filt = ReadFilter(filter_name) + mean_wavel[filter_name] = read_filt.mean_wavelength() + print(" [DONE]") return create_box( @@ -2845,6 +2725,11 @@ def get_pt_profiles( of the array is (n_pressures, n_samples). """ + from species.util.retrieval_util import ( + pt_ret_model, + pt_spline_interp, + ) + hdf5_file = h5py.File(self.database, "r") dset = hdf5_file[f"results/fit/{tag}/samples"] @@ -3108,6 +2993,8 @@ def add_comparison( None """ + from species.read.read_object import ReadObject + read_obj = ReadObject(object_name) parallax = read_obj.get_parallax()[0] # (mas) @@ -3221,6 +3108,14 @@ def add_retrieval( None """ + from species.util.retrieval_util import ( + list_to_dict, + pt_ret_model, + pt_spline_interp, + quench_pressure, + scale_cloud_abund, + ) + print("Storing samples in the database...", end="", flush=True) json_filename = os.path.join(output_folder, "params.json") @@ -3643,7 +3538,7 @@ def get_retrieval_spectra( random: Optional[int], wavel_range: Optional[Union[Tuple[float, float], str]] = None, spec_res: Optional[float] = None, - ) -> Tuple[List[ModelBox], Union[ReadRadtrans]]: + ) -> Tuple[List[ModelBox], Union[Any]]: """ Function for extracting random spectra from the posterior distribution that was sampled with @@ -3677,10 +3572,13 @@ def get_retrieval_spectra( ------- list(ModelBox) Boxes with the randomly sampled spectra. - ReadRadtrans + species.read.read_radtrans.ReadRadtrans Instance of :class:`~species.read.read_radtrans.ReadRadtrans`. """ + from species.read.read_radtrans import ReadRadtrans + from species.util.radtrans_util import retrieval_spectrum + # Open configuration file config_file = os.path.join(os.getcwd(), "species_config.ini") @@ -4074,6 +3972,15 @@ def petitcode_param( Dictionary with parameters for ``petitCODE``. """ + from species.read.read_radtrans import ReadRadtrans + + from species.util.retrieval_util import ( + find_cloud_deck, + log_x_cloud_base, + pt_ret_model, + pt_spline_interp, + ) + if sample_type == "median": model_param = self.get_median_sample(tag) diff --git a/species/data/filter_data/__init__.py b/species/data/filter_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/filters.py b/species/data/filter_data/filter_data.py similarity index 56% rename from species/data/filters.py rename to species/data/filter_data/filter_data.py index 20365a04..b52a2944 100644 --- a/species/data/filters.py +++ b/species/data/filter_data/filter_data.py @@ -9,6 +9,7 @@ from typing import Optional, Tuple +import h5py import numpy as np from astropy.io.votable import parse_single_table @@ -18,6 +19,7 @@ @typechecked def download_filter( filter_id: str, + input_path: str, ) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[str]]: """ Function for downloading filter profile data @@ -29,6 +31,8 @@ def download_filter( Filter name as listed on the website of the `SVO Filter Profile Service `_. + input_path : str + Folder where the data is located. Returns ------- @@ -42,73 +46,71 @@ def download_filter( if filter_id == "Magellan/VisAO.rp": url = "https://xwcl.science/magao/visao/VisAO_rp_filter_curve.dat" - urllib.request.urlretrieve(url, "VisAO_rp_filter_curve.dat") + filter_path = os.path.join(input_path, "VisAO_rp_filter_curve.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission, _, _ = np.loadtxt( - "VisAO_rp_filter_curve.dat", unpack=True - ) + wavelength, transmission, _, _ = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("VisAO_rp_filter_curve.dat") + os.remove(filter_path) elif filter_id == "Magellan/VisAO.ip": url = "https://xwcl.science/magao/visao/VisAO_ip_filter_curve.dat" - urllib.request.urlretrieve(url, "VisAO_ip_filter_curve.dat") + filter_path = os.path.join(input_path, "VisAO_ip_filter_curve.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission, _, _ = np.loadtxt( - "VisAO_ip_filter_curve.dat", unpack=True - ) + wavelength, transmission, _, _ = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("VisAO_ip_filter_curve.dat") + os.remove(filter_path) elif filter_id == "Magellan/VisAO.zp": url = "https://xwcl.science/magao/visao/VisAO_zp_filter_curve.dat" - urllib.request.urlretrieve(url, "VisAO_zp_filter_curve.dat") + filter_path = os.path.join(input_path, "VisAO_zp_filter_curve.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission, _, _ = np.loadtxt( - "VisAO_zp_filter_curve.dat", unpack=True - ) + wavelength, transmission, _, _ = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("VisAO_zp_filter_curve.dat") + os.remove(filter_path) elif filter_id == "Keck/NIRC2.NB_4.05": # The filter profile of Br_alpha has been digitized from # https://www2.keck.hawaii.edu/inst/nirc2/filters.html url = "https://home.strw.leidenuniv.nl/~stolker/species/Keck_NIRC2.NB_4.05.dat" - urllib.request.urlretrieve(url, "Keck_NIRC2.NB_4.05.dat") + filter_path = os.path.join(input_path, "Keck_NIRC2.NB_4.05.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission = np.loadtxt("Keck_NIRC2.NB_4.05.dat", unpack=True) + wavelength, transmission = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("Keck_NIRC2.NB_4.05.dat") + os.remove(filter_path) elif filter_id == "Keck/NIRC.Y": # The filter profile of the Y band has been # adopted from Hillenbrand et al. (2002) url = "https://home.strw.leidenuniv.nl/~stolker/species/Keck_NIRC.Y.dat" - urllib.request.urlretrieve(url, "Keck_NIRC.Y.dat") + filter_path = os.path.join(input_path, "Keck_NIRC.Y.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission = np.loadtxt("Keck_NIRC.Y.dat", unpack=True) + wavelength, transmission = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("Keck_NIRC.Y.dat") + os.remove(filter_path) elif filter_id in ["LCO/VisAO.Ys", "Magellan/VisAO.Ys"]: url = "https://xwcl.science/magao/visao/VisAO_Ys_filter_curve.dat" - urllib.request.urlretrieve(url, "VisAO_Ys_filter_curve.dat") + filter_path = os.path.join(input_path, "VisAO_Ys_filter_curve.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission, _, _ = np.loadtxt( - "VisAO_Ys_filter_curve.dat", unpack=True - ) + wavelength, transmission, _, _ = np.loadtxt(filter_path, unpack=True) # Remove wavelengths with zero transmission wavelength = wavelength[:-7] @@ -116,34 +118,37 @@ def download_filter( det_type = "photon" - os.remove("VisAO_Ys_filter_curve.dat") + os.remove(filter_path) elif filter_id == "ALMA/band6": url = "https://home.strw.leidenuniv.nl/~stolker/species/alma_band6.dat" - urllib.request.urlretrieve(url, "alma_band6.dat") + filter_path = os.path.join(input_path, "alma_band6.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission = np.loadtxt("alma_band6.dat", unpack=True) + wavelength, transmission = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("alma_band6.dat") + os.remove(filter_path) elif filter_id == "ALMA/band7": url = "https://home.strw.leidenuniv.nl/~stolker/species/alma_band7.dat" - urllib.request.urlretrieve(url, "alma_band7.dat") + filter_path = os.path.join(input_path, "alma_band7.dat") + urllib.request.urlretrieve(url, filter_path) - wavelength, transmission = np.loadtxt("alma_band7.dat", unpack=True) + wavelength, transmission = np.loadtxt(filter_path, unpack=True) det_type = "photon" - os.remove("alma_band7.dat") + os.remove(filter_path) else: url = "http://svo2.cab.inta-csic.es/svo/theory/fps/fps.php?ID=" + filter_id - urllib.request.urlretrieve(url, "filter.xml") + filter_path = os.path.join(input_path, "filter.xml") + urllib.request.urlretrieve(url, filter_path) try: - table = parse_single_table("filter.xml") + table = parse_single_table(filter_path) wavelength = table.array["Wavelength"] transmission = table.array["Transmission"] @@ -155,17 +160,17 @@ def download_filter( warnings.warn( f"Filter '{filter_id}' is not available " - f"on the SVO Filter Profile Service." + "on the SVO Filter Profile Service." ) except: - os.remove("filter.xml") + os.remove(filter_path) raise ValueError( f"The filter data of '{filter_id}' could not " - f"be downloaded. Perhaps the website of the " - f"SVO Filter Profile Service (http://svo2.cab." - f"inta-csic.es/svo/theory/fps/) is not available?" + "be downloaded. Perhaps the website of the " + "SVO Filter Profile Service (http://svo2.cab." + "inta-csic.es/svo/theory/fps/) is not available?" ) if transmission is not None: @@ -185,14 +190,14 @@ def download_filter( det_type = "photon" warnings.warn( - f"Detector type ({det_type}) not " - f"recognized. Setting detector " - f"type to photon-counting detector." + f"Detector type, '{det_type}', not " + "recognized. Setting detector " + "type to photon-counting detector." ) wavelength *= 1e-4 # (um) - os.remove("filter.xml") + os.remove(filter_path) if wavelength is not None: indices = [] @@ -236,3 +241,47 @@ def download_filter( transmission = transmission[indices] return wavelength, transmission, det_type + + +@typechecked +def add_filter_profile( + input_path: str, database: h5py._hl.files.File, filter_name: str +) -> None: + """ + Function for downloading and adding a filter profile + to the HDF5 database. + + Parameters + ---------- + input_path : str + Folder where the data is located. + database : h5py._hl.files.File + Database. + filter_name : str + Filter name from the SVO Filter Profile Service (e.g., + 'Paranal/NACO.Lp') or a user-defined name if a ``filename`` + is specified. + + Returns + ------- + None + NoneType + """ + + wavelength, transmission, detector_type = download_filter(filter_name, input_path) + + if wavelength is not None and transmission is not None: + wavel_new = [wavelength[0]] + transm_new = [transmission[0]] + + for i in range(wavelength.size - 1): + if wavelength[i + 1] > wavel_new[-1]: + # Required for the issue with the Keck/NIRC2.J filter on SVO + wavel_new.append(wavelength[i + 1]) + transm_new.append(transmission[i + 1]) + + dset = database.create_dataset( + f"filters/{filter_name}", data=np.column_stack((wavel_new, transm_new)) + ) + + dset.attrs["det_type"] = str(detector_type) diff --git a/species/data/isochrone_data/__init__.py b/species/data/isochrone_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/isochrone_data/add_isochrone.py b/species/data/isochrone_data/add_isochrone.py new file mode 100644 index 00000000..71d4d51d --- /dev/null +++ b/species/data/isochrone_data/add_isochrone.py @@ -0,0 +1,100 @@ +""" +Module for isochrone data from evolutionary models. +""" + +from typing import Optional + +import h5py + +from species.data.isochrone_data.iso_ames import add_ames +from species.data.isochrone_data.iso_atmo import add_atmo +from species.data.isochrone_data.iso_baraffe2015 import add_baraffe2015 +from species.data.isochrone_data.iso_btsettl import add_btsettl +from species.data.isochrone_data.iso_linder2019 import add_linder2019 +from species.data.isochrone_data.iso_manual import add_manual +from species.data.isochrone_data.iso_marleau import add_marleau +from species.data.isochrone_data.iso_nextgen import add_nextgen +from species.data.isochrone_data.iso_saumon2008 import add_saumon2008 +from species.data.isochrone_data.iso_sonora import add_sonora + + +def add_isochrone_grid( + data_folder: str, + hdf5_file: h5py._hl.files.File, + model_name: str, + filename: Optional[str] = None, + tag: Optional[str] = None, +) -> None: + """ + Function for adding an isochrone grid to the database. + + Parameters + ---------- + input_path : str + Folder where the data is located. + database : h5py._hl.files.File + Database. + model_name : str + Evolutionary model ('ames', 'atmo', 'baraffe2015', + 'bt-settl', 'linder2019', 'nextgen', 'saumon2008', + 'sonora', or 'manual'). Isochrones will be + automatically downloaded. Alternatively, + isochrone data can be downloaded from + https://phoenix.ens-lyon.fr/Grids/ or + https://perso.ens-lyon.fr/isabelle.baraffe/, and can + be manually added by setting the ``filename`` and + ``tag`` arguments, and setting ``model='manual'``. + filename : str, None + Filename with the isochrone data. Setting the argument + is only required when ``model='manual'``. Otherwise, + the argument can be set to ``None``. + tag : str, None + Database tag name where the isochrone that will be + stored. Setting the argument is only required when + ``model='manual'``. Otherwise, the argument can be + set to ``None``. + + Returns + ------- + None + NoneType + """ + + if model_name == "ames": + add_ames(hdf5_file, data_folder) + + elif model_name == "atmo": + add_atmo(hdf5_file, data_folder) + + elif model_name == "baraffe2015": + add_baraffe2015(hdf5_file, data_folder) + + elif model_name == "bt-settl": + add_btsettl(hdf5_file, data_folder) + + elif model_name == "linder2019": + add_linder2019(hdf5_file, data_folder) + + elif model_name == "manual": + add_manual(hdf5_file, tag, filename) + + elif model_name == "marleau": + add_marleau(hdf5_file, tag, filename) + + elif model_name == "nextgen": + add_nextgen(hdf5_file, data_folder) + + elif model_name == "saumon2008": + add_saumon2008(hdf5_file, data_folder) + + elif model_name == "sonora": + add_sonora(hdf5_file, data_folder) + + else: + raise ValueError( + f"The evolutionary model_name '{model_name}' is " + "not supported. Please choose another " + "argument for 'model_name'. Have a look " + "at the documentation of add_isochrones " + "for details on the supported model_names." + ) diff --git a/species/data/isochrone_data/iso_ames.py b/species/data/isochrone_data/iso_ames.py new file mode 100644 index 00000000..1f44baad --- /dev/null +++ b/species/data/isochrone_data/iso_ames.py @@ -0,0 +1,59 @@ +import os +import urllib.request + +import h5py +import numpy as np + +from species.data.isochrone_data.iso_manual import add_manual + + +def add_ames(database, input_path): + """ + Function for adding the AMES-Cond and AMES-Dusty + isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_list = [ + "https://home.strw.leidenuniv.nl/~stolker/species/" + "model.AMES-Cond-2000.M-0.0.MKO.Vega", + "https://home.strw.leidenuniv.nl/~stolker/species/" + "model.AMES-dusty.M-0.0.MKO.Vega", + ] + + iso_tags = ["AMES-Cond", "AMES-Dusty"] + iso_size = ["235 kB", "182 kB"] + + for i, url_item in enumerate(url_list): + input_file = url_item.split("/")[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print( + f"Downloading {iso_tags[i]} isochrones ({iso_size[i]})...", + end="", + flush=True, + ) + urllib.request.urlretrieve(url_item, data_file) + print(" [DONE]") + + add_manual( + database=database, + tag=iso_tags[i].lower(), + file_name=data_file, + model_name="ames", + ) diff --git a/species/data/isochrone_data/iso_atmo.py b/species/data/isochrone_data/iso_atmo.py new file mode 100644 index 00000000..f157adf3 --- /dev/null +++ b/species/data/isochrone_data/iso_atmo.py @@ -0,0 +1,135 @@ +import glob +import os +import urllib.request + +import h5py +import numpy as np + + +def add_atmo(database, input_path): + """ + Function for adding the AMES-Cond and AMES-Dusty + isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_iso = ( + "https://home.strw.leidenuniv.nl/~stolker/" + "species/atmo_evolutionary_tracks.tgz" + ) + + # iso_tags = ["ATMO-CEQ", "ATMO-NEQ-weak", , "ATMO-NEQ-strong"] + # iso_size = ["235 kB", "182 kB"] + + iso_tag = "ATMO" + iso_size = "9.6 MB" + + data_folder = os.path.join(input_path, "atmo_evolutionary_tracks") + + if not os.path.exists(data_folder): + os.makedirs(data_folder) + + input_file = url_iso.rsplit("/", maxsplit=1)[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) + urllib.request.urlretrieve(url_iso, data_file) + print(" [DONE]") + + print(f"Unpacking {iso_tag} isochrones ({iso_size})...", end="", flush=True) + extract_tarfile(data_file, data_folder) + print(" [DONE]") + + iso_files = [ + "ATMO_CEQ", + "ATMO_NEQ_weak", + "ATMO_NEQ_strong", + ] + + labels = [ + "ATMO equilibrium chemistry", + "ATMO non-equilibrium chemistry (weak)", + "ATMO non-equilibrium chemistry (strong)", + ] + + db_tags = [ + "atmo-ceq", + "atmo-neq-weak", + "atmo-neq-strong", + ] + + for j, iso_item in enumerate(iso_files): + iso_path = os.path.join(data_folder, iso_item) + iso_path = os.path.join(iso_path, "MKO_WISE_IRAC") + + file_list = sorted(glob.glob(iso_path + "/*.txt")) + + for i, file_item in enumerate(file_list): + # Mass (Msun) - Age (Gyr) - Teff (K) - log(L/Lsun) - Radius (Rsun) - log(g) + if i == 0: + iso_data = np.loadtxt(file_item) + + else: + iso_load = np.loadtxt(file_item) + iso_data = np.vstack((iso_data, iso_load)) + + with open(file_item, encoding="utf-8") as open_file: + parameters = open_file.readline() + filter_names = parameters.split()[7:] + + iso_data[:, 0] *= constants.M_SUN / constants.M_JUP # (Msun) -> (Mjup) + iso_data[:, 1] *= 1e3 # (Gyr) -> (Myr) + iso_data[:, 4] *= constants.R_SUN / constants.R_JUP # (Rsun) -> (Rjup) + + print(f"Adding isochrones: {labels[j]}...", end="", flush=True) + + dtype = h5py.string_dtype(encoding="utf-8", length=None) + + dset = database.create_dataset( + f"isochrones/{db_tags[j]}/filters", (np.size(filter_names),), dtype=dtype + ) + + dset[...] = filter_names + + database.create_dataset( + f"isochrones/{db_tags[j]}/mass", data=iso_data[:, 0] + ) # (Mjup) + dset = database.create_dataset( + f"isochrones/{db_tags[j]}/age", data=iso_data[:, 1] + ) # (Myr) + database.create_dataset( + f"isochrones/{db_tags[j]}/teff", data=iso_data[:, 2] + ) # (K) + database.create_dataset( + f"isochrones/{db_tags[j]}/log_lum", data=iso_data[:, 3] + ) # log(L/Lsun) + database.create_dataset( + f"isochrones/{db_tags[j]}/radius", data=iso_data[:, 4] + ) # (Rjup) + database.create_dataset( + f"isochrones/{db_tags[j]}/log_g", data=iso_data[:, 5] + ) # log(g) + + database.create_dataset( + f"isochrones/{db_tags[j]}/magnitudes", data=iso_data[:, 6:] + ) + + dset.attrs["model"] = "atmo" + + print(" [DONE]") + print(f"Database tag: {db_tags[j]}") diff --git a/species/data/isochrone_data/iso_baraffe2015.py b/species/data/isochrone_data/iso_baraffe2015.py new file mode 100644 index 00000000..02ae10be --- /dev/null +++ b/species/data/isochrone_data/iso_baraffe2015.py @@ -0,0 +1,75 @@ +import os +import urllib.request + +import h5py +import numpy as np + + +def add_baraffe2015(database, input_path): + """ + Function for adding the Baraffe et al. (2015) + isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_iso = ( + "http://perso.ens-lyon.fr/isabelle.baraffe/BHAC15dir/BHAC15_tracks+structure" + ) + + iso_tag = "Baraffe et al. (2015)" + iso_size = "1.4 MB" + db_tag = "baraffe2015" + + input_file = url_iso.rsplit("/", maxsplit=1)[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) + urllib.request.urlretrieve(url_iso, data_file) + print(" [DONE]") + + # M/Ms, log t(yr), Teff, log(L/Ls), log(g), R/Rs, + # Log(Li/Li0), log(Tc), log(ROc), Mrad, Rrad, k2conv, k2rad + mass, log_age, teff, log_lum, log_g, radius, _, _, _, _, _, _, _ = np.loadtxt( + data_file, unpack=True, skiprows=45, comments="!" + ) + + age = 1e-6 * 10.0**log_age # (Myr) + mass *= constants.M_SUN / constants.M_JUP # (Msun) -> (Mjup) + radius *= constants.R_SUN / constants.R_JUP # (Msun) -> (Mjup) + + iso_data = np.column_stack([age, mass, teff, log_lum, log_g, radius]) + + print(f"Adding isochrones: {iso_tag}...", end="", flush=True) + + dset = database.create_dataset( + f"isochrones/{db_tag}/age", data=iso_data[:, 0] + ) # (Myr) + database.create_dataset(f"isochrones/{db_tag}/mass", data=iso_data[:, 1]) # (Mjup) + database.create_dataset(f"isochrones/{db_tag}/teff", data=iso_data[:, 2]) # (K) + database.create_dataset( + f"isochrones/{db_tag}/log_lum", data=iso_data[:, 3] + ) # log(L/Lsun) + database.create_dataset(f"isochrones/{db_tag}/log_g", data=iso_data[:, 4]) # log(g) + database.create_dataset( + f"isochrones/{db_tag}/radius", data=iso_data[:, 5] + ) # (Rjup) + + dset.attrs["model"] = db_tag + + print(" [DONE]") + print(f"Database tag: {db_tag}") diff --git a/species/data/isochrone_data/iso_btsettl.py b/species/data/isochrone_data/iso_btsettl.py new file mode 100644 index 00000000..37e1ceb6 --- /dev/null +++ b/species/data/isochrone_data/iso_btsettl.py @@ -0,0 +1,49 @@ +import os +import urllib.request + +import h5py +import numpy as np + + +def add_btsettl(database, input_path): + """ + Function for adding the BT-Settl isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_iso = ( + "https://home.strw.leidenuniv.nl/~stolker/species/" + "model.BT-Settl.M-0.0.MKO.Vega" + ) + + iso_tag = "BT-Settl" + iso_size = "113 kB" + + input_file = url_iso.rsplit("/", maxsplit=1)[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) + urllib.request.urlretrieve(url_iso, data_file) + print(" [DONE]") + + add_manual( + database=database, + tag=iso_tag.lower(), + file_name=data_file, + model_name="bt-settl", + ) diff --git a/species/data/isochrone_data/iso_linder2019.py b/species/data/isochrone_data/iso_linder2019.py new file mode 100644 index 00000000..6290247c --- /dev/null +++ b/species/data/isochrone_data/iso_linder2019.py @@ -0,0 +1,166 @@ +import glob +import os +import shutil +import urllib.request + +import h5py +import numpy as np + + +def add_linder2019(database, input_path): + """ + Function for adding the `Linder et al. (2019) + `_ + isochrones data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + filters = ( + "Paranal/NACO.J", + "Paranal/NACO.H", + "Paranal/NACO.Ks", + "Paranal/NACO.Lp", + "Paranal/NACO.Mp", + "Generic/Cousins.R", + "Generic/Cousins.I", + "WISE/WISE.W1", + "WISE/WISE.W2", + "WISE/WISE.W3", + "WISE/WISE.W4", + "JWST/NIRCam.F115W", + "JWST/NIRCam.F150W", + "JWST/NIRCam.F200W", + "JWST/NIRCam.F277W", + "JWST/NIRCam.F356W", + "JWST/NIRCam.F444W", + "JWST/MIRI.F560W", + "JWST/MIRI.F770W", + "JWST/MIRI.F1000W", + "JWST/MIRI.F1280W", + "JWST/MIRI.F1500W", + "JWST/MIRI.F1800W", + "JWST/MIRI.F2100W", + "JWST/MIRI.F2550W", + "Paranal/VISIR.B87", + "Paranal/VISIR.SiC", + "Paranal/SPHERE.IRDIS_B_Y", + "Paranal/SPHERE.IRDIS_B_J", + "Paranal/SPHERE.IRDIS_B_H", + "Paranal/SPHERE.IRDIS_B_Ks", + "Paranal/SPHERE.IRDIS_D_J23_2", + "Paranal/SPHERE.IRDIS_D_J23_3", + "Paranal/SPHERE.IRDIS_D_H23_2", + "Paranal/SPHERE.IRDIS_D_H23_3", + "Paranal/SPHERE.IRDIS_D_K12_1", + "Paranal/SPHERE.IRDIS_D_K12_2", + ) + + data_folder = os.path.join(input_path, "linder_2019") + + if os.path.exists(data_folder): + # The folder should be removed if the TAR file was previously + # unpacked because the file permissions are set to read-only + # such that the extract_tarfile will cause an error if the + # files need to be overwritten + shutil.rmtree(data_folder) + + os.makedirs(data_folder) + + url = "https://cdsarc.u-strasbg.fr/viz-bin/nph-Cat/tar.gz?J/A+A/623/A85" + + input_file = "J_A+A_623_A85.tar.gz" + + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print( + "Downloading Linder et al. (2019) isochrones (536 kB)...", + end="", + flush=True, + ) + urllib.request.urlretrieve(url, data_file) + print(" [DONE]") + + print("Unpacking Linder et al. (2019) isochrones (536 kB)...", end="", flush=True) + extract_tarfile(data_file, data_folder) + print(" [DONE]") + + iso_folder = os.path.join(data_folder, "isochrones") + iso_files = sorted(glob.glob(iso_folder + "/*")) + + for iso_item in iso_files: + file_name = iso_item.split("/")[-1] + file_param = file_name[:-4].split("_") + + if int(file_param[3]) == -2: + atm_model = "petitCODE" + elif int(file_param[3]) == -3: + atm_model = "HELIOS" + else: + raise ValueError("Atmospheric model not recognized.") + + metallicity = float(file_param[5]) + + if len(file_param) == 7: + # Skip _brighter and _fainter files + continue + + if len(file_param) == 8: + fsed = float(file_param[7]) + else: + fsed = None + + iso_data = np.loadtxt(iso_item) + + print( + f"Adding isochrones: Linder et al. (2019) {atm_model}...", + end="", + flush=True, + ) + + age = 1e-6 * 10.0 ** iso_data[:, 0] # (Myr) + mass = iso_data[:, 1] * constants.M_EARTH / constants.M_JUP # (Mjup) + radius = iso_data[:, 2] # (Rjup) + log_lum = np.log10(8.710e-10 * iso_data[:, 3]) # log(L/Lsun) + teff = iso_data[:, 4] # (K) + logg = iso_data[:, 5] # log(g/cgs) + magnitudes = iso_data[:, 6:] + + if fsed is None: + tag_label = f"linder2019-{atm_model}-metal_{metallicity}" + else: + tag_label = f"linder2019-{atm_model}-metal_{metallicity}-fsed_{fsed}" + + dtype = h5py.string_dtype(encoding="utf-8", length=None) + + dset = database.create_dataset( + f"isochrones/{tag_label}/filters", (np.size(filters),), dtype=dtype + ) + + dset[...] = filters + + dset = database.create_dataset(f"isochrones/{tag_label}/age", data=age) # (Myr) + database.create_dataset(f"isochrones/{tag_label}/mass", data=mass) # (Mjup) + database.create_dataset( + f"isochrones/{tag_label}/log_lum", data=log_lum + ) # log(L/Lsun) + database.create_dataset(f"isochrones/{tag_label}/teff", data=teff) # (K) + database.create_dataset(f"isochrones/{tag_label}/log_g", data=logg) # log(g) + database.create_dataset(f"isochrones/{tag_label}/radius", data=radius) # (Rjup) + database.create_dataset(f"isochrones/{tag_label}/magnitudes", data=magnitudes) + + dset.attrs["model"] = "linder2019" + + print(" [DONE]") + print(f"Database tag: {tag_label}") diff --git a/species/data/isochrone_data/iso_manual.py b/species/data/isochrone_data/iso_manual.py new file mode 100644 index 00000000..b88a5afe --- /dev/null +++ b/species/data/isochrone_data/iso_manual.py @@ -0,0 +1,125 @@ +import os + +import h5py +import numpy as np + +from species.core import constants + + +def add_manual(database, tag, file_name, model_name="manual"): + """ + Function for adding any of the isochrones from + https://phoenix.ens-lyon.fr/Grids/ or + https://perso.ens-lyon.fr/isabelle.baraffe/ to + the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + tag : str + Tag name in the database. + file_name : str + Filename with the isochrones data. + model_name : str + Model name that is stored as attribute of the + isochrone dataset in the HDF5 database. + + Returns + ------- + NoneType + None + """ + + # Read in all the data, ignoring empty lines or lines with "---" + + data = [] + + check_baraffe = False + baraffe_continue = False + + with open(file_name, encoding="utf-8") as open_file: + for line in open_file: + if "BHAC15" in line: + check_baraffe = True + continue + + if not baraffe_continue: + if "(Gyr)" in line: + baraffe_continue = True + else: + continue + + if line[0] == "!": + line = line[1:] + + elif line[:2] == " !": + line = line[2:] + + if "---" in line or line == "\n": + continue + + data.append(list(filter(None, line.rstrip().split(" ")))) + + iso_data = [] + + for line in data: + if "(Gyr)" in line: + age = line[-1] + + elif "lg(g)" in line: + # Isochrones from Phoenix website + header = ["M/Ms", "Teff(K)"] + line[1:] + + elif "M/Ms" in line: + # Isochrones from Baraffe et al. (2015) + header = line.copy() + + else: + line.insert(0, age) + iso_data.append(line) + + header = np.asarray(header, dtype=str) + iso_data = np.asarray(iso_data, dtype=float) + + iso_data[:, 0] *= 1e3 # (Myr) + iso_data[:, 1] *= constants.M_SUN / constants.M_JUP # (Mjup) + iso_data[:, 5] *= 1e9 # (cm) + iso_data[:, 5] *= 1e-2 / constants.R_JUP # (cm) -> (Rjup) + + index_sort = np.argsort(iso_data[:, 0]) + iso_data = iso_data[index_sort, :] + + print(f"Adding isochrones: {tag}...", end="", flush=True) + + if check_baraffe: + filters = header[6:] + else: + filters = header[7:] + + dtype = h5py.string_dtype(encoding="utf-8", length=None) + + dset = database.create_dataset( + f"isochrones/{tag}/filters", (np.size(filters),), dtype=dtype + ) + + dset[...] = filters + + dset = database.create_dataset( + f"isochrones/{tag}/age", data=iso_data[:, 0] + ) # (Myr) + database.create_dataset(f"isochrones/{tag}/mass", data=iso_data[:, 1]) # (Mjup) + database.create_dataset(f"isochrones/{tag}/teff", data=iso_data[:, 2]) # (K) + database.create_dataset( + f"isochrones/{tag}/log_lum", data=iso_data[:, 3] + ) # log(L/Lsun) + database.create_dataset(f"isochrones/{tag}/log_g", data=iso_data[:, 4]) # log(g) + database.create_dataset(f"isochrones/{tag}/radius", data=iso_data[:, 5]) # (Rjup) + database.create_dataset(f"isochrones/{tag}/deuterium", data=iso_data[:, 6]) + database.create_dataset(f"isochrones/{tag}/lithium", data=iso_data[:, 7]) + database.create_dataset(f"isochrones/{tag}/magnitudes", data=iso_data[:, 8:]) + + dset.attrs["model"] = model_name + + print(" [DONE]") + print(f"Database tag: {tag}") diff --git a/species/data/isochrone_data/iso_marleau.py b/species/data/isochrone_data/iso_marleau.py new file mode 100644 index 00000000..ab4a1909 --- /dev/null +++ b/species/data/isochrone_data/iso_marleau.py @@ -0,0 +1,54 @@ +import os + +import h5py +import numpy as np + + +def add_marleau(database, tag, file_name): + """ + Function for adding the Marleau et al. isochrone data + to the database. The isochrone data can be requested + from Gabriel Marleau. + + https://ui.adsabs.harvard.edu/abs/2019A%26A...624A..20M/abstract + + Parameters + ---------- + database : h5py._hl.files.File + Database. + tag : str + Tag name in the database. + file_name : str + Filename with the isochrones data. + + Returns + ------- + NoneType + None + """ + + # M age S_0 L S(t) R Teff + # (M_J) (Gyr) (k_B/baryon) (L_sol) (k_B/baryon) (R_J) (K) + mass, age, _, luminosity, _, radius, teff = np.loadtxt(file_name, unpack=True) + + age *= 1e3 # (Myr) + luminosity = np.log10(luminosity) + + mass_cgs = 1e3 * mass * constants.M_JUP # (g) + radius_cgs = 1e2 * radius * constants.R_JUP # (cm) + + logg = np.log10(1e3 * constants.GRAVITY * mass_cgs / radius_cgs**2) + + print(f"Adding isochrones: {tag}...", end="", flush=True) + + isochrones = np.vstack((age, mass, teff, luminosity, logg)) + isochrones = np.transpose(isochrones) + + index_sort = np.argsort(isochrones[:, 0]) + isochrones = isochrones[index_sort, :] + + dset = database.create_dataset(f"isochrones/{tag}/evolution", data=isochrones) + + dset.attrs["model"] = "marleau" + + print(" [DONE]") diff --git a/species/data/isochrone_data/iso_nextgen.py b/species/data/isochrone_data/iso_nextgen.py new file mode 100644 index 00000000..1cb08c34 --- /dev/null +++ b/species/data/isochrone_data/iso_nextgen.py @@ -0,0 +1,49 @@ +import os +import urllib.request + +import h5py +import numpy as np + + +def add_nextgen(database, input_path): + """ + Function for adding the NextGen isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_iso = ( + "https://home.strw.leidenuniv.nl/~stolker/species/" + "model.NextGen.M-0.0.MKO.Vega" + ) + + iso_tag = "NextGen" + iso_size = "177 kB" + + input_file = url_iso.rsplit("/", maxsplit=1)[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) + urllib.request.urlretrieve(url_iso, data_file) + print(" [DONE]") + + add_manual( + database=database, + tag=iso_tag.lower(), + file_name=data_file, + model_name="nextgen", + ) diff --git a/species/data/isochrone_data/iso_saumon2008.py b/species/data/isochrone_data/iso_saumon2008.py new file mode 100644 index 00000000..8046ef8c --- /dev/null +++ b/species/data/isochrone_data/iso_saumon2008.py @@ -0,0 +1,127 @@ +import os +import urllib.request + +import h5py +import numpy as np + + +def add_saumon2008(database, input_path): + """ + Function for adding the Saumon & Marley (2008) + isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url_iso = "https://home.strw.leidenuniv.nl/~stolker/species/BD_evolution.tgz" + + iso_tag = "Saumon & Marley (2008)" + iso_size = "800 kB" + + data_folder = os.path.join(input_path, "saumon_marley_2008") + + if not os.path.exists(data_folder): + os.makedirs(data_folder) + + input_file = url_iso.rsplit("/", maxsplit=1)[-1] + data_file = os.path.join(input_path, input_file) + + if not os.path.isfile(data_file): + print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) + urllib.request.urlretrieve(url_iso, data_file) + print(" [DONE]") + + print(f"Unpacking {iso_tag} isochrones ({iso_size})...", end="", flush=True) + extract_tarfile(data_file, data_folder) + print(" [DONE]") + + iso_files = [ + "nc_solar_age", + "nc-0.3_age", + "nc+0.3_age", + "f2_solar_age", + "hybrid_solar_age", + ] + + labels = [ + "Cloudless [M/H] = 0.0", + "Cloudless [M/H] = -0.3", + "Cloudless [M/H] = +0.3", + "Cloudy f_sed = 2", + "Hybrid (cloudless / f_sed = 2)", + ] + + db_tags = [ + "saumon2008-nc_solar", + "saumon2008-nc_-0.3", + "saumon2008-nc_+0.3", + "saumon2008-f2_solar", + "saumon2008-hybrid_solar", + ] + + for j, item in enumerate(iso_files): + iso_path = os.path.join(data_folder, item) + + iso_data = [] + + with open(iso_path, encoding="utf-8") as open_file: + for i, line in enumerate(open_file): + if i == 0 or " " not in line.strip(): + continue + + # age(Gyr) M/Msun log(L/Lsun) Teff(K) log(g) R/Rsun + param = list(filter(None, line.strip().split(" "))) + param = list(map(float, param)) + + param[0] = 1e3 * param[0] # (Gyr) -> (Myr) + param[1] = ( + param[1] * constants.M_SUN / constants.M_JUP + ) # (Msun) -> (Mjup) + param[5] = ( + param[5] * constants.R_SUN / constants.R_JUP + ) # (Rsun) -> (Rjup) + + iso_data.append( + [param[0], param[1], param[2], param[3], param[4], param[5]] + ) + + print(f"Adding isochrones: {iso_tag} {labels[j]}...", end="", flush=True) + + iso_data = np.array(iso_data) + + dset = database.create_dataset( + f"isochrones/{db_tags[j]}/age", data=iso_data[:, 0] + ) # (Myr) + database.create_dataset( + f"isochrones/{db_tags[j]}/mass", data=iso_data[:, 1] + ) # (Mjup) + database.create_dataset( + f"isochrones/{db_tags[j]}/log_lum", data=iso_data[:, 2] + ) # log(L/Lsun) + database.create_dataset( + f"isochrones/{db_tags[j]}/teff", data=iso_data[:, 3] + ) # (K) + database.create_dataset( + f"isochrones/{db_tags[j]}/log_g", data=iso_data[:, 4] + ) # log(g) + database.create_dataset( + f"isochrones/{db_tags[j]}/radius", data=iso_data[:, 5] + ) # (Rjup) + + dset.attrs["model"] = "saumon2008" + + print(" [DONE]") + print(f"Database tag: {db_tags[j]}") diff --git a/species/data/isochrone_data/iso_sonora.py b/species/data/isochrone_data/iso_sonora.py new file mode 100644 index 00000000..9fecffbe --- /dev/null +++ b/species/data/isochrone_data/iso_sonora.py @@ -0,0 +1,112 @@ +import os +import urllib.request + +import h5py +import numpy as np + + +def add_sonora(database, input_path): + """ + Function for adding the + `Sonora Bobcat `_ + isochrone data to the database. + + Parameters + ---------- + database : h5py._hl.files.File + Database. + input_path : str + Folder where the data is located. + + Returns + ------- + NoneType + None + """ + + if not os.path.exists(input_path): + os.makedirs(input_path) + + url = "https://zenodo.org/record/5063476/files/evolution_and_photometery.tar.gz" + + input_file = "evolution_and_photometery.tar.gz" + data_file = os.path.join(input_path, input_file) + sub_folder = input_file.split(".", maxsplit=1)[0] + data_folder = os.path.join(input_path, sub_folder) + + if not os.path.exists(data_folder): + os.makedirs(data_folder) + + if not os.path.isfile(data_file): + print("Downloading Sonora Bobcat evolution (929 kB)...", end="", flush=True) + urllib.request.urlretrieve(url, data_file) + print(" [DONE]") + + print("Unpacking Sonora Bobcat evolution (929 kB)...", end="", flush=True) + extract_tarfile(data_file, data_folder) + print(" [DONE]") + + iso_files = [ + "evo_tables+0.0/nc+0.0_co1.0_age", + "evo_tables+0.5/nc+0.5_co1.0_age", + "evo_tables-0.5/nc-0.5_co1.0_age", + ] + + labels = ["[M/H] = +0.0", "[M/H] = +0.5", "[M/H] = -0.5"] + + for i, item in enumerate(iso_files): + iso_file = f"evolution_tables/{item}" + iso_path = os.path.join(data_folder, iso_file) + + iso_data = [] + + with open(iso_path, encoding="utf-8") as open_file: + for j, line in enumerate(open_file): + if j == 0 or " " not in line.strip(): + continue + + # age(Gyr) M/Msun log(L/Lsun) Teff(K) log(g) R/Rsun + param = list(filter(None, line.strip().split(" "))) + param = list(map(float, param)) + + param[0] = 1e3 * param[0] # (Gyr) -> (Myr) + param[1] = ( + param[1] * constants.M_SUN / constants.M_JUP + ) # (Msun) -> (Mjup) + param[5] = ( + param[5] * constants.R_SUN / constants.R_JUP + ) # (Rsun) -> (Rjup) + + iso_data.append( + [param[0], param[1], param[2], param[3], param[4], param[5]] + ) + + print(f"Adding isochrones: Sonora {labels[i]}...", end="", flush=True) + + iso_data = np.array(iso_data) + + metallicity = labels[i].split(" ")[2] + + dset = database.create_dataset( + f"isochrones/sonora{metallicity}/age", data=iso_data[:, 0] + ) # (Myr) + database.create_dataset( + f"isochrones/sonora{metallicity}/mass", data=iso_data[:, 1] + ) # (Mjup) + database.create_dataset( + f"isochrones/sonora{metallicity}/log_lum", data=iso_data[:, 2] + ) # log(L/Lsun) + database.create_dataset( + f"isochrones/sonora{metallicity}/teff", data=iso_data[:, 3] + ) # (K) + database.create_dataset( + f"isochrones/sonora{metallicity}/log_g", data=iso_data[:, 4] + ) # log(g) + database.create_dataset( + f"isochrones/sonora{metallicity}/radius", data=iso_data[:, 5] + ) # (Rjup) + + dset.attrs["model"] = "sonora" + + print(" [DONE]") + print(f"Database tag: sonora{metallicity}") diff --git a/species/data/isochrones.py b/species/data/isochrones.py deleted file mode 100644 index bd1c39c4..00000000 --- a/species/data/isochrones.py +++ /dev/null @@ -1,910 +0,0 @@ -""" -Module for isochrone data from evolutionary models. -""" - -import glob -import os -import shutil -import urllib.request - -import h5py -import numpy as np - -from species.core import constants -from species.util.data_util import extract_tarfile - - -def add_manual(database, tag, file_name, model_name="manual"): - """ - Function for adding any of the isochrones from - https://phoenix.ens-lyon.fr/Grids/ or - https://perso.ens-lyon.fr/isabelle.baraffe/ to - the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - tag : str - Tag name in the database. - file_name : str - Filename with the isochrones data. - model_name : str - Model name that is stored as attribute of the - isochrone dataset in the HDF5 database. - - Returns - ------- - NoneType - None - """ - - # Read in all the data, ignoring empty lines or lines with "---" - - data = [] - - check_baraffe = False - baraffe_continue = False - - with open(file_name, encoding="utf-8") as open_file: - for line in open_file: - if "BHAC15" in line: - check_baraffe = True - continue - - if not baraffe_continue: - if "(Gyr)" in line: - baraffe_continue = True - else: - continue - - if line[0] == "!": - line = line[1:] - - elif line[:2] == " !": - line = line[2:] - - if "---" in line or line == "\n": - continue - - data.append(list(filter(None, line.rstrip().split(" ")))) - - iso_data = [] - - for line in data: - if "(Gyr)" in line: - age = line[-1] - - elif "lg(g)" in line: - # Isochrones from Phoenix website - header = ["M/Ms", "Teff(K)"] + line[1:] - - elif "M/Ms" in line: - # Isochrones from Baraffe et al. (2015) - header = line.copy() - - else: - line.insert(0, age) - iso_data.append(line) - - header = np.asarray(header, dtype=str) - iso_data = np.asarray(iso_data, dtype=float) - - iso_data[:, 0] *= 1e3 # (Myr) - iso_data[:, 1] *= constants.M_SUN / constants.M_JUP # (Mjup) - iso_data[:, 5] *= 1e9 # (cm) - iso_data[:, 5] *= 1e-2 / constants.R_JUP # (cm) -> (Rjup) - - index_sort = np.argsort(iso_data[:, 0]) - iso_data = iso_data[index_sort, :] - - print(f"Adding isochrones: {tag}...", end="", flush=True) - - if check_baraffe: - filters = header[6:] - else: - filters = header[7:] - - dtype = h5py.string_dtype(encoding="utf-8", length=None) - - dset = database.create_dataset( - f"isochrones/{tag}/filters", (np.size(filters),), dtype=dtype - ) - - dset[...] = filters - - dset = database.create_dataset( - f"isochrones/{tag}/age", data=iso_data[:, 0] - ) # (Myr) - database.create_dataset(f"isochrones/{tag}/mass", data=iso_data[:, 1]) # (Mjup) - database.create_dataset(f"isochrones/{tag}/teff", data=iso_data[:, 2]) # (K) - database.create_dataset( - f"isochrones/{tag}/log_lum", data=iso_data[:, 3] - ) # log(L/Lsun) - database.create_dataset(f"isochrones/{tag}/log_g", data=iso_data[:, 4]) # log(g) - database.create_dataset(f"isochrones/{tag}/radius", data=iso_data[:, 5]) # (Rjup) - database.create_dataset(f"isochrones/{tag}/deuterium", data=iso_data[:, 6]) - database.create_dataset(f"isochrones/{tag}/lithium", data=iso_data[:, 7]) - database.create_dataset(f"isochrones/{tag}/magnitudes", data=iso_data[:, 8:]) - - dset.attrs["model"] = model_name - - print(" [DONE]") - print(f"Database tag: {tag}") - - -def add_ames(database, input_path): - """ - Function for adding the AMES-Cond and AMES-Dusty - isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_list = [ - "https://home.strw.leidenuniv.nl/~stolker/species/" - "model.AMES-Cond-2000.M-0.0.MKO.Vega", - "https://home.strw.leidenuniv.nl/~stolker/species/" - "model.AMES-dusty.M-0.0.MKO.Vega", - ] - - iso_tags = ["AMES-Cond", "AMES-Dusty"] - iso_size = ["235 kB", "182 kB"] - - for i, url_item in enumerate(url_list): - input_file = url_item.split("/")[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print( - f"Downloading {iso_tags[i]} isochrones ({iso_size[i]})...", - end="", - flush=True, - ) - urllib.request.urlretrieve(url_item, data_file) - print(" [DONE]") - - add_manual( - database=database, - tag=iso_tags[i].lower(), - file_name=data_file, - model_name="ames", - ) - - -def add_atmo(database, input_path): - """ - Function for adding the AMES-Cond and AMES-Dusty - isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_iso = ( - "https://home.strw.leidenuniv.nl/~stolker/" - "species/atmo_evolutionary_tracks.tgz" - ) - - # iso_tags = ["ATMO-CEQ", "ATMO-NEQ-weak", , "ATMO-NEQ-strong"] - # iso_size = ["235 kB", "182 kB"] - - iso_tag = "ATMO" - iso_size = "9.6 MB" - - data_folder = os.path.join(input_path, "atmo_evolutionary_tracks") - - if not os.path.exists(data_folder): - os.makedirs(data_folder) - - input_file = url_iso.rsplit("/", maxsplit=1)[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) - urllib.request.urlretrieve(url_iso, data_file) - print(" [DONE]") - - print(f"Unpacking {iso_tag} isochrones ({iso_size})...", end="", flush=True) - extract_tarfile(data_file, data_folder) - print(" [DONE]") - - iso_files = [ - "ATMO_CEQ", - "ATMO_NEQ_weak", - "ATMO_NEQ_strong", - ] - - labels = [ - "ATMO equilibrium chemistry", - "ATMO non-equilibrium chemistry (weak)", - "ATMO non-equilibrium chemistry (strong)", - ] - - db_tags = [ - "atmo-ceq", - "atmo-neq-weak", - "atmo-neq-strong", - ] - - for j, iso_item in enumerate(iso_files): - iso_path = os.path.join(data_folder, iso_item) - iso_path = os.path.join(iso_path, "MKO_WISE_IRAC") - - file_list = sorted(glob.glob(iso_path + "/*.txt")) - - for i, file_item in enumerate(file_list): - # Mass (Msun) - Age (Gyr) - Teff (K) - log(L/Lsun) - Radius (Rsun) - log(g) - if i == 0: - iso_data = np.loadtxt(file_item) - - else: - iso_load = np.loadtxt(file_item) - iso_data = np.vstack((iso_data, iso_load)) - - with open(file_item, encoding="utf-8") as open_file: - parameters = open_file.readline() - filter_names = parameters.split()[7:] - - iso_data[:, 0] *= constants.M_SUN / constants.M_JUP # (Msun) -> (Mjup) - iso_data[:, 1] *= 1e3 # (Gyr) -> (Myr) - iso_data[:, 4] *= constants.R_SUN / constants.R_JUP # (Rsun) -> (Rjup) - - print(f"Adding isochrones: {labels[j]}...", end="", flush=True) - - dtype = h5py.string_dtype(encoding="utf-8", length=None) - - dset = database.create_dataset( - f"isochrones/{db_tags[j]}/filters", (np.size(filter_names),), dtype=dtype - ) - - dset[...] = filter_names - - database.create_dataset( - f"isochrones/{db_tags[j]}/mass", data=iso_data[:, 0] - ) # (Mjup) - dset = database.create_dataset( - f"isochrones/{db_tags[j]}/age", data=iso_data[:, 1] - ) # (Myr) - database.create_dataset( - f"isochrones/{db_tags[j]}/teff", data=iso_data[:, 2] - ) # (K) - database.create_dataset( - f"isochrones/{db_tags[j]}/log_lum", data=iso_data[:, 3] - ) # log(L/Lsun) - database.create_dataset( - f"isochrones/{db_tags[j]}/radius", data=iso_data[:, 4] - ) # (Rjup) - database.create_dataset( - f"isochrones/{db_tags[j]}/log_g", data=iso_data[:, 5] - ) # log(g) - - database.create_dataset( - f"isochrones/{db_tags[j]}/magnitudes", data=iso_data[:, 6:] - ) - - dset.attrs["model"] = "atmo" - - print(" [DONE]") - print(f"Database tag: {db_tags[j]}") - - -def add_baraffe2015(database, input_path): - """ - Function for adding the Baraffe et al. (2015) - isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_iso = ( - "http://perso.ens-lyon.fr/isabelle.baraffe/BHAC15dir/BHAC15_tracks+structure" - ) - - iso_tag = "Baraffe et al. (2015)" - iso_size = "1.4 MB" - db_tag = "baraffe2015" - - input_file = url_iso.rsplit("/", maxsplit=1)[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) - urllib.request.urlretrieve(url_iso, data_file) - print(" [DONE]") - - # M/Ms, log t(yr), Teff, log(L/Ls), log(g), R/Rs, - # Log(Li/Li0), log(Tc), log(ROc), Mrad, Rrad, k2conv, k2rad - mass, log_age, teff, log_lum, log_g, radius, _, _, _, _, _, _, _ = np.loadtxt( - data_file, unpack=True, skiprows=45, comments="!" - ) - - age = 1e-6 * 10.0**log_age # (Myr) - mass *= constants.M_SUN / constants.M_JUP # (Msun) -> (Mjup) - radius *= constants.R_SUN / constants.R_JUP # (Msun) -> (Mjup) - - iso_data = np.column_stack([age, mass, teff, log_lum, log_g, radius]) - - print(f"Adding isochrones: {iso_tag}...", end="", flush=True) - - dset = database.create_dataset( - f"isochrones/{db_tag}/age", data=iso_data[:, 0] - ) # (Myr) - database.create_dataset(f"isochrones/{db_tag}/mass", data=iso_data[:, 1]) # (Mjup) - database.create_dataset(f"isochrones/{db_tag}/teff", data=iso_data[:, 2]) # (K) - database.create_dataset( - f"isochrones/{db_tag}/log_lum", data=iso_data[:, 3] - ) # log(L/Lsun) - database.create_dataset(f"isochrones/{db_tag}/log_g", data=iso_data[:, 4]) # log(g) - database.create_dataset( - f"isochrones/{db_tag}/radius", data=iso_data[:, 5] - ) # (Rjup) - - dset.attrs["model"] = db_tag - - print(" [DONE]") - print(f"Database tag: {db_tag}") - - -def add_btsettl(database, input_path): - """ - Function for adding the BT-Settl isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_iso = ( - "https://home.strw.leidenuniv.nl/~stolker/species/" - "model.BT-Settl.M-0.0.MKO.Vega" - ) - - iso_tag = "BT-Settl" - iso_size = "113 kB" - - input_file = url_iso.rsplit("/", maxsplit=1)[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) - urllib.request.urlretrieve(url_iso, data_file) - print(" [DONE]") - - add_manual( - database=database, - tag=iso_tag.lower(), - file_name=data_file, - model_name="bt-settl", - ) - - -def add_marleau(database, tag, file_name): - """ - Function for adding the Marleau et al. isochrone data - to the database. The isochrone data can be requested - from Gabriel Marleau. - - https://ui.adsabs.harvard.edu/abs/2019A%26A...624A..20M/abstract - - Parameters - ---------- - database : h5py._hl.files.File - Database. - tag : str - Tag name in the database. - file_name : str - Filename with the isochrones data. - - Returns - ------- - NoneType - None - """ - - # M age S_0 L S(t) R Teff - # (M_J) (Gyr) (k_B/baryon) (L_sol) (k_B/baryon) (R_J) (K) - mass, age, _, luminosity, _, radius, teff = np.loadtxt(file_name, unpack=True) - - age *= 1e3 # (Myr) - luminosity = np.log10(luminosity) - - mass_cgs = 1e3 * mass * constants.M_JUP # (g) - radius_cgs = 1e2 * radius * constants.R_JUP # (cm) - - logg = np.log10(1e3 * constants.GRAVITY * mass_cgs / radius_cgs**2) - - print(f"Adding isochrones: {tag}...", end="", flush=True) - - isochrones = np.vstack((age, mass, teff, luminosity, logg)) - isochrones = np.transpose(isochrones) - - index_sort = np.argsort(isochrones[:, 0]) - isochrones = isochrones[index_sort, :] - - dset = database.create_dataset(f"isochrones/{tag}/evolution", data=isochrones) - - dset.attrs["model"] = "marleau" - - print(" [DONE]") - - -def add_nextgen(database, input_path): - """ - Function for adding the NextGen isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_iso = ( - "https://home.strw.leidenuniv.nl/~stolker/species/" - "model.NextGen.M-0.0.MKO.Vega" - ) - - iso_tag = "NextGen" - iso_size = "177 kB" - - input_file = url_iso.rsplit("/", maxsplit=1)[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) - urllib.request.urlretrieve(url_iso, data_file) - print(" [DONE]") - - add_manual( - database=database, - tag=iso_tag.lower(), - file_name=data_file, - model_name="nextgen", - ) - - -def add_saumon(database, input_path): - """ - Function for adding the Saumon & Marley (2008) - isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url_iso = "https://home.strw.leidenuniv.nl/~stolker/species/BD_evolution.tgz" - - iso_tag = "Saumon & Marley (2008)" - iso_size = "800 kB" - - data_folder = os.path.join(input_path, "saumon_marley_2008") - - if not os.path.exists(data_folder): - os.makedirs(data_folder) - - input_file = url_iso.rsplit("/", maxsplit=1)[-1] - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print(f"Downloading {iso_tag} isochrones ({iso_size})...", end="", flush=True) - urllib.request.urlretrieve(url_iso, data_file) - print(" [DONE]") - - print(f"Unpacking {iso_tag} isochrones ({iso_size})...", end="", flush=True) - extract_tarfile(data_file, data_folder) - print(" [DONE]") - - iso_files = [ - "nc_solar_age", - "nc-0.3_age", - "nc+0.3_age", - "f2_solar_age", - "hybrid_solar_age", - ] - - labels = [ - "Cloudless [M/H] = 0.0", - "Cloudless [M/H] = -0.3", - "Cloudless [M/H] = +0.3", - "Cloudy f_sed = 2", - "Hybrid (cloudless / f_sed = 2)", - ] - - db_tags = [ - "saumon2008-nc_solar", - "saumon2008-nc_-0.3", - "saumon2008-nc_+0.3", - "saumon2008-f2_solar", - "saumon2008-hybrid_solar", - ] - - for j, item in enumerate(iso_files): - iso_path = os.path.join(data_folder, item) - - iso_data = [] - - with open(iso_path, encoding="utf-8") as open_file: - for i, line in enumerate(open_file): - if i == 0 or " " not in line.strip(): - continue - - # age(Gyr) M/Msun log(L/Lsun) Teff(K) log(g) R/Rsun - param = list(filter(None, line.strip().split(" "))) - param = list(map(float, param)) - - param[0] = 1e3 * param[0] # (Gyr) -> (Myr) - param[1] = ( - param[1] * constants.M_SUN / constants.M_JUP - ) # (Msun) -> (Mjup) - param[5] = ( - param[5] * constants.R_SUN / constants.R_JUP - ) # (Rsun) -> (Rjup) - - iso_data.append( - [param[0], param[1], param[2], param[3], param[4], param[5]] - ) - - print(f"Adding isochrones: {iso_tag} {labels[j]}...", end="", flush=True) - - iso_data = np.array(iso_data) - - dset = database.create_dataset( - f"isochrones/{db_tags[j]}/age", data=iso_data[:, 0] - ) # (Myr) - database.create_dataset( - f"isochrones/{db_tags[j]}/mass", data=iso_data[:, 1] - ) # (Mjup) - database.create_dataset( - f"isochrones/{db_tags[j]}/log_lum", data=iso_data[:, 2] - ) # log(L/Lsun) - database.create_dataset( - f"isochrones/{db_tags[j]}/teff", data=iso_data[:, 3] - ) # (K) - database.create_dataset( - f"isochrones/{db_tags[j]}/log_g", data=iso_data[:, 4] - ) # log(g) - database.create_dataset( - f"isochrones/{db_tags[j]}/radius", data=iso_data[:, 5] - ) # (Rjup) - - dset.attrs["model"] = "saumon2008" - - print(" [DONE]") - print(f"Database tag: {db_tags[j]}") - - -def add_sonora(database, input_path): - """ - Function for adding the - `Sonora Bobcat `_ - isochrone data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - if not os.path.exists(input_path): - os.makedirs(input_path) - - url = "https://zenodo.org/record/5063476/files/evolution_and_photometery.tar.gz" - - input_file = "evolution_and_photometery.tar.gz" - data_file = os.path.join(input_path, input_file) - sub_folder = input_file.split(".", maxsplit=1)[0] - data_folder = os.path.join(input_path, sub_folder) - - if not os.path.exists(data_folder): - os.makedirs(data_folder) - - if not os.path.isfile(data_file): - print("Downloading Sonora Bobcat evolution (929 kB)...", end="", flush=True) - urllib.request.urlretrieve(url, data_file) - print(" [DONE]") - - print("Unpacking Sonora Bobcat evolution (929 kB)...", end="", flush=True) - extract_tarfile(data_file, data_folder) - print(" [DONE]") - - iso_files = [ - "evo_tables+0.0/nc+0.0_co1.0_age", - "evo_tables+0.5/nc+0.5_co1.0_age", - "evo_tables-0.5/nc-0.5_co1.0_age", - ] - - labels = ["[M/H] = +0.0", "[M/H] = +0.5", "[M/H] = -0.5"] - - for i, item in enumerate(iso_files): - iso_file = f"evolution_tables/{item}" - iso_path = os.path.join(data_folder, iso_file) - - iso_data = [] - - with open(iso_path, encoding="utf-8") as open_file: - for j, line in enumerate(open_file): - if j == 0 or " " not in line.strip(): - continue - - # age(Gyr) M/Msun log(L/Lsun) Teff(K) log(g) R/Rsun - param = list(filter(None, line.strip().split(" "))) - param = list(map(float, param)) - - param[0] = 1e3 * param[0] # (Gyr) -> (Myr) - param[1] = ( - param[1] * constants.M_SUN / constants.M_JUP - ) # (Msun) -> (Mjup) - param[5] = ( - param[5] * constants.R_SUN / constants.R_JUP - ) # (Rsun) -> (Rjup) - - iso_data.append( - [param[0], param[1], param[2], param[3], param[4], param[5]] - ) - - print(f"Adding isochrones: Sonora {labels[i]}...", end="", flush=True) - - iso_data = np.array(iso_data) - - metallicity = labels[i].split(" ")[2] - - dset = database.create_dataset( - f"isochrones/sonora{metallicity}/age", data=iso_data[:, 0] - ) # (Myr) - database.create_dataset( - f"isochrones/sonora{metallicity}/mass", data=iso_data[:, 1] - ) # (Mjup) - database.create_dataset( - f"isochrones/sonora{metallicity}/log_lum", data=iso_data[:, 2] - ) # log(L/Lsun) - database.create_dataset( - f"isochrones/sonora{metallicity}/teff", data=iso_data[:, 3] - ) # (K) - database.create_dataset( - f"isochrones/sonora{metallicity}/log_g", data=iso_data[:, 4] - ) # log(g) - database.create_dataset( - f"isochrones/sonora{metallicity}/radius", data=iso_data[:, 5] - ) # (Rjup) - - dset.attrs["model"] = "sonora" - - print(" [DONE]") - print(f"Database tag: sonora{metallicity}") - - -def add_linder2019(database, input_path): - """ - Function for adding the `Linder et al. (2019) - `_ - isochrones data to the database. - - Parameters - ---------- - database : h5py._hl.files.File - Database. - input_path : str - Folder where the data is located. - - Returns - ------- - NoneType - None - """ - - filters = ( - "Paranal/NACO.J", - "Paranal/NACO.H", - "Paranal/NACO.Ks", - "Paranal/NACO.Lp", - "Paranal/NACO.Mp", - "Generic/Cousins.R", - "Generic/Cousins.I", - "WISE/WISE.W1", - "WISE/WISE.W2", - "WISE/WISE.W3", - "WISE/WISE.W4", - "JWST/NIRCam.F115W", - "JWST/NIRCam.F150W", - "JWST/NIRCam.F200W", - "JWST/NIRCam.F277W", - "JWST/NIRCam.F356W", - "JWST/NIRCam.F444W", - "JWST/MIRI.F560W", - "JWST/MIRI.F770W", - "JWST/MIRI.F1000W", - "JWST/MIRI.F1280W", - "JWST/MIRI.F1500W", - "JWST/MIRI.F1800W", - "JWST/MIRI.F2100W", - "JWST/MIRI.F2550W", - "Paranal/VISIR.B87", - "Paranal/VISIR.SiC", - "Paranal/SPHERE.IRDIS_B_Y", - "Paranal/SPHERE.IRDIS_B_J", - "Paranal/SPHERE.IRDIS_B_H", - "Paranal/SPHERE.IRDIS_B_Ks", - "Paranal/SPHERE.IRDIS_D_J23_2", - "Paranal/SPHERE.IRDIS_D_J23_3", - "Paranal/SPHERE.IRDIS_D_H23_2", - "Paranal/SPHERE.IRDIS_D_H23_3", - "Paranal/SPHERE.IRDIS_D_K12_1", - "Paranal/SPHERE.IRDIS_D_K12_2", - ) - - data_folder = os.path.join(input_path, "linder_2019") - - if os.path.exists(data_folder): - # The folder should be removed if the TAR file was previously - # unpacked because the file permissions are set to read-only - # such that the extract_tarfile will cause an error if the - # files need to be overwritten - shutil.rmtree(data_folder) - - os.makedirs(data_folder) - - url = "https://cdsarc.u-strasbg.fr/viz-bin/nph-Cat/tar.gz?J/A+A/623/A85" - - input_file = "J_A+A_623_A85.tar.gz" - - data_file = os.path.join(input_path, input_file) - - if not os.path.isfile(data_file): - print( - "Downloading Linder et al. (2019) isochrones (536 kB)...", - end="", - flush=True, - ) - urllib.request.urlretrieve(url, data_file) - print(" [DONE]") - - print("Unpacking Linder et al. (2019) isochrones (536 kB)...", end="", flush=True) - extract_tarfile(data_file, data_folder) - print(" [DONE]") - - iso_folder = os.path.join(data_folder, "isochrones") - iso_files = sorted(glob.glob(iso_folder + "/*")) - - for iso_item in iso_files: - file_name = iso_item.split("/")[-1] - file_param = file_name[:-4].split("_") - - if int(file_param[3]) == -2: - atm_model = "petitCODE" - elif int(file_param[3]) == -3: - atm_model = "HELIOS" - else: - raise ValueError("Atmospheric model not recognized.") - - metallicity = float(file_param[5]) - - if len(file_param) == 7: - # Skip _brighter and _fainter files - continue - - if len(file_param) == 8: - fsed = float(file_param[7]) - else: - fsed = None - - iso_data = np.loadtxt(iso_item) - - print( - f"Adding isochrones: Linder et al. (2019) {atm_model}...", - end="", - flush=True, - ) - - age = 1e-6 * 10.0 ** iso_data[:, 0] # (Myr) - mass = iso_data[:, 1] * constants.M_EARTH / constants.M_JUP # (Mjup) - radius = iso_data[:, 2] # (Rjup) - log_lum = np.log10(8.710e-10 * iso_data[:, 3]) # log(L/Lsun) - teff = iso_data[:, 4] # (K) - logg = iso_data[:, 5] # log(g/cgs) - magnitudes = iso_data[:, 6:] - - if fsed is None: - tag_label = f"linder2019-{atm_model}-metal_{metallicity}" - else: - tag_label = f"linder2019-{atm_model}-metal_{metallicity}-fsed_{fsed}" - - dtype = h5py.string_dtype(encoding="utf-8", length=None) - - dset = database.create_dataset( - f"isochrones/{tag_label}/filters", (np.size(filters),), dtype=dtype - ) - - dset[...] = filters - - dset = database.create_dataset(f"isochrones/{tag_label}/age", data=age) # (Myr) - database.create_dataset(f"isochrones/{tag_label}/mass", data=mass) # (Mjup) - database.create_dataset( - f"isochrones/{tag_label}/log_lum", data=log_lum - ) # log(L/Lsun) - database.create_dataset(f"isochrones/{tag_label}/teff", data=teff) # (K) - database.create_dataset(f"isochrones/{tag_label}/log_g", data=logg) # log(g) - database.create_dataset(f"isochrones/{tag_label}/radius", data=radius) # (Rjup) - database.create_dataset(f"isochrones/{tag_label}/magnitudes", data=magnitudes) - - dset.attrs["model"] = "linder2019" - - print(" [DONE]") - print(f"Database tag: {tag_label}") diff --git a/species/data/misc_data/__init__.py b/species/data/misc_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/accretion.py b/species/data/misc_data/accretion_data.py similarity index 100% rename from species/data/accretion.py rename to species/data/misc_data/accretion_data.py diff --git a/species/data/dust.py b/species/data/misc_data/dust_data.py similarity index 100% rename from species/data/dust.py rename to species/data/misc_data/dust_data.py diff --git a/species/data/model_data/__init__.py b/species/data/model_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/custom_model.py b/species/data/model_data/custom_model.py similarity index 100% rename from species/data/custom_model.py rename to species/data/model_data/custom_model.py diff --git a/species/data/model_data.json b/species/data/model_data/model_data.json similarity index 100% rename from species/data/model_data.json rename to species/data/model_data/model_data.json diff --git a/species/data/model_spectra.py b/species/data/model_data/model_spectra.py similarity index 100% rename from species/data/model_spectra.py rename to species/data/model_data/model_spectra.py diff --git a/species/data/phot_data/__init__.py b/species/data/phot_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/leggett.py b/species/data/phot_data/phot_leggett.py similarity index 100% rename from species/data/leggett.py rename to species/data/phot_data/phot_leggett.py diff --git a/species/data/vlm_plx.py b/species/data/phot_data/phot_vlm_plx.py similarity index 100% rename from species/data/vlm_plx.py rename to species/data/phot_data/phot_vlm_plx.py diff --git a/species/data/spec_data/__init__.py b/species/data/spec_data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/species/data/spec_data/add_spec_data.py b/species/data/spec_data/add_spec_data.py new file mode 100644 index 00000000..b4008911 --- /dev/null +++ b/species/data/spec_data/add_spec_data.py @@ -0,0 +1,70 @@ +""" +Module for empirical spectral libraries. +""" + +from typing import List, Optional + +import h5py + +from typeguard import typechecked + +from species.data.spec_data.spec_allers2013 import add_allers2013 +from species.data.spec_data.spec_bonnefoy2014 import add_bonnefoy2014 +from species.data.spec_data.spec_irtf import add_irtf +from species.data.spec_data.spec_kesseli2017 import add_kesseli2017 +from species.data.spec_data.spec_spex import add_spex +from species.data.spec_data.spec_vega import add_vega + + +@typechecked +def add_spec_library( + input_path: str, + database: h5py._hl.files.File, + spec_library: str, + sptypes: Optional[List[str]] = None, +) -> None: + """ + Function for adding spectral libraries to the database. + + Parameters + ---------- + input_path : str + Folder where the data is located. + database : h5py._hl.files.File + Database. + spec_library : str + Name of the spectral library ('irtf', 'spex', 'kesseli+2017', + 'bonnefoy+2014', 'allers+2013'). + sptypes : list(str), None + Spectral types ('F', 'G', 'K', 'M', 'L', 'T'). Currently + only implemented for ``spec_library='irtf'``. + + Returns + ------- + None + NoneType + """ + + if spec_library[0:11] == "allers+2013": + add_allers2013(input_path, database) + + elif spec_library[0:13] == "bonnefoy+2014": + add_bonnefoy2014(input_path, database) + + elif spec_library[0:5] == "irtf": + add_irtf(input_path, database, sptypes) + + elif spec_library[0:12] == "kesseli+2017": + add_kesseli2017(input_path, database) + + elif spec_library[0:5] == "spex": + add_spex(input_path, database) + + elif spec_library[0:5] == "vega": + add_vega(input_path, database) + + else: + raise ValueError( + f"The spectral library '{spec_library}' is not supported. " + "Please adjust the argument of 'spec_library'." + ) diff --git a/species/data/allers2013.py b/species/data/spec_data/spec_allers2013.py similarity index 99% rename from species/data/allers2013.py rename to species/data/spec_data/spec_allers2013.py index 77dbd017..2caeb0b0 100644 --- a/species/data/allers2013.py +++ b/species/data/spec_data/spec_allers2013.py @@ -167,5 +167,3 @@ def add_allers2013(input_path: str, database: h5py._hl.files.File) -> None: print_message = "Adding spectra... [DONE]" print(f"\r{print_message}") - - database.close() diff --git a/species/data/bonnefoy2014.py b/species/data/spec_data/spec_bonnefoy2014.py similarity index 99% rename from species/data/bonnefoy2014.py rename to species/data/spec_data/spec_bonnefoy2014.py index a693c7d5..dd98bfb9 100644 --- a/species/data/bonnefoy2014.py +++ b/species/data/spec_data/spec_bonnefoy2014.py @@ -140,5 +140,3 @@ def add_bonnefoy2014(input_path: str, database: h5py._hl.files.File) -> None: print_message = "Adding spectra... [DONE]" print(f"\r{print_message}") - - database.close() diff --git a/species/data/irtf.py b/species/data/spec_data/spec_irtf.py similarity index 99% rename from species/data/irtf.py rename to species/data/spec_data/spec_irtf.py index 351b4ad1..bed41ff7 100644 --- a/species/data/irtf.py +++ b/species/data/spec_data/spec_irtf.py @@ -189,5 +189,3 @@ def add_irtf( print_message = "Adding spectra... [DONE]" print(f"\r{print_message}") - - database.close() diff --git a/species/data/kesseli2017.py b/species/data/spec_data/spec_kesseli2017.py similarity index 99% rename from species/data/kesseli2017.py rename to species/data/spec_data/spec_kesseli2017.py index fad2f8d2..341a0181 100644 --- a/species/data/kesseli2017.py +++ b/species/data/spec_data/spec_kesseli2017.py @@ -101,5 +101,3 @@ def add_kesseli2017(input_path: str, database: h5py._hl.files.File) -> None: print_message = "Adding spectra... [DONE]" print(f"\r{print_message}") - - database.close() diff --git a/species/data/spex.py b/species/data/spec_data/spec_spex.py similarity index 99% rename from species/data/spex.py rename to species/data/spec_data/spec_spex.py index fe7bf14a..11e2d26a 100644 --- a/species/data/spex.py +++ b/species/data/spec_data/spec_spex.py @@ -264,5 +264,3 @@ def add_spex(input_path: str, database: h5py._hl.files.File) -> None: print_message = "Adding spectra... [DONE]" print(f"\r{print_message}") - - database.close() diff --git a/species/data/vega.py b/species/data/spec_data/spec_vega.py similarity index 100% rename from species/data/vega.py rename to species/data/spec_data/spec_vega.py diff --git a/species/fit/fit_model.py b/species/fit/fit_model.py index 6ed3dcff..30e9049e 100644 --- a/species/fit/fit_model.py +++ b/species/fit/fit_model.py @@ -7,6 +7,7 @@ from typing import Optional, Union, List, Tuple, Dict +import h5py import numpy as np import spectres @@ -610,11 +611,7 @@ def __init__( if isinstance(inc_phot, bool): if inc_phot: # Select all filters if inc_phot=True - from species.data.database import Database - - species_db = Database() - object_box = species_db.get_object(object_name) - inc_phot = object_box.filters + inc_phot = self.object.list_filters() else: inc_phot = [] @@ -622,11 +619,7 @@ def __init__( if isinstance(inc_spec, bool): if inc_spec: # Select all spectra if inc_spec=True - from species.data.database import Database - - species_db = Database() - object_box = species_db.get_object(object_name) - inc_spec = list(object_box.spectrum.keys()) + inc_spec = list(self.object.get_spectrum().keys()) else: inc_spec = [] diff --git a/species/phot/syn_phot.py b/species/phot/syn_phot.py index db34a820..76f7f894 100644 --- a/species/phot/syn_phot.py +++ b/species/phot/syn_phot.py @@ -14,7 +14,7 @@ from typeguard import typechecked -from species.data.vega import add_vega +from species.data.spec_data.spec_vega import add_vega from species.read.read_filter import ReadFilter from species.util.convert_util import apparent_to_absolute, parallax_to_distance diff --git a/species/plot/plot_color.py b/species/plot/plot_color.py index 45d73e3d..51319937 100644 --- a/species/plot/plot_color.py +++ b/species/plot/plot_color.py @@ -226,8 +226,8 @@ def plot_color_magnitude( else: raise ValueError( - f"Found a {type(item)} while only ColorMagBox and IsochroneBox " - f"objects can be provided to 'boxes'." + f"Found a {type(item)} while only ColorMagBox and " + "IsochroneBox objects can be provided to 'boxes'." ) if empirical: @@ -1059,7 +1059,7 @@ def plot_color_color( else: raise ValueError( f"Found a {type(item)} while only ColorColorBox and " - f"IsochroneBox objects can be provided to 'boxes'." + "IsochroneBox objects can be provided to 'boxes'." ) fig = plt.figure(figsize=figsize) diff --git a/species/plot/plot_mcmc.py b/species/plot/plot_mcmc.py index d06c3aaf..c9732c2e 100644 --- a/species/plot/plot_mcmc.py +++ b/species/plot/plot_mcmc.py @@ -1035,8 +1035,8 @@ def plot_size_distributions( if "lognorm_radius" not in box.parameters and "powerlaw_max" not in box.parameters: raise ValueError( - "The SamplesBox does not contain extinction parameter for a log-normal " - "or power-law size distribution." + "The SamplesBox does not contain extinction parameter " + "for a log-normal or power-law size distribution." ) samples = box.samples diff --git a/species/read/__init__.py b/species/read/__init__.py index e69de29b..429a01d4 100644 --- a/species/read/__init__.py +++ b/species/read/__init__.py @@ -0,0 +1,11 @@ +__all__ = [ + "read_calibration", + "read_color", + "read_filter", + "read_isochrone", + "read_model", + "read_object", + "read_planck", + "read_radtrans", + "read_spectrum", +] diff --git a/species/read/read_calibration.py b/species/read/read_calibration.py index 62e5a09d..0a2152a3 100644 --- a/species/read/read_calibration.py +++ b/species/read/read_calibration.py @@ -100,7 +100,7 @@ def resample_spectrum( Returns ------- - SpectrumBox + species.core.box.SpectrumBox Box with the resampled spectrum. """ @@ -212,7 +212,7 @@ def get_spectrum( Returns ------- - SpectrumBox + species.core.box.SpectrumBox Box with the spectrum. """ diff --git a/species/read/read_color.py b/species/read/read_color.py index f4ca3077..908e9825 100644 --- a/species/read/read_color.py +++ b/species/read/read_color.py @@ -1,11 +1,11 @@ """ -Module with reading functionalities of color and magnitude data from -photometric and libraries. +Module with reading functionalities of color and magnitude +data from photometric and libraries. """ import os -import configparser +from configparser import ConfigParser from typing import Optional, Tuple import h5py @@ -55,7 +55,7 @@ def __init__( config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] @@ -286,7 +286,7 @@ def __init__( config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] diff --git a/species/read/read_filter.py b/species/read/read_filter.py index ca24a591..218f1cf3 100644 --- a/species/read/read_filter.py +++ b/species/read/read_filter.py @@ -3,9 +3,10 @@ """ import os +import sys import warnings -import configparser +from configparser import ConfigParser from typing import Union, Tuple import h5py @@ -14,7 +15,8 @@ from typeguard import typechecked from scipy import interpolate -from species.data.vega import add_vega +from species.data.filter_data.filter_data import add_filter_profile +from species.data.spec_data.spec_vega import add_vega class ReadFilter: @@ -43,22 +45,15 @@ def __init__(self, filter_name: str) -> None: config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] + self.data_folder = config["species"]["data_folder"] - h5_file = h5py.File(self.database, "r") - - if "filters" not in h5_file or self.filter_name not in h5_file["filters"]: - h5_file.close() - from species.data.database import Database - - species_db = Database() - species_db.add_filter(self.filter_name) - - else: - h5_file.close() + with h5py.File(self.database, "a") as hdf5_file: + if f"filters/{self.filter_name}" not in hdf5_file: + add_filter_profile(self.data_folder, hdf5_file, self.filter_name) @typechecked def get_filter(self) -> np.ndarray: @@ -72,8 +67,8 @@ def get_filter(self) -> np.ndarray: array has 2 dimensions with the shape (n_wavelengths, 2). """ - with h5py.File(self.database, "r") as h5_file: - data = np.asarray(h5_file[f"filters/{self.filter_name}"]) + with h5py.File(self.database, "r") as hdf5_file: + data = np.asarray(hdf5_file[f"filters/{self.filter_name}"]) if data.shape[0] == 2 and data.shape[1] > data.shape[0]: # Required for backward compatibility @@ -156,11 +151,11 @@ def effective_wavelength(self) -> Union[np.float32, np.float64]: filter_profile = self.get_filter() - with h5py.File(self.database, "a") as h5_file: - if "spectra/calibration/vega" not in h5_file: - add_vega(self.data_folder, h5_file) + with h5py.File(self.database, "a") as hdf5_file: + if "spectra/calibration/vega" not in hdf5_file: + add_vega(self.data_folder, hdf5_file) - vega_spec = np.array(h5_file["spectra/calibration/vega"]) + vega_spec = np.array(hdf5_file["spectra/calibration/vega"]) flux_interp = interpolate.interp1d( vega_spec[0,], @@ -231,8 +226,8 @@ def detector_type(self) -> str: Detector type ('energy' or 'photon'). """ - with h5py.File(self.database, "r") as h5_file: - dset = h5_file[f"filters/{self.filter_name}"] + with h5py.File(self.database, "r") as hdf5_file: + dset = hdf5_file[f"filters/{self.filter_name}"] if "det_type" in dset.attrs: det_type = dset.attrs["det_type"] diff --git a/species/read/read_isochrone.py b/species/read/read_isochrone.py index 7ebac1fd..95ae9ae5 100644 --- a/species/read/read_isochrone.py +++ b/species/read/read_isochrone.py @@ -2,10 +2,10 @@ Module with reading functionalities for isochrones and cooling curves. """ -import configparser import os import warnings +from configparser import ConfigParser from typing import Dict, List, Optional, Tuple, Union import h5py @@ -108,15 +108,15 @@ def __init__( config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] self.interp_method = config["species"]["interp_method"] if self.tag is None: - with h5py.File(self.database, "r") as h5_file: - tag_list = list(h5_file["isochrones"]) + with h5py.File(self.database, "r") as hdf5_file: + tag_list = list(hdf5_file["isochrones"]) self.tag = input( "Please select one of the following " @@ -126,9 +126,9 @@ def __init__( f"\n{tag_list}:\n" ) - with h5py.File(self.database, "r") as h5_file: - if f"isochrones/{self.tag}" not in h5_file: - tag_list = list(h5_file["isochrones"]) + with h5py.File(self.database, "r") as hdf5_file: + if f"isochrones/{self.tag}" not in hdf5_file: + tag_list = list(hdf5_file["isochrones"]) raise ValueError( f"There is no isochrone data stored with the " @@ -229,18 +229,18 @@ def _read_data( there are magnitudes available. """ - with h5py.File(self.database, "r") as h5_file: - model_name = h5_file[f"isochrones/{self.tag}/age"].attrs["model"] + with h5py.File(self.database, "r") as hdf5_file: + model_name = hdf5_file[f"isochrones/{self.tag}/age"].attrs["model"] - iso_age = np.asarray(h5_file[f"isochrones/{self.tag}/age"]) - iso_mass = np.asarray(h5_file[f"isochrones/{self.tag}/mass"]) - iso_teff = np.asarray(h5_file[f"isochrones/{self.tag}/teff"]) - iso_loglum = np.asarray(h5_file[f"isochrones/{self.tag}/log_lum"]) - iso_logg = np.asarray(h5_file[f"isochrones/{self.tag}/log_g"]) - iso_radius = np.asarray(h5_file[f"isochrones/{self.tag}/radius"]) + iso_age = np.asarray(hdf5_file[f"isochrones/{self.tag}/age"]) + iso_mass = np.asarray(hdf5_file[f"isochrones/{self.tag}/mass"]) + iso_teff = np.asarray(hdf5_file[f"isochrones/{self.tag}/teff"]) + iso_loglum = np.asarray(hdf5_file[f"isochrones/{self.tag}/log_lum"]) + iso_logg = np.asarray(hdf5_file[f"isochrones/{self.tag}/log_g"]) + iso_radius = np.asarray(hdf5_file[f"isochrones/{self.tag}/radius"]) - if f"isochrones/{self.tag}/magnitudes" in h5_file: - iso_mag = np.asarray(h5_file[f"isochrones/{self.tag}/magnitudes"]) + if f"isochrones/{self.tag}/magnitudes" in hdf5_file: + iso_mag = np.asarray(hdf5_file[f"isochrones/{self.tag}/magnitudes"]) else: iso_mag = None @@ -1352,9 +1352,9 @@ def get_filters(self) -> Optional[List[str]]: the isochrone data. """ - with h5py.File(self.database, "r") as h5_file: - if "filters" in h5_file[f"isochrones/{self.tag}"]: - filters = list(h5_file[f"isochrones/{self.tag}/filters/"]) + with h5py.File(self.database, "r") as hdf5_file: + if "filters" in hdf5_file[f"isochrones/{self.tag}"]: + filters = list(hdf5_file[f"isochrones/{self.tag}/filters/"]) # Convert from bytes to strings for i, item in enumerate(filters): diff --git a/species/read/read_model.py b/species/read/read_model.py index 50272f5c..4b3a8020 100644 --- a/species/read/read_model.py +++ b/species/read/read_model.py @@ -2,10 +2,10 @@ Module with reading functionalities for atmospheric model spectra. """ -import configparser import os import warnings +from configparser import ConfigParser from typing import Dict, List, Optional, Tuple, Union import h5py @@ -17,8 +17,6 @@ from scipy.interpolate import interp1d, RegularGridInterpolator from species.core import constants -from species.data.model_spectra import add_model_grid -from species.data.vega import add_vega from species.core.box import ( ColorColorBox, ColorMagBox, @@ -26,6 +24,8 @@ PhotometryBox, create_box, ) +from species.data.model_data.model_spectra import add_model_grid +from species.data.spec_data.spec_vega import add_vega from species.phot.syn_phot import SyntheticPhotometry from species.read.read_filter import ReadFilter from species.read.read_planck import ReadPlanck @@ -94,7 +94,7 @@ def __init__( config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] @@ -741,7 +741,7 @@ def get_model( Returns ------- - ModelBox + species.core.box.ModelBox Box with the model spectrum. """ @@ -1188,7 +1188,7 @@ def get_data( Returns ------- - ModelBox + species.core.box.ModelBox Box with the model spectrum. """ @@ -1809,7 +1809,7 @@ def binary_spectrum( Returns ------- - ModelBox + species.core.box.ModelBox Box with the model spectrum. """ @@ -1958,7 +1958,7 @@ def create_color_magnitude( Returns ------- - ColorMagBox + species.core.box.ColorMagBox Box with the colors and magnitudes. """ @@ -2039,7 +2039,7 @@ def create_color_color( Returns ------- - ColorColorBox + species.core.box.ColorColorBox Box with the colors. """ diff --git a/species/read/read_object.py b/species/read/read_object.py index d1f28890..25be094d 100644 --- a/species/read/read_object.py +++ b/species/read/read_object.py @@ -3,8 +3,8 @@ """ import os -import configparser +from configparser import ConfigParser from typing import List, Optional, Union, Tuple import h5py @@ -39,7 +39,7 @@ def __init__(self, object_name: str) -> None: config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] diff --git a/species/read/read_planck.py b/species/read/read_planck.py index a85751e7..f13fbc64 100644 --- a/species/read/read_planck.py +++ b/species/read/read_planck.py @@ -2,9 +2,9 @@ Module with reading functionalities for Planck spectra. """ -import configparser import os +from configparser import ConfigParser from typing import Dict, List, Optional, Tuple, Union import numpy as np @@ -65,7 +65,7 @@ def __init__( config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] diff --git a/species/read/read_spectrum.py b/species/read/read_spectrum.py index 4c769fe7..e7935162 100644 --- a/species/read/read_spectrum.py +++ b/species/read/read_spectrum.py @@ -2,9 +2,9 @@ Module with reading functionalities for spectral libraries. """ -import configparser import os +from configparser import ConfigParser from typing import List, Optional import h5py @@ -13,6 +13,7 @@ from typeguard import typechecked from species.core.box import PhotometryBox, SpectrumBox, create_box +from species.data.spec_data.add_spec_data import add_spec_library from species.phot.syn_phot import SyntheticPhotometry from species.read.read_filter import ReadFilter @@ -52,10 +53,11 @@ def __init__(self, spec_library: str, filter_name: Optional[str] = None) -> None config_file = os.path.join(os.getcwd(), "species_config.ini") - config = configparser.ConfigParser() + config = ConfigParser() config.read(config_file) self.database = config["species"]["database"] + self.data_folder = config["species"]["data_folder"] @typechecked def get_spectrum( @@ -83,15 +85,9 @@ def get_spectrum( Box with the spectra. """ - h5_file = h5py.File(self.database, "r") - - if self.spec_library not in h5_file[f"spectra"]: - h5_file.close() - from species.data.database import Database - - species_db = Database() - species_db.add_spectra(self.spec_library, sptypes) - h5_file = h5py.File(self.database, "r") + with h5py.File(self.database, "a") as hdf5_file: + if f"spectra/{self.spec_library}" not in hdf5_file: + add_spec_library(self.data_folder, hdf5_file, self.spec_library, sptypes) list_wavelength = [] list_flux = [] @@ -102,93 +98,94 @@ def get_spectrum( list_parallax = [] list_spec_res = [] - for item in h5_file[f"spectra/{self.spec_library}"]: - dset = h5_file[f"spectra/{self.spec_library}/{item}"] + with h5py.File(self.database, "r") as hdf5_file: + for item in hdf5_file[f"spectra/{self.spec_library}"]: + dset = hdf5_file[f"spectra/{self.spec_library}/{item}"] - wavelength = dset[:, 0] # (um) - flux = dset[:, 1] # (W m-2 um-1) - error = dset[:, 2] # (W m-2 um-1) + wavelength = dset[:, 0] # (um) + flux = dset[:, 1] # (W m-2 um-1) + error = dset[:, 2] # (W m-2 um-1) - if exclude_nan: - nan_index = np.isnan(flux) + if exclude_nan: + nan_index = np.isnan(flux) - wavelength = wavelength[~nan_index] - flux = flux[~nan_index] - error = error[~nan_index] + wavelength = wavelength[~nan_index] + flux = flux[~nan_index] + error = error[~nan_index] - if self.wavel_range is None: - wl_index = np.arange(0, len(wavelength), 1) + if self.wavel_range is None: + wl_index = np.arange(0, len(wavelength), 1) - else: - wl_index = ( - (flux > 0.0) - & (wavelength > self.wavel_range[0]) - & (wavelength < self.wavel_range[1]) - ) + else: + wl_index = ( + (flux > 0.0) + & (wavelength > self.wavel_range[0]) + & (wavelength < self.wavel_range[1]) + ) - count = np.count_nonzero(wl_index) + count = np.count_nonzero(wl_index) - if count > 0: - index = np.where(wl_index)[0] + if count > 0: + index = np.where(wl_index)[0] - if index[0] > 0: - wl_index[index[0] - 1] = True + if index[0] > 0: + wl_index[index[0] - 1] = True - if index[-1] < len(wl_index) - 1: - wl_index[index[-1] + 1] = True + if index[-1] < len(wl_index) - 1: + wl_index[index[-1] + 1] = True - list_wavelength.append(wavelength[wl_index]) - list_flux.append(flux[wl_index]) - list_error.append(error[wl_index]) + list_wavelength.append(wavelength[wl_index]) + list_flux.append(flux[wl_index]) + list_error.append(error[wl_index]) - attrs = dset.attrs + attrs = dset.attrs - if "name" in attrs: - if isinstance(dset.attrs["name"], str): - list_name.append(dset.attrs["name"]) + if "name" in attrs: + if isinstance(dset.attrs["name"], str): + list_name.append(dset.attrs["name"]) + else: + list_name.append(dset.attrs["name"].decode("utf-8")) else: - list_name.append(dset.attrs["name"].decode("utf-8")) - else: - list_name.append("") + list_name.append("") - if "simbad" in attrs: - if isinstance(dset.attrs["simbad"], str): - list_simbad.append(dset.attrs["simbad"]) + if "simbad" in attrs: + if isinstance(dset.attrs["simbad"], str): + list_simbad.append(dset.attrs["simbad"]) + else: + list_simbad.append(dset.attrs["simbad"].decode("utf-8")) else: - list_simbad.append(dset.attrs["simbad"].decode("utf-8")) - else: - list_simbad.append("") + list_simbad.append("") - if "sptype" in attrs: - if isinstance(dset.attrs["sptype"], str): - list_sptype.append(dset.attrs["sptype"]) + if "sptype" in attrs: + if isinstance(dset.attrs["sptype"], str): + list_sptype.append(dset.attrs["sptype"]) + else: + list_sptype.append(dset.attrs["sptype"].decode("utf-8")) else: - list_sptype.append(dset.attrs["sptype"].decode("utf-8")) - else: - list_sptype.append("None") + list_sptype.append("None") - if "parallax" in attrs: - list_parallax.append( - (dset.attrs["parallax"], dset.attrs["parallax_error"]) - ) - else: - list_parallax.append((np.nan, np.nan)) + if "parallax" in attrs: + list_parallax.append( + (dset.attrs["parallax"], dset.attrs["parallax_error"]) + ) + else: + list_parallax.append((np.nan, np.nan)) + + if "spec_res" in attrs: + list_spec_res.append(dset.attrs["spec_res"]) + else: + list_spec_res.append(np.nan) - if "spec_res" in attrs: - list_spec_res.append(dset.attrs["spec_res"]) else: + list_wavelength.append(np.array([])) + list_flux.append(np.array([])) + list_error.append(np.array([])) + list_name.append("") + list_simbad.append("") + list_sptype.append("None") + list_parallax.append((np.nan, np.nan)) list_spec_res.append(np.nan) - else: - list_wavelength.append(np.array([])) - list_flux.append(np.array([])) - list_error.append(np.array([])) - list_name.append("") - list_simbad.append("") - list_sptype.append("None") - list_parallax.append((np.nan, np.nan)) - list_spec_res.append(np.nan) - spec_box = SpectrumBox() spec_box.spec_library = self.spec_library diff --git a/species/util/box_util.py b/species/util/box_util.py index 0541803d..b25536f6 100644 --- a/species/util/box_util.py +++ b/species/util/box_util.py @@ -21,12 +21,12 @@ def update_objectbox( ) -> ObjectBox: """ Function for updating the spectra and/or photometric fluxes in - an :class:`~species.core.box.ObjectBox`, for example by applying - a flux scaling and/or error inflation. + an :class:`~species.core.box_types.ObjectBox`, for example by + applying a flux scaling and/or error inflation. Parameters ---------- - objectbox : ObjectBox + objectbox : species.core.box.ObjectBox Box with the object's data, including the spectra and/or photometric fluxes. model_param : dict @@ -41,7 +41,7 @@ def update_objectbox( Returns ------- - ObjectBox + species.core.box.ObjectBox The input box which includes the spectra with the scaled fluxes and/or inflated errors. """ diff --git a/species/util/dust_util.py b/species/util/dust_util.py index 43ab18bf..fa6857b1 100644 --- a/species/util/dust_util.py +++ b/species/util/dust_util.py @@ -15,7 +15,7 @@ from scipy.stats import lognorm from species.read.read_filter import ReadFilter -from species.data.dust import add_cross_sections, add_optical_constants +from species.data.misc_data.dust_data import add_cross_sections, add_optical_constants @typechecked diff --git a/species/util/fit_util.py b/species/util/fit_util.py index 88aeadf3..c0b08acb 100644 --- a/species/util/fit_util.py +++ b/species/util/fit_util.py @@ -35,7 +35,7 @@ def multi_photometry( filters and a specified atmosphere model and related parameters. This function can for example be used for calculating the synthetic photometry from a best-fit model spectrum. It returns - a :class:`~species.core.box.SynphotBox` that can be provided + a :class:`~species.core.box_types.SynphotBox` that can be provided as input to :func:`~species.plot.plot_spectrum.plot_spectrum`. Parameters diff --git a/species/util/model_util.py b/species/util/model_util.py index d8e9c5a2..164f189f 100644 --- a/species/util/model_util.py +++ b/species/util/model_util.py @@ -10,7 +10,7 @@ from typeguard import typechecked from species.core import constants -from species.core.box import create_box +from species.core.box import ModelBox, create_box from species.util.spec_util import create_wavelengths @@ -19,7 +19,7 @@ def powerlaw_spectrum( wavel_range: Union[Tuple[float, float], Tuple[np.float32, np.float32]], model_param: Dict[str, float], spec_res: float = 100.0, -): +) -> ModelBox: """ Function for calculating a power-law spectrum. The power-law function is calculated in log(wavelength)-log(flux) space but @@ -70,7 +70,7 @@ def gaussian_spectrum( model_param: Dict[str, float], spec_res: float = 100.0, double_gaussian: bool = False, -): +) -> ModelBox: """ Function for calculating a Gaussian spectrum (i.e. for an emission line). @@ -131,7 +131,8 @@ def gaussian_spectrum( return model_box -# def add_luminosity(modelbox): +# @typechecked +# def add_luminosity(modelbox: ModelBox) -> ModelBox: # """ # Function to add the luminosity of a model spectrum to the parameter # dictionary of the box. diff --git a/species/util/plot_util.py b/species/util/plot_util.py index cd15d539..0fbed7a5 100644 --- a/species/util/plot_util.py +++ b/species/util/plot_util.py @@ -1392,7 +1392,7 @@ def create_model_label( List with selected indices of the young/low-gravity objects. """ - data_file = Path(__file__).parents[1].resolve() / "data/model_data.json" + data_file = Path(__file__).parents[1].resolve() / "data/model_data/model_data.json" with open(data_file, "r", encoding="utf-8") as json_file: model_data = json.load(json_file) diff --git a/species/util/radtrans_util.py b/species/util/radtrans_util.py index 3e9ccb91..29f65ff3 100644 --- a/species/util/radtrans_util.py +++ b/species/util/radtrans_util.py @@ -82,7 +82,7 @@ def retrieval_spectrum( Returns ------- - ModelBox + species.core.box.ModelBox Box with the petitRADTRANS spectrum. """ diff --git a/species/util/test_util.py b/species/util/test_util.py index 2ed85624..46d636f2 100644 --- a/species/util/test_util.py +++ b/species/util/test_util.py @@ -28,4 +28,5 @@ def create_config(test_path): config.write("[species]\n") config.write(f"database = {database_file}\n") config.write(f"data_folder = {data_folder}\n") - config.write("interp_method = linear") + config.write("interp_method = linear\n") + config.write("vega_mag = 0.03")