ACID-code 2.0.0a2__py3-none-any.whl → 2.0.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ACID_code/acid.py +76 -33
- ACID_code/data.py +161 -93
- ACID_code/lsd.py +33 -17
- ACID_code/mcmc.py +96 -11
- ACID_code/result.py +79 -27
- ACID_code/utils.py +9 -5
- {acid_code-2.0.0a2.dist-info → acid_code-2.0.0a4.dist-info}/METADATA +1 -1
- acid_code-2.0.0a4.dist-info/RECORD +15 -0
- acid_code-2.0.0a2.dist-info/RECORD +0 -15
- {acid_code-2.0.0a2.dist-info → acid_code-2.0.0a4.dist-info}/WHEEL +0 -0
- {acid_code-2.0.0a2.dist-info → acid_code-2.0.0a4.dist-info}/licenses/LICENSE +0 -0
- {acid_code-2.0.0a2.dist-info → acid_code-2.0.0a4.dist-info}/top_level.txt +0 -0
ACID_code/acid.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
import traceback
|
|
2
3
|
import warnings
|
|
3
4
|
warnings.filterwarnings("ignore")
|
|
4
5
|
import sys, emcee, os, time, inspect, inspect, contextlib
|
|
@@ -15,6 +16,10 @@ from .result import Result
|
|
|
15
16
|
from .data import Data, Config, MaskingLines, LineList, DataList
|
|
16
17
|
from .errors import ContinuumError
|
|
17
18
|
from .utils import IntLike, Scalar, Array1D, Array2D
|
|
19
|
+
try:
|
|
20
|
+
import dynesty
|
|
21
|
+
except ImportError:
|
|
22
|
+
dynesty = None
|
|
18
23
|
|
|
19
24
|
@beartype
|
|
20
25
|
class Acid:
|
|
@@ -55,9 +60,9 @@ class Acid:
|
|
|
55
60
|
|
|
56
61
|
Important note: All defaults in the signature are None, meaning if any values are input, they will override the default :py:class:`Config` and/or :py:class:`Data` values or
|
|
57
62
|
any values that have already been input. The defaults within the config are written below. The config defaults can also be accessed via
|
|
58
|
-
:py:attr:`ACID_code.Config.defaults` (returning a dictionary of defaults for both initialisation and
|
|
63
|
+
:py:attr:`ACID_code.Config.defaults` (returning a dictionary of defaults for both initialisation and the ACID method).
|
|
59
64
|
|
|
60
|
-
All parameters below and in
|
|
65
|
+
All parameters below and in the ACID method are stored in the :py:class:`Config` instance, unless explicitly stated to be in the :py:class:`Data` instance.
|
|
61
66
|
The :py:class:`Config` instance is for runtime settings and the :py:class:`Data` instance is for storing data and any calculations.
|
|
62
67
|
|
|
63
68
|
Parameters
|
|
@@ -68,12 +73,11 @@ class Acid:
|
|
|
68
73
|
choose your own velocity grid, by default None, stored in the Data instance.
|
|
69
74
|
linelist : :py:type:`Array2D | str` | :py:class:`LineList` | dict`, optional
|
|
70
75
|
The linelist to use for LSD. The linelist should have wavelengths in angstroms and relative depths between 0 and 1.
|
|
71
|
-
This is a required parameter
|
|
76
|
+
This is a required parameter. It can be of the forms:
|
|
72
77
|
- String: A path to a VALD linelist in string format. Support for other linelists may be added in the future or on request.
|
|
73
78
|
- :py:type:`Array2D`: A 2D array-like object indexed such that 0 is wavelengths and 1 is depths.
|
|
74
79
|
- dict: A dictionary with keys "wavelengths" and "depths", each containing array-like objects for the wavelengths and depths respectively.
|
|
75
80
|
- :py:class:`LineList`: The :py:class:`LineList` class is used to expose the linelist for masking or getting/plotting the linelist. You can input an instance if you have one.
|
|
76
|
-
- If None, linelist_wl and linelist_depths must be provided (see below), by default None, stored in the Data instance.
|
|
77
81
|
order : :py:type:`IntLike`, optional
|
|
78
82
|
If this ACID instance is intended as a run on a specific order, then you can designate this instance for that order. This will allow
|
|
79
83
|
the resulting Data instance to track of which order the profiles correspond to. Note that orders can be indexed by the correct indexing
|
|
@@ -96,7 +100,7 @@ class Acid:
|
|
|
96
100
|
By default 2 (medium).
|
|
97
101
|
sampler_progress : :py:type:`bool`, optional
|
|
98
102
|
A verbosity override for just the MCMC sampling progress.
|
|
99
|
-
By default None which does not override, but if True/False, it will overwrite with that value.
|
|
103
|
+
By default None which does not override, but if True/False, it will overwrite with that value, and use/don't use a tqdm output for the sampler.
|
|
100
104
|
masking_lines : :py:type:`dict` | :py:class:`MaskingLines`, optional
|
|
101
105
|
Telluric lines (in angstroms) and widths in (km/s) to mask from the wavelength regions from. Unless you'd like to change the default masking
|
|
102
106
|
lines, we recommend just using the defaults (leaving this as None), which are based on telluric lines and strong hydrogen/metal lines in the
|
|
@@ -112,7 +116,7 @@ class Acid:
|
|
|
112
116
|
The path to save the sampler HDF5 backend file to.
|
|
113
117
|
If None, the sampler is not saved and only stored in memory. By default None.
|
|
114
118
|
Note that if your path points to an existing file, it will be overwritten on Acid initialization.
|
|
115
|
-
If
|
|
119
|
+
If existing, we use the emcee HDF5 backend to store and load the sampler.
|
|
116
120
|
Should be a valid file path that ends with ".h5". If the directory containing it does not exist, it will be created.
|
|
117
121
|
Note that if you later try and save the sampler through the data class, it is converted to a HD5 backend.
|
|
118
122
|
data : :py:class:`Data` | :py:class:`DataList`, optional
|
|
@@ -231,6 +235,8 @@ class Acid:
|
|
|
231
235
|
dev_perc : IntLike|None = None, # Config
|
|
232
236
|
n_sig : IntLike|None = None, # Config
|
|
233
237
|
skips : IntLike|None = None, # Config
|
|
238
|
+
od : bool|None = None, # Config
|
|
239
|
+
sampler_type : str|None = None, # Config
|
|
234
240
|
parallel : bool|None = None, # Config
|
|
235
241
|
cores : IntLike|None = None, # Config
|
|
236
242
|
nwalkers : IntLike|None = None, # Config, then Data just before MCMC
|
|
@@ -308,6 +314,14 @@ class Acid:
|
|
|
308
314
|
skips : :py:type:`IntLike`, optional
|
|
309
315
|
An option to only run acid on one in every n pixels, where n is the integer argument. This is only useful for
|
|
310
316
|
testing to get a quicker result especially for larger wavelength ranges or datasets, by default 1 (no skipping)
|
|
317
|
+
od : :py:type:`bool`, optional
|
|
318
|
+
If True, runs ACID in optical depth, otherwise, the LSD methods and ACID fitting is performed in flux. By default None which defaults to True.
|
|
319
|
+
Note that the whole point of ACID is to run LSD in OD, we highly recommend leaving this unless you specifically want to compare.
|
|
320
|
+
sampler_type : :py:type:`str`, optional
|
|
321
|
+
If you really try to wish to use the dynesty nested sampler, you can set this to "dynesty". It is almost entirely unsupported
|
|
322
|
+
by the rest of the code other than to just get a finished result object, and much slower. We highly recommend using None or "emcee" (default).
|
|
323
|
+
The only reason I added this was to get the Bayesian evidence for model comparison.
|
|
324
|
+
If "dynesty" is chosen, the dynesty package needs to be installed, and the nsteps parameter is treated as "nlive" to be passed to the NestedSampler.
|
|
311
325
|
parallel : :py:type:`bool`, optional
|
|
312
326
|
If True uses multiprocessing to calculate the profiles for each frame in parallel, see
|
|
313
327
|
https://acid-code.readthedocs.io/en/stable/using_ACID.html#multiprocessing for more details. By default True
|
|
@@ -411,6 +425,8 @@ class Acid:
|
|
|
411
425
|
"dev_perc" : dev_perc,
|
|
412
426
|
"n_sig" : n_sig,
|
|
413
427
|
"skips" : skips,
|
|
428
|
+
"od" : od,
|
|
429
|
+
"sampler_type" : sampler_type,
|
|
414
430
|
"parallel" : parallel,
|
|
415
431
|
"cores" : cores,
|
|
416
432
|
"nwalkers" : nwalkers,
|
|
@@ -435,6 +451,14 @@ class Acid:
|
|
|
435
451
|
print("Parallel MCMC on Windows is not currently supported. Running MCMC serially.")
|
|
436
452
|
self.config.parallel = False
|
|
437
453
|
|
|
454
|
+
if self.config.sampler_type == "dynesty":
|
|
455
|
+
if dynesty is None:
|
|
456
|
+
raise ImportError("The 'dynesty' sampler requires the 'dynesty' package to be installed.\nPlease install it with 'pip install dynesty' or choose a different sampler type.")
|
|
457
|
+
if self.config.sampler_type == "dynesty" and not self.config.deterministic_profile:
|
|
458
|
+
raise ValueError("The 'dynesty' sampler can only be run with deterministic_profile=True (otherwise you'll be waiting hours for a single result)")
|
|
459
|
+
if self.config.sampler_type == "dynesty" and self.config.max_steps is not None:
|
|
460
|
+
raise ValueError("Cannot use max_steps as dynesty already natively supports this with live points, set nsteps=nlive. See the dynesty docs for more details.")
|
|
461
|
+
|
|
438
462
|
# --- Start of the ACID method ---
|
|
439
463
|
|
|
440
464
|
# Setup and data validation done in data class and applies skips
|
|
@@ -484,8 +508,9 @@ class Acid:
|
|
|
484
508
|
# The code for telluric masking is contained without the MaskingLines class, which both telluric_lines
|
|
485
509
|
# and hydrogen_lines are instances of.
|
|
486
510
|
line_mask = self.config.masking_lines.get_masks(self.data.wavelengths["combined"])
|
|
487
|
-
line_mask
|
|
488
|
-
|
|
511
|
+
if line_mask != []:
|
|
512
|
+
line_mask = np.all(line_mask, axis=0)
|
|
513
|
+
self.data.errors["combined"][line_mask] = 1e12
|
|
489
514
|
|
|
490
515
|
# Get the initial polynomial coefficents
|
|
491
516
|
if not hasattr(self.data.wavelengths, "combined_normalized"):
|
|
@@ -558,22 +583,25 @@ class Acid:
|
|
|
558
583
|
self.data.nwalkers = self.data.ndim * 3 if self.config.nwalkers is None else self.config.nwalkers
|
|
559
584
|
rng = np.random.default_rng(self.config.seed)
|
|
560
585
|
|
|
561
|
-
# Starting values of walkers with independent variation
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if
|
|
567
|
-
|
|
586
|
+
# Starting values of walkers with independent variation#
|
|
587
|
+
if self.config.sampler_type == "emcee":
|
|
588
|
+
sigma = 0.8 * 0.005
|
|
589
|
+
initial_state = []
|
|
590
|
+
for i in range(0, len(self.data.model_inputs)):
|
|
591
|
+
if i < len(self.data.velocities):
|
|
592
|
+
if not self.config.deterministic_profile:
|
|
593
|
+
pos = rng.normal(self.data.model_inputs[i], sigma, (self.data.nwalkers, ))
|
|
594
|
+
else:
|
|
595
|
+
continue
|
|
568
596
|
else:
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
597
|
+
x1 = self.data.model_inputs[i]
|
|
598
|
+
rounded_sigma = round(x1, 1-int(floor(log10(abs(x1))))-1)
|
|
599
|
+
sigma = abs(rounded_sigma) / 10
|
|
600
|
+
pos = rng.normal(self.data.model_inputs[i], sigma, (self.data.nwalkers, ))
|
|
601
|
+
initial_state.append(pos)
|
|
602
|
+
initial_state = np.array(initial_state).T
|
|
603
|
+
else:
|
|
604
|
+
initial_state = None
|
|
577
605
|
|
|
578
606
|
### ACID initialialised ###
|
|
579
607
|
self.data.setup_time += time.time() - init_t0
|
|
@@ -617,12 +645,12 @@ class Acid:
|
|
|
617
645
|
"""
|
|
618
646
|
This method is no longer supported in ACID. Please use the ACID function with the appropriate inputs for HARPS spectra instead.
|
|
619
647
|
Future versions of ACID will provide functions to load and configure data from a range of different standard instruments.
|
|
620
|
-
If you still really wish to use ACID_HARPS, the last stable version of ACID with the method is 1.4.5. Try: pip install
|
|
648
|
+
If you still really wish to use ACID_HARPS, the last stable version of ACID with the method is 1.4.5. Try: pip install ACID_code_v2==1.4.5
|
|
621
649
|
"""
|
|
622
650
|
raise NotImplementedError(f"ACID_HARPS is no longer supported in ACID. \n"
|
|
623
651
|
f"Please use the ACID function with the appropriate inputs for HARPS spectra instead. \n"
|
|
624
652
|
f"Future versions of ACID will provide functions to load and configure data from a range of different standard instruments. \n"
|
|
625
|
-
f"If you still really wish to use ACID_HARPS, the last stable version of ACID with the method is 1.4.5. Try: pip install
|
|
653
|
+
f"If you still really wish to use ACID_HARPS, the last stable version of ACID with the method is 1.4.5. Try: pip install ACID_code_v2==1.4.5")
|
|
626
654
|
|
|
627
655
|
def combine_spec(
|
|
628
656
|
self,
|
|
@@ -834,9 +862,12 @@ class Acid:
|
|
|
834
862
|
self.data.plot_continuum_fit(plot_type=plot_type)
|
|
835
863
|
|
|
836
864
|
if np.any(flux_obs <= 0) or np.any(new_errors <= 0):
|
|
837
|
-
|
|
865
|
+
error = ContinuumError("Continuum fit resulted in non-positive flux or errors, which is not physical.\n " \
|
|
838
866
|
"Consider adjusting the polynomial order or continuum percentile. Use verbose=3 to see the plot of the continuum fit.\n " \
|
|
839
867
|
"Note that this will only work for interactive terminals or displays which work with plt.show()")
|
|
868
|
+
self.data.exception = error
|
|
869
|
+
self.data.traceback = traceback.format_stack()
|
|
870
|
+
raise error
|
|
840
871
|
|
|
841
872
|
return poly_coeffs, flux_obs, new_errors
|
|
842
873
|
|
|
@@ -856,7 +887,7 @@ class Acid:
|
|
|
856
887
|
sn = self.data.sn["combined"]
|
|
857
888
|
|
|
858
889
|
# Use the initial LSD run to get the forward model and scaled residuals
|
|
859
|
-
forward, _profile = mcmc.MCMC(x, y, yerr, self.data.alpha).full_model(self.data.model_inputs)
|
|
890
|
+
forward, _profile = mcmc.MCMC(x, y, yerr, self.data.alpha, od=self.config.od).full_model(self.data.model_inputs)
|
|
860
891
|
residuals = (y - forward) / forward
|
|
861
892
|
|
|
862
893
|
# Chunk masking based on deviation from residuals
|
|
@@ -953,7 +984,8 @@ class Acid:
|
|
|
953
984
|
"""
|
|
954
985
|
|
|
955
986
|
# Get default sampler kwargs from initial state
|
|
956
|
-
|
|
987
|
+
if self.config.sampler_type == "emcee":
|
|
988
|
+
sampler_kwargs, mcmc_kwargs = self._get_sampler_kwargs(nsteps, state)
|
|
957
989
|
pool_context = nullcontext(None)
|
|
958
990
|
|
|
959
991
|
if self.config.parallel:
|
|
@@ -964,14 +996,25 @@ class Acid:
|
|
|
964
996
|
|
|
965
997
|
ctx = mp.get_context("fork")
|
|
966
998
|
pool_context = ctx.Pool(processes=self.config.cores, initializer=mcmc._mp_init_worker, initargs=(self.data,))
|
|
967
|
-
|
|
999
|
+
log_prob = mcmc._mp_log_probability if self.config.sampler_type == "emcee" else mcmc._mp_log_likelihood
|
|
1000
|
+
ptform = mcmc._mp_ptform
|
|
1001
|
+
queue_size = os.cpu_count()
|
|
968
1002
|
else:
|
|
969
1003
|
MCMC = mcmc.MCMC(self.data)
|
|
970
|
-
|
|
971
|
-
|
|
1004
|
+
log_prob = MCMC if self.config.sampler_type == "emcee" else MCMC.dynesty_logprob
|
|
1005
|
+
ptform = MCMC.ptform
|
|
1006
|
+
queue_size = None
|
|
1007
|
+
|
|
972
1008
|
with pool_context as pool:
|
|
973
|
-
self.
|
|
974
|
-
|
|
1009
|
+
if self.config.sampler_type == "emcee":
|
|
1010
|
+
self.sampler = EnsembleSampler(log_prob_fn=log_prob, pool=pool, **sampler_kwargs)
|
|
1011
|
+
self.sampler.run_mcmc(**mcmc_kwargs)
|
|
1012
|
+
else:
|
|
1013
|
+
import dynesty
|
|
1014
|
+
if self.config.parallel:
|
|
1015
|
+
pool.size = self.config.cores
|
|
1016
|
+
self.sampler = dynesty.NestedSampler(log_prob, ptform, self.data.ndim, self.config.nsteps, pool=pool, queue_size=queue_size)
|
|
1017
|
+
self.sampler.run_nested(print_progress=self.config.verbose>1)
|
|
975
1018
|
|
|
976
1019
|
def run_mcmc_until_converged(self, max_steps:IntLike, state=None) -> None:
|
|
977
1020
|
"""
|