"""
Creation of XML input files
"""
from xml.dom import minidom
from xml.etree.ElementTree import Element, SubElement, tostring
import numpy as np
from basta.constants import sydsun as sydc
from basta.constants import freqtypes
from basta.utils_xml import create_xmltag
[docs]
def generate_xml(
gridfile,
asciifile,
outputpath,
params,
fitparams,
outparams,
outputfile="results.ascii",
sunnumax=sydc.SUNnumax,
sundnu=sydc.SUNdnu,
solarmodel=False,
missingval=-999.999,
centroid=None,
uncert=None,
plotfmt=None,
nameinplot=False,
odea=None,
intpolparams=None,
bayweights=True,
priors=None,
overwriteparams=None,
freqparams=None,
glitchparams=None,
filters=None,
dustframe=None,
cornerplots=False,
kielplots=False,
freqplots=False,
optionaloutputs=True,
delimiter=None,
):
"""
Converts an ascii table into an xml input file. Defines the properties
of the fit including the type of grid and parameters to be fitted.
Parameters
----------
gridfile : str
Absolute path of the grid used in the fit.
asciifile: str
Absolute path of ascii table of input values. The table should
containing all ``params`` specified as an input (see below) and in
the same order.
outputpath : str
Absolute path for all output of the code.
params : tuple
Names of the parameters to be read from the ``asciifile``.
fitparams : tuple
Names of the parameters to be fitted. These must exists in the list
of parameters in the grid. These lists can be found in constants.
outparams : tuple
A tuple of parameters for which the results of the Bayesian analysis
is printed in the ``outputfile``.
outputfile : str
Name of the output file where all ``outparams`` will be printed out
sunnumax : float
Value of the solar frequency of maximum power used in the scaling
relations
sundnu : float
Value of the solar large frequency separation used in the scaling
relations
solarmodel : bool
Activate solar scaling of asteroseismic quantities (dnu's).
missingval : int or float or str
Used to replace a missing value from the ``asciifile``. It can be an
integer, float, or 'nan'.
centroid : str
Which centroid value of the posterior is to be reported as the
result. The standard option is 'median' (the Bayesian 50'th
percentile), the other option is 'mean'.
uncert : str
The reported type of uncertaities. The standard option is 'quantiles'
reporting the Bayesian 16'th and 84'th percentiles of the posterior,
while the other option is 'std' for the standard deviation.
plotfmt : str
Format of outputted plots, simply given directly to pyplot.savefig.
Default is 'png' for quick figures. For detailed plots, 'pdf' is
recommended.
nameinplot : bool
Toggle to include star identifier in plots, not simply in filename
of plot.
odea : tuple or None
Specifies the input physics used to compute the grid.
`o` : overshoot efficiency, 0.0 = no overshoot.
`d` : microscopic diffusion, 0.0 = no diffusion.
`e` : eta reimers for mass-loss, 0.0 = no mass-loss.
`a` : alpha enhancement with respect to solar value, 0.0 = no alpha
enhancement.
intpolparams : dict
Contains inputted settings for performing interpolation with BASTA.
A single dictionary is given, but can be applied star-by-star.
bayweights : bool
Enable the usage of Bayesian weights across tracks or isochrones to
properly take into account spacing in the creation of the grid and
evolutionary speed.
priors : dict
A dictionary with the name of priors to be considered in the fitting
{'IMF': 'string', 'param1': {values}, 'param2': {values}, ...}
which defines the IMF used, and sets the absolute value of the
tolerances for the selection of models in the grid to compute the
likelihood. Accepted values are 'min', 'max', and 'abstol'. If not
specified, all models in the grid are evaluated.
overwriteparams : dict
A dictionary in the format
{'param1' : (value1, uncert1), 'param2': (value2, uncert2), ...}
used to set a value and uncertainty for all stars fitted. It will
overwrite the input parameters for the individual stars!
freqparams : dict
A dictionary containing the input for fitting individual frequencies
or ratios.
grparams : dict
A dictionary containing control options for fitting glitches along
with ratios.
filters : tuple of strings
If calculating distances to stars, specify photometric filters
dustframe : str
Type of reference frame for the dustmap. Possible options
`galactic` : The default coordinate system. Input coordinates are
expected in galactic coordinates longitude (l) and latitude (b).
`icrs` : Input coordinates are expected in celestial right
ascension (ra) and declination (dec).
cornerplots : tuple
Names of parameters to be included in the output plots. These can be
any of the available parameters of the grid used, and can be found
in :func:`make_basti` and :func:`make_garstec`.
kielplots : tuple
Names of fitted parameters to be indicated in the Kiel diagram. If
all fitparams are wanted plotted, set to True. False by default.
freqplots : bool
Whether or not to genereate echelle- and ratio-diagrams when fitting
frequencies. False by default.
optionaloutputs : bool
Defines if the additional output files are stored
delimiter : str or None
Inputted delimiter if asciifile uses a special delimiter not easily
recognised by numpy.genfromtxt
"""
inp = np.genfromtxt(
asciifile, dtype=None, names=params, encoding=None, delimiter=delimiter
)
if inp.ndim == 0:
inp = inp.reshape(1, -1)[0]
# If missingval is nan, redefine it
if missingval == "nan":
missingval = -999.999
for i, inp1 in enumerate(inp):
for j, inp2 in enumerate(inp1):
try:
if np.isnan(inp2):
inp[i][j] = missingval
except Exception:
pass
# Create main element stars
main = Element("stars")
default = SubElement(main, "default")
# Add library path to <default>
SubElement(default, "library", {"path": gridfile})
# Add output path to <default>
if not outputpath.endswith("/"):
outputpath += "/"
SubElement(default, "output", {"path": outputpath})
# Add output file path (relative to output path) to <default>
SubElement(default, "outputfile", {"value": outputfile})
# Add solar dnu and numax to <default>
SubElement(default, "solardnu", {"value": str(sundnu)})
SubElement(default, "solarnumax", {"value": str(sunnumax)})
# Add solar model toggle to <default>
if solarmodel is None or isinstance(solarmodel, bool):
SubElement(default, "solarmodel", {"value": str(solarmodel)})
else:
SubElement(default, "solarmodel", {"value": solarmodel})
# Add missingvalue to <default>
SubElement(default, "missingval", {"value": str(missingval)})
# Add output stats types (centroid and undert) to <default>
if centroid is not None:
SubElement(default, "centroid", {"value": str(centroid)})
if uncert is not None:
SubElement(default, "uncert", {"value": str(uncert)})
# Add plotformat to <default> if user provided
if plotfmt is not None:
SubElement(default, "plotfmt", {"value": str(plotfmt)})
# Add nameinplot to <default> if set by user
if nameinplot:
SubElement(default, "nameinplot", {"value": "True"})
# Add ove, eta, diffusion and alphaFe to <basti> if isochrone
if odea:
# Add <basti> to <default>
bastielement = SubElement(default, "basti")
oeaname = ("ove", "dif", "eta", "alphaFe")
for i, par in enumerate(odea):
SubElement(bastielement, oeaname[i], {"value": str(par)})
# Add subelement <interpolation> to <default> (if defiend)
intpollim = {}
if intpolparams:
intpolelement = SubElement(default, "interpolation")
SubElement(intpolelement, "method", intpolparams["method"])
if "name" in intpolparams:
SubElement(intpolelement, "name", {"value": intpolparams["name"]})
if "trackresolution" in intpolparams:
trackres = intpolparams["trackresolution"]
for tag, val in trackres.items():
if isinstance(val, (float, int)):
trackres[tag] = str(val)
SubElement(intpolelement, "trackresolution", trackres)
if "gridresolution" in intpolparams:
gridres = intpolparams["gridresolution"]
for tag, val in gridres.items():
if isinstance(val, (float, int)):
gridres[tag] = str(val)
if "resolution" in gridres:
for restag, resval in gridres["resolution"].items():
gridres[restag] = str(resval)
del gridres["resolution"]
SubElement(intpolelement, "gridresolution", gridres)
if "limits" in intpolparams:
limelement = SubElement(intpolelement, "limits")
for name, item in intpolparams["limits"].items():
for n, v in item.items():
item[n] = str(v)
SubElement(limelement, name, item)
if name not in fitparams:
rules = np.asarray(list(intpolparams["limits"][name].keys()))
hits = np.argwhere(
[x in ["sigmacut", "abstol"] for x in rules]
).flatten()
if len(hits):
intpollim[name] = list(rules[hits])
# Add subelement <bayesianweights> to <default>
if isinstance(bayweights, (bool, str)):
SubElement(default, "bayesianweights", {"value": str(bayweights)})
# Add subelement <fitparams to <default>
fitelement = SubElement(default, "fitparams")
if isinstance(fitparams, str):
fitparams = [fitparams]
for param in fitparams:
paramdic = {}
SubElement(fitelement, param, paramdic)
# Add subelement priors to <default> (if any priors are included)
if priors:
priorelement = SubElement(default, "priors")
for param in priors.keys():
if param == "IMF":
SubElement(priorelement, priors[param])
elif param == "dif":
# Restrict diffusion in mixed grids
if priors[param]:
dif_switch = {"min": "0.5"}
else:
dif_switch = {"max": "0.5"}
SubElement(priorelement, param, dif_switch)
else:
# Catch new-style IMF-type priors
try:
SubElement(priorelement, param, priors[param])
except TypeError:
SubElement(priorelement, param)
# Add subelement overwriteparams to <default> (if any global parameters)
if overwriteparams:
globalelement = SubElement(default, "overwriteparams")
for param in overwriteparams:
paramdic = {}
if param == "phase":
paramdic["value"] = overwriteparams[param]
else:
paramdic["value"] = str(overwriteparams[param][0])
paramdic["error"] = str(overwriteparams[param][1])
SubElement(globalelement, param, paramdic)
# Add subelement freqparams to <default> (if any)
if freqparams and any(x in fitparams for x in freqtypes.alltypes):
freqelement = SubElement(default, "freqparams")
for param in freqparams:
# The following are handled in create_xmltag
if param in ["excludemodes", "nottrustedfile", "onlyradial"]:
continue
SubElement(freqelement, param, {"value": str(freqparams[param])})
# Add subelement grparams to <default> if specified
if glitchparams and any(x in fitparams for x in freqtypes.glitches):
glitchelement = SubElement(default, "glitchparams")
for param in glitchparams:
SubElement(glitchelement, param, {"value": str(glitchparams[param])})
# We need to check these before handling distance input
if isinstance(cornerplots, (str, bool)) and len(cornerplots):
cornerplots = [str(cornerplots)]
if isinstance(outparams, (str, bool)) and len(outparams):
outparams = [str(outparams)]
# Handle distance related input
if (
("parallax" in fitparams)
or ("distance" in outparams)
or ("distance" in cornerplots)
):
# Convert to tuple if the user specified only one filter as a sting
if isinstance(filters, str):
filters = (filters,)
if len(filters) == 0:
raise ValueError("No filters were given for parallax/distance fitting")
# Add to <default>
distanceelement = SubElement(default, "distanceInput")
BCfilterelement = SubElement(distanceelement, "filters")
for param in filters:
SubElement(BCfilterelement, param)
SubElement(distanceelement, "dustframe", {"value": str(dustframe)})
# Add coordinate system to the individual targets
if dustframe == "galactic":
distparams = ("lat", "lon")
elif dustframe in ["icrs"]:
distparams = ("RA", "DEC")
else:
print("Illegal dustframe specified! Not adding coordinates.")
distparams = ()
# Add magnitudes to list of parameters
starparams = fitparams + filters
else:
starparams = fitparams
distparams = ()
# Add subelement <cornerplots> to <default>
cornerplotselement = SubElement(default, "cornerplots")
for param in cornerplots:
SubElement(cornerplotselement, param)
# Add subelement <kielplots> to <default>
kielplotselement = SubElement(default, "kielplots")
if isinstance(kielplots, bool):
if kielplots:
SubElement(kielplotselement, str(kielplots))
else:
if isinstance(kielplots, str) and len(kielplots):
kielplots = [kielplots]
for param in kielplots:
SubElement(kielplotselement, param)
# Add subelement <freqplots> to <default>
freqplotselement = SubElement(default, "freqplots")
if isinstance(freqplots, (bool, str)) and freqplots:
SubElement(freqplotselement, str(freqplots))
elif freqplots == "True":
SubElement(freqplotselement, "True")
elif isinstance(freqplots, (list, tuple)):
for plot in freqplots:
SubElement(freqplotselement, plot)
# Add subelement <outparams> to <default>
outparamselement = SubElement(default, "outparams")
for param in tuple(outparams):
SubElement(outparamselement, param)
# Add optional output files to <default>
optoutputelement = SubElement(default, "optionaloutputfiles")
if isinstance(optionaloutputs, (bool, str)):
SubElement(optoutputelement, str(optionaloutputs))
# Create xml tags for all the targets in the input file
for i in range(len(inp)):
main = create_xmltag(
main,
params,
inp[i],
starparams,
distparams,
freqparams,
missingval,
intpollim,
)
# Make the XML into a string:
xml = tostring(main)
# Make the XML pretty
reparsed = minidom.parseString(xml)
pretty_xml = reparsed.toprettyxml()
return pretty_xml