Source code for radvel.driver

"""
Driver functions for the radvel pipeline.\
These functions are meant to be used only with\
the `cli.py` command line interface.
"""
from __future__ import annotations, print_function

import collections
import copy
import os
import sys
from argparse import ArgumentParser
from collections import OrderedDict  # noqa: F401 - needed for eval(status.get('ic_compare', 'ic'))

import numpy as np
import pandas as pd
from astropy import constants as c

import radvel
from radvel.likelihood import GPLikelihood
from radvel.mcmc import statevars
from radvel.plot import mcmc_plots, orbit_plots
from radvel.posterior import Posterior

if sys.version_info[0] < 3:
    import ConfigParser as configparser
else:
    import configparser
    from configparser import NoSectionError


[docs] def plots(args: ArgumentParser) -> None: """ Generate plots Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join( args.outputdir, "{}_radvel.stat".format(conf_base) ) status = load_status(statfile) P, post = radvel.utils.initialize_posterior(config_file, decorr=args.decorr) assert status.getboolean('fit', 'run'), \ "Must perform max-likelihood fit before plotting" post = radvel.posterior.load(status.get('fit', 'postfile')) for ptype in args.type: print("Creating {} plot for {}".format(ptype, conf_base)) if ptype == 'rv': args.plotkw['uparams'] = post.uparams args.plotkw['status'] = status if 'saveplot' not in args.plotkw: saveto = os.path.join( args.outputdir, conf_base+'_rv_multipanel.pdf' ) else: saveto = args.plotkw['saveplot'] args.plotkw.pop('saveplot') P, _ = radvel.utils.initialize_posterior(config_file) if hasattr(P, 'bjd0'): args.plotkw['epoch'] = P.bjd0 if args.gp or isinstance(post.likelihood, GPLikelihood): GPPlot = orbit_plots.GPMultipanelPlot( post, saveplot=saveto, **args.plotkw ) GPPlot.plot_multipanel() else: RVPlot = orbit_plots.MultipanelPlot( post, saveplot=saveto, **args.plotkw ) RVPlot.plot_multipanel() # check to make sure that Posterior is not GP, print warning if it is if isinstance(post.likelihood, radvel.likelihood.CompositeLikelihood): like_list = post.likelihood.like_list else: like_list = [post.likelihood] for like in like_list: if isinstance(like, radvel.likelihood.GPLikelihood): print("WARNING: GP Likelihood(s) detected. \ You may want to use the '--gp' flag when making these plots.") break if ptype == 'corner' or ptype == 'auto' or ptype == 'trend': sampler_type = _pick_sampler(args, status) if ptype == 'corner': assert status.getboolean(sampler_type, 'run'), \ "Must run MCMC or nested sampling before making corner plot" else: assert sampler_type == 'mcmc', 'Must run MCMC before making auto or trend plots' autocorr = pd.read_csv(status.get(sampler_type, 'autocorrfile')) chains = pd.read_csv(status.get(sampler_type, 'chainfile')) if ptype == 'auto': saveto = os.path.join(args.outputdir, conf_base+'_auto.pdf') Auto = mcmc_plots.AutoPlot(autocorr, saveplot=saveto) Auto.plot() if ptype == 'corner': saveto = os.path.join(args.outputdir, conf_base+'_corner.pdf') Corner = mcmc_plots.CornerPlot(post, chains, saveplot=saveto) Corner.plot() if ptype == 'trend': nwalkers = status.getint('mcmc', 'nwalkers') nensembles = status.getint('mcmc', 'nensembles') saveto = os.path.join(args.outputdir, conf_base+'_trends.pdf') Trend = mcmc_plots.TrendPlot(post, chains, nwalkers, nensembles, saveto) Trend.plot() if ptype == 'derived': assert status.has_section('derive'), \ "Must run `radvel derive` before plotting derived parameters" P, _ = radvel.utils.initialize_posterior(config_file) chains = pd.read_csv(status.get('derive', 'chainfile')) saveto = os.path.join( args.outputdir, conf_base+'_corner_derived_pars.pdf' ) Derived = mcmc_plots.DerivedPlot(chains, P, saveplot=saveto) Derived.plot() savestate = {'{}_plot'.format(ptype): os.path.relpath(saveto)} save_status(statfile, 'plot', savestate)
[docs] def fit(args: ArgumentParser) -> None: """Perform maximum a posteriori fit Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] print("Performing maximum a posteriori fitting for {}".format(conf_base)) P, post = radvel.utils.initialize_posterior(config_file, decorr=args.decorr) post = radvel.fitting.maxlike_fitting(post, verbose=True) postfile = os.path.join(args.outputdir, '{}_post_obj.pkl'.format(conf_base)) post.writeto(postfile) savestate = {'run': True, 'postfile': os.path.relpath(postfile)} save_status(os.path.join(args.outputdir, '{}_radvel.stat'.format(conf_base)), 'fit', savestate)
[docs] def mcmc(args: ArgumentParser) -> None: """Perform MCMC error analysis Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) if args.save or args.proceed: backend_loc = os.path.join(args.outputdir, conf_base+'_rawchain.h5') else: backend_loc = None status = load_status(statfile) P, post = radvel.utils.initialize_posterior(config_file, decorr=args.decorr) if status.getboolean('fit', 'run'): print("Loading starting positions from previous MAP fit") post = radvel.posterior.load(status.get('fit', 'postfile')) msg1 = ( "Running MCMC for {}, N_walkers = {}, N_steps = {}, N_ensembles = {}, Min Auto Factor = {}, " ).format(conf_base, args.nwalkers, args.nsteps, args.ensembles, args.minAfactor) msg2 = ( "Max Auto Relative-Change = {}, Max G-R = {}, Min Tz = {} ..." ).format(args.maxArchange, args.maxGR, args.minTz) print(msg1 + '\n' + msg2) chains = radvel.mcmc(post, nwalkers=args.nwalkers, nrun=args.nsteps, ensembles=args.ensembles, minAfactor=args.minAfactor, maxArchange=args.maxArchange, burnAfactor=args.burnAfactor, burnGR=args.burnGR, maxGR=args.maxGR, minTz=args.minTz, minsteps=args.minsteps, minpercent=args.minpercent, thin=args.thin, serial=args.serial, save=args.save, savename=backend_loc, proceed=args.proceed, proceedname=backend_loc, headless=args.headless, progress_callback=getattr(args, 'progress_callback', None)) mintz = statevars.mintz maxgr = statevars.maxgr minafactor = statevars.minafactor maxarchange = statevars.maxarchange post, post_summary, chains = sampling_postprocessing(post, chains) print("Saving output files...") saveto = os.path.join(args.outputdir, conf_base+'_post_summary.csv') post_summary.to_csv(saveto, sep=',') postfile = os.path.join(args.outputdir, '{}_post_obj.pkl'.format(conf_base)) post.writeto(postfile) csvfn = os.path.join(args.outputdir, conf_base+'_chains.csv.bz2') chains.to_csv(csvfn, compression='bz2') auto = pd.DataFrame() auto['autosamples'] = statevars.autosamples auto['automin'] = statevars.automin auto['automean'] = statevars.automean auto['automax'] = statevars.automax auto['factor'] = statevars.factor autocorr = os.path.join(args.outputdir, conf_base+'_autocorr.csv') auto.to_csv(autocorr, sep=',') savestate = {'run': True, 'postfile': os.path.relpath(postfile), 'chainfile': os.path.relpath(csvfn), 'autocorrfile': os.path.relpath(autocorr), 'summaryfile': os.path.relpath(saveto), 'nwalkers': statevars.nwalkers, 'nensembles': args.ensembles, 'maxsteps': args.nsteps*statevars.nwalkers*args.ensembles, 'nsteps': statevars.ncomplete, 'nburn': statevars.nburn, 'minafactor': minafactor, 'maxarchange': maxarchange, 'minTz': mintz, 'maxGR': maxgr} save_status(statfile, 'mcmc', savestate) statevars.reset()
def sampling_postprocessing( post: Posterior, chains: pd.DataFrame ) -> tuple[Posterior, pd.DataFrame, pd.DataFrame]: # Convert chains into synth basis synthchains = chains.copy() for par in post.params.keys(): if not post.vector.vector[post.vector.indices[par]][1]: synthchains[par] = post.vector.vector[post.vector.indices[par]][0] synthchains = post.params.basis.to_synth(synthchains) synth_quantile = synthchains.quantile([0.159, 0.5, 0.841]) # Get quantiles and update posterior object to median # values returned by MCMC chains post_summary = chains.quantile([0.159, 0.5, 0.841]) for k in chains.columns: if k in post.params.keys(): post.vector.vector[post.vector.indices[k]][0] = post_summary[k][0.5] post.vector.vector_to_dict() print("Performing post-sampling maximum likelihood fit...") post = radvel.fitting.maxlike_fitting(post, verbose=False) final_logprob = post.logprob() final_residuals = post.likelihood.residuals().std() final_chisq = np.sum(post.likelihood.residuals()**2 / (post.likelihood.errorbars()**2)) deg_of_freedom = len(post.likelihood.y) - len(post.likelihood.get_vary_params()) final_chisq_reduced = final_chisq / deg_of_freedom post.vector.vector_to_dict() synthparams = post.params.basis.to_synth(post.params) print("Calculating uncertainties...") post.uparams = {} post.medparams = {} post.maxparams = {} for par in synthparams.keys(): maxlike = synthparams[par].value med = synth_quantile[par][0.5] high = synth_quantile[par][0.841] - med low = med - synth_quantile[par][0.159] err = np.mean([high, low]) if maxlike == -np.inf and med == -np.inf and np.isnan(low) and np.isnan(high): err = 0.0 else: err = radvel.utils.round_sig(err) if err > 0.0: med, err, errhigh = radvel.utils.sigfig(med, err) maxlike, err, errhigh = radvel.utils.sigfig(maxlike, err) post.uparams[par] = err post.medparams[par] = med post.maxparams[par] = maxlike print("Final loglikelihood = %f" % final_logprob) print("Final RMS = %f" % final_residuals) print("Final reduced chi-square = {}".format(final_chisq_reduced)) print("Best-fit parameters:") print(post) return post, post_summary, chains def _cast_str_arg(arg: str) -> int | float | bool | str: try: return int(arg) except ValueError: pass try: return float(arg) except ValueError: pass if arg.lower() == "true": return True elif arg.lower() == "false": return False else: return arg def _process_kwargs_str(kwargs_str: str | None) -> dict: if kwargs_str is None: return {} kwargs_dict = {} pairs_str = kwargs_str.split(" ") for pair in pairs_str: key, value = pair.split("=") kwargs_dict[key] = _cast_str_arg(value) return kwargs_dict
[docs] def nested_sampling(args: ArgumentParser) -> None: """Perform nested sampling Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) run_kwargs = _process_kwargs_str(args.run_kwargs) sampler_kwargs = _process_kwargs_str(args.sampler_kwargs) backend_loc = os.path.join(args.outputdir, conf_base+'_ns') status = load_status(statfile) P, post = radvel.utils.initialize_posterior(config_file, decorr=args.decorr) print(f'Running nested sampling backend {args.sampler}') results = radvel.nested_sampling.run( post, output_dir=backend_loc, proceed=args.proceed, overwrite=args.overwrite, sampler=args.sampler, run_kwargs=run_kwargs, sampler_kwargs=sampler_kwargs ) chains = results["samples"] post, post_summary, chains = sampling_postprocessing(post, chains) print("Saving output files...") saveto = os.path.join(args.outputdir, conf_base+'_post_summary_ns.csv') post_summary.to_csv(saveto, sep=',') postfile = os.path.join(args.outputdir, '{}_post_obj_ns.pkl'.format(conf_base)) post.writeto(postfile) csvfn = os.path.join(args.outputdir, conf_base+'_chains_ns.csv.bz2') chains.to_csv(csvfn, compression='bz2') savestate = {'run': True, 'postfile': os.path.relpath(postfile), 'chainfile': os.path.relpath(csvfn), 'ns_outdir': os.path.relpath(backend_loc), 'summaryfile': os.path.relpath(saveto), } savestate = {**savestate, **sampler_kwargs, **run_kwargs} save_status(statfile, 'ns', savestate)
[docs] def ic_compare(args: ArgumentParser) -> None: """Compare different models and comparative statistics including AIC and BIC statistics. Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) status = load_status(statfile) P, post = radvel.utils.initialize_posterior(config_file, decorr=args.decorr) assert status.getboolean('fit', 'run'), \ "Must perform max-liklihood fit before running Information Criteria comparisons" post = radvel.posterior.load(status.get('fit', 'postfile')) choices = ['nplanets', 'e', 'trend', 'jit', 'gp'] statsdictlist = [] paramlist = [] compareparams = args.type ipost = copy.deepcopy(post) if args.simple: statsdictlist += radvel.fitting.model_comp(ipost, params=[], verbose=args.verbose) else: if hasattr(args, 'fixjitter') and args.fixjitter: for param in ipost.params: if len(param) >= 3 and param[0:3] == 'jit': ipost.params[param].vary = False for compareparam in compareparams: assert compareparam in choices, \ "Valid parameter choices for 'ic -t' are combinations of: "\ + " ".join(choices) paramlist.append(compareparam) if hasattr(args, 'mixed') and not args.mixed: statsdictlist += radvel.fitting.model_comp(ipost, params=[compareparam], verbose=args.verbose) if hasattr(args, 'mixed') and not args.mixed: new_statsdictlist = [] for dicti in statsdictlist: anymatch = False for seendict in new_statsdictlist: if collections.Counter(dicti['Free Params'][0]) == \ collections.Counter(seendict['Free Params'][0]): anymatch = True continue if not anymatch: new_statsdictlist.append(dicti) statsdictlist = new_statsdictlist if not hasattr(args, 'mixed') or (hasattr(args, 'mixed') and args.mixed): statsdictlist += radvel.fitting.model_comp(ipost, params=paramlist, verbose=args.verbose) savestate = {'ic': statsdictlist} save_status(statfile, 'ic_compare', savestate)
[docs] def tables(args: ArgumentParser) -> None: """Generate TeX code for tables in summary report Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) status = load_status(statfile) P, post = radvel.utils.initialize_posterior(config_file) sampler_type = _pick_sampler(args, status) if sampler_type == 'mcmc': minafactor = status.get('mcmc', 'minafactor') maxarchange = status.get('mcmc', 'maxarchange') maxgr = status.get('mcmc', 'maxgr') mintz = status.get('mcmc', 'mintz') elif sampler_type == 'ns': minafactor = maxarchange = maxgr = mintz = None else: raise ValueError( "Got an unexpected sampler_type {}. Please report this error on GitHub".format( sampler_type ) ) chains = pd.read_csv(status.get(sampler_type, 'chainfile')) post = radvel.posterior.load(status.get(sampler_type, 'postfile')) if 'derive' in status.sections() and status.getboolean('derive', 'run'): dchains = pd.read_csv(status.get('derive', 'chainfile')) chains = chains.join(dchains, rsuffix='_derived') derived = True else: derived = False report = radvel.report.RadvelReport(P, post, chains, minafactor, maxarchange, maxgr, mintz, derived=derived) tabletex = radvel.report.TexTable(report) attrdict = {'priors': 'tab_prior_summary', 'rv': 'tab_rv', 'params': 'tab_params', 'derived': 'tab_derived', 'crit': 'tab_crit'} for tabtype in args.type: print("Generating LaTeX code for {} table".format(tabtype)) if tabtype == 'ic_compare': assert status.has_option('ic_compare', 'ic'), \ "Must run Information Criteria comparison before making comparison tables" if 'ic_compare' in status.keys(): status['ic_compare']['ic'] = status['ic_compare']['ic'].replace('-inf', '-np.inf') compstats = eval(status.get('ic_compare', 'ic')) report = radvel.report.RadvelReport( P, post, chains, minafactor, maxarchange, maxgr, mintz, compstats=compstats ) tabletex = radvel.report.TexTable(report) tex = tabletex.tab_comparison() elif tabtype == 'rv': tex = getattr(tabletex, attrdict[tabtype])(name_in_title=args.name_in_title, max_lines=None) elif tabtype == 'crit': tex = getattr(tabletex, attrdict[tabtype])(name_in_title=args.name_in_title) else: if tabtype == 'derived': assert status.has_option('derive', 'run'), \ "Must run `radvel derive` before making derived parameter table" assert tabtype in attrdict, 'Invalid Table Type %s ' % tabtype tex = getattr(tabletex, attrdict[tabtype])(name_in_title=args.name_in_title) saveto = os.path.join( args.outputdir, '{}_{}.tex'.format(conf_base, tabtype) ) with open(saveto, 'w+') as f: f.write(tex) savestate = {'{}_tex'.format(tabtype): os.path.relpath(saveto)} save_status(statfile, 'table', savestate)
def _pick_sampler(args: ArgumentParser, status: configparser.RawConfigParser) -> str: has_mcmc = status.has_section('mcmc') and status.getboolean('mcmc', 'run') has_ns = status.has_section('ns') and status.getboolean('ns', 'run') assert has_mcmc or has_ns, "Must run MCMC or nested sampling before making tables or deriving parameters" if args.sampler == 'auto': if has_mcmc: sampler_type = 'mcmc' elif has_ns: sampler_type = 'ns' else: raise ValueError('No sampling results found. Run MCMC or nested sampling first') elif args.sampler == 'mcmc': if not has_mcmc: raise ValueError('No MCMC results found. Run MCMC or use NS results with --sampler=ns') sampler_type = 'mcmc' elif args.sampler == 'ns': if not has_ns: raise ValueError('No NS results found. Run NS or use MCMC results with --sampler=mcmc') sampler_type = 'ns' else: raise ValueError('Invalid sampler {}. Must be one of "auto", "mcmc" or "ns"'.format(args.sampler)) return sampler_type
[docs] def derive(args: ArgumentParser) -> None: """Derive physical parameters from posterior samples Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) status = load_status(statfile) msg = "Multiplying mcmc or nested sampling chains by physical parameters for {}".format( conf_base ) print(msg) sampler_type = _pick_sampler(args, status) P, post = radvel.utils.initialize_posterior(config_file) postfile = status.get(sampler_type, 'postfile') post = radvel.posterior.load(postfile) chains = pd.read_csv(status.get(sampler_type, 'chainfile')) try: mstar = np.random.normal( loc=P.stellar['mstar'], scale=P.stellar['mstar_err'], size=len(chains) ) except AttributeError: print("Unable to calculate derived parameters, stellar parameters not defined the config file.") return if (mstar <= 0.0).any(): num_nan = np.sum(mstar <= 0.0) nan_perc = float(num_nan) / len(chains) mstar[mstar <= 0] = np.abs(mstar[mstar <= 0]) print("WARNING: {} ({:.2f} %) of Msini samples are NaN. The stellar mass posterior may contain negative \ values. Interpret posterior with caution.".format(num_nan, nan_perc)) # Convert chains into synth basis synthchains = chains.copy() for par in post.params.keys(): if not post.params[par].vary: synthchains[par] = post.params[par].value synthchains = post.params.basis.to_synth(synthchains) savestate = {'run': True} outcols = [] for i in np.arange(1, P.nplanets + 1, 1): # Grab parameters from the chain def _has_col(key: str) -> bool: cols = list(synthchains.columns) return cols.count('{}{}'.format(key, i)) == 1 def _get_param(key: str) -> float: if _has_col(key): return synthchains['{}{}'.format(key, i)] else: return P.params['{}{}'.format(key, i)].value def _set_param(key: str, value: float): chains['{}{}'.format(key, i)] = value def _get_colname(key: str) -> str: return '{}{}'.format(key, i) per = _get_param('per') k = _get_param('k') e = _get_param('e') mpsini = radvel.utils.Msini(k, per, mstar, e, Msini_units='earth') _set_param('mpsini', mpsini) outcols.append(_get_colname('mpsini')) mtotal = mstar + (mpsini * c.M_earth.value) / c.M_sun.value # get total star plus planet mass a = radvel.utils.semi_major_axis(per, mtotal) # changed from mstar to mtotal _set_param('a', a) outcols.append(_get_colname('a')) musini = (mpsini * c.M_earth.value) / (mstar * c.M_sun.value) _set_param('musini', musini) outcols.append(_get_colname('musini')) try: rp = np.random.normal( loc=P.planet['rp{}'.format(i)], scale=P.planet['rp_err{}'.format(i)], size=len(chains) ) _set_param('rp', rp) _set_param('rhop', radvel.utils.density(mpsini, rp)) outcols.append(_get_colname('rhop')) except (AttributeError, KeyError): pass # Get quantiles and update posterior object to median # values returned by MCMC chains quantiles = chains.quantile([0.159, 0.5, 0.841]) csvfn = os.path.join(args.outputdir, conf_base+'_derived_quantiles.csv') quantiles.to_csv(csvfn, columns=outcols) # saved derived paramters to posterior file post.derived = quantiles[outcols] post.writeto(postfile) savestate['quantfile'] = os.path.relpath(csvfn) csvfn = os.path.join(args.outputdir, conf_base+'_derived.csv.bz2') chains.to_csv(csvfn, columns=outcols, compression='bz2') savestate['chainfile'] = os.path.relpath(csvfn) print("Derived parameters:", outcols) save_status(statfile, 'derive', savestate)
[docs] def report(args: ArgumentParser) -> None: """Generate summary report Args: args (ArgumentParser): command line arguments """ config_file = args.setupfn conf_base = os.path.basename(config_file).split('.')[0] print("Assembling report for {}".format(conf_base)) statfile = os.path.join(args.outputdir, "{}_radvel.stat".format(conf_base)) status = load_status(statfile) if 'ic_compare' in status.keys(): status['ic_compare']['ic'] = status['ic_compare']['ic'].replace('-inf', '-np.inf') sampler_type = _pick_sampler(args, status) P, post = radvel.utils.initialize_posterior(config_file) if sampler_type == 'mcmc': minafactor = status.get('mcmc', 'minafactor') maxarchange = status.get('mcmc', 'maxarchange') maxgr = status.get('mcmc', 'maxgr') mintz = status.get('mcmc', 'mintz') elif sampler_type == 'ns': minafactor = maxarchange = maxgr = mintz = None else: raise ValueError( "Got an unexpected sampler_type {}. Please report this error on GitHub".format( sampler_type ) ) chains = pd.read_csv(status.get(sampler_type, 'chainfile')) post = radvel.posterior.load(status.get(sampler_type, 'postfile')) if 'derive' in status.sections() and status.getboolean('derive', 'run'): dchains = pd.read_csv(status.get('derive', 'chainfile')) chains = chains.join(dchains, rsuffix='_derived') derived = True else: derived = False try: compstats = eval(status.get('ic_compare', 'ic')) except (KeyError, NoSectionError): print("WARNING: Could not find {} model comparison \ in {}.\nPlease make sure that you have run `radvel ic -t {}` (or, e.g., `radvel \ ic -t nplanets e trend jit gp`)\ \nif you would like to include the model comparison table in the \ report.".format(args.comptype, statfile, args.comptype)) compstats = None report = radvel.report.RadvelReport(P, post, chains, minafactor, maxarchange, maxgr, mintz, compstats=compstats, derived=derived) report.runname = conf_base report_depfiles = [] for ptype, pfile in status.items('plot'): report_depfiles.append(pfile) with radvel.utils.working_directory(args.outputdir): rfile = os.path.join(conf_base+"_results.pdf") report_depfiles = [os.path.basename(p) for p in report_depfiles] report.compile( rfile, depfiles=report_depfiles, latex_compiler=args.latex_compiler )
[docs] def save_status(statfile: str, section: str, statevars: dict) -> None: """Save pipeline status Args: statfile (string): name of output file section (string): name of section to write statevars (dict): dictionary of all options to populate the specified section """ config = configparser.RawConfigParser() if os.path.isfile(statfile): config.read(statfile) if not config.has_section(section): config.add_section(section) for key, val in statevars.items(): config.set(section, key, val) with open(statfile, 'w') as f: config.write(f)
[docs] def load_status(statfile: str) -> configparser.RawConfigParser: """Load pipeline status Args: statfile (string): name of configparser file Returns: configparser.RawConfigParser """ config = configparser.RawConfigParser() gl = config.read(statfile) return config