RunSimulationSpectrum.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <div class="row items-baseline">
  3. <div class="col-xs-12 col-sm-auto text-weight-bold text-center q-px-md q-py-sm">
  4. <q-tooltip
  5. v-if=" $store.state.guiRuntime.safeFromWL > $store.state.simulationSetup.gui.fromWL ||
  6. $store.state.guiRuntime.safeToWL < $store.state.simulationSetup.gui.toWL "
  7. anchor="top middle" self="center middle"
  8. class="bg-amber-4 text-black shadow-4">
  9. Will use materials<br> spectrum range.
  10. </q-tooltip>
  11. <q-btn :loading="isRunning"
  12. :disable="isRunning||!isNmieLoaded"
  13. color="primary"
  14. no-caps
  15. :label="isNmieLoaded ? 'Run simulation' : 'Loading...'"
  16. @click="runSpectrumSimulation">
  17. <template #loading>
  18. <q-spinner-gears />
  19. </template>
  20. </q-btn>
  21. </div>
  22. <div class="col-xs-grow col-sm q-px-xs">
  23. <div class="row justify-xs-center justify-sm-start items-baseline">
  24. <div class="col-auto">
  25. <q-btn
  26. color="primary"
  27. no-caps
  28. @click="saveSpectrumSimulation"
  29. >Save</q-btn>
  30. </div>
  31. </div>
  32. </div>
  33. </div>
  34. </template>
  35. <script lang="ts">
  36. import {
  37. defineComponent,
  38. computed, watch,
  39. onActivated,
  40. nextTick
  41. } from 'vue'
  42. import { useStore } from 'src/store'
  43. import { getModeName, range, rangeInt} from 'components/utils'
  44. import { cloneDeep } from 'lodash'
  45. import { saveAs } from 'file-saver'
  46. export default defineComponent({
  47. name: 'RunSimulationSpectrum',
  48. setup() {
  49. const $store = useStore()
  50. const isRunning = computed({
  51. get: ()=> $store.state.simulationSetup.nmies.spectrum.isNmieRunning,
  52. set: val => {
  53. val ? $store.commit('simulationSetup/markNmieAsStarted') : $store.commit('simulationSetup/markNmieAsFinished')
  54. }
  55. })
  56. const isNmieLoaded = computed(()=>{ return $store.state.simulationSetup.nmies.spectrum.instance })
  57. const sourceUnits = computed( ()=>$store.state.guiRuntime.sourceUnits)
  58. function getWLs(safeFromWL:number, safeToWL:number){
  59. let fromWL = $store.state.simulationSetup.current.fromWL
  60. let toWL = $store.state.simulationSetup.current.toWL
  61. if (fromWL < safeFromWL) fromWL=safeFromWL
  62. if (toWL > safeToWL) toWL=safeToWL
  63. const pointsWL = $store.state.simulationSetup.current.pointsWL
  64. if (sourceUnits.value.endsWith('Hz') || sourceUnits.value.endsWith('eV')) {
  65. const fromF = 1./fromWL
  66. const toF = 1./toWL
  67. const stepF = (fromF-toF)/(pointsWL-1)
  68. const Fs = range(toF, fromF, stepF);
  69. let WLs = []
  70. for (const f of Fs) WLs.push(1./f)
  71. return WLs
  72. }
  73. const stepWL = (toWL-fromWL)/(pointsWL-1)
  74. return range(fromWL, toWL, stepWL)
  75. }
  76. function initQ(mode_n:number[], mode_types:number[]) {
  77. let Qsca:number[] = [], Qabs:number[] = [], Qext:number[] = []
  78. let Qsca_n:number[][][] = [[], []]
  79. let Qabs_n:number[][][] = [[], []]
  80. let Qext_n:number[][][] = [[], []]
  81. mode_types.forEach(function (mode_type) {
  82. mode_n.forEach(function () {
  83. Qsca_n[mode_type].push([])
  84. Qabs_n[mode_type].push([])
  85. Qext_n[mode_type].push([])
  86. })
  87. })
  88. return {Qsca, Qabs, Qext, Qsca_n, Qabs_n, Qext_n}
  89. }
  90. //-------------------------------------------------------------------------//
  91. //--------------------- Main --------------------------------------------//
  92. //-------------------------------------------------------------------------//
  93. function runSpectrumSimulation() {
  94. if (isRunning.value) {
  95. console.log('Some Nmie is already running!')
  96. return
  97. }
  98. isRunning.value = true
  99. setTimeout(()=>{
  100. void nextTick(()=> {
  101. $store.commit('simulationSetup/copySetupFromGuiToCurrent')
  102. const host = $store.state.simulationSetup.current.hostIndex
  103. const safeFromWL = $store.state.guiRuntime.safeFromWL
  104. const safeToWL = $store.state.guiRuntime.safeToWL
  105. const WLs = getWLs(safeFromWL, safeToWL)
  106. const mode_n = rangeInt($store.state.simulationSetup.current.numberOfModesToPlot, 1);
  107. const mode_types = range(0, 1);
  108. let {Qsca, Qabs, Qext, Qsca_n, Qabs_n, Qext_n} = initQ(mode_n, mode_types)
  109. try {
  110. if (!$store.state.simulationSetup.nmies.spectrum.instance) throw 'ERROR! Scattnlay module was not loaded'
  111. const nmie = $store.state.simulationSetup.nmies.spectrum.instance
  112. const layers = cloneDeep($store.state.simulationSetup.current.layers)
  113. const nmieStartedTime = performance.now()
  114. for (const WL of WLs) {
  115. nmie.SetWavelength(WL)
  116. nmie.ClearTarget()
  117. for (const layer of layers) {
  118. if (layer.material.nSpline) layer.n = layer.material.nSpline.at(WL)
  119. if (layer.material.kSpline) layer.k = layer.material.kSpline.at(WL)
  120. nmie.AddTargetLayerReIm(layer.layerWidth * host, layer.n / host, layer.k / host)
  121. }
  122. nmie.SetModeNmaxAndType(-1, -1)
  123. nmie.RunMieCalculation()
  124. Qsca.push(nmie.GetQsca())
  125. Qabs.push(nmie.GetQabs())
  126. Qext.push(nmie.GetQext())
  127. for (const mode_type of mode_types) {
  128. for (const n of mode_n) {
  129. nmie.SetModeNmaxAndType(n, mode_type)
  130. nmie.RunMieCalculation()
  131. Qsca_n[mode_type][n - 1].push(nmie.GetQsca())
  132. Qabs_n[mode_type][n - 1].push(nmie.GetQabs())
  133. Qext_n[mode_type][n - 1].push(nmie.GetQext())
  134. }
  135. }
  136. }
  137. const nmieTotalRunTime = (performance.now()-nmieStartedTime)/1000
  138. // console.log('Total simulation time:', nmieTotalRunTime, 's')
  139. $store.commit('simulationSetup/setNmieTotalRunTime', nmieTotalRunTime)
  140. $store.commit('plotRuntime/setQ', {WLs, Qsca, Qabs, Qext, Qsca_n, Qabs_n, Qext_n})
  141. $store.commit('plotRuntime/setWLsInUnits', sourceUnits.value)
  142. $store.commit('plotRuntime/updateNumberOfPlotsFromPreviousSimulations')
  143. $store.commit('plotRuntime/setCommonLabel', $store.state.simulationSetup.current.plotLabel)
  144. $store.commit('simulationSetup/setPlotLabel', '')
  145. $store.commit('plotRuntime/updateSpectrumPlots')
  146. } catch (e) {
  147. console.log(e)
  148. }
  149. isRunning.value = false
  150. })
  151. }, 100)
  152. }
  153. watch(isNmieLoaded, ()=>{
  154. if (isNmieLoaded.value) runSpectrumSimulation()
  155. })
  156. onActivated(()=>{
  157. if (isNmieLoaded.value) {
  158. if ($store.state.plotRuntime.spectrumPlots.data.length == 0) runSpectrumSimulation()
  159. else {
  160. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  161. // @ts-ignore
  162. // eslint-disable-next-line
  163. const data_x_length = $store.state.plotRuntime.spectrumPlots.data[0].x.length
  164. if (!data_x_length) runSpectrumSimulation()
  165. }
  166. }
  167. })
  168. return { isRunning, isNmieLoaded,
  169. runSpectrumSimulation,
  170. saveSpectrumSimulation(){
  171. const fileHeader = '# # You can open and plot this file using Python\n' +
  172. '# # (without manually removing this header, it will be skipped), see example below.\n' +
  173. '# import numpy as np\n' +
  174. '# from matplotlib import pyplot as plt\n' +
  175. '# data = np.genfromtxt(\'scattnlay-spectra.txt\', skip_header=21, names=True, delimiter=\', \')\n' +
  176. '# x = data[data.dtype.names[0]] # x-axis has units\n' +
  177. '# # Possible labels for spectrum data: Qsca, Qabs, Qext,\n' +
  178. '# # Qsca_E_dipole, etc. (see last comment before spectra data)\n' +
  179. '# a = data[\'Qsca\']\n' +
  180. '# b = data[\'Qsca_E_dipole\']\n' +
  181. '# c = data[\'Qsca_H_dipole\']\n' +
  182. '# \n' +
  183. '# plt.figure()\n' +
  184. '# plt.plot(x, a, label=\'Qsca\')\n' +
  185. '# plt.plot(x, b, label=\'Qsca E dipole\')\n' +
  186. '# plt.plot(x, c, label=\'Qsca H dipole\')\n' +
  187. '# plt.legend()\n' +
  188. '# plt.xlabel(data.dtype.names[0].replace(\'_\', \', \'))\n' +
  189. '# plt.ylabel(\'Normalized cross-sections\')\n' +
  190. '# plt.show()\n\n'
  191. let xTitle = 'x'
  192. if ( $store.state.plotRuntime.spectrumPlots.layout.xaxis ) {
  193. xTitle = String($store.state.plotRuntime.spectrumPlots.layout.xaxis.title)
  194. }
  195. let columnNames = '# ' + xTitle + ', Qsca, Qabs, Qext, '
  196. const mode_n = rangeInt($store.state.simulationSetup.current.numberOfModesToPlot, 1);
  197. const mode_types = range(0, 1);
  198. for (const n of mode_n) {
  199. for (const mode_type of mode_types) {
  200. const modeTypeName = mode_type == 0 ? 'E' : 'H'
  201. columnNames += 'Qsca_' + modeTypeName + '_' +getModeName(n)+', '
  202. columnNames += 'Qabs_' + modeTypeName + '_' +getModeName(n)+', '
  203. columnNames += 'Qext_' + modeTypeName + '_' +getModeName(n)+', '
  204. }
  205. }
  206. columnNames = columnNames.slice(0, -2)
  207. columnNames += '\n'
  208. let body = ''
  209. const WLs = $store.state.plotRuntime.WLsInUnits
  210. const Qsca = $store.state.plotRuntime.Qsca
  211. const Qabs = $store.state.plotRuntime.Qabs
  212. const Qext = $store.state.plotRuntime.Qext
  213. const Qsca_n = $store.state.plotRuntime.Qsca_n
  214. const Qabs_n = $store.state.plotRuntime.Qabs_n
  215. const Qext_n = $store.state.plotRuntime.Qext_n
  216. for (let i = 0; i < WLs.length; ++i) {
  217. let row = WLs[i].toString() + ', '
  218. + Qsca[i].toString() + ', '
  219. + Qabs[i].toString() + ', '
  220. + Qext[i].toString() + ', '
  221. for (const n of mode_n) {
  222. for (const mode_type of mode_types) {
  223. row += Qsca_n[mode_type][n - 1][i].toString() + ', '
  224. row += Qabs_n[mode_type][n - 1][i].toString() + ', '
  225. row += Qext_n[mode_type][n - 1][i].toString() + ', '
  226. }
  227. }
  228. row = row.slice(0, -2)
  229. row += '\n'
  230. body += row
  231. }
  232. const scattnlaySpectra = new Blob([fileHeader+columnNames+body],
  233. {type: 'text/plain;charset=utf-8',
  234. endings: 'native'} //TODO test if newline is correctly written in Windows, MacOS
  235. )
  236. saveAs(scattnlaySpectra, 'scattnlay-spectra.txt');
  237. }
  238. }
  239. },
  240. })
  241. </script>