@@ -8,23 +8,21 @@ export interface ShiftsItem {
88 spheres : number
99}
1010
11- interface LorentzianOptions {
11+ type PeakShape = 'gaussian' | 'lorentzian'
12+
13+ interface PeakShapeOptions {
1214 x : number
1315 fwhm : number
1416}
1517
16- function lorentzian2 ( options : LorentzianOptions ) {
17- const { x, fwhm } = options
18- return fwhm ** 2 / ( 4 * x ** 2 + fwhm ** 2 )
19- }
20-
21- interface GenerateSpectrumOptions {
18+ export interface GenerateSpectrumOptions {
2219 from ?: number
2320 to ?: number
2421 nbPoints ?: number
2522 lineWidth ?: number
2623 frequency ?: number
2724 tolerance ?: number
25+ peakShape ?: PeakShape
2826}
2927
3028interface GroupItem {
@@ -38,7 +36,37 @@ interface Data1D {
3836 re : number [ ]
3937}
4038
41- const getLorentzianFactor = ( area = 0.9999 ) => {
39+ const GAUSSIAN_EXP_FACTOR = - 4 * Math . LN2
40+
41+ function lorentzian ( options : PeakShapeOptions ) {
42+ const { x, fwhm } = options
43+ return fwhm ** 2 / ( 4 * x ** 2 + fwhm ** 2 )
44+ }
45+
46+ function gaussian ( options : PeakShapeOptions ) {
47+ const { x, fwhm } = options
48+ return Math . exp ( GAUSSIAN_EXP_FACTOR * Math . pow ( x / fwhm , 2 ) )
49+ }
50+
51+ function erfinv ( x : number ) : number {
52+ let a = 0.147
53+ if ( x === 0 ) return 0
54+ let ln1MinusXSqrd = Math . log ( 1 - x * x )
55+ let lnEtcBy2Plus2 = ln1MinusXSqrd / 2 + 2 / ( Math . PI * a )
56+ let firstSqrt = Math . sqrt ( lnEtcBy2Plus2 ** 2 - ln1MinusXSqrd / a )
57+ let secondSqrt = Math . sqrt ( firstSqrt - lnEtcBy2Plus2 )
58+ return secondSqrt * ( x > 0 ? 1 : - 1 )
59+ }
60+
61+ function getGaussianFactor ( area = 0.9999 ) {
62+ if ( area >= 1 ) {
63+ throw new Error ( 'area should be (0 - 1)' )
64+ }
65+
66+ return Math . sqrt ( 2 ) * erfinv ( area )
67+ }
68+
69+ function getLorentzianFactor ( area = 0.9999 ) {
4270 if ( area >= 1 ) {
4371 throw new Error ( 'area should be (0 - 1)' )
4472 }
@@ -49,6 +77,22 @@ const getLorentzianFactor = (area = 0.9999) => {
4977 )
5078}
5179
80+ function peakShapeFunction ( options : PeakShapeOptions , shape : PeakShape ) {
81+ if ( shape === 'lorentzian' ) {
82+ return lorentzian ( options )
83+ }
84+
85+ return gaussian ( options )
86+ }
87+
88+ function getPeakShapeFactor ( area : number , shape : PeakShape ) {
89+ if ( shape === 'lorentzian' ) {
90+ return getLorentzianFactor ( area )
91+ }
92+
93+ return getGaussianFactor ( area )
94+ }
95+
5296function groupEquivalentShifts ( shifts : ShiftsItem [ ] , tolerance = 0.001 ) {
5397 const groups : GroupItem [ ] = [ ]
5498
@@ -81,6 +125,7 @@ export function generatePredictedSpectrumData(
81125 frequency = 400 ,
82126 lineWidth = 1 ,
83127 tolerance = 0.001 ,
128+ peakShape = 'lorentzian' ,
84129 } = options
85130
86131 if ( ! shifts || shifts . length === 0 ) return [ ]
@@ -107,24 +152,22 @@ export function generatePredictedSpectrumData(
107152 const stepSize = ( to - from ) / ( nbPoints - 1 )
108153 const groupedShifts = groupEquivalentShifts ( acceptedShifts , tolerance )
109154
110- console . log ( groupedShifts )
111-
112- const limit = ( lineWidth * getLorentzianFactor ( 0.99 ) ) / frequency
113- console . log ( limit )
155+ const limit = ( lineWidth * getPeakShapeFactor ( 0.99 , peakShape ) ) / frequency
114156 for ( let i = 0 ; i < nbPoints ; i ++ ) {
115157 const x = from + i * stepSize
116158 let intensity = 0
117159 for ( const { prediction, count } of groupedShifts ) {
118160 if ( Math . abs ( x - prediction ) <= limit ) {
119161 intensity +=
120- lorentzian2 ( { x : x - prediction , fwhm : lineWidth / frequency } ) *
121- count
162+ peakShapeFunction (
163+ { x : x - prediction , fwhm : lineWidth / frequency } ,
164+ peakShape
165+ ) * count
122166 }
123167 }
124- if ( intensity > 0 ) {
125- data . x . push ( x )
126- data . re . push ( intensity )
127- }
168+
169+ data . x . push ( x )
170+ data . re . push ( intensity )
128171 }
129172
130173 return data
0 commit comments