主页面-功能介绍主页面-功能介绍小白初入职场第一篇总结,废话比较多,求轻喷~波谱拟合用来对某种材料或物质的谱图进行识别和分析,每种物质可以有多种成分,每种成分用component1、component2...表示,用Add another component和Remove component来控制每种成分的增加和删除,每种成分由多种原子核构成,即nuclei,用Add nucleus和Remove nucleus来控制每种成分内原子核数量,每新增一个原子核,波谱就会分裂一次,谱峰数量由(1->2->4->8...)依次分裂。另外可以通过更改默认参数,改变波谱形态,成分参数中:Relative amount表示每种成分占绘图分量的百分比,百分比之和不超过100,giso用来计算分裂的中心位置,LineWidth用来控设置谱峰到谱谷的宽度,%Lorentzian表示谱峰形态,一共两种形态,高斯和洛伦兹,两者之和为100;原子核参数:No of equivalent nuclei用来改变原子核个数,如果一种成分内包含很多个一模一样的参数时,就可以通过改变这个参数实现,Nuclear spin用来改变原子核种类,Hyperfine用来设置分裂后两峰之间的宽度。再来一张图:每种成分数量和参数、每种成分内每种原子核数量和参数设置好后,对数据进行处理,由三种结果,卷积、积分、二重积分,那就来看看数据的处理逻辑吧~从数据流角度,主要进行三步处理:数据->数据裂变->光谱计算->绘图,左边是算法实现所需的参数、右边是对数据及每个步骤的描述。代码实现代码实现遇到一个坑,一开始写demo的时候用的vue+Ant design of vue,在select等其他组件的使用上都是正常的,但是在input number中就很变态了,给input number绑定的change事件,用户在输入两位以上数据的时候,change事件会触发两次!!!想避免这个问题,于是用blur事件,问题又来了,因为这个页面中组件的生成和删除需要动态渲染,并且根据前面的介绍很容易知道组件的渲染是有两层结构的,那么在用户进行点击或输入操作的时候,就需要传递一个参数(用来定位是哪个component以及每个component下面对应的某一个nucle等等),能力有限( ╯□╰ )目前我没有找到解决办法,于是转elementUI框架。组件的动态渲染用了一个比较巧妙的办法,一开始我打算用render来写,后来从部门大神那里学到通过遍历列表进行渲染,脑子之间还是有差距的。。。
Component {{i+1}}.


Component {{i+1}}.

同理原子核的动态渲染也是这么实现的:
{{j+1}}. No of equivalent nuclei:


{{j+1}}. No of equivalent nuclei:

然后每次增加和删除只需要操作数组列表的长度即可~各参数的绑定:component中参数均使用一维数组,chenge事件需传递一维数组的下标,component内的nucleui均使用二维数组,change事件需传递二维数组的下标。以上介绍参数定义,接下来是数据处理:
// 首先计算裂变数据
stickspectrum (w) {

// console.log('组件信息', w)

const stick = new Array(2) // 返回包含stick[0]的stick光谱数组,stick[1]是位置

stick[0] = new Array()// 光谱强度

stick[1] = new Array()// 光谱位置

stick[1][0] = this.h * this.frequency / (this.r[w].g * this.mu)


for (var j = 0; j < this.r[w].equiv.length; j++) {

// console.log('stick[0].length', stick[0].length) //分裂后的光谱数据长度

for (var i = stick[0].length - 1; i >= 0; i--) {

stick[0][i] /= Math.pow((2 * this.r[w].spin[j] + 1), this.r[w].equiv[j])

stick[1][i] -= this.r[w].equiv[j] * this.r[w].spin[j] * this.r[w].hfc[j]


for (var k = 0; k < 2 * this.r[w].equiv[j] * this.r[w].spin[j]; k++) {

stick[1].splice(i + k + 1, 0, stick[1][i] + this.r[w].hfc[j] * (k + 1))

stick[0].splice(i + k + 1, 0, 0)

}

for (var k = 0; k < this.r[w].equiv[j]; k++) {

for (var m = i + 2 * this.r[w].spin[j] * k; m >= i; m--) {

for (var ii = 0; ii < 2 * this.r[w].spin[j]; ii++) {

stick[0][m + ii + 1] += stick[0][m]

}

}

}

}

}

return stick

},

// 首先计算裂变数据
stickspectrum (w) {

// console.log('组件信息', w)

const stick = new Array(2) // 返回包含stick[0]的stick光谱数组,stick[1]是位置

stick[0] = new Array()// 光谱强度

stick[1] = new Array()// 光谱位置

stick[1][0] = this.h * this.frequency / (this.r[w].g * this.mu)


for (var j = 0; j < this.r[w].equiv.length; j++) {

// console.log('stick[0].length', stick[0].length) //分裂后的光谱数据长度

for (var i = stick[0].length - 1; i >= 0; i--) {

stick[0][i] /= Math.pow((2 * this.r[w].spin[j] + 1), this.r[w].equiv[j])

stick[1][i] -= this.r[w].equiv[j] * this.r[w].spin[j] * this.r[w].hfc[j]


for (var k = 0; k < 2 * this.r[w].equiv[j] * this.r[w].spin[j]; k++) {

stick[1].splice(i + k + 1, 0, stick[1][i] + this.r[w].hfc[j] * (k + 1))

stick[0].splice(i + k + 1, 0, 0)

}

for (var k = 0; k < this.r[w].equiv[j]; k++) {

for (var m = i + 2 * this.r[w].spin[j] * k; m >= i; m--) {

for (var ii = 0; ii < 2 * this.r[w].spin[j]; ii++) {

stick[0][m + ii + 1] += stick[0][m]

}

}

}

}

}

return stick

},

// 再对裂变后的数据进行光谱计算
spectrum (stick) {

let xmin = Infinity; let xmax = 0

for (var k = 0; k < this.r.length; k++) {

xmin = Math.min(Math.min.apply(Math, stick[k][1]) - 10 * this.r[k].width, xmin)

xmax = Math.max(Math.max.apply(Math, stick[k][1]) + 10 * this.r[k].width, xmax)

}

const tmp = xmax - xmin

xmax += tmp * 0.05

xmin -= tmp * 0.05

const step = (xmax - xmin) / (this.No_integers - 1)

for (let i = 0; i < this.No_integers; i++) {

this.XY[0][i][0] = xmin + step * i

this.XY[0][i][1] = 0

this.XYint[0][i][0] = this.XY[0][i][0]

this.XYint[0][i][1] = 0

this.XYdoubleint[0][i][0] = this.XY[0][i][0]

this.XYdoubleint[0][i][1] = 0

}


for (let k = 0; k < this.r.length; k++) { // 分量累加

const sticks = new Array(this.No_integers)

for (var i = 0; i < stick[k][0].length; i++) {

var j = Math.round((stick[k][1][i] - xmin) / step)

sticks[j] = sticks[j] ? sticks[j] + stick[k][0][i] : stick[k][0][i]

}


const tmp = new Array(this.No_integers)// 第一种光谱绘图位置数据

let ind = 0

for (var i = 0; i < this.No_integers; i++) {

if (sticks[i]) { // 建立峰值索引——sticks[i]===1即峰值所在。

tmp[ind] = i

ind++

}

}

const tmpint = new Array(this.No_integers) // 用来保存每个分量的积分

const tmpdoubleint = new Array(this.No_integers) // 用来保存每个分量的二重积分

for (var i = 0; i < this.No_integers; i++) tmpint[i] = 0

tmpdoubleint[0] = 0

const rwid = Number(this.r[k].width)

const rwid2 = Math.pow(rwid, 2)

const lortmp = Number(this.r[k].percent) * Number(this.r[k].lor) / 100 * Math.sqrt(3) / Math.PI // 洛伦兹线乘积

const gaustmp = Number(this.r[k].percent) * (100 - Number(this.r[k].lor)) / 100 * Math.sqrt(2 / Math.PI) // 高斯线乘法器


for (let i = 0; i < this.No_integers; i++) {

for (let j = 0; j < ind; j++) {

const delta = this.XY[0][i][0] - this.XY[0][tmp[j]][0]

const delta2 = Math.pow(delta, 2)

if ((rwid > step && Math.abs(-0.5 * rwid - delta) < 0.5 * step) || (rwid < step && -0.5 * rwid - delta > 0 && -0.5 * rwid - delta < step)) {

this.XY[0][i][1] += sticks[tmp[j]] * (lortmp * 0.5 / rwid2 + gaustmp * 2 / Math.sqrt(Math.E) / rwid2)

} else if ((rwid > step && Math.abs(0.5 * rwid - delta) < 0.5 * step) || (rwid < step && delta - 0.5 * rwid > 0 && delta - 0.5 * rwid < step)) {

this.XY[0][i][1] -= sticks[tmp[j]] * (lortmp * 0.5 / rwid2 + gaustmp * 2 / Math.sqrt(Math.E) / rwid2)

} else {

this.XY[0][i][1] += sticks[tmp[j]] * (gaustmp * (-4) / rwid / rwid2 * delta * Math.exp(-2 * delta2 / rwid2) + lortmp * (-delta) * rwid / Math.pow((delta2 + 3 / 4 * rwid2), 2)) // 其他情况下的正常计算,高斯+洛伦兹

}

this.dataarray = [this.XY, this.XYint, this.XYdoubleint]

tmpint[i] += sticks[tmp[j]] * (gaustmp * Math.exp(-2 * delta2 / rwid2) / rwid + lortmp / 2 / rwid / (0.75 + delta2 / rwid2)) // 高斯+洛伦兹积分-明确计算以避免积分误差

}

}

for (let j = 1; j < this.No_integers; j++) {

tmpdoubleint[j] = tmpdoubleint[j - 1] + step * (tmpint[j] + tmpint[j - 1]) / 2

} // 二重积分

// console.log('二重积分', tmpdoubleint)


const mm = tmpdoubleint[this.No_integers - 1] / Number(this.r[k].percent) // 有多少积分高于理论(只发生在非常尖锐的线)

for (let j = 1; j < this.No_integers; j++) {

this.XYdoubleint[0][j][1] += mm > 1 ? tmpdoubleint[j] / mm : tmpdoubleint[j] // 第三种频谱数据 如果二重积分高于理论,将其标准化

this.XYint[0][j][1] += tmpint[j] // 第二种频谱数据

}

}

// console.log('XYint', this.XYint[0])

},

// 再对裂变后的数据进行光谱计算
spectrum (stick) {

let xmin = Infinity; let xmax = 0

for (var k = 0; k < this.r.length; k++) {

xmin = Math.min(Math.min.apply(Math, stick[k][1]) - 10 * this.r[k].width, xmin)

xmax = Math.max(Math.max.apply(Math, stick[k][1]) + 10 * this.r[k].width, xmax)

}

const tmp = xmax - xmin

xmax += tmp * 0.05

xmin -= tmp * 0.05

const step = (xmax - xmin) / (this.No_integers - 1)

for (let i = 0; i < this.No_integers; i++) {

this.XY[0][i][0] = xmin + step * i

this.XY[0][i][1] = 0

this.XYint[0][i][0] = this.XY[0][i][0]

this.XYint[0][i][1] = 0

this.XYdoubleint[0][i][0] = this.XY[0][i][0]

this.XYdoubleint[0][i][1] = 0

}


for (let k = 0; k < this.r.length; k++) { // 分量累加

const sticks = new Array(this.No_integers)

for (var i = 0; i < stick[k][0].length; i++) {

var j = Math.round((stick[k][1][i] - xmin) / step)

sticks[j] = sticks[j] ? sticks[j] + stick[k][0][i] : stick[k][0][i]

}


const tmp = new Array(this.No_integers)// 第一种光谱绘图位置数据

let ind = 0

for (var i = 0; i < this.No_integers; i++) {

if (sticks[i]) { // 建立峰值索引——sticks[i]===1即峰值所在。

tmp[ind] = i

ind++

}

}

const tmpint = new Array(this.No_integers) // 用来保存每个分量的积分

const tmpdoubleint = new Array(this.No_integers) // 用来保存每个分量的二重积分

for (var i = 0; i < this.No_integers; i++) tmpint[i] = 0

tmpdoubleint[0] = 0

const rwid = Number(this.r[k].width)

const rwid2 = Math.pow(rwid, 2)

const lortmp = Number(this.r[k].percent) * Number(this.r[k].lor) / 100 * Math.sqrt(3) / Math.PI // 洛伦兹线乘积

const gaustmp = Number(this.r[k].percent) * (100 - Number(this.r[k].lor)) / 100 * Math.sqrt(2 / Math.PI) // 高斯线乘法器


for (let i = 0; i < this.No_integers; i++) {

for (let j = 0; j < ind; j++) {

const delta = this.XY[0][i][0] - this.XY[0][tmp[j]][0]

const delta2 = Math.pow(delta, 2)

if ((rwid > step && Math.abs(-0.5 * rwid - delta) < 0.5 * step) || (rwid < step && -0.5 * rwid - delta > 0 && -0.5 * rwid - delta < step)) {

this.XY[0][i][1] += sticks[tmp[j]] * (lortmp * 0.5 / rwid2 + gaustmp * 2 / Math.sqrt(Math.E) / rwid2)

} else if ((rwid > step && Math.abs(0.5 * rwid - delta) < 0.5 * step) || (rwid < step && delta - 0.5 * rwid > 0 && delta - 0.5 * rwid < step)) {

this.XY[0][i][1] -= sticks[tmp[j]] * (lortmp * 0.5 / rwid2 + gaustmp * 2 / Math.sqrt(Math.E) / rwid2)

} else {

this.XY[0][i][1] += sticks[tmp[j]] * (gaustmp * (-4) / rwid / rwid2 * delta * Math.exp(-2 * delta2 / rwid2) + lortmp * (-delta) * rwid / Math.pow((delta2 + 3 / 4 * rwid2), 2)) // 其他情况下的正常计算,高斯+洛伦兹

}

this.dataarray = [this.XY, this.XYint, this.XYdoubleint]

tmpint[i] += sticks[tmp[j]] * (gaustmp * Math.exp(-2 * delta2 / rwid2) / rwid + lortmp / 2 / rwid / (0.75 + delta2 / rwid2)) // 高斯+洛伦兹积分-明确计算以避免积分误差

}

}

for (let j = 1; j < this.No_integers; j++) {

tmpdoubleint[j] = tmpdoubleint[j - 1] + step * (tmpint[j] + tmpint[j - 1]) / 2

} // 二重积分

// console.log('二重积分', tmpdoubleint)


const mm = tmpdoubleint[this.No_integers - 1] / Number(this.r[k].percent) // 有多少积分高于理论(只发生在非常尖锐的线)

for (let j = 1; j < this.No_integers; j++) {

this.XYdoubleint[0][j][1] += mm > 1 ? tmpdoubleint[j] / mm : tmpdoubleint[j] // 第三种频谱数据 如果二重积分高于理论,将其标准化

this.XYint[0][j][1] += tmpint[j] // 第二种频谱数据

}

}

// console.log('XYint', this.XYint[0])

},
计算完成的光谱,返回三种数据XY、XYint、XYdouble,然后就是绘图~