From e5a79568545372679dc86ba49b0023bccc4f5694 Mon Sep 17 00:00:00 2001 From: Ansgar Wehrhahn <31626864+AWehrhahn@users.noreply.github.com> Date: Thu, 3 Dec 2020 15:09:52 +0100 Subject: [PATCH] Support for GES linelist --- src/pysme/atmosphere/atmosphere.py | 2 + src/pysme/linelist/ges.py | 107 +++++++++++++++++++++++++++++ src/pysme/linelist/linelist.py | 36 ++++++++-- src/pysme/sme.py | 2 + 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/pysme/linelist/ges.py diff --git a/src/pysme/atmosphere/atmosphere.py b/src/pysme/atmosphere/atmosphere.py index 2aaccf8b..ec80c181 100644 --- a/src/pysme/atmosphere/atmosphere.py +++ b/src/pysme/atmosphere/atmosphere.py @@ -78,6 +78,8 @@ def __init__(self, **kwargs): monh = kwargs.pop("monh", kwargs.pop("feh", 0)) abund = kwargs.pop("abund", "empty") abund_format = kwargs.pop("abund_format", "sme") + if "geom" in kwargs.keys() and kwargs["geom"] == "": + kwargs["geom"] = None super().__init__(**kwargs) self.abund = Abund(monh=monh, pattern=abund, type=abund_format) diff --git a/src/pysme/linelist/ges.py b/src/pysme/linelist/ges.py new file mode 100644 index 00000000..7ee7ec7a --- /dev/null +++ b/src/pysme/linelist/ges.py @@ -0,0 +1,107 @@ +""" +Module for handling linelist data from the VALD3 database (http://vald.astro.uu.se/). + + +""" +import logging +import re +from io import StringIO +from os.path import dirname, join +import sys + +import numpy as np +import pandas as pd +from astropy import units as u +import pybtex.database + +from astropy.io import fits + + +from ..abund import Abund +from .linelist import LineListError, LineList + +logger = logging.getLogger(__name__) + + +class GesError(LineListError): + """ Vald Data File Error """ + + +class GesFile(LineList): + """Atomic data for a list of spectral lines. + """ + + def __init__(self, filename, medium=None): + self.filename = filename + linelist = self.loads(filename) + + super().__init__(linelist, lineformat=self.lineformat, medium=self.medium) + # Convert to desired medium + if medium is not None: + self.medium = medium + + @staticmethod + def load(filename): + """ + Read line data file from the VALD extract stellar service + + Parameters + ---------- + filename : str + Name of the VALD linelist file to read + + Returns + ------- + vald : ValdFile + Parsed vald file + """ + return GesFile(filename) + + def loads(self, filename): + logger.info("Loading GES file %s", filename) + + hdu = fits.open(filename) + data = hdu[1].data + + linedata = { + "species": data["name"][:, 0] + " " + data["ion"].astype(str), + "atom_number": np.zeros(len(data)), + "ionization": data["ion"], + "wlcent": data["lambda"], + "excit": data["e_low"], + "gflog": data["log_gf"], + "gflog_err": data["log_gf_err"], + "gamrad": data["rad_damp"], + "gamqst": data["stark_damp"], + "gamvw": data["vdw_damp"], + "lande": data["lande_mean"], + "depth": data["depth"], + "reference": data["lambda_ref"], + "lande_lower": data["lande_low"], + "lande_upper": data["lande_up"], + "j_lo": data["j_low"], + "e_upp": data["e_up"], + "j_up": data["j_up"], + "term_lower": data["label_low"], + "term_upper": data["label_up"], + } + + sort = np.argsort(linedata["wlcent"]) + + # We need to make sure all data is in the system defined byteorder + # Otherwise pandas will not work properly + for key, value in linedata.items(): + if (value.dtype.byteorder == ">" and sys.byteorder == "little") or ( + value.dtype.byteorder == "<" and sys.byteorder == "big" + ): + value = value.byteswap().newbyteorder() + linedata[key] = value[sort] + + linelist = pd.DataFrame.from_dict(linedata) + self.lineformat = "long" + self.unit = "Angstrom" + self._medium = "air" + + # self.citation_info += self.parse_references(refdata, fmt) + + return linelist diff --git a/src/pysme/linelist/linelist.py b/src/pysme/linelist/linelist.py index 27899488..f8b68e29 100644 --- a/src/pysme/linelist/linelist.py +++ b/src/pysme/linelist/linelist.py @@ -79,7 +79,9 @@ def parse_line_error(error_flags, values=None): } error = np.ones(len(error_flags), dtype=float) for i, (flag, _) in enumerate(zip(error_flags, values)): - if flag[0] in [" ", "_", "P"]: + if len(flag) == 0: + error[i] = 0.5 + elif flag[0] in [" ", "_", "P"]: # undefined or predicted error[i] = 0.5 elif flag[0] == "E": @@ -88,22 +90,46 @@ def parse_line_error(error_flags, values=None): error[i] = 10 ** float(flag[1:]) elif flag[0] == "C": # Cancellation Factor, i.e. relative error - error[i] = abs(float(flag[1:])) + try: + error[i] = abs(float(flag[1:])) + except ValueError: + error[i] = 0.5 elif flag[0] == "N": # NIST quality class flag = flag[1:5].strip() - error[i] = nist[flag] + try: + error[i] = nist[flag] + except KeyError: + error[i] = 0.5 return error @staticmethod - def from_IDL_SME(**kwargs): + def guess_format(kwargs): + short_line_format = kwargs.pop( + "short_line_format", kwargs.pop("short_format", None) + ) + if short_line_format is not None: + return short_line_format + + keys = kwargs.keys() + if ( + "line_extra" in keys + and "line_lulande" in keys + and "line_term_low" in keys + and "line_term_upp" in keys + ): + return 2 + return 1 + + @classmethod + def from_IDL_SME(cls, **kwargs): """ extract LineList from IDL SME structure keywords """ species = kwargs.pop("species").astype("U") atomic = np.asarray(kwargs.pop("atomic"), dtype="