funcnodes-span 0.1.6__tar.gz → 0.1.8__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funcnodes-span
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary:
5
5
  Author: Kourosh Rezaei
6
6
  Author-email: kouroshrezaei90@gmail.com
@@ -4,7 +4,7 @@ from .normalization import NORM_NODE_SHELF as NORM
4
4
  from .smoothing import SMOOTH_NODE_SHELF as SMOOTH
5
5
  from .peak_analysis import PEAKS_NODE_SHELF as PEAK
6
6
 
7
- __version__ = "0.1.6"
7
+ __version__ = "0.1.8"
8
8
 
9
9
  NODE_SHELF = fn.Shelf(
10
10
  name="Spectral Analysis",
@@ -2,10 +2,11 @@ from funcnodes import NodeDecorator, Shelf
2
2
  import numpy as np
3
3
  from enum import Enum
4
4
  from exposedfunctionality import controlled_wrapper
5
- from typing import Optional, TypedDict, List
5
+ from typing import Optional, TypedDict, List, Tuple
6
6
  from scipy.signal import find_peaks
7
7
  from scipy.stats import norm
8
- from scipy import interpolate
8
+ from scipy import signal, interpolate
9
+ from scipy.ndimage import gaussian_filter1d
9
10
  import copy
10
11
  import lmfit
11
12
  import plotly.graph_objs as go
@@ -179,7 +180,7 @@ def compute_peak_properties(
179
180
  return peak_properties
180
181
 
181
182
 
182
- @NodeDecorator(id="span.basics.peaks", name="Peak finder node")
183
+ @NodeDecorator(id="span.basics.peaks", name="Peak finder")
183
184
  @controlled_wrapper(find_peaks, wrapper_attribute="__fnwrapped__")
184
185
  def peak_finder(
185
186
  x_array: np.ndarray,
@@ -310,14 +311,14 @@ class FittingModel(Enum):
310
311
  Moffat = "Moffat"
311
312
  Pearson4 = "Pearson4"
312
313
  Pearson7 = "Pearson7"
314
+ SkewedGaussian = "Skewed Gaussian"
315
+ SkewedVoigt = "Skewed Voigt"
313
316
  StudentsT = "StudentsT"
314
317
  BreitWigner = "Breit-Wigner"
315
318
  LogNormal = "Log-Normal"
316
319
  DampedOscillator = "Damped Oscillator"
317
320
  DampedHarmonicOscillator = "Damped Harmonic Oscillator"
318
321
  ExponentialGaussian = "Exponential Gaussian"
319
- SkewedGaussian = "Skewed Gaussian"
320
- SkewedVoigt = "Skewed Voigt"
321
322
  ThermalDistribution = "Thermal Distribution"
322
323
  Doniach = "Doniach"
323
324
  PowerLaw = "Power Law"
@@ -332,6 +333,25 @@ class FittingModel(Enum):
332
333
  return cls.Gaussian.value
333
334
 
334
335
 
336
+ @NodeDecorator(
337
+ "span.basics.interpolation_1d",
338
+ name="Interpolation 1D",
339
+ outputs=[
340
+ {
341
+ "name": "x_interpolated",
342
+ },
343
+ {"name": "y_interpolated"},
344
+ ],
345
+ )
346
+ def interpolation_1d(
347
+ x: np.array, y: np.array, multipled_by: int = 10
348
+ ) -> Tuple[np.ndarray, np.ndarray]:
349
+ f_interpol = interpolate.interp1d(x, y)
350
+ x_interpolated = np.linspace(x[0], x[-1], num=len(x) * multipled_by, endpoint=True)
351
+ y_interpolated = f_interpol(x_interpolated)
352
+ return x_interpolated, y_interpolated
353
+
354
+
335
355
  @NodeDecorator(id="span.basics.fit", name="Fit 1D")
336
356
  def fit_1D(
337
357
  x_array: np.ndarray,
@@ -469,6 +489,109 @@ def fit_1D(
469
489
  return peak_properties_list
470
490
 
471
491
 
492
+
493
+
494
+
495
+ @NodeDecorator(
496
+ "span.basics.force_fit",
497
+ name="Advanced peak finder",
498
+ )
499
+ def force_peak_finder(
500
+ x: np.array,
501
+ y: np.array,
502
+ basic_peaks: PeakProperties,
503
+ ) -> List[PeakProperties]:
504
+ # """
505
+ # Identify and return the two peaks around the main peak in the given peaks dictionary.
506
+
507
+ # Parameters:
508
+ # - peaks (dict): A dictionary containing peak information.
509
+ # It should have the keys 'peaks' and 'data'.
510
+ # 'peaks' should contain a list of dictionaries with keys 'Initial index', 'Index', and 'Ending index'.
511
+ # 'data' should contain arrays 'x' and 'y'.
512
+
513
+ # Returns:
514
+ # - dict: A dictionary containing information about the two identified peaks.
515
+ # """
516
+ peaks = copy.deepcopy(basic_peaks)
517
+
518
+ main_peak_i_index = peaks["i_index"]
519
+ main_peak_r_index = peaks["index"]
520
+ main_peak_f_index = peaks["f_index"]
521
+ y_array = y
522
+ x_array = x
523
+ # Calculate first and second derivatives
524
+ y_array_p = np.diff(y_array, 1, -1, y_array[0])
525
+ y_array_pp = np.diff(y_array, 2, -1, y_array[0:2])
526
+ # Smooth derivatives using Gaussian filter
527
+ y_array_p = gaussian_filter1d(y_array_p, 5)
528
+ y_array_pp = gaussian_filter1d(y_array_pp, 5)
529
+
530
+ maxx = [main_peak_r_index]
531
+ minn = [main_peak_i_index, main_peak_f_index]
532
+ # Find local maxima and minima of derivatives
533
+ max_p = signal.argrelmax(y_array_p)[0]
534
+ min_p = signal.argrelmin(y_array_p)[0]
535
+ max_pp = signal.argrelmax(y_array_pp)[0]
536
+ min_pp = signal.argrelmin(y_array_pp)[0]
537
+
538
+ # main_peak_i_index = peaks["i_index"]
539
+ # main_peak_r_index = peaks["index"]
540
+ # main_peak_f_index = peaks["f_index"]
541
+
542
+
543
+ # Determine which peak is on the left and right side of the main peak
544
+ if (
545
+ x_array[main_peak_r_index] - x_array[main_peak_i_index]
546
+ > x_array[main_peak_f_index] - x_array[main_peak_r_index]
547
+ ): # seond peak is in the leftside of the max peak #TODO: fix this
548
+ common_point = max([num for num in max_pp if num < main_peak_r_index])
549
+
550
+ print("Left convoluted")
551
+ peak1 = {
552
+ "I.Index": main_peak_i_index,
553
+ "R.Index": max(
554
+ [num for num in min_p if num < main_peak_r_index]
555
+ ), # TODO: fix this
556
+ "F.Index": common_point,
557
+ }
558
+ peak2 = {
559
+ "I.Index": common_point,
560
+ "R.Index": main_peak_r_index,
561
+ "F.Index": main_peak_f_index,
562
+ }
563
+ else:
564
+ common_point = next((x for x in max_pp if x > main_peak_r_index), None)
565
+ print("Right convoluted")
566
+ peak1 = {
567
+ "I.Index": main_peak_i_index,
568
+ "R.Index": main_peak_r_index,
569
+ "F.Index": common_point,
570
+ }
571
+ peak2 = {
572
+ "I.Index": common_point,
573
+ "R.Index": next((x for x in max_p if x > main_peak_r_index), None),
574
+ "F.Index": main_peak_f_index,
575
+ }
576
+ peak_lst = []
577
+ peak_lst.append([peak1["I.Index"], peak1["R.Index"], peak1["F.Index"]])
578
+ peak_lst.append([peak2["I.Index"], peak2["R.Index"], peak2["F.Index"]])
579
+ peak_properties_list = []
580
+ for peak_nr, peak in enumerate(peak_lst):
581
+ peak_properties = compute_peak_properties(
582
+ x_array=x_array,
583
+ y_array=y_array,
584
+ peak_indices=peak,
585
+ peak_nr=peak_nr,
586
+ is_force_fitted=True,
587
+ )
588
+ peak_properties_list.append(peak_properties)
589
+
590
+ return peak_properties_list
591
+
592
+
593
+
594
+
472
595
  # Define a mapping from "C0", "C1", etc., to CSS color names
473
596
  color_map = {
474
597
  "C0": "blue",
@@ -487,6 +610,9 @@ color_map = {
487
610
  @NodeDecorator(id="span.basics.fit.plot", name="Plot fit 1D")
488
611
  def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
489
612
  peak = peaks[0]
613
+ if not peak['_is_fitted']:
614
+ raise ValueError("No fitting information is available.")
615
+
490
616
  x = peak["fitting_info"]["userkws"]["x"]
491
617
  # Extract data from peaks
492
618
  y = peak["fitting_info"]["data"]
@@ -549,7 +675,7 @@ def plot_fitted_peaks(peaks: List[PeakProperties]) -> go.Figure:
549
675
 
550
676
 
551
677
  PEAKS_NODE_SHELF = Shelf(
552
- nodes=[peak_finder, fit_1D, plot_fitted_peaks],
678
+ nodes=[peak_finder, interpolation_1d, force_peak_finder, fit_1D, plot_fitted_peaks],
553
679
  subshelves=[],
554
680
  name="Peak analysis",
555
681
  description="Tools for the peak analysis of the spectra",
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "funcnodes-span"
3
- version = "0.1.6"
3
+ version = "0.1.8"
4
4
  description = ""
5
5
  authors = ["Kourosh Rezaei <kouroshrezaei90@gmail.com>"]
6
6
  readme = "README.md"
@@ -8,12 +8,10 @@ readme = "README.md"
8
8
  [tool.poetry.dependencies]
9
9
  python = "^3.11"
10
10
  scipy = "^1.14.0"
11
-
12
11
  lmfit = "^1.3.2"
13
12
  funcnodes = "^0.2.21"
14
13
  funcnodes_numpy = "^0.1.57"
15
14
  funcnodes_pandas = "^0.1.9"
16
-
17
15
  funcnodes_plotly = "^0.1.7"
18
16
 
19
17
  pooch = "^1.8.2"
File without changes