ACID-code 1.5.0a2__py3-none-any.whl → 1.5.0a3__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 CHANGED
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ import contextlib
2
3
  import warnings
3
4
  warnings.filterwarnings("ignore")
4
5
  import sys, emcee, os, time, inspect, inspect
@@ -169,7 +170,7 @@ class Acid:
169
170
  if linelist is not None or linelist_wl is not None or linelist_depths is not None:
170
171
  raise ValueError("Use either 'linelist', or legacy 'linelist_path', not both. See the API for more details.")
171
172
  linelist = kwargs.pop("linelist_path")
172
- if self.verbose > 0:
173
+ if self.config.verbose > 0:
173
174
  print("Warning: 'linelist_path' is a legacy argument for inputting a linelist, " \
174
175
  f"please use 'linelist' instead.\n The 'linelist_path' argument does not support full input validation.")
175
176
  # Anything left in kwargs is invalid
@@ -957,8 +958,11 @@ class Acid:
957
958
  step_number += self.config.check_interval
958
959
 
959
960
  try:
960
- # We want to keep the time for get_autocorr_time to run constant, so thin accordingly
961
- tau = self.sampler.get_autocorr_time(tol=0, thin=step_number//self.config.check_interval)
961
+ # We want to keep the time for get_autocorr_time to run constant, so thin accordingly
962
+ with open(os.devnull, "w") as devnull, \
963
+ contextlib.redirect_stdout(devnull), \
964
+ contextlib.redirect_stderr(devnull):
965
+ tau = self.sampler.get_autocorr_time(tol=0, thin=step_number//self.config.check_interval)
962
966
  except emcee.autocorr.AutocorrError:
963
967
  continue
964
968
 
@@ -983,7 +987,11 @@ class Acid:
983
987
  step_number += self.config.check_interval
984
988
 
985
989
  try:
986
- tau = self.sampler.get_autocorr_time(tol=0)
990
+ # We want to keep the time for get_autocorr_time to run constant, so thin accordingly
991
+ with open(os.devnull, "w") as devnull, \
992
+ contextlib.redirect_stdout(devnull), \
993
+ contextlib.redirect_stderr(devnull):
994
+ tau = self.sampler.get_autocorr_time(tol=0, thin=step_number//self.config.check_interval)
987
995
  except emcee.autocorr.AutocorrError:
988
996
  continue
989
997
 
ACID_code/data.py CHANGED
@@ -328,9 +328,10 @@ class Data:
328
328
  ndim : Optional[int] = None
329
329
 
330
330
  # Data required/calculated in results/after MCMC sampling
331
- profiles : Optional[np.ndarray] = None # the array to store all frames of the MCMC sampling
332
- nsteps : Optional[int] = 0
333
- max_steps : Optional[int] = None
331
+ profiles : Optional[np.ndarray] = None # the array to store all frames of the MCMC sampling
332
+ combined_profiles : Optional[np.ndarray] = None
333
+ nsteps : Optional[int] = 0
334
+ max_steps : Optional[int] = None
334
335
 
335
336
  # Other useful data and figures:
336
337
  initialisation_time : Optional[float] = None # time taken for initialization
@@ -606,6 +607,12 @@ class Data:
606
607
  if input_sn.shape[0] != input_flux.shape[0]:
607
608
  raise ValueError("The number of frames for the SN must match the number of frames in wavelengths, flux, and errors.")
608
609
 
610
+ # Ensure data is sorted by wavelength
611
+ sort_idx = np.argsort(input_wavelengths, axis=-1)
612
+ input_wavelengths = np.take_along_axis(input_wavelengths, sort_idx, axis=-1)
613
+ input_flux = np.take_along_axis(input_flux, sort_idx, axis=-1)
614
+ input_errors = np.take_along_axis(input_errors, sort_idx, axis=-1)
615
+
609
616
  # Apply skips
610
617
  input_wavelengths = input_wavelengths[:, ::skips]
611
618
  input_flux = input_flux[:, ::skips]
@@ -632,7 +639,7 @@ class Data:
632
639
  self.errors = {"input": input_errors}
633
640
  self.sn = {"input": input_sn}
634
641
 
635
- # Apply skips and set inputs to class variables
642
+ # Set inputs to class variables
636
643
  self.wavelengths["input"] = input_wavelengths
637
644
  self.flux["input"] = input_flux
638
645
  self.errors["input"] = input_errors
ACID_code/load.py CHANGED
@@ -6,15 +6,18 @@ from __future__ import annotations
6
6
  from beartype import beartype
7
7
  from .data import Data
8
8
  from . import utils
9
+ from .utils import Array2D
9
10
  from astropy.io import fits
10
11
 
11
12
  @beartype
12
13
  class Load:
14
+
13
15
  def __init__(
14
16
  self,
15
- instrument: str,
16
- file : str,
17
- blaze_file: str|None = None,
17
+ instrument : str,
18
+ file : str,
19
+ blaze_file : str|None = None,
20
+ blaze_profile : Array2D|None = None
18
21
  ):
19
22
  """
20
23
  Parameters
@@ -22,6 +25,16 @@ class Load:
22
25
  instrument : str
23
26
  The name of the instrument the data was taken with. This will determine how the data is loaded and configured.
24
27
  Current options are "HARPS", "HARPS-N", "GEMINI-GHOST", "GEMINI-GRACES"
28
+ file : str
29
+ The path to the fits file containing the data to be loaded.
30
+ blaze_file : str, optional
31
+ The path to the fits file containing the blaze profile for the data to be loaded.
32
+ If not provided, no blaze correction is applied. By default, None
33
+ blaze_profile : Array2D, optional
34
+ Instead load the blaze profile yourself and input it here. This will override the blaze_file input if
35
+ both are provided. By default, None
36
+
37
+
25
38
  """
26
39
 
27
40
  self.file = file
@@ -67,6 +80,12 @@ class Load:
67
80
  self.hdul = fits.open(file) # standard astropy error catching will catch if this fails
68
81
  self.loader(self.hdul)
69
82
 
83
+ # If blaze profile is provided, load it
84
+ if blaze_profile is not None:
85
+ self.data.blaze = blaze_profile
86
+ elif blaze_file is not None:
87
+ pass # TODO: load blaze file and save to self.data.blaze or tailor blaze files to each instrument
88
+
70
89
  def HARPS(self):
71
90
  # La Silla HARPS spectra
72
91
  try:
ACID_code/profiles.py CHANGED
@@ -4,25 +4,50 @@ from scipy.optimize import curve_fit
4
4
  import matplotlib.pyplot as plt
5
5
  from beartype import beartype
6
6
  from scipy.special import wofz
7
+ from .utils import Array1D
8
+ from .data import Data
7
9
 
8
10
  @beartype
9
11
  class Profiles:
10
12
  """A class for fitting spectral line profiles such as Voigt and Gaussian profiles.
11
13
  """
12
- def __init__(self, velocities, flux, flux_err=None):
14
+ def __init__(
15
+ self,
16
+ velocities : Array1D = None,
17
+ flux : Array1D = None,
18
+ flux_err : Array1D = None,
19
+ data : Data = None
20
+ ) -> None:
13
21
  """Initializes the Profiles class with velocity, flux, and optional flux error data.
14
22
 
15
23
  Parameters
16
24
  ----------
17
- velocities : array_like
25
+ velocities : Array1D
18
26
  The velocity values corresponding to the spectral line profile.
19
- flux : array_like
20
- The flux values of the spectral line profile.
21
- flux_err : array_like, optional
22
- The errors associated with the flux values, by default None.
27
+ Must be provided if no data instance is passed, by default None.
28
+ flux : Array1D
29
+ The flux values of the spectral line profile
30
+ Must be provided if no data instance is passed, by default None.
31
+ flux_err : Array1D, optional
32
+ The errors associated with the flux values, by default None. If not
33
+ input, they won't be used in the fitting process.
34
+ data : Data, optional
35
+ A data instance to draw velocities, flux and flux errors. Will raise an
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.
23
38
  """
24
39
 
25
- flux -= 1
40
+ if data is not None:
41
+ if not getattr(data, 'velocities', None) or not getattr(data, 'profiles', None):
42
+ raise ValueError("Data instance must have attributes 'velocities' and 'profiles'. Try running ACID first.")
43
+ velocities = data.velocities
44
+ flux = data.profiles[0,0] # Subtract 1 to convert from normalized flux to absorption depth
45
+ flux_err = data.profiles[0,1]
46
+ else:
47
+ if velocities is None or flux is None:
48
+ raise ValueError("If no data instance is provided, then at least velocities and flux must be provided.")
49
+
50
+ flux -= 1 # Subtract 1 to convert from normalized flux to absorption depth
26
51
 
27
52
  self.velocities = velocities
28
53
  self.flux = flux
@@ -35,35 +60,38 @@ class Profiles:
35
60
  self.fitted_x = np.linspace(np.min(velocities), np.max(velocities), 1000)
36
61
  pass
37
62
 
38
- def plot_fit(self, model:str|None='all', **kwargs):
63
+ def plot_fit(self, model:str|None='voigt', return_fig=False, **kwargs):
39
64
  """Plots the original data and the fitted profile if available.
40
65
 
41
66
  Parameters
42
67
  ----------
43
68
  model : str | None, optional
44
69
  The type of model to plot. String options are 'voigt', 'gaussian',
45
- 'lorentzian', or 'all'. Choosing None will plot whichever models have
70
+ 'lorentzian', 'none', or 'all'. Choosing 'none' or None will plot whichever models have
46
71
  already been fitted for, by default 'all'.
72
+ return_fig : bool, optional
73
+ Whether to return the (fig, ax) tuple and not call plt.show(). If False, calls
74
+ plt.show() and returns None. By default False.
47
75
  **kwargs : dict
48
76
  Additional keyword arguments to pass to the fitting functions if the models
49
77
  have not been fitted yet.
50
78
  Returns
51
79
  -------
52
- None
80
+ tuple | None
53
81
  """
54
- models = ["voigt", "gaussian", "lorentzian"]
82
+ models = ["voigt", "gaussian", "lorentzian", "none"]
55
83
  if model is not None:
56
84
  model = model.lower()
57
85
  if model not in models + ['all']:
58
86
  raise ValueError("Model must be 'voigt', 'gaussian', 'lorentzian' or 'all'.")
59
-
87
+
60
88
  if model == 'all':
61
- model_list = models
62
- elif model is None:
89
+ model_list = models[:-1] # Exclude 'none'
90
+ elif model is None or model == "none":
63
91
  model_list = list(self.fitted_y.keys())
64
92
  else:
65
93
  model_list = [model]
66
-
94
+
67
95
  for m in model_list:
68
96
  if m not in self.fitted_y:
69
97
  fit_func = getattr(self, f"fit_{m}")
ACID_code/result.py CHANGED
@@ -206,50 +206,58 @@ class Result:
206
206
 
207
207
  self.profiles = np.zeros((len(self.data.flux["input"]), 2, len(self.data.velocities)))
208
208
 
209
- for counter in range(len(self.data.flux["input"])):
210
- flux = np.copy(self.data.flux["input"][counter])
211
- error = np.copy(self.data.errors["input"][counter])
212
- wavelengths = np.copy(self.data.wavelengths["input"][counter])
213
- sn = np.copy(self.data.sn["input"][counter])
209
+ # First get the combined profile, and then calculate each frame's profile if there are multiple frames.
210
+ # 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):
213
+ if counter == 0:
214
+ flux = np.copy(self.data.flux["combined"])
215
+ error = np.copy(self.data.errors["combined"])
216
+ wavelengths = np.copy(self.data.wavelengths["combined"])
217
+ sn = np.copy(self.data.sn["combined"])
218
+ error[self.data.residual_masks] = 1e12
219
+ else:
220
+ flux = np.copy(self.data.flux["input"][counter-1])[self.data.nanmask]
221
+ error = np.copy(self.data.errors["input"][counter-1])[self.data.nanmask]
222
+ wavelengths = np.copy(self.data.wavelengths["input"][counter-1])[self.data.nanmask]
223
+ sn = np.copy(self.data.sn["input"][counter-1])
224
+
225
+ # Masking based off residuals interpolated onto new wavelength grid
226
+ reference_wave = self.data.wavelengths["input"][np.nanargmax(self.data.sn["input"])]
227
+ reference_wave = reference_wave[self.data.nanmask]
214
228
 
215
- flux = flux[self.data.nanmask]
216
- error = error[self.data.nanmask]
217
- wavelengths = wavelengths[self.data.nanmask]
229
+ reference_mask = np.zeros_like(reference_wave, dtype=bool)
230
+ reference_mask[self.data.residual_masks] = True
231
+ reference_interp1d = interp1d(reference_wave, reference_mask.astype(float), kind="nearest", bounds_error=False, fill_value=0.0)
232
+ interpolated_mask = reference_interp1d(wavelengths) > 0.5
233
+ error[interpolated_mask] = 1e12
218
234
 
219
235
  # Build continuum model
220
236
  a, b = utils.get_normalisation_coeffs(wavelengths)
221
237
  norm_wavelengths = (a*wavelengths)+b
222
- mdl1 = P.polyval(norm_wavelengths, self.poly_cos)
223
-
224
- # Masking based off residuals interpolated onto new wavelength grid
225
- reference_wave = self.data.wavelengths["input"][np.nanargmax(self.data.sn["input"])]
226
- reference_wave = reference_wave[self.data.nanmask]
227
- mask_pos = np.ones(reference_wave.shape)
228
- mask_pos[self.data.residual_masks]=1e12
229
- f2 = interp1d(reference_wave, mask_pos, bounds_error = False, fill_value = np.nan)
230
- interp_mask_pos = f2(wavelengths)
231
- interp_mask_idx = tuple([interp_mask_pos>=1e12])
232
-
233
- error[interp_mask_idx]=1e12
238
+ mdl = P.polyval(norm_wavelengths, self.poly_cos)
234
239
 
235
240
  # correcting continuum
236
- error = np.sqrt((error/mdl1)**2 + (continuum_error/mdl1)**2)
237
- flux /= mdl1
241
+ error = np.sqrt((error/mdl)**2 + (continuum_error/mdl)**2)
242
+ flux /= mdl
238
243
 
239
- remove = tuple([flux<0])
240
- flux[remove] = 1.
241
- error[remove] = 1e12
244
+ # 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
242
247
 
243
248
  LSD_profiles = LSD(self.data)
244
- LSD_profiles.run_LSD(wavelengths, flux, error, sn=sn)
249
+ LSD_profiles.run_LSD(wavelengths, flux, error, sn=sn, alpha=alpha)
245
250
 
246
251
  profile_f = LSD_profiles.profile_F
247
252
  profile_errors_f = LSD_profiles.profile_errors_F
248
- # profile_f = profile_f-1
249
253
 
250
- self.profiles[counter]=[profile_f, profile_errors_f]
254
+ if counter == 0:
255
+ self.combined_profile = [profile_f, profile_errors_f]
256
+ else:
257
+ self.profiles[counter-1] = [profile_f, profile_errors_f]
251
258
 
252
259
  self.data.profiles = self.profiles # point Data.profiles to Result.profiles to keep them in sync
260
+ self.data.combined_profiles = self.combined_profile
253
261
  self.data.get_profiles_time = time() - t0
254
262
  self.data.full_run_time = self.data.initialisation_time + self.data.mcmc_time + self.data.get_profiles_time
255
263
 
@@ -259,6 +267,22 @@ class Result:
259
267
  def __getitem__(self, item) -> np.ndarray:
260
268
  """Allows indexing into the profiles array directly from the Result object.
261
269
  """
270
+ if isinstance(item, tuple):
271
+ if len(item) == 3:
272
+ # Legacy support for indexing style
273
+ _order, frame, velocity = item
274
+ item = (frame, velocity)
275
+ elif len(item) == 2:
276
+ item = item
277
+ elif len(item) == 1:
278
+ return self.combined_profile[item[0]]
279
+ elif isinstance(item, int):
280
+ return self.combined_profile[item]
281
+ elif isinstance(item, str):
282
+ if "combined" in item.lower():
283
+ return self.combined_profile
284
+ elif "profile" in item.lower():
285
+ return self.profiles
262
286
  return self.profiles[item]
263
287
 
264
288
  @_require_profiles
@@ -473,18 +497,15 @@ class Result:
473
497
  @_require_profiles
474
498
  def plot_forward_model(
475
499
  self,
476
- input_version :str = "masked",
477
500
  grid :bool = True,
478
501
  labels :dict|None = None,
479
502
  return_fig :bool = False,
480
503
  subplot_kwargs :dict|None = None,
481
504
  ) -> None | tuple:
482
- """Plots the forward model fit to the observed spectrum.
505
+ """Plots the forward model calculated from the final profiles to the combined input spectrum.
483
506
 
484
507
  Parameters
485
508
  ----------
486
- input_version : str, optional
487
- Which input spectrum to use: 'combined', 'input', 'masked', by default 'masked'
488
509
  grid : bool, optional
489
510
  Show or hide grid, by default True
490
511
  labels : dict | None, optional
@@ -499,11 +520,6 @@ class Result:
499
520
  If return_fig is True, returns a tuple (fig, ax) of the figure and axes objects containing the plot.
500
521
  Otherwise, displays the plot and returns None.
501
522
  """
502
-
503
- # Validate all inputs and set defaults
504
- input_version = input_version.lower()
505
- if input_version not in self.data.wavelengths.keys():
506
- raise ValueError(f"input_version must be one of {list(self.data.wavelengths.keys())}")
507
523
 
508
524
  # Set default labels
509
525
  default_labels = {
@@ -523,20 +539,26 @@ class Result:
523
539
  subplot_kwargs = utils.set_dict_defaults(subplot_kwargs, {"figsize": (10, 8)})
524
540
 
525
541
  # Get input data
526
- input_wavelengths = self.data.wavelengths[input_version]
527
- input_flux = self.data.flux[input_version]
528
- input_errors = self.data.errors[input_version]
542
+ wavelengths = self.data.wavelengths["combined"]
543
+ flux = self.data.flux["combined"]
529
544
 
530
- # Get model flux
531
- samples = self.sampler.get_chain(discard=self.burnin, thin=self.thin, flat=True)
532
- theta_median = np.median(samples, axis=0)
533
- model_flux, _ = self.model(theta_median)
545
+ a, b = utils.get_normalisation_coeffs(wavelengths)
546
+ profile = utils.flux_to_od(self.combined_profile[0])[0]
547
+
548
+ # Get flat_samples which are the same samples used to calculate the final profile
549
+ flat_samples = self.sampler.get_chain(discard=self.burnin, thin=self.thin, flat=True)
550
+ nvel = len(self.data.velocities) if self.config.deterministic_profile is False else 0
551
+ poly_samples = flat_samples[:, nvel:] # continuum polynomial samples
552
+ continuum_model = P.polyval(wavelengths*a+b, np.median(poly_samples, axis=0))
553
+ model_flux = np.exp(- (self.data.alpha @ profile)) * continuum_model
534
554
 
535
555
  # Plotting
536
556
  fig, ax = plt.subplots(2, 1, **subplot_kwargs)
537
- ax[0].plot(input_wavelengths, input_flux, color='black', linewidth=1, label='Observed Spectrum')
538
- ax[0].plot(input_wavelengths, model_flux, color='C0', linewidth=1, label='Forward Model Fit')
539
- ax[1].plot(input_wavelengths, input_flux - model_flux, color='C0', linewidth=1, label='Residuals')
557
+ ax[0].plot(wavelengths, flux, color='black', linewidth=1, label='Observed Spectrum')
558
+ ax[0].plot(wavelengths, model_flux, color='C0', linewidth=1, label='Forward Model Fit')
559
+ 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')
561
+ ax[1].axhline(0, color='black', linestyle='--', linewidth=1)
540
562
  ax[0].set_title(labels["title"])
541
563
  ax[1].set_xlabel(labels["xlabel"])
542
564
  ax[0].set_ylabel(labels["ylabel"])
@@ -739,7 +761,7 @@ class Result:
739
761
  try:
740
762
  self.thin = int(np.min(self.tau)/5)
741
763
  if self.converged:
742
- self.burnin = int(2 * np.max(self.tau))
764
+ self.burnin = int(3 * np.max(self.tau))
743
765
  else:
744
766
  self.burnin = self.nsteps - 1000 # just the last 1000 steps
745
767
  except:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ACID_code
3
- Version: 1.5.0a2
3
+ Version: 1.5.0a3
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=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,,
@@ -1,14 +0,0 @@
1
- ACID_code/__init__.py,sha256=5ocFY2Pyeo4VclLFRlph8LM8tdIoPtwUmrQwgUnbVIk,463
2
- ACID_code/acid.py,sha256=mL3_BZmlOvcYS8IqWsyU4RZ6v5DIgpKEPrljSaXU2eA,69197
3
- ACID_code/data.py,sha256=Ab2EHDQi85-ziN-eF5MpNSBaPPf4GJ06IJYiLR7SptQ,46738
4
- ACID_code/load.py,sha256=EdQxHf_tVC4ekfm1LDoO5NhCwo7EYk5FlqlpcEzLjF0,3481
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=NH_bK7eZYmPk4hCv1a5eXLqxjdny8i0BBBiwfNxnsB8,11809
8
- ACID_code/result.py,sha256=Q0YZUlM_gt3_NW2ntXh-daj6B0nqrGSsgu6g3oNOK4Y,38773
9
- ACID_code/utils.py,sha256=Y3RdQR-rrTsaUpABDV0eRhwMvubcLD1w18ubGziKt0g,16292
10
- acid_code-1.5.0a2.dist-info/licenses/LICENSE,sha256=L6dUgqjvHmRoobrBCPSHKC4UtRM5Ldp1DJBC4bnLk3w,1070
11
- acid_code-1.5.0a2.dist-info/METADATA,sha256=rZA4IHcjdX3prbyh9pEw5sDBxJhrcBefmcF0xoZLqDE,2979
12
- acid_code-1.5.0a2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
13
- acid_code-1.5.0a2.dist-info/top_level.txt,sha256=O4OaSabv1ebFYQmHgftr1PGAv6BvC2l81Y3HjgNehQI,10
14
- acid_code-1.5.0a2.dist-info/RECORD,,