diff --git a/.gitignore b/.gitignore index 17a319a..9120eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,6 @@ out/ .gradle build/ - +/notebooks/.ipynb_checkpoints !gradle-wrapper.jar \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 4d49a3c..0add116 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,15 +9,15 @@ allprojects { } group = "ru.inr.mass" - version = "0.1.0-dev-1" + version = "0.1.0" } val dataforgeVersion by extra("0.5.2") -val tablesVersion: String by extra("0.1.1") +val tablesVersion: String by extra("0.1.2") val kmathVersion by extra("0.3.0-dev-17") val plotlyVersion: String by extra("0.5.0") ksciencePublish{ - git("https://mipt-npm.jetbrains.space/p/numass/code/numass/") + github("numass") space("https://maven.pkg.jetbrains.space/mipt-npm/p/numass/maven") } \ No newline at end of file diff --git a/notebooks/jupyter-demo.ipynb b/notebooks/jupyter-demo.ipynb new file mode 100644 index 0000000..eb3356b --- /dev/null +++ b/notebooks/jupyter-demo.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2a640dcc-4696-408f-b1f0-0cdf917e4719", + "metadata": {}, + "outputs": [], + "source": [ + "@file:Repository(\"https://repo.kotlin.link\")\n", + "@file:Repository(\"*mavenLocal\")\n", + "@file:DependsOn(\"ru.inr.mass:numass-workspace:0.1.0\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d193fb2-6c18-4c64-b5c3-3e9ed4099d5b", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "val repo: DataTree = Numass.readNumassRepository(\"D:\\\\Work\\\\Numass\\\\data\\\\test\")\n", + "repo" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "373de92b-2533-4cfc-820b-ca90b9d028fc", + "metadata": {}, + "outputs": [], + "source": [ + "val numassSet = repo[\"set_7\"]\n", + "numassSet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0845a15-2928-4e6e-a891-fee130ef5326", + "metadata": {}, + "outputs": [], + "source": [ + "val point = numassSet.points.first{it.voltage == 14000.0}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "842dfbcb-17f0-4df9-b5d4-2835f15f3a50", + "metadata": {}, + "outputs": [], + "source": [ + "point" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50df6925-82f5-4330-a1c2-3e43fb9cd17d", + "metadata": {}, + "outputs": [], + "source": [ + "Plotly.plotNumassBlock(point, eventExtractor = NumassEventExtractor.TQDC)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196f94ca-0439-4190-bda7-8d692c37b2db", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "val frames = point.listFrames()\n", + "Plotly.page {\n", + " p { +\"${frames.size} frames\" }\n", + " h2 { +\"Random frames\" }\n", + " plot {\n", + " val random = kotlin.random.Random(1234)\n", + "\n", + " repeat(10) {\n", + " val frame = frames.random(random)\n", + " scatter {\n", + " y.numbers = frame.signal.map { (it.toUShort().toInt() - Short.MAX_VALUE).toShort() }\n", + " }\n", + " }\n", + " }\n", + " h2 { +\"Analysis\" }\n", + " plot {\n", + " histogram {\n", + " name = \"max\"\n", + " x.numbers = frames.map { frame -> frame.signal.maxOf { (it.toUShort().toInt() - Short.MAX_VALUE).toShort() } }\n", + " }\n", + "\n", + " histogram {\n", + " name = \"max-min\"\n", + " xbins {\n", + " size = 2.0\n", + " }\n", + " x.numbers = frames.map { frame ->\n", + " frame.signal.maxOf { it.toUShort().toInt() - Short.MAX_VALUE } -\n", + " frame.signal.minOf { it.toUShort().toInt() - Short.MAX_VALUE }\n", + " }\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5320d9d5-eae3-469b-a1f2-5d33d3db286c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Kotlin", + "language": "kotlin", + "name": "kotlin" + }, + "language_info": { + "codemirror_mode": "text/x-kotlin", + "file_extension": ".kt", + "mimetype": "text/x-kotlin", + "name": "kotlin", + "nbconvert_exporter": "", + "pygments_lexer": "kotlin", + "version": "1.6.20-dev-6372" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/numass.json b/notebooks/numass.json deleted file mode 100644 index 4406c7c..0000000 --- a/notebooks/numass.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "imports": [ - "kscience.plotly.*", - "kscience.plotly.models.*", - "kscience.plotly.JupyterPlotly", - "space.kscience.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", - "https://kotlin.bintray.com/kotlinx" - ], - "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-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAmplitudeSpectrum.kt b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAmplitudeSpectrum.kt index 9c452ff..ab2e4c3 100644 --- a/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAmplitudeSpectrum.kt +++ b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAmplitudeSpectrum.kt @@ -12,7 +12,9 @@ public class NumassAmplitudeSpectrum(public val amplitudes: Map) public val minChannel: UShort by lazy { amplitudes.keys.minOf { it } } public val maxChannel: UShort by lazy { amplitudes.keys.maxOf { it } } - public fun binned(binSize: UInt, range: UIntRange = minChannel..maxChannel): Map { + public val channels: UIntRange by lazy { minChannel..maxChannel } + + public fun binned(binSize: UInt, range: UIntRange = channels): Map { val keys = sequence { var left = range.first do { @@ -24,6 +26,9 @@ public class NumassAmplitudeSpectrum(public val amplitudes: Map) return keys.associateWith { bin -> amplitudes.filter { it.key in bin }.values.sum().toDouble() } } + + public fun sum(range: UIntRange = channels): ULong = + amplitudes.filter { it.key in range }.values.sum() } /** diff --git a/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAnalyzerParameters.kt b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAnalyzerParameters.kt index 264f337..34e0fc9 100644 --- a/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAnalyzerParameters.kt +++ b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassAnalyzerParameters.kt @@ -15,9 +15,9 @@ public class TimeAnalyzerParameters : Scheme() { /** * The relative fraction of events that should be removed by time cut */ - public var crFraction by double() - public var min by double(0.0) - public var crMin by double(0.0) + public var crFraction: Double? by double() + public var min: Double by double(0.0) + public var crMin: Double by double(0.0) /** * The number of events in chunk to split the chain into. If null, no chunks are used diff --git a/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassEventExtractor.kt b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassEventExtractor.kt index ee6d7c4..db2c8bc 100644 --- a/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassEventExtractor.kt +++ b/numass-analysis/src/commonMain/kotlin/ru/inr/mass/data/analysis/NumassEventExtractor.kt @@ -1,6 +1,7 @@ package ru.inr.mass.data.analysis import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import ru.inr.mass.data.api.NumassBlock import ru.inr.mass.data.api.NumassEvent @@ -12,6 +13,30 @@ public fun interface NumassEventExtractor { * A default event extractor that ignores frames */ public val EVENTS_ONLY: NumassEventExtractor = NumassEventExtractor { it.events } + public val TQDC: NumassEventExtractor = NumassEventExtractor { block -> + block.frames.map { frame -> + var max = 0 + var min = 0 + var indexOfMax = 0 + + frame.signal.forEachIndexed { index, sh -> + val corrected = sh.toUShort().toInt() - Short.MAX_VALUE + if (corrected >= max) { + max = corrected + indexOfMax = index + } + if (corrected <= min) { + min = corrected + } + } + + NumassEvent( + (max - min).toShort().toUShort(), + frame.timeOffset + frame.tickSize.inWholeNanoseconds * indexOfMax, + block + ) + } + } } } diff --git a/numass-analysis/src/jvmMain/kotlin/ru/inr/mass/data/analysis/timeHistogram.kt b/numass-analysis/src/jvmMain/kotlin/ru/inr/mass/data/analysis/timeHistogram.kt index 03061d2..2a6aa79 100644 --- a/numass-analysis/src/jvmMain/kotlin/ru/inr/mass/data/analysis/timeHistogram.kt +++ b/numass-analysis/src/jvmMain/kotlin/ru/inr/mass/data/analysis/timeHistogram.kt @@ -8,6 +8,7 @@ import ru.inr.mass.data.api.NumassBlock import ru.inr.mass.data.api.getTime import space.kscience.kmath.histogram.UnivariateHistogram import kotlin.math.max +import kotlin.time.DurationUnit public fun Flow.zipWithNext(block: (l: T, r: T) -> R): Flow { var current: T? = null @@ -26,7 +27,7 @@ public fun NumassBlock.timeHistogram( runBlocking { extractor.extract(this@timeHistogram).zipWithNext { l, r -> if(l.owner == r.owner) { - max((r.getTime() - l.getTime()).inWholeMicroseconds,0L) + max((r.getTime() - l.getTime()).toDouble(DurationUnit.SECONDS),0.0) } else { 0 } diff --git a/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassFrame.kt b/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassFrame.kt index 4ce3191..2e60fce 100644 --- a/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassFrame.kt +++ b/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassFrame.kt @@ -1,17 +1,17 @@ package ru.inr.mass.data.api -import kotlinx.datetime.Instant import kotlin.time.Duration + /** * The continuous frame of digital detector data * Created by darksnake on 06-Jul-17. - * @param time The absolute start time of the frame + * @param timeOffset The time offset relative to block start in nanos * @param tickSize The time interval per tick * @param signal The buffered signal shape in ticks */ public class NumassFrame( - public val time: Instant, + public val timeOffset: Long, public val tickSize: Duration, public val signal: ShortArray, ) { diff --git a/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassPoint.kt b/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassPoint.kt index 2626055..51be227 100644 --- a/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassPoint.kt +++ b/numass-data-model/src/commonMain/kotlin/ru/inr/mass/data/api/NumassPoint.kt @@ -89,6 +89,8 @@ public interface NumassPoint : ParentBlock { } } +public val NumassPoint.title: String get() = "p$index(HV=$voltage)" + /** * Get the first block if it exists. Throw runtime exception otherwise. * diff --git a/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/ProtoNumassPoint.kt b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/ProtoNumassPoint.kt index f9faf3c..70a62d6 100644 --- a/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/ProtoNumassPoint.kt +++ b/numass-data-proto/src/main/kotlin/ru/inr/mass/data/proto/ProtoNumassPoint.kt @@ -19,9 +19,7 @@ package ru.inr.mass.data.proto import io.ktor.utils.io.core.readBytes import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking -import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.Instant -import kotlinx.datetime.plus import okio.ByteString import org.slf4j.LoggerFactory import ru.inr.mass.data.api.* @@ -202,9 +200,9 @@ public class ProtoNumassBlock( get() { val tickSize = block.bin_size.nanoseconds return block.frames.asFlow().map { frame -> - val time = startTime.plus(frame.time, DateTimeUnit.NANOSECOND) + //val time = startTime.plus(frame.time, DateTimeUnit.NANOSECOND) val frameData = frame.data_ - NumassFrame(time, tickSize, frameData.toShortArray()) + NumassFrame(frame.time, tickSize, frameData.toShortArray()) } } diff --git a/numass-data-proto/src/test/kotlin/ru/inr/mass/data/proto/TestNumassDirectory.kt b/numass-data-proto/src/test/kotlin/ru/inr/mass/data/proto/TestNumassDirectory.kt index 5448158..8303b26 100644 --- a/numass-data-proto/src/test/kotlin/ru/inr/mass/data/proto/TestNumassDirectory.kt +++ b/numass-data-proto/src/test/kotlin/ru/inr/mass/data/proto/TestNumassDirectory.kt @@ -33,7 +33,7 @@ class TestNumassDirectory { fun testTQDCRead() = runBlocking { val pointPath = Path.of("src/test/resources", "testData/tqdc") val set: NumassSet = context.readNumassDirectory(pointPath) - val point = set.first { it.voltage == 16000.0 } + val point = set.first { it.voltage == 18200.0 } point.getChannels().forEach { (channel, block) -> println("$channel: $block") if(block is ParentBlock){ diff --git a/numass-workspace/build.gradle.kts b/numass-workspace/build.gradle.kts index 0d46591..3dd15b5 100644 --- a/numass-workspace/build.gradle.kts +++ b/numass-workspace/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - kotlin("jvm") - id("ru.mipt.npm.gradle.common") + id("ru.mipt.npm.gradle.jvm") + id("com.github.johnrengelman.shadow") version "7.1.1" `maven-publish` } @@ -11,6 +11,7 @@ kotlin { val dataforgeVersion: String by rootProject.extra val plotlyVersion: String by rootProject.extra val kmathVersion: String by rootProject.extra +val tablesVersion: String by rootProject.extra dependencies { implementation(projects.numassDataProto) @@ -19,6 +20,7 @@ dependencies { implementation("space.kscience:dataforge-workspace:$dataforgeVersion") implementation("space.kscience:plotlykt-jupyter:$plotlyVersion") implementation("space.kscience:kmath-jupyter:$kmathVersion") + implementation("space.kscience:tables-kt:$tablesVersion") } kscience{ diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/notebook/NumassJupyter.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/notebook/NumassJupyter.kt index 9d06756..1120daf 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/notebook/NumassJupyter.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/notebook/NumassJupyter.kt @@ -4,10 +4,17 @@ package ru.inr.mass.notebook import org.jetbrains.kotlinx.jupyter.api.HTML import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration import ru.inr.mass.data.api.NumassBlock +import ru.inr.mass.data.api.NumassFrame import ru.inr.mass.data.api.NumassSet +import ru.inr.mass.data.proto.NumassDirectorySet import ru.inr.mass.workspace.Numass -import ru.inr.mass.workspace.numassSet +import ru.inr.mass.workspace.plotNumassBlock +import ru.inr.mass.workspace.plotNumassSet +import space.kscience.dataforge.data.DataTree import space.kscience.plotly.Plotly +import space.kscience.plotly.scatter +import space.kscience.plotly.toHTML +import space.kscience.plotly.toPage internal class NumassJupyter : JupyterIntegration() { override fun Builder.onLoaded() { @@ -30,11 +37,26 @@ internal class NumassJupyter : JupyterIntegration() { render { + HTML(Plotly.plotNumassBlock(it).toPage().render()) + } + render { numassFrame -> + HTML( + Plotly.plot { + scatter { + x.numbers = numassFrame.signal.indices.map { numassFrame.tickSize.times(it).inWholeNanoseconds } + y.numbers = numassFrame.signal.toList() + } + }.toHTML() + ) } render { numassSet -> - HTML(Plotly.numassSet(numassSet).render(), true) + HTML(Plotly.plotNumassSet(numassSet).toPage().render()) + } + + render> { tree -> + HTML("TODO: render repository tree") } } } \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/demo.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/demo.kt index f247f88..e46515e 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/demo.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/demo.kt @@ -2,7 +2,7 @@ package ru.inr.mass.scripts import ru.inr.mass.data.proto.NumassDirectorySet import ru.inr.mass.workspace.Numass.readNumassRepository -import ru.inr.mass.workspace.numassSet +import ru.inr.mass.workspace.plotNumassSet import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.await import space.kscience.dataforge.data.getData @@ -14,5 +14,5 @@ suspend fun main() { //val dataPath = Path.of("D:\\Work\\Numass\\data\\2018_04\\Adiabacity_19\\set_4\\") //val testSet = NUMASS.context.readNumassDirectory(dataPath) val testSet = repo.getData("Adiabacity_19.set_3")?.await() ?: error("Not found") - Plotly.numassSet(testSet).makeFile() + Plotly.plotNumassSet(testSet).makeFile() } \ No newline at end of file diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/run_2021_11.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/run_2021_11.kt index 6ad4038..1a83990 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/run_2021_11.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/scripts/run_2021_11.kt @@ -1,19 +1,19 @@ package ru.inr.mass.scripts -import kotlinx.coroutines.flow.toList import kotlinx.html.h2 import kotlinx.html.p import kotlinx.serialization.json.Json import ru.inr.mass.workspace.Numass.readNumassDirectory +import ru.inr.mass.workspace.listFrames import space.kscience.dataforge.meta.MetaSerializer import space.kscience.plotly.* suspend fun main() { //val repo: DataTree = readNumassRepository("D:\\Work\\numass-data\\") - val directory = readNumassDirectory("D:\\Work\\numass-data\\set_3\\") + val directory = readNumassDirectory("D:\\Work\\Numass\\data\\test\\set_7") val point = directory.points.first() - val frames = point.frames.toList() + val frames = point.listFrames() Plotly.page { p { +"${frames.size} frames" } h2 { +"Random frames" } @@ -23,7 +23,7 @@ suspend fun main() { repeat(10) { val frame = frames.random(random) scatter { - y.numbers = frame.signal.map { it.toUShort().toInt() - Short.MAX_VALUE } + y.numbers = frame.signal.map { (it.toUShort().toInt() - Short.MAX_VALUE).toShort() } } } } @@ -31,7 +31,7 @@ suspend fun main() { plot { histogram { name = "max" - x.numbers = frames.map { frame -> frame.signal.maxOf { it.toUShort().toInt() - Short.MAX_VALUE } } + x.numbers = frames.map { frame -> frame.signal.maxOf { (it.toUShort().toInt() - Short.MAX_VALUE).toShort() } } } histogram { diff --git a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/Numass.kt b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/Numass.kt index 72be471..503b194 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/Numass.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/Numass.kt @@ -1,8 +1,10 @@ package ru.inr.mass.workspace import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import ru.inr.mass.data.api.NumassBlock import ru.inr.mass.data.api.NumassSet import ru.inr.mass.data.proto.NumassDirectorySet import ru.inr.mass.data.proto.readNumassDirectory @@ -45,3 +47,7 @@ object Numass { operator fun DataSet.get(name: String): NumassSet? = runBlocking { getData(Name.parse(name))?.await() } + +fun NumassBlock.listFrames() = runBlocking { frames.toList() } + +fun NumassBlock.listEvents() = runBlocking { events.toList() } \ 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 index e8f261f..5a7b625 100644 --- a/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/plots.kt +++ b/numass-workspace/src/main/kotlin/ru/inr/mass/workspace/plots.kt @@ -7,7 +7,10 @@ import ru.inr.mass.data.analysis.NumassAmplitudeSpectrum import ru.inr.mass.data.analysis.NumassEventExtractor import ru.inr.mass.data.analysis.amplitudeSpectrum import ru.inr.mass.data.analysis.timeHistogram +import ru.inr.mass.data.api.NumassBlock +import ru.inr.mass.data.api.NumassPoint import ru.inr.mass.data.api.NumassSet +import ru.inr.mass.data.api.title import ru.inr.mass.data.proto.HVData import ru.inr.mass.data.proto.NumassDirectorySet import space.kscience.dataforge.values.asValue @@ -20,6 +23,7 @@ import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.DoubleBuffer import space.kscience.plotly.* import space.kscience.plotly.models.* +import kotlin.time.DurationUnit /** * Plot a kmath histogram @@ -52,47 +56,89 @@ fun Plot.hvData(data: HVData): Trace = scatter { y.numbers = data.map { it.value } } -fun Plotly.numassSet( - set: NumassSet, + +fun Plotly.plotNumassBlock( + block: NumassBlock, amplitudeBinSize: UInt = 20U, eventExtractor: NumassEventExtractor = NumassEventExtractor.EVENTS_ONLY, -): PlotlyPage = - Plotly.page { - h1 { - +"Numass point set ${ShapeType.path}" - } - h2 { - +"Amplitude spectrum" - } - plot { - runBlocking { - set.points.sortedBy { it.index }.forEach { - histogram(it.amplitudeSpectrum(eventExtractor), amplitudeBinSize) + splitChannels: Boolean = true +): PlotlyFragment = Plotly.fragment { + plot { + runBlocking { + if (splitChannels && block is NumassPoint) { + block.getChannels().forEach { (channel, channelBlock) -> + val spectrum = channelBlock.amplitudeSpectrum(eventExtractor) + histogram(spectrum, amplitudeBinSize) { + name = block.title + "[$channel]" + } } - } - } - - h2 { - +"Time spectra" - } - plot { - set.points.sortedBy { it.index }.forEach { - histogram(it.timeHistogram(1e3)) - } - layout.yaxis.type = AxisType.log - - } - if (set is NumassDirectorySet) { - set.getHvData()?.let { entries -> - h2 { - +"HV" - } - plot { - hvData(entries) + } else { + scatter { + val spectrum = block.amplitudeSpectrum(eventExtractor) + histogram(spectrum, amplitudeBinSize) } } } } +} + +fun Plotly.plotNumassSet( + set: NumassSet, + amplitudeBinSize: UInt = 20U, + eventExtractor: NumassEventExtractor = NumassEventExtractor.EVENTS_ONLY, +): PlotlyFragment = Plotly.fragment { + + h1 { +"Numass point set ${(set as? NumassDirectorySet)?.path ?: ""}" } + + //TODO do in parallel + val spectra = runBlocking { + set.points.sortedBy { it.index }.map { it to it.amplitudeSpectrum(eventExtractor) } + } + + h2 { +"Amplitude spectrum" } + + plot { + spectra.forEach { (point, spectrum) -> + histogram(spectrum, amplitudeBinSize) { + name = point.title + } + } + } + + h2 { +"Time spectra" } + + plot { + spectra.forEach { (point,spectrum) -> + val countRate = runBlocking { + spectrum.sum().toDouble() / point.getLength().toDouble(DurationUnit.SECONDS) + } + val binSize = 1.0 / countRate / 10.0 + histogram(point.timeHistogram(binSize)) { + name = point.title + } + } + layout.yaxis.type = AxisType.log + } + + h2 { +"Integral spectrum" } + + plot { + scatter { + mode = ScatterMode.markers + x.numbers = spectra.map { it.first.voltage } + y.numbers = spectra.map { it.second.sum().toLong() } + } + } + + if (set is NumassDirectorySet) { + set.getHvData()?.let { entries -> + h2 { +"HV" } + plot { + hvData(entries) + } + } + } +} /** * Add a number buffer accessor for Plotly trace values