ACID-code 1.5.0a3__py3-none-any.whl → 1.5.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/data.py CHANGED
@@ -581,6 +581,10 @@ class Data:
581
581
  # Make any values < 0 or infinite equal to nan, which are gracefully later handled.
582
582
  input_wavelengths, input_flux, input_errors = utils.mask_invalid(input_wavelengths, input_flux, input_errors, verbose=self.config.verbose)
583
583
 
584
+ # Check that none of the inputs are all nan
585
+ if np.all(np.isnan(input_wavelengths)) or np.all(np.isnan(input_flux)) or np.all(np.isnan(input_errors)):
586
+ raise ValueError("Any of the input wavelengths, spectra, and errors cannot be all NaN.")
587
+
584
588
  # Guess sn if input_sn not provided
585
589
  if input_sn is None:
586
590
  input_sn = utils.guess_SNR(input_wavelengths, input_flux, input_errors)
@@ -767,8 +771,10 @@ class DataList:
767
771
  data_list = [data_list]
768
772
 
769
773
  if verbose is not None:
770
- self.data[0].config.verbose = verbose # verbose in config is a property and has good validation
771
- self.verbose = self.data[0].config.verbose
774
+ old_verbose = np.copy(data_list[0].config.verbose)
775
+ data_list[0].config.verbose = verbose # verbose in config is a property and has good validation
776
+ self.verbose = np.copy(data_list[0].config.verbose)
777
+ data_list[0].config.verbose = old_verbose # reset to old verbose
772
778
  else:
773
779
  self.verbose = np.max([data.config.verbose for data in data_list])
774
780
 
@@ -838,6 +844,82 @@ class DataList:
838
844
  if len(np.unique(self.orders)) != len(self.orders):
839
845
  raise ValueError("All Data instances within the inputted list must have unique order numbers.")
840
846
 
847
+ def run_ACID(
848
+ self,
849
+ orders : Array1D|int|str|None = None,
850
+ store_sampler : bool = True
851
+ ) -> None:
852
+ from .acid import Acid
853
+
854
+ if isinstance(orders, int):
855
+ orders = [orders]
856
+ elif isinstance(orders, str):
857
+ if orders == "all":
858
+ orders = self.orders
859
+ else:
860
+ raise ValueError("If orders is a string, it must be 'all' to run ACID on all orders. Got: {orders!r}")
861
+ elif orders is None:
862
+ orders = self.orders
863
+ elif isinstance(orders, list):
864
+ if not all(isinstance(o, int) for o in orders):
865
+ raise ValueError("If orders is a list, all elements must be integers. Got: {orders!r}")
866
+ if not all(o in self.orders for o in orders):
867
+ raise ValueError("All orders in the input list must be in the DataList. Got: {orders!r}, but available orders are: {self.orders!r}")
868
+ else:
869
+ raise ValueError("orders must be an int, a list of ints, 'all', or None. Got: {orders!r}")
870
+
871
+ for order in orders:
872
+ result = Acid(data=self.data_list[self.o2i[order]]).ACID()
873
+ if self.save_dir is not None:
874
+ results_dir = os.path.join(self.save_dir, "results")
875
+ os.makedirs(results_dir, exist_ok=True)
876
+ save_path = os.path.join(results_dir, f"order_{order}.pkl")
877
+ result.save(save_path, store_sampler=store_sampler)
878
+ return
879
+
880
+ @classmethod
881
+ def create(
882
+ cls,
883
+ wavelengths : Array2D = None,
884
+ flux : Array2D = None,
885
+ errors : Array2D = None,
886
+ sn : Array1D = None,
887
+ order_range : Array1D|None = None,
888
+ load = None,
889
+ config : Config|None = None,
890
+ linelist : Array2D|None|str|LineList|dict = None,
891
+ velocities : Array1D|None = None,
892
+ save_dir : str|None = None,
893
+ verbose : IntLike|bool|None = None,
894
+ ) -> DataList:
895
+
896
+ if load is not None:
897
+ from .load import Load
898
+ if not isinstance(load, Load):
899
+ raise ValueError("load must be an instance of the Load class. Got: {load!r}")
900
+ wavelengths, flux, errors, sn = load.get_data()
901
+ order_range = load.order_range
902
+
903
+ if order_range is None:
904
+ order_range = np.arange(len(wavelengths))
905
+
906
+ if len(order_range) != len(wavelengths):
907
+ raise ValueError("The length of the order_range must match the number of frames in the input data.")
908
+
909
+ config_dict = config.to_dict() if config is not None else {}
910
+
911
+ datalist = []
912
+ for order in order_range:
913
+ data = Data()
914
+ data.set_inputs(wavelengths[order], flux[order], errors[order], sn[order])
915
+ data.set_linelist(linelist=linelist)
916
+ data.velocities = velocities
917
+ config_dict["order"] = order
918
+ data.config = Config(**config_dict)
919
+ datalist.append(data)
920
+
921
+ return cls(datalist, save_dir=save_dir, verbose=verbose)
922
+
841
923
  def save(self, save_dir:str|None=None):
842
924
  d = {}
843
925
  self.save_dir = save_dir
@@ -864,9 +946,8 @@ class DataList:
864
946
  @save_dir.setter
865
947
  def save_dir(self, dir):
866
948
  if dir is None:
867
- return # do not change save_dir
868
- if not os.path.isdir(dir):
869
- raise ValueError(f"save_dir must be a valid path to a directory to save the DataList, or None to not save to disk. Got: {dir}")
949
+ return None
950
+ os.mkdir(dir, exist_ok=True)
870
951
  self._save_dir = dir
871
952
 
872
953
  class LineList:
ACID_code/load.py CHANGED
@@ -4,7 +4,8 @@ Each function will load a data object that can be directly input into the ACID i
4
4
  """
5
5
  from __future__ import annotations
6
6
  from beartype import beartype
7
- from .data import Data
7
+ import numpy as np
8
+ from .data import Data, DataList
8
9
  from . import utils
9
10
  from .utils import Array2D
10
11
  from astropy.io import fits
@@ -33,9 +34,8 @@ class Load:
33
34
  blaze_profile : Array2D, optional
34
35
  Instead load the blaze profile yourself and input it here. This will override the blaze_file input if
35
36
  both are provided. By default, None
36
-
37
-
38
37
  """
38
+ raise NotImplementedError("This class is still in development and not yet ready for use. Please check back later.")
39
39
 
40
40
  self.file = file
41
41
  self.blaze_file = blaze_file
@@ -86,18 +86,46 @@ class Load:
86
86
  elif blaze_file is not None:
87
87
  pass # TODO: load blaze file and save to self.data.blaze or tailor blaze files to each instrument
88
88
 
89
+ def to_datalist(self, **kwargs) -> DataList:
90
+ datalist = DataList.create(
91
+ wavelengths = self.wavelengths,
92
+ flux = self.flux,
93
+ errors = self.errors,
94
+ sn = self.sn,
95
+ order_range = self.order_range,
96
+ **kwargs,
97
+ )
98
+ return datalist
99
+
89
100
  def HARPS(self):
90
101
  # La Silla HARPS spectra
91
102
  try:
92
103
  hdr = self.hdul[0].header
93
104
  data = self.hdul[0].data
105
+ wavelengths = self.hdul[1].data
94
106
  except Exception as e:
95
107
  raise ValueError(self.load_exception + str(e))
96
-
97
- pass
108
+
109
+
110
+ if self.blaze_profile is not None:
111
+ try:
112
+ blaze_hdu = fits.open(self.blaze_file)
113
+ blaze_profiles = np.array(blaze_hdu[0].data)
114
+ self.flux = data / blaze_profiles
115
+ except:
116
+ print("Error loading blaze profile. No blaze correction applied.")
117
+
118
+ return
98
119
 
99
120
  def HARPS_N(self):
100
- # La Palma HARPS-N spectra
121
+ hdul = fits.open(self.file)
122
+ hdr = hdul[0].header
123
+ flux = hdul[1].data
124
+ errors = hdul[2].data
125
+ dq = hdul[3].data
126
+ wavelengths = hdul[5].data
127
+ sn = np.array([hdr[f"HIERARCH TNG QC ORDER{i+1} SNR"] for i in range(len(flux))])
128
+
101
129
  pass
102
130
 
103
131
  def GEMINI_GHOST(self):
ACID_code/lsd.py CHANGED
@@ -89,9 +89,9 @@ class LSD:
89
89
  # Clip linelist to wavelength range of spectrum
90
90
  wavelengths_linelist, depths_linelist = utils.clip_wavelengths(wavelengths, wavelengths_linelist, depths_linelist)
91
91
  if len(wavelengths_linelist) == 0:
92
- raise ValueError(f"No lines in linelist are within the wavelength range of the observed spectrum. "\
93
- f"Please check your linelist and input spectrum. You may have mismatched wavelengths "\
94
- f"units between linelist and spectrum or an empty linelist.")
92
+ raise ValueError(f"No lines in linelist are within the wavelength range of the observed spectrum. \n"\
93
+ f"You may have mismatched wavelengths units between linelist and spectrum or an empty linelist.\n"\
94
+ f"Please check your linelist and input spectrum.")
95
95
 
96
96
  # Apply S/N cut (of 1/(3*SN)) to linelist
97
97
  wavelengths_linelist, depths_linelist = self.sn_clip(wavelengths_linelist, depths_linelist, sn)
ACID_code/profiles.py CHANGED
@@ -16,7 +16,7 @@ class Profiles:
16
16
  velocities : Array1D = None,
17
17
  flux : Array1D = None,
18
18
  flux_err : Array1D = None,
19
- data : Data = None
19
+ data : Data = None,
20
20
  ) -> None:
21
21
  """Initializes the Profiles class with velocity, flux, and optional flux error data.
22
22
 
@@ -34,7 +34,7 @@ class Profiles:
34
34
  data : Data, optional
35
35
  A data instance to draw velocities, flux and flux errors. Will raise an
36
36
  exception if they do not exist within the class.
37
- Must be provided if all three of the above inputs were not passed, by default None.
37
+ Must be provided if all three of the above inputs were not passed, by default None.
38
38
  """
39
39
 
40
40
  if data is not None:
ACID_code/result.py CHANGED
@@ -162,13 +162,11 @@ class Result:
162
162
  # Obtain flattened samples
163
163
  flat_samples = self.sampler.get_chain(discard=self.burnin, thin=self.thin, flat=True)
164
164
 
165
- # Getting the final profile and continuum values - median of last 1000 steps
165
+ # Getting the final profile and continuum values
166
166
  nvel = len(self.data.velocities) if self.config.deterministic_profile is False else 0
167
167
  quartiles = np.percentile(flat_samples, [16, 50, 84], axis=0)
168
168
  errors = np.diff(quartiles, axis=0)
169
169
  errors = np.max(errors, axis=0) # why?
170
- self.profile = quartiles[1, :nvel]
171
- self.profile_err = errors[:nvel]
172
170
  self.poly_cos = quartiles[1, nvel:]
173
171
  self.poly_cos_err = errors[nvel:]
174
172
 
@@ -191,7 +189,6 @@ class Result:
191
189
  n_samples, ncoeffs = coeffs.shape
192
190
  npix = powers.shape[0]
193
191
  matrix_size_gb = (2 * n_samples * npix + n_samples * ncoeffs + npix * ncoeffs) * 8 * 1e-9
194
-
195
192
  # If memory exceeded, fallback to using 1000 random samples
196
193
  if matrix_size_gb > m_available:
197
194
  if self.config.verbose > 0:
@@ -204,12 +201,11 @@ class Result:
204
201
  conts = (coeffs @ powers.T)
205
202
  continuum_error = np.std(conts, axis=0)
206
203
 
207
- self.profiles = np.zeros((len(self.data.flux["input"]), 2, len(self.data.velocities)))
208
-
209
204
  # First get the combined profile, and then calculate each frame's profile if there are multiple frames.
210
205
  # If there is one frame, then the combined_profile is the same as the single frame profile.
211
-
212
- for counter in range(len(self.data.flux["input"])+1):
206
+ nframes = len(self.data.flux["input"])
207
+ self.profiles = np.zeros((nframes, 2, len(self.data.velocities)))
208
+ for counter in range(nframes+1):
213
209
  if counter == 0:
214
210
  flux = np.copy(self.data.flux["combined"])
215
211
  error = np.copy(self.data.errors["combined"])
@@ -242,8 +238,8 @@ class Result:
242
238
  flux /= mdl
243
239
 
244
240
  # Check whether we can skip alpha by reusing the same alpha, only true if the wavelength grid is identical
245
- condition = np.all(wavelengths==self.data.wavelengths["combined"])
246
- alpha = self.data.alpha if condition is True else None
241
+ condition = np.array_equal(wavelengths, self.data.wavelengths["combined"])
242
+ alpha = self.data.alpha if condition else None
247
243
 
248
244
  LSD_profiles = LSD(self.data)
249
245
  LSD_profiles.run_LSD(wavelengths, flux, error, sn=sn, alpha=alpha)
@@ -543,7 +539,7 @@ class Result:
543
539
  flux = self.data.flux["combined"]
544
540
 
545
541
  a, b = utils.get_normalisation_coeffs(wavelengths)
546
- profile = utils.flux_to_od(self.combined_profile[0])[0]
542
+ profile = utils.flux_to_od(self.combined_profile[0])
547
543
 
548
544
  # Get flat_samples which are the same samples used to calculate the final profile
549
545
  flat_samples = self.sampler.get_chain(discard=self.burnin, thin=self.thin, flat=True)
@@ -557,7 +553,7 @@ class Result:
557
553
  ax[0].plot(wavelengths, flux, color='black', linewidth=1, label='Observed Spectrum')
558
554
  ax[0].plot(wavelengths, model_flux, color='C0', linewidth=1, label='Forward Model Fit')
559
555
  ax[0].plot(wavelengths, continuum_model, color='C1', linewidth=1, label='Fitted Continuum', linestyle='--')
560
- ax[1].plot(wavelengths, flux - model_flux, color='C0', linewidth=1, label='Residuals')
556
+ ax[1].plot(wavelengths, model_flux - flux, color='C0', linewidth=1, label='Residuals')
561
557
  ax[1].axhline(0, color='black', linestyle='--', linewidth=1)
562
558
  ax[0].set_title(labels["title"])
563
559
  ax[1].set_xlabel(labels["xlabel"])
ACID_code/utils.py CHANGED
@@ -354,7 +354,7 @@ def flux_to_od(flux=None, errors=None, linelist=None):
354
354
  if linelist is not None:
355
355
  out.append(-np.log(1 - linelist))
356
356
 
357
- return tuple(out)
357
+ return tuple(out) if len(out) > 1 else out[0]
358
358
 
359
359
  def od_to_flux(od=None, errors=None, linelist=None):
360
360
  """Converts optical depth to flux, errors, and linelist.
@@ -389,7 +389,7 @@ def od_to_flux(od=None, errors=None, linelist=None):
389
389
  if linelist is not None:
390
390
  out.append(1-np.exp(-linelist))
391
391
 
392
- return tuple(out)
392
+ return tuple(out) if len(out) > 1 else out[0]
393
393
 
394
394
  def configure_mp_environ(os):
395
395
  """Configures the multiprocessing environment variables for optimal performance.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ACID_code
3
- Version: 1.5.0a3
3
+ Version: 1.5.0a4
4
4
  Summary: Returns line profiles from input spectra by fitting the stellar continuum and performing LSD
5
5
  Author: Lucy Dolan
6
6
  Author-email: Benjamin Cadell <bcadell01@qub.ac.uk>
@@ -0,0 +1,14 @@
1
+ ACID_code/__init__.py,sha256=5ocFY2Pyeo4VclLFRlph8LM8tdIoPtwUmrQwgUnbVIk,463
2
+ ACID_code/acid.py,sha256=7BslKV9iGt6CXAdGTp2wrxes7cw592WHmff_ArweGt0,69763
3
+ ACID_code/data.py,sha256=5Oms-M5Jhi-0OT1mceBeVW9Pmrf0IWvBpMm-iZjV4po,50722
4
+ ACID_code/load.py,sha256=3gzIZpAv7flgX5ekWdRDI95hkPTxvtp2F1liygpivOQ,5453
5
+ ACID_code/lsd.py,sha256=YcShX62fqYCy2ZmFDOXEzKl-5nUM5QvYmCsk_KO8F9E,30495
6
+ ACID_code/mcmc.py,sha256=tsWZrEy8IZt-Hh3GJka3sIU8SkM4GivSgKyo8lWoycA,10564
7
+ ACID_code/profiles.py,sha256=Nm77ERkDByWKD5DeQv8VspV9eP98vqw-IyRYer-9qm8,13455
8
+ ACID_code/result.py,sha256=XTkLjTO6Wmu5MZeAl-1z80jyAIiXVaXnPC0_MDRqNrA,40303
9
+ ACID_code/utils.py,sha256=XF3Wi_t4bkBGYKrXeMipBNrs3Wp6R_YA3w9Qu-t1DCs,16348
10
+ acid_code-1.5.0a4.dist-info/licenses/LICENSE,sha256=L6dUgqjvHmRoobrBCPSHKC4UtRM5Ldp1DJBC4bnLk3w,1070
11
+ acid_code-1.5.0a4.dist-info/METADATA,sha256=hkq1Gwp55q4EciL0n5ugIkZkVB2fflEjOt8oNTenHrI,2979
12
+ acid_code-1.5.0a4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
13
+ acid_code-1.5.0a4.dist-info/top_level.txt,sha256=O4OaSabv1ebFYQmHgftr1PGAv6BvC2l81Y3HjgNehQI,10
14
+ acid_code-1.5.0a4.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- ACID_code/__init__.py,sha256=5ocFY2Pyeo4VclLFRlph8LM8tdIoPtwUmrQwgUnbVIk,463
2
- ACID_code/acid.py,sha256=7BslKV9iGt6CXAdGTp2wrxes7cw592WHmff_ArweGt0,69763
3
- ACID_code/data.py,sha256=AKLEhB5b3E__pgBmdLtQjzvKqDnYBIKmbeUsrnvk-Co,47133
4
- ACID_code/load.py,sha256=a-9mFRsT8UDU-A5m-fPwDtPOS9ibPz8GnDdZaoiBGbo,4372
5
- ACID_code/lsd.py,sha256=XBv3PuT5of5QW5EKon7wK_wuy0b8byjLbyXh_xvH2qM,30492
6
- ACID_code/mcmc.py,sha256=tsWZrEy8IZt-Hh3GJka3sIU8SkM4GivSgKyo8lWoycA,10564
7
- ACID_code/profiles.py,sha256=Z2Ttnk49-Aq5oR--qdmbv3R4EM7TjtUJ9YXGy8S1024,13453
8
- ACID_code/result.py,sha256=_MY8Brr5z8eXhzcR46wMmE8BHHAIi0ErpmAvyy0U9lE,40424
9
- ACID_code/utils.py,sha256=Y3RdQR-rrTsaUpABDV0eRhwMvubcLD1w18ubGziKt0g,16292
10
- acid_code-1.5.0a3.dist-info/licenses/LICENSE,sha256=L6dUgqjvHmRoobrBCPSHKC4UtRM5Ldp1DJBC4bnLk3w,1070
11
- acid_code-1.5.0a3.dist-info/METADATA,sha256=w5ukCW5ClgyTYXSumEs65_9p85rY1mw3pL5L4ydgQaA,2979
12
- acid_code-1.5.0a3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
13
- acid_code-1.5.0a3.dist-info/top_level.txt,sha256=O4OaSabv1ebFYQmHgftr1PGAv6BvC2l81Y3HjgNehQI,10
14
- acid_code-1.5.0a3.dist-info/RECORD,,