diff --git a/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/HVEntry.kt b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/HVEntry.kt new file mode 100644 index 0000000..b38fd45 --- /dev/null +++ b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/HVEntry.kt @@ -0,0 +1,27 @@ +package ru.inr.mass.data.proto + +import hep.dataforge.io.Envelope +import hep.dataforge.meta.get +import hep.dataforge.meta.string +import kotlinx.datetime.Instant +import kotlinx.datetime.toInstant +import kotlinx.io.text.forEachUtf8Line + +public data class HVEntry(val timestamp: Instant, val value: Double, val channel: Int = 1) { + public companion object { + public fun readString(line: String): HVEntry { + val (timeStr, channelStr, valueStr) = line.split(' ') + return HVEntry(timeStr.toInstant(), valueStr.toDouble(), channelStr.toInt()) + } + + public fun readEnvelope(envelope: Envelope): List { + check(envelope.meta["type"].string == "voltage"){"Expecting voltage type envelope"} + return buildList { + envelope.data?.read { + forEachUtf8Line { str -> add(readString(str)) } + } + } + } + } +} + diff --git a/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/NumassDirectorySet.kt b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/NumassDirectorySet.kt index 2c9daf0..057d100 100644 --- a/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/NumassDirectorySet.kt +++ b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/NumassDirectorySet.kt @@ -23,7 +23,7 @@ public class NumassDirectorySet internal constructor( override val meta: Meta by lazy { val metaFilePath = path / "meta" if (metaFilePath.exists()) { - val envelope = context.io.readEnvelopeFile(metaFilePath) ?: error("Envelope could not be read from $metaFilePath") + val envelope = context.io.readEnvelopeFile(metaFilePath) envelope.meta } else { context.logger.warn { "Meta file does not exist for Numass set $metaFilePath" } @@ -31,7 +31,7 @@ public class NumassDirectorySet internal constructor( } } - override val points: List by lazy> { + override val points: List by lazy { Files.list(path).filter { it.fileName.name.startsWith("p") }.map { pointPath -> @@ -43,11 +43,22 @@ public class NumassDirectorySet internal constructor( } }.toList().filterNotNull() } + + @OptIn(DFExperimental::class) + public val hvData: List? get(){ + val hvFile = path/"voltage" + return if( hvFile.exists()){ + val envelope = context.io.readEnvelopeFile(hvFile) + HVEntry.readEnvelope(envelope) + } else { + null + } + } } @OptIn(DFExperimental::class) public fun Context.readNumassPointFile(path: Path): NumassPoint? { - val envelope = io.readEnvelopeFile(path) ?: error("Envelope could not be read from $path") + val envelope = io.readEnvelopeFile(path) return ProtoNumassPoint.fromEnvelope(envelope) } diff --git a/numass-workspace/numass.json b/numass-workspace/numass.json new file mode 100644 index 0000000..39fe1e9 --- /dev/null +++ b/numass-workspace/numass.json @@ -0,0 +1,34 @@ +{ + "imports": [ + "kscience.plotly.*", + "kscience.plotly.models.*", + "kscience.plotly.JupyterPlotly", + "hep.dataforge.meta.*", + "kotlinx.html.*", + "ru.inr.mass.workspace.*" + ], + "repositories": [ + "*mavenLocal", + "https://dl.bintray.com/mipt-npm/dataforge", + "https://dl.bintray.com/mipt-npm/kscience", + "https://dl.bintray.com/mipt-npm/dev" + ], + "properties": { + "v": "0.1.0-SNAPSHOT" + }, + "link": "https://mipt-npm.jetbrains.space/p/numass/code/numass", + "dependencies": [ + "ru.inr.mass:numass-workspace:$v" + ], + "init": [ + "DISPLAY(HTML(JupyterPlotly.loadJs().toString()))", + "DISPLAY(HTML(\"

Plotly.kt jupyter integration is in the development phase. Expect glitches!

\"))" + ], + "renderers": { + "kscience.plotly.HtmlFragment": "HTML($it.toString())", + "kscience.plotly.Plot": "HTML(JupyterPlotly.renderPlot($it))", + "kscience.plotly.PlotlyFragment": "HTML(JupyterPlotly.renderFragment($it))", + "kscience.plotly.PlotlyPage": "HTML(JupyterPlotly.renderPage($it), true)", + "ru.inr.mass.data.proto.NumassDirectorySet": "HTML(JupyterPlotly.renderPage(${it.plotlyPage}), true)" + } +} \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/AmplitudeSpectrum.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/AmplitudeSpectrum.kt index e82e6b2..2606482 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/AmplitudeSpectrum.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/AmplitudeSpectrum.kt @@ -20,7 +20,7 @@ fun NumassPoint.spectrum(): UnivariateHistogram = fun Collection.spectrum(): UnivariateHistogram { if (distinctBy { it.voltage }.size != 1) { - numass.logger.warn { "Spectrum is generated from points with different hv value: $this" } + NUMASS.logger.warn { "Spectrum is generated from points with different hv value: $this" } } return UnivariateHistogram.uniform(1.0) { @@ -30,4 +30,17 @@ fun Collection.spectrum(): UnivariateHistogram { } } } +} + +/** + * Re-shape the spectrum with the increased bin size and range. Throws a error if new bin is smaller than before. + */ +fun UnivariateHistogram.reShape( + binSize: Int, + channelRange: IntRange, +): UnivariateHistogram = UnivariateHistogram.uniform(binSize.toDouble()) { + this@reShape.filter { it.position.toInt() in channelRange }.forEach { bin -> + if(bin.size > binSize.toDouble()) error("Can't reShape the spectrum with increased binning") + putMany(bin.position, bin.value.toLong()) + } } \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/files.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/files.kt new file mode 100644 index 0000000..5117685 --- /dev/null +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/files.kt @@ -0,0 +1,6 @@ +package ru.inr.mass.workspace + +import ru.inr.mass.data.proto.NumassDirectorySet +import ru.inr.mass.data.proto.readNumassDirectory + +fun readNumassDirectory(path: String): NumassDirectorySet = NUMASS.context.readNumassDirectory(path) \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/plots.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/plots.kt new file mode 100644 index 0000000..3ffa2ed --- /dev/null +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/plots.kt @@ -0,0 +1,62 @@ +package ru.inr.mass.workspace + +import kotlinx.html.h1 +import kotlinx.html.h2 +import kscience.kmath.histogram.UnivariateHistogram +import kscience.kmath.misc.UnstableKMathAPI +import kscience.plotly.* +import kscience.plotly.models.Bar +import kscience.plotly.models.Scatter +import kscience.plotly.models.Trace +import ru.inr.mass.data.api.NumassPoint +import ru.inr.mass.data.proto.HVEntry +import ru.inr.mass.data.proto.NumassDirectorySet + +@OptIn(UnstableKMathAPI::class) +fun Trace.fromSpectrum(histogram: UnivariateHistogram) { + x.numbers = histogram.map { it.position } + y.numbers = histogram.map { it.value } +} + +/** + * + */ +@OptIn(UnstableKMathAPI::class) +fun Plot.spectrum(name: String, histogram: UnivariateHistogram): Bar = bar { + this.name = name + fromSpectrum(histogram) +} + +fun Plot.amplitudeSpectrum(point: NumassPoint, binSize: Int = 20, range: IntRange = 0..4096, name: String = point.toString()): Bar = bar { + spectrum(name, point.spectrum().reShape(binSize, range)) +} + +/** + * Generate a plot from hv data + */ +fun Plot.hvData(data: List): Scatter = scatter { + x.strings = data.map { it.timestamp.toString() } + y.numbers = data.map { it.value } +} + +fun NumassDirectorySet.plotlyPage(binSize: Int = 20, range: IntRange = 0..4096): PlotlyPage = Plotly.page { + h1 { + +"Numass point set $path" + } + h2{ + +"Amplitude spectrum" + } + plot { + points.forEach { + amplitudeSpectrum(it,binSize, range) + } + } + hvData?.let {entries-> + h2{ + +"HV" + } + plot { + hvData(entries) + } + } +} \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/workspace.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/workspace.kt index f3280f3..b406cb1 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/workspace.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/workspace.kt @@ -3,8 +3,8 @@ package ru.inr.mass.workspace import hep.dataforge.workspace.Workspace import ru.inr.mass.data.proto.NumassProtoPlugin -val numass = Workspace { - context("numass") { +val NUMASS = Workspace { + context("NUMASS") { plugin(NumassProtoPlugin) } } \ No newline at end of file