Building function servers

This commit is contained in:
Alexander Nozik 2018-03-20 22:08:37 +03:00
parent ae97c0be68
commit caf8897194
38 changed files with 637 additions and 804 deletions

View File

@ -11,7 +11,7 @@ class ServerApp : App(BoardView::class) {
override fun start(stage: Stage) {
getConfig(this@ServerApp).ifPresent {
getConfig(this@ServerApp)?.let {
controller.configure(it)
}

View File

@ -49,7 +49,7 @@ internal fun createChannel(meta: Meta): PKT8Channel {
/**
* Created by darksnake on 28-Sep-16.
*/
class PKT8Channel(private val _meta: Meta, private val func: (Double) -> Double) : Named, Metoid {
class PKT8Channel(override val meta: Meta, private val func: (Double) -> Double) : Named, Metoid {
private val _name: String by meta.stringValue()
@ -57,10 +57,6 @@ class PKT8Channel(private val _meta: Meta, private val func: (Double) -> Double)
return _name
}
override fun getMeta(): Meta {
return _meta
}
fun description(): String {
return meta.getString("description", "")
}

View File

@ -85,7 +85,7 @@ class PKT8Device(context: Context, meta: Meta) : PortSensor(context, meta) {
val abuf: String by stringState(ABUF)
private val duration = Duration.parse(getMeta().getString("averagingDuration", "PT30S"))
private val duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
private fun buildLoader(connection: StorageConnection): TableLoader {
val storage = connection.storage

View File

@ -176,7 +176,7 @@ class PKT8Display : DeviceDisplay<PKT8Device>(), PKT8ValueListener {
}
init {
if (device.getMeta().hasMeta("plotConfig")) {
if (device.meta.hasMeta("plotConfig")) {
with(plotFrame.plots) {
//configure(device.meta().getMeta("plotConfig"))
TimePlot.setMaxItems(this, 1000)
@ -204,7 +204,7 @@ class PKT8Display : DeviceDisplay<PKT8Device>(), PKT8ValueListener {
} else {
device.channels.values.find { it.name == channelName }?.let {
TimePlot(it.name).apply {
configure(it.getMeta())
configure(it.meta)
plotFrame.add(this)
}
}

View File

@ -74,7 +74,7 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
val isFilamentOn: Boolean
get() = getState("filamentOn").booleanValue()
private val averagingDuration: Duration = Duration.parse(getMeta().getString("averagingDuration", "PT30S"))
private val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
@Throws(ControlException::class)
override fun init() {
@ -140,7 +140,7 @@ class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
@Throws(MeasurementException::class)
override fun createMeasurement(): PeakJumpMeasurement {
val measurementMeta = getMeta().getMeta("peakJump")
val measurementMeta = meta.getMeta("peakJump")
val s = measurementMeta.getString("type", "peakJump")
if (s == "peakJump") {
val measurement = PeakJumpMeasurement(measurementMeta)

View File

@ -71,7 +71,7 @@ class MspDisplay() : DeviceDisplay<MspDevice>(), DeviceListener, NamedValueListe
inner class MspView : View("Numass mass-spectrometer measurement") {
private val plotFrameMeta: Meta = device.getMeta().getMeta("plotConfig", device.meta)
private val plotFrameMeta: Meta = device.meta.getMeta("plotConfig", device.meta)
private val plotFrame: PlotFrame by lazy {
val basePlotConfig = MetaBuilder("plotFrame")

View File

@ -68,7 +68,7 @@ class VacCollectorDisplay : DeviceDisplay<VacCollectorDevice>() {
private val plottables = TimePlottableGroup().apply {
viewList.forEach {
val plot = TimePlot(it.getTitle(), it.device.name)
plot.configure(it.device.getMeta())
plot.configure(it.device.meta)
add(plot)
}
setValue("thickness", 3)

View File

@ -6,7 +6,6 @@
package inr.numass.control.readvac
import hep.dataforge.control.devices.Device
import hep.dataforge.control.devices.PortSensor.CONNECTED_STATE
import hep.dataforge.control.devices.Sensor
import hep.dataforge.control.measurements.Measurement
import hep.dataforge.control.measurements.MeasurementListener
@ -78,7 +77,7 @@ open class VacDisplay : DeviceDisplay<Sensor>(), MeasurementListener {
}
fun getTitle(): String{
return device.getMeta().getString("title", device.name);
return device.meta.getString("title", device.name);
}
inner class VacView : View("Numass vacuumeter ${getTitle()}") {
@ -123,7 +122,7 @@ open class VacDisplay : DeviceDisplay<Sensor>(), MeasurementListener {
prefHeight = 60.0
alignment = Pos.CENTER_RIGHT
textProperty().bind(valueProperty)
device.getMeta().optValue("color").ifPresent { colorValue -> textFill = Color.valueOf(colorValue.stringValue()) }
device.meta.optValue("color").ifPresent { colorValue -> textFill = Color.valueOf(colorValue.stringValue()) }
style {
fontSize = 24.pt
fontWeight = FontWeight.BOLD
@ -136,7 +135,7 @@ open class VacDisplay : DeviceDisplay<Sensor>(), MeasurementListener {
prefHeight = 60.0
prefWidth = 75.0
alignment = Pos.CENTER_LEFT
text = device.getMeta().getString("units", "mbar")
text = device.meta.getString("units", "mbar")
style {
fontSize = 24.pt
}

View File

@ -2,8 +2,8 @@ package inr.numass
import hep.dataforge.io.envelopes.*
import hep.dataforge.values.Value
import inr.numass.data.legacy.NumassFileEnvelope.LEGACY_END_SEQUENCE
import inr.numass.data.legacy.NumassFileEnvelope.LEGACY_START_SEQUENCE
import inr.numass.data.legacy.NumassFileEnvelope.Companion.LEGACY_END_SEQUENCE
import inr.numass.data.legacy.NumassFileEnvelope.Companion.LEGACY_START_SEQUENCE
import org.slf4j.LoggerFactory
import java.io.IOException
import java.nio.ByteBuffer

View File

@ -1,17 +1,16 @@
package inr.numass.data
import hep.dataforge.io.envelopes.Envelope
import hep.dataforge.kodex.nullable
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import inr.numass.data.api.NumassBlock
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import inr.numass.data.api.SimpleNumassPoint
import inr.numass.data.api.*
import inr.numass.data.storage.ProtoBlock
import java.io.InputStream
import java.util.stream.Collectors
import java.util.stream.Stream
import java.util.zip.ZipInputStream
import kotlin.streams.asSequence
/**
* Created by darksnake on 30-Jan-17.
@ -19,16 +18,16 @@ import java.util.zip.ZipInputStream
object NumassDataUtils {
fun join(name: String, sets: Collection<NumassSet>): NumassSet {
return object : NumassSet {
override fun getPoints(): Stream<NumassPoint> {
override val points: Stream<out NumassPoint> by lazy {
val points = sets.stream().flatMap<NumassPoint> { it.points }
.collect(Collectors.groupingBy<NumassPoint, Double> { it.voltage })
return points.entries.stream().map { entry -> SimpleNumassPoint(entry.key, entry.value) }
points.entries.stream().map { entry -> SimpleNumassPoint(entry.key, entry.value) }
}
override fun getMeta(): Meta {
override val meta: Meta by lazy {
val metaBuilder = MetaBuilder()
sets.forEach { set -> metaBuilder.putNode(set.name, set.meta) }
return metaBuilder
metaBuilder
}
override fun getName(): String {
@ -52,9 +51,41 @@ val Envelope.dataStream: InputStream
this.data.stream
}
val NumassBlock.channel: Int
val NumassBlock.channel: Int?
get() = if (this is ProtoBlock) {
this.channel
} else {
0
this.meta.optValue("channel").map { it.intValue() }.nullable
}
fun NumassBlock.transformChain(transform: (NumassEvent, NumassEvent) -> Pair<Short, Long>): NumassBlock {
return SimpleBlock(this.startTime, this.length, this.meta) { owner ->
this.events.asSequence()
.sortedBy { it.timeOffset }
.zipWithNext(transform).map { NumassEvent(it.first, it.second, owner) }.asIterable()
}
}
fun NumassBlock.filterChain(condition: (NumassEvent, NumassEvent) -> Boolean): NumassBlock {
return SimpleBlock(this.startTime, this.length, this.meta) { owner ->
this.events.asSequence()
.sortedBy { it.timeOffset }
.zipWithNext().filter { condition.invoke(it.first, it.second) }.map { it.second }.asIterable()
}
}
fun NumassBlock.filter(condition: (NumassEvent) -> Boolean): NumassBlock {
return SimpleBlock(this.startTime, this.length, this.meta) { owner ->
this.events.asSequence()
.filter(condition).asIterable()
}
}
fun NumassBlock.transform(transform: (NumassEvent) -> OrphanNumassEvent): NumassBlock {
return SimpleBlock(this.startTime, this.length, this.meta) { owner ->
this.events.asSequence()
.map { transform(it).adopt(owner) }
.asIterable()
}
}

View File

@ -88,7 +88,7 @@ class TimeAnalyzer @JvmOverloads constructor(private val processor: SignalProces
.reduce(null) { v1, v2 -> this.combineBlockResults(v1, v2) }
val map = HashMap(res.asMap())
map.put(HV_KEY, Value.of(point.voltage))
map[HV_KEY] = Value.of(point.voltage)
return ValueMap(map)
}

View File

@ -1,5 +1,6 @@
package inr.numass.data.api
import hep.dataforge.meta.Meta
import java.time.Duration
import java.time.Instant
import java.util.*
@ -9,9 +10,14 @@ import java.util.stream.Stream
* A block constructed from a set of other blocks. Internal blocks are not necessary subsequent. Blocks are automatically sorted.
* Created by darksnake on 16.07.2017.
*/
class MetaBlock : NumassBlock {
class MetaBlock(blocks: Collection<NumassBlock>, override val meta: Meta = Meta.empty()) : NumassBlock {
private val blocks = TreeSet(Comparator.comparing<NumassBlock, Instant>{ it.startTime })
init{
this.blocks.addAll(blocks)
}
override val startTime: Instant
get() = blocks.first().startTime
@ -28,11 +34,5 @@ class MetaBlock : NumassBlock {
.sorted(Comparator.comparing<NumassBlock, Instant>{ it.startTime })
.flatMap{ it.frames }
constructor(vararg blocks: NumassBlock) {
this.blocks.addAll(Arrays.asList(*blocks))
}
constructor(blocks: Collection<NumassBlock>) {
this.blocks.addAll(blocks)
}
}

View File

@ -1,12 +1,33 @@
package inr.numass.data.api
import hep.dataforge.meta.Meta
import hep.dataforge.meta.Metoid
import inr.numass.data.channel
import kotlinx.coroutines.experimental.runBlocking
import java.io.Serializable
import java.time.Duration
import java.time.Instant
import java.util.stream.Stream
typealias NumassChannel = Int
/**
* A single numass event with given amplitude and time.
*
* @author Darksnake
* @property amp the amplitude of the event
* @property timeOffset time in nanoseconds relative to block start
* @property owner an owner block for this event
*
*/
class NumassEvent(val amp: Short, val timeOffset: Long, val owner: NumassBlock) : Serializable {
val channel: Int?
get() = owner.channel
val time: Instant
get() = owner.startTime.plusNanos(timeOffset)
}
/**
@ -15,13 +36,7 @@ typealias NumassChannel = Int
*
* Created by darksnake on 06-Jul-17.
*/
interface NumassBlock {
/**
* A channel
*/
val channel: NumassChannel
get() = DEFAULT_CHANNEL
interface NumassBlock : Metoid {
/**
* The absolute start time of the block
@ -42,8 +57,37 @@ interface NumassBlock {
* Stream of frames. Could be empty
*/
val frames: Stream<NumassFrame>
companion object {
val DEFAULT_CHANNEL: NumassChannel = -1
}
}
typealias OrphanNumassEvent = Pair<Short, Long>
inline fun OrphanNumassEvent.adopt(parent: NumassBlock): NumassEvent {
return NumassEvent(this.first, this.second, parent)
}
val OrphanNumassEvent.timeOffset: Long
get() = this.second
val OrphanNumassEvent.amp: Short
get() = this.first
/**
* A simple in-memory implementation of block of events. No frames are allowed
* Created by darksnake on 08.07.2017.
*/
class SimpleBlock(
override val startTime: Instant,
override val length: Duration,
override val meta: Meta = Meta.empty(),
producer: suspend (NumassBlock) -> Iterable<NumassEvent>) : NumassBlock, Serializable {
private val eventList = runBlocking { producer(this@SimpleBlock).toList()}
override val frames: Stream<NumassFrame> = Stream.empty()
override val events: Stream<NumassEvent>
get() = eventList.stream()
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inr.numass.data.api
import java.io.Serializable
import java.time.Instant
/**
* A single numass event with given amplitude and time.
*
* @author Darksnake
* @property amp the amplitude of the event
* @property blockTime
* @property timeOffset time in nanoseconds relative to block start
*
*/
class NumassEvent(val amp: Short, val timeOffset: Long, val block: NumassBlock? = null) : Serializable {
val channel: NumassChannel?
get() = block?.channel
val time: Instant
get() = (block?.startTime ?: Instant.EPOCH).plusNanos(timeOffset)
}

View File

@ -23,7 +23,7 @@ import java.util.stream.Stream
*/
interface NumassSet : Named, Metoid, Iterable<NumassPoint>, Provider {
val points: Stream<NumassPoint>
val points: Stream<out NumassPoint>
/**
* Get the first point if it exists. Throw runtime exception otherwise.
@ -58,7 +58,7 @@ interface NumassSet : Named, Metoid, Iterable<NumassPoint>, Provider {
* @param voltage
* @return
*/
fun optPoint(voltage: Double): Optional<NumassPoint> {
fun optPoint(voltage: Double): Optional<out NumassPoint> {
return points.filter { it -> it.voltage == voltage }.findFirst()
}
@ -73,7 +73,7 @@ interface NumassSet : Named, Metoid, Iterable<NumassPoint>, Provider {
}
@Provides(NUMASS_POINT_PROVIDER_KEY)
fun optPoint(voltage: String): Optional<NumassPoint> {
fun optPoint(voltage: String): Optional<out NumassPoint> {
return optPoint(java.lang.Double.parseDouble(voltage))
}

View File

@ -1,19 +0,0 @@
package inr.numass.data.api
import java.io.Serializable
import java.time.Duration
import java.time.Instant
import java.util.stream.Stream
/**
* A simple in-memory implementation of block of events. No frames are allowed
* Created by darksnake on 08.07.2017.
*/
class SimpleBlock(override val startTime: Instant, override val length: Duration, private val eventList: List<NumassEvent>) : NumassBlock, Serializable {
override val frames: Stream<NumassFrame> = Stream.empty()
override val events: Stream<NumassEvent>
get() = eventList.stream()
}

View File

@ -24,14 +24,13 @@ import java.util.stream.Stream
*/
class NumassDatFile @Throws(IOException::class)
constructor(private val name: String, private val path: Path, meta: Meta) : NumassSet {
private val meta: Meta
override val meta: Meta
private val hVdev: Double
get() = getMeta().getDouble("dat.hvDev", 2.468555393226049)
get() = meta.getDouble("dat.hvDev", 2.468555393226049)
override//int lab = readBlock(channel,1).get();
//TODO check point start
val points: Stream<NumassPoint>
override val points: Stream<NumassPoint>
get() = try {
Files.newByteChannel(path, READ).use { channel ->
var lab: Int
@ -58,16 +57,12 @@ constructor(private val name: String, private val path: Path, meta: Meta) : Numa
.build()
}
override fun getMeta(): Meta {
return meta
}
override fun getName(): String {
return name
}
private fun hasUset(): Boolean {
return getMeta().getBoolean("dat.uSet", true)
return meta.getBoolean("dat.uSet", true)
}
@Throws(IOException::class)
@ -127,10 +122,10 @@ constructor(private val name: String, private val path: Path, meta: Meta) : Numa
if (phoneFlag) {
timeDiv /= 20.0
length = (length*20).toShort()
length = (length * 20).toShort()
}
val events = ArrayList<NumassEvent>()
val events = ArrayList<Pair<Short, Long>>()
var lab = readBlock(channel, 1).get().toInt()
while (lab == 0xBF) {
@ -161,13 +156,15 @@ constructor(private val name: String, private val path: Path, meta: Meta) : Numa
val uRead = ending.getInt(39)
val uSet: Double
if (!this.hasUset()) {
uSet = uRead.toDouble() / 10.0 / hVdev
uSet = if (!this.hasUset()) {
uRead.toDouble() / 10.0 / hVdev
} else {
uSet = voltage / 10.0
voltage / 10.0
}
val block = SimpleBlock(absoluteTime.toInstant(ZoneOffset.UTC), Duration.ofSeconds(length.toLong()), events)
val block = SimpleBlock(absoluteTime.toInstant(ZoneOffset.UTC), Duration.ofSeconds(length.toLong())) { parent ->
events.map { it.adopt(parent) }
}
val pointMeta = MetaBuilder("point")
.setValue(HV_KEY, uSet)
@ -192,7 +189,7 @@ constructor(private val name: String, private val path: Path, meta: Meta) : Numa
}
@Throws(IOException::class)
private fun readEvent(channel: SeekableByteChannel, b: Int, timeDiv: Double): NumassEvent {
private fun readEvent(channel: SeekableByteChannel, b: Int, timeDiv: Double): OrphanNumassEvent {
val chanel: Short
val time: Long
@ -219,7 +216,7 @@ constructor(private val name: String, private val path: Path, meta: Meta) : Numa
else -> throw IOException("Event head expected")
}
return NumassEvent(chanel, (time / timeDiv).toLong())
return Pair(chanel, (time / timeDiv).toLong())
}
@Throws(IOException::class)

View File

@ -22,55 +22,38 @@ import java.util.stream.StreamSupport
*/
class ClassicNumassPoint(private val envelope: Envelope) : NumassPoint {
override fun getBlocks(): Stream<NumassBlock> {
// double u = envelope.meta().getDouble("external_meta.HV1_value", 0);
val length: Long
if (envelope.meta.hasValue("external_meta.acquisition_time")) {
length = envelope.meta.getValue("external_meta.acquisition_time").longValue()
} else {
length = envelope.meta.getValue("acquisition_time").longValue()
override val blocks: Stream<NumassBlock>
get() {
val length: Long = if (envelope.meta.hasValue("external_meta.acquisition_time")) {
envelope.meta.getValue("external_meta.acquisition_time").longValue()
} else {
envelope.meta.getValue("acquisition_time").longValue()
}
return Stream.of(ClassicBlock(startTime, Duration.ofSeconds(length)))
}
return Stream.of(ClassicBlock(startTime, Duration.ofSeconds(length)))
}
override fun getStartTime(): Instant {
return if (meta.hasValue("start_time")) {
override val startTime: Instant
get() = if (meta.hasValue("start_time")) {
meta.getValue("start_time").timeValue()
} else {
Instant.EPOCH
}
}
override fun getVoltage(): Double {
return meta.getDouble("external_meta.HV1_value", 0.0)
}
override val meta: Meta = envelope.meta
override fun getIndex(): Int {
return meta.getInt("external_meta.point_index", -1)
}
override val voltage: Double = meta.getDouble("external_meta.HV1_value", 0.0)
override val index: Int = meta.getInt("external_meta.point_index", -1)
override fun getMeta(): Meta {
return envelope.meta
}
//TODO split blocks using meta
private inner class ClassicBlock
// private final long blockOffset;
private inner class ClassicBlock(
override val startTime: Instant,
override val length: Duration,
override val meta: Meta = Meta.empty()) : NumassBlock, Iterable<NumassEvent> {
(private val startTime: Instant, private val length: Duration)// this.blockOffset = blockOffset;
: NumassBlock, Iterable<NumassEvent> {
override fun getStartTime(): Instant {
return startTime
}
override fun getLength(): Duration {
return length
}
override fun getEvents(): Stream<NumassEvent> {
return StreamSupport.stream(this.spliterator(), false)
}
override val events: Stream<NumassEvent>
get() = StreamSupport.stream(this.spliterator(), false)
override fun iterator(): Iterator<NumassEvent> {
val timeCoef = envelope.meta.getDouble("time_coeff", 50.0)
@ -84,16 +67,16 @@ class ClassicNumassPoint(private val envelope: Envelope) : NumassPoint {
override fun hasNext(): Boolean {
try {
if (buffer.hasRemaining()) {
return true
return if (buffer.hasRemaining()) {
true
} else {
buffer.flip()
val num = channel.read(buffer)
if (num > 0) {
buffer.flip()
return true
true
} else {
return false
false
}
}
} catch (e: IOException) {
@ -104,10 +87,10 @@ class ClassicNumassPoint(private val envelope: Envelope) : NumassPoint {
}
override fun next(): NumassEvent {
val channel = java.lang.Short.toUnsignedInt(buffer.short).toShort()
val amp = java.lang.Short.toUnsignedInt(buffer.short).toShort()
val time = Integer.toUnsignedLong(buffer.int)
val status = buffer.get() // status is ignored
return NumassEvent(channel, startTime, (time * timeCoef).toLong())
return NumassEvent(amp, (time * timeCoef).toLong(), this@ClassicBlock)
}
}
} catch (ex: IOException) {
@ -117,9 +100,7 @@ class ClassicNumassPoint(private val envelope: Envelope) : NumassPoint {
}
override fun getFrames(): Stream<NumassFrame> {
return Stream.empty()
}
override val frames: Stream<NumassFrame> = Stream.empty()
}
companion object {

View File

@ -53,6 +53,8 @@ class NumassDataLoader(
override var isReadOnly: Boolean = true
) : AbstractLoader(storage, name, meta), ObjectLoader<Envelope>, NumassSet, Provider {
override val meta: Meta = items[META_FRAGMENT_NAME]?.get()?.meta ?: Meta.empty()
private val hvEnvelope: Optional<Envelope>
get() = Optional.ofNullable(items[HV_FRAGMENT_NAME]).map { it.get() }
@ -74,13 +76,8 @@ class NumassDataLoader(
return items.keys
}
override fun getMeta(): Meta {
return items[META_FRAGMENT_NAME]?.get()?.meta ?: Meta.empty()
}
override fun getHvData(): Optional<Table> {
return hvEnvelope.map { hvEnvelope ->
override val hvData: Optional<Table>
get() = hvEnvelope.map { hvEnvelope ->
try {
ColumnedDataReader(hvEnvelope.data.stream, "timestamp", "block", "value").toTable()
} catch (ex: IOException) {
@ -89,11 +86,11 @@ class NumassDataLoader(
}
}
}
override fun getPoints(): Stream<NumassPoint> {
return pointEnvelopes.map { ClassicNumassPoint(it) }
}
override val points: Stream<NumassPoint>
get() {
return pointEnvelopes.map { ClassicNumassPoint(it) }
}
override fun pull(fragmentName: String): Envelope {
//PENDING read data to memory?
@ -111,9 +108,8 @@ class NumassDataLoader(
throw TODO("Not supported yet.")
}
override fun getStartTime(): Instant {
return meta.optValue("start_time").map<Instant> { it.timeValue() }.orElseGet { super.startTime }
}
override val startTime: Instant = meta.optValue("start_time").map<Instant> { it.timeValue() }.orElseGet { super.startTime }
override val isOpen: Boolean
get() = true
@ -161,7 +157,7 @@ class NumassDataLoader(
|| fileName.startsWith(POINT_FRAGMENT_NAME))
}.forEach { file ->
try {
items[FileStorage.entryName(file)] = Supplier{ NumassFileEnvelope.open(file, true) }
items[FileStorage.entryName(file)] = Supplier { NumassFileEnvelope.open(file, true) }
} catch (ex: Exception) {
LoggerFactory.getLogger(NumassDataLoader::class.java)
.error("Can't load numass data directory " + FileStorage.entryName(directory), ex)

View File

@ -3,6 +3,7 @@ package inr.numass.data.storage
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.io.envelopes.Envelope
import hep.dataforge.kodex.buildMeta
import hep.dataforge.meta.Meta
import inr.numass.data.NumassProto
import inr.numass.data.api.NumassBlock
@ -36,14 +37,12 @@ class ProtoNumassPoint(private val envelope: Envelope) : NumassPoint {
get() = point.channelsList.stream()
.flatMap { channel ->
channel.blocksList.stream()
.map { block -> ProtoBlock(channel.num.toInt(), block, meta) }
.map { block -> ProtoBlock(channel.num.toInt(), block) }
.sorted(Comparator.comparing<ProtoBlock, Instant> { it.startTime })
}
override fun getMeta(): Meta {
return envelope.meta
}
override val meta: Meta =envelope.meta
companion object {
fun readFile(path: Path): ProtoNumassPoint {
@ -62,7 +61,12 @@ class ProtoNumassPoint(private val envelope: Envelope) : NumassPoint {
}
}
class ProtoBlock(override val channel: Int, private val block: NumassProto.Point.Channel.Block, private val meta: Meta) : NumassBlock {
class ProtoBlock(val channel: Int, private val block: NumassProto.Point.Channel.Block) : NumassBlock {
override val meta: Meta by lazy {
buildMeta{
"channel" to channel
}
}
override val startTime: Instant
get() = ProtoNumassPoint.ofEpochNanos(block.time)

View File

@ -1,155 +1,157 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.scripts
import hep.dataforge.grind.Grind
import hep.dataforge.values.Values
import inr.numass.NumassUtils
import inr.numass.data.api.NumassPoint
import inr.numass.data.storage.NumassDataLoader
import inr.numass.utils.NMEventGeneratorWithPulser
import inr.numass.utils.PileUpSimulator
import inr.numass.utils.UnderflowCorrection
import org.apache.commons.math3.random.JDKRandomGenerator
rnd = new JDKRandomGenerator();
////Loading data
File dataDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_1\\set_28")
//File dataDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_2_wide\\set_7")
if (!dataDir.exists()) {
println "dataDir directory does not exist"
}
def data = NumassDataLoader.fromLocalDir(null, dataDir).getNMPoints()
//File rootDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_1")
////File rootDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_2_wide")
////File rootDir = new File("D:\\Work\\Numass\\data\\2017_01\\Fill_2_wide")
///*
// * To change this license header, choose License Headers in Project Properties.
// * To change this template file, choose Tools | Templates
// * and open the template in the editor.
// */
//
//NumassStorage storage = NumassStorage.buildLocalNumassRoot(rootDir, true);
//package inr.numass.scripts
//
//Collection<NMPoint> data = NumassDataUtils.joinSpectra(
// StorageUtils.loaderStream(storage)
// .filter { it.key.matches("set_3.") }
// .map {
// println "loading ${it.key}"
// it.value
//import hep.dataforge.grind.Grind
//import hep.dataforge.values.Values
//import inr.numass.NumassUtils
//import inr.numass.data.api.NumassPoint
//import inr.numass.data.storage.NumassDataLoader
//
//import inr.numass.data.PileUpSimulator
//import inr.numass.utils.UnderflowCorrection
//import org.apache.commons.math3.random.JDKRandomGenerator
//
//rnd = new JDKRandomGenerator();
//
//////Loading data
//File dataDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_1\\set_28")
////File dataDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_2_wide\\set_7")
//if (!dataDir.exists()) {
// println "dataDir directory does not exist"
//}
//def data = NumassDataLoader.fromLocalDir(null, dataDir).getNMPoints()
//
////File rootDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_1")
//////File rootDir = new File("D:\\Work\\Numass\\data\\2016_10\\Fill_2_wide")
//////File rootDir = new File("D:\\Work\\Numass\\data\\2017_01\\Fill_2_wide")
////
////NumassStorage storage = NumassStorage.buildLocalNumassRoot(rootDir, true);
////
////Collection<NMPoint> data = NumassDataUtils.joinSpectra(
//// StorageUtils.loaderStream(storage)
//// .filter { it.key.matches("set_3.") }
//// .map {
//// println "loading ${it.key}"
//// it.value
//// }
////)
//
////Simulation process
//Map<String, List<NumassPoint>> res = [:]
//
//List<NumassPoint> generated = new ArrayList<>();
//List<NumassPoint> registered = new ArrayList<>();
//List<NumassPoint> firstIteration = new ArrayList<>();
//List<NumassPoint> secondIteration = new ArrayList<>();
//List<NumassPoint> pileup = new ArrayList<>();
//
//lowerChannel = 400;
//upperChannel = 1800;
//
//PileUpSimulator buildSimulator(NumassPoint point, double cr, NumassPoint reference = null, boolean extrapolate = true, double scale = 1d) {
// def cfg = Grind.buildMeta(cr: cr) {
// pulser(mean: 3450, sigma: 86.45, freq: 66.43)
// }
// //NMEventGeneratorWithPulser generator = new NMEventGeneratorWithPulser(rnd, cfg)
//
// def generator = b
//
// if (extrapolate) {
// double[] chanels = new double[RawNMPoint.MAX_CHANEL];
// double[] values = new double[RawNMPoint.MAX_CHANEL];
// Values fitResult = new UnderflowCorrection().fitPoint(point, 400, 600, 1800, 20);
//
// def amp = fitResult.getDouble("amp")
// def sigma = fitResult.getDouble("expConst")
// if (sigma > 0) {
//
// for (int i = 0; i < upperChannel; i++) {
// chanels[i] = i;
// if (i < lowerChannel) {
// values[i] = point.getLength()*amp * Math.exp((i as double) / sigma)
// } else {
// values[i] = Math.max(0, point.getCount(i) - (reference == null ? 0 : reference.getCount(i)) as int);
// }
// }
//)
//Simulation process
Map<String, List<NumassPoint>> res = [:]
List<NumassPoint> generated = new ArrayList<>();
List<NumassPoint> registered = new ArrayList<>();
List<NumassPoint> firstIteration = new ArrayList<>();
List<NumassPoint> secondIteration = new ArrayList<>();
List<NumassPoint> pileup = new ArrayList<>();
lowerChannel = 400;
upperChannel = 1800;
PileUpSimulator buildSimulator(NumassPoint point, double cr, NumassPoint reference = null, boolean extrapolate = true, double scale = 1d) {
def cfg = Grind.buildMeta(cr: cr) {
pulser(mean: 3450, sigma: 86.45, freq: 66.43)
}
NMEventGeneratorWithPulser generator = new NMEventGeneratorWithPulser(rnd, cfg)
if (extrapolate) {
double[] chanels = new double[RawNMPoint.MAX_CHANEL];
double[] values = new double[RawNMPoint.MAX_CHANEL];
Values fitResult = new UnderflowCorrection().fitPoint(point, 400, 600, 1800, 20);
def amp = fitResult.getDouble("amp")
def sigma = fitResult.getDouble("expConst")
if (sigma > 0) {
for (int i = 0; i < upperChannel; i++) {
chanels[i] = i;
if (i < lowerChannel) {
values[i] = point.getLength()*amp * Math.exp((i as double) / sigma)
} else {
values[i] = Math.max(0, point.getCount(i) - (reference == null ? 0 : reference.getCount(i)) as int);
}
}
generator.loadSpectrum(chanels, values)
} else {
generator.loadSpectrum(point, reference, lowerChannel, upperChannel);
}
} else {
generator.loadSpectrum(point, reference, lowerChannel, upperChannel);
}
return new PileUpSimulator(point.length * scale, rnd, generator).withUset(point.voltage).generate();
}
double adjustCountRate(PileUpSimulator simulator, NumassPoint point) {
double generatedInChannel = simulator.generated().getCountInWindow(lowerChannel, upperChannel);
double registeredInChannel = simulator.registered().getCountInWindow(lowerChannel, upperChannel);
return (generatedInChannel / registeredInChannel) * (point.getCountInWindow(lowerChannel, upperChannel) / point.getLength());
}
data.forEach { point ->
double cr = NumassUtils.countRateWithDeadTime(point, lowerChannel, upperChannel, 6.55e-6);
PileUpSimulator simulator = buildSimulator(point, cr);
//second iteration to exclude pileup overlap
NumassPoint pileupPoint = simulator.pileup();
firstIteration.add(simulator.registered());
//updating count rate
cr = adjustCountRate(simulator, point);
simulator = buildSimulator(point, cr, pileupPoint);
pileupPoint = simulator.pileup();
secondIteration.add(simulator.registered());
cr = adjustCountRate(simulator, point);
simulator = buildSimulator(point, cr, pileupPoint);
generated.add(simulator.generated());
registered.add(simulator.registered());
pileup.add(simulator.pileup());
}
res.put("original", data);
res.put("generated", generated);
res.put("registered", registered);
// res.put("firstIteration", new SimulatedPoint("firstIteration", firstIteration));
// res.put("secondIteration", new SimulatedPoint("secondIteration", secondIteration));
res.put("pileup", pileup);
def keys = res.keySet();
//print spectra for selected point
double u = 16500d;
List<Map> points = res.values().collect { it.find { it.voltage == u }.getMap(20, true) }
println "\n Spectrum example for U = ${u}\n"
print "channel\t"
println keys.join("\t")
points.first().keySet().each {
print "${it}\t"
println points.collect { map -> map[it] }.join("\t")
}
//printing count rate in window
print "U\tLength\t"
print keys.collect { it + "[total]" }.join("\t") + "\t"
print keys.collect { it + "[pulse]" }.join("\t") + "\t"
println keys.join("\t")
for (int i = 0; i < data.size(); i++) {
print "${data.get(i).getVoltage()}\t"
print "${data.get(i).getLength()}\t"
print keys.collect { res[it].get(i).getTotalCount() }.join("\t") + "\t"
print keys.collect { res[it].get(i).getCountInWindow(3100, 3800) }.join("\t") + "\t"
println keys.collect { res[it].get(i).getCountInWindow(400, 3100) }.join("\t")
}
// generator.loadSpectrum(chanels, values)
// } else {
// generator.loadSpectrum(point, reference, lowerChannel, upperChannel);
// }
// } else {
// generator.loadSpectrum(point, reference, lowerChannel, upperChannel);
// }
//
// return new PileUpSimulator(point.length * scale, rnd, generator).withUset(point.voltage).generate();
//}
//
//double adjustCountRate(PileUpSimulator simulator, NumassPoint point) {
// double generatedInChannel = simulator.generated().getCountInWindow(lowerChannel, upperChannel);
// double registeredInChannel = simulator.registered().getCountInWindow(lowerChannel, upperChannel);
// return (generatedInChannel / registeredInChannel) * (point.getCountInWindow(lowerChannel, upperChannel) / point.getLength());
//}
//
//data.forEach { point ->
// double cr = NumassUtils.countRateWithDeadTime(point, lowerChannel, upperChannel, 6.55e-6);
//
// PileUpSimulator simulator = buildSimulator(point, cr);
//
// //second iteration to exclude pileup overlap
// NumassPoint pileupPoint = simulator.pileup();
// firstIteration.add(simulator.registered());
//
// //updating count rate
// cr = adjustCountRate(simulator, point);
// simulator = buildSimulator(point, cr, pileupPoint);
//
// pileupPoint = simulator.pileup();
// secondIteration.add(simulator.registered());
//
// cr = adjustCountRate(simulator, point);
// simulator = buildSimulator(point, cr, pileupPoint);
//
// generated.add(simulator.generated());
// registered.add(simulator.registered());
// pileup.add(simulator.pileup());
//}
//res.put("original", data);
//res.put("generated", generated);
//res.put("registered", registered);
//// res.put("firstIteration", new SimulatedPoint("firstIteration", firstIteration));
//// res.put("secondIteration", new SimulatedPoint("secondIteration", secondIteration));
//res.put("pileup", pileup);
//
//def keys = res.keySet();
//
////print spectra for selected point
//double u = 16500d;
//
//List<Map> points = res.values().collect { it.find { it.voltage == u }.getMap(20, true) }
//
//println "\n Spectrum example for U = ${u}\n"
//
//print "channel\t"
//println keys.join("\t")
//
//points.first().keySet().each {
// print "${it}\t"
// println points.collect { map -> map[it] }.join("\t")
//}
//
////printing count rate in window
//print "U\tLength\t"
//print keys.collect { it + "[total]" }.join("\t") + "\t"
//print keys.collect { it + "[pulse]" }.join("\t") + "\t"
//println keys.join("\t")
//
//for (int i = 0; i < data.size(); i++) {
// print "${data.get(i).getVoltage()}\t"
// print "${data.get(i).getLength()}\t"
// print keys.collect { res[it].get(i).getTotalCount() }.join("\t") + "\t"
// print keys.collect { res[it].get(i).getCountInWindow(3100, 3800) }.join("\t") + "\t"
// println keys.collect { res[it].get(i).getCountInWindow(400, 3100) }.join("\t")
//}

View File

@ -32,7 +32,7 @@ new GrindShell(ctx).eval {
def blocks = (1..num).collect {
def chain = GeneratorKt.buildSimpleEventChain(cr, new JDKRandomGenerator(),{10000})
def chain = GeneratorKt.generateEvents(cr, new JDKRandomGenerator(),{10000})
GeneratorKt.generateBlock(Instant.now().plusNanos(it * length), length, chain)
}

View File

@ -200,7 +200,7 @@ public class MonitorCorrectAction extends OneToOneAction<Table, Table> {
}
private boolean isMonitorPoint(double monitor, Values point) {
return point.getValue(NumassPoint.Companion.getHV_KEY()).doubleValue() == monitor;
return point.getValue(NumassPoint.HV_KEY).doubleValue() == monitor;
}
private Instant getTime(Values point) {

View File

@ -1,176 +0,0 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inr.numass.utils;
import hep.dataforge.meta.Meta;
import hep.dataforge.tables.Table;
import inr.numass.data.api.NumassBlock;
import inr.numass.data.api.NumassEvent;
import inr.numass.data.api.SimpleBlock;
import org.apache.commons.math3.distribution.EnumeratedRealDistribution;
import org.apache.commons.math3.distribution.RealDistribution;
import org.apache.commons.math3.random.RandomGenerator;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import static inr.numass.data.analyzers.NumassAnalyzer.COUNT_RATE_KEY;
/**
* A generator for Numass events with given energy spectrum
*
* @author Darksnake
*/
@Deprecated
public class NMEventGenerator {
protected final RandomGenerator rnd;
protected double cr;
protected RealDistribution distribution;
protected NumassEvent prevEvent;
public NMEventGenerator(RandomGenerator rnd, double cr) {
this.cr = cr;
this.rnd = rnd;
}
public NMEventGenerator(RandomGenerator rnd, Meta meta) {
this.cr = meta.getDouble("cr");
this.rnd = rnd;
}
public void setSpectrum(Table spectrum) {
double[] chanels = new double[spectrum.size()];
double[] values = new double[spectrum.size()];
for (int i = 0; i < spectrum.size(); i++) {
chanels[i] = spectrum.get("channel", i).doubleValue();
values[i] = spectrum.get(COUNT_RATE_KEY, i).doubleValue();
}
distribution = new EnumeratedRealDistribution(chanels, values);
}
// public void loadSpectrum(RawNMPoint point, int minChanel, int maxChanel) {
// List<Short> shorts = new ArrayList<>();
// point.getEvents().stream()
// .filter((event) -> ((event.getAmp() > minChanel) && (event.getAmp() < maxChanel)))
// .forEach((event) -> shorts.add(event.getAmp()));
// double[] doubles = new double[shorts.size()];
//
// for (int i = 0; i < shorts.size(); i++) {
// doubles[i] = shorts.get(i);
// }
//
// EmpiricalDistribution d = new EmpiricalDistribution();
// d.load(doubles);
//
// distribution = d;
// }
//
// public void loadSpectrum(Map<Double, Double> spectrum) {
// double[] chanels = new double[spectrum.size()];
// double[] values = new double[spectrum.size()];
// int i = 0;
// for (Map.Entry<Double, Double> entry : spectrum.entrySet()) {
// chanels[i] = entry.getKey();
// values[i] = entry.getValue();
// }
// distribution = new EnumeratedRealDistribution(chanels, values);
// }
//
// public void loadSpectrum(double[] channels, double[] values) {
// distribution = new EnumeratedRealDistribution(channels, values);
// }
//
// public void loadSpectrum(NumassPoint point) {
// double[] chanels = new double[RawNMPoint.MAX_CHANEL];
// double[] values = new double[RawNMPoint.MAX_CHANEL];
// for (int i = 0; i < RawNMPoint.MAX_CHANEL; i++) {
// chanels[i] = i;
// values[i] = point.getCount(i);
// }
// distribution = new EnumeratedRealDistribution(chanels, values);
// }
//
// public void loadSpectrum(NumassPoint point, NumassPoint reference) {
// double[] chanels = new double[RawNMPoint.MAX_CHANEL];
// double[] values = new double[RawNMPoint.MAX_CHANEL];
// for (int i = 0; i < RawNMPoint.MAX_CHANEL; i++) {
// chanels[i] = i;
// values[i] = Math.max(0, point.getCount(i) - reference.getCount(i));
// }
// distribution = new EnumeratedRealDistribution(chanels, values);
// }
//
// /**
// * @param point
// * @param reference
// * @param lower lower channel for spectrum generation
// * @param upper upper channel for spectrum generation
// */
// public void loadSpectrum(NumassPoint point, NumassPoint reference, int lower, int upper) {
// double[] chanels = new double[RawNMPoint.MAX_CHANEL];
// double[] values = new double[RawNMPoint.MAX_CHANEL];
// for (int i = lower; i < upper; i++) {
// chanels[i] = i;
// values[i] = Math.max(0, point.getCount(i) - (reference == null ? 0 : reference.getCount(i)));
// }
// distribution = new EnumeratedRealDistribution(chanels, values);
// }
protected short generateChannel() {
if (distribution != null) {
return (short) distribution.sample();
} else {
return 1600;
}
}
protected long generateDeltaTime() {
return (long) (nextExpDecay(1d / cr) * 1e9);
}
protected NumassEvent nextEvent(NumassEvent prev) {
if (prev == null) {
return new NumassEvent(generateChannel(), Instant.EPOCH, 0);
} else {
return new NumassEvent(generateChannel(), prev.getBlockTime(), prev.getTimeOffset() + generateDeltaTime());
}
}
// @Override
// public synchronized NumassEvent get() {
// return prevEvent = nextEvent(prevEvent);
// }
public NumassBlock generateBlock(Instant stsrt, long length) {
List<NumassEvent> events = new ArrayList<>();
NumassEvent event = nextEvent(null);
while (event.getTimeOffset() < length) {
events.add(event);
event = nextEvent(event);
}
return new SimpleBlock(stsrt, Duration.ofNanos(length), events);
}
private double nextExpDecay(double mean) {
return - mean * Math.log(1 - rnd.nextDouble());
}
}

View File

@ -1,56 +0,0 @@
package inr.numass.utils;
import hep.dataforge.meta.Meta;
import inr.numass.data.api.NumassEvent;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.RealDistribution;
import org.apache.commons.math3.random.RandomGenerator;
/**
* Created by darksnake on 25-Nov-16.
*/
public class NMEventGeneratorWithPulser extends NMEventGenerator {
private RealDistribution pulserChanelDistribution;
private double pulserDist;
private NumassEvent pulserEvent;
private NumassEvent nextEvent;
public NMEventGeneratorWithPulser(RandomGenerator rnd, Meta meta) {
super(rnd, meta);
pulserChanelDistribution = new NormalDistribution(
meta.getDouble("pulser.mean", 3450),
meta.getDouble("pulser.sigma", 86.45)
);
pulserDist = 1d / meta.getDouble("pulser.freq", 66.43);
pulserEvent = generatePulserEvent();
}
public synchronized NumassEvent get() {
//expected next event
if (nextEvent == null) {
nextEvent = nextEvent(prevEvent);
}
//if pulser event is first, then leave next event as is and return pulser event
if (pulserEvent.getTimeOffset() < nextEvent.getTimeOffset()) {
NumassEvent res = pulserEvent;
pulserEvent = generatePulserEvent();
return res;
} else {
//else return saved next event and generate next one
prevEvent = nextEvent;
nextEvent = nextEvent(prevEvent);
return prevEvent;
}
}
private NumassEvent generatePulserEvent() {
short channel = (short) pulserChanelDistribution.sample();
double time;
if (pulserEvent == null) {
time = rnd.nextDouble() * pulserDist * 1e9;
} else {
time = pulserEvent.getTimeOffset() + pulserDist*1e9;
}
return new NumassEvent(channel, (long) time);
}
}

View File

@ -1,156 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.utils;
import inr.numass.data.api.NumassBlock;
import inr.numass.data.api.NumassEvent;
import inr.numass.data.api.SimpleBlock;
import org.apache.commons.math3.random.RandomGenerator;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Math.max;
/**
* @author <a href="mailto:altavir@gmail.com">Alexander Nozik</a>
*/
public class PileUpSimulator {
private final static double us = 1e-6;//microsecond
private final long pointLength;
private final RandomGenerator rnd;
private final List<NumassEvent> generated = new ArrayList<>();
private final List<NumassEvent> pileup = new ArrayList<>();
private final List<NumassEvent> registered = new ArrayList<>();
private NMEventGenerator generator;
private double uSet = 0;
private AtomicInteger doublePileup = new AtomicInteger(0);
public PileUpSimulator(long length, RandomGenerator rnd, double countRate) {
this.rnd = rnd;
generator = new NMEventGenerator(rnd, countRate);
this.pointLength = length;
}
public PileUpSimulator(long pointLength, NMEventGenerator generator) {
this.pointLength = pointLength;
this.generator = generator;
this.rnd = generator.rnd;
}
public PileUpSimulator withUset(double uset) {
this.uSet = uset;
return this;
}
public NumassBlock generated() {
return new SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength), generated);
}
public NumassBlock registered() {
return new SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength), registered);
}
public NumassBlock pileup() {
return new SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength), pileup);
}
/**
* The amplitude for pileup event
*
* @param x
* @return
*/
private short pileupChannel(double x, short prevChanel, short nextChanel) {
assert x > 0;
//эмпирическая формула для канала
double coef = max(0, 0.99078 + 0.05098 * x - 0.45775 * x * x + 0.10962 * x * x * x);
if (coef < 0 || coef > 1) {
throw new Error();
}
return (short) (prevChanel + coef * nextChanel);
}
/**
* pileup probability
*
* @param delay
* @return
*/
private boolean pileup(double delay) {
double prob = 1d / (1d + Math.pow(delay / (2.5 + 0.2), 42.96));
return random(prob);
}
/**
* Probability for next event to register
*
* @param delay
* @return
*/
private boolean nextEventRegistered(short prevChanel, double delay) {
double average = 6.76102 - 4.31897E-4 * prevChanel + 7.88429E-8 * prevChanel * prevChanel + 0.2;
double prob = 1d - 1d / (1d + Math.pow(delay / average, 75.91));
return random(prob);
}
private boolean random(double prob) {
return rnd.nextDouble() <= prob;
}
public synchronized PileUpSimulator generate() {
NumassEvent next = null;
double lastRegisteredTime = 0; // Time of DAQ closing
//flag that shows that previous event was pileup
boolean pileupFlag = false;
while (true) {
next = generator.nextEvent(next);
if (next.getTimeOffset() > pointLength) {
break;
}
generated.add(next);
//not counting double pileups
if (generated.size() > 1) {
double delay = (next.getTimeOffset() - lastRegisteredTime) / us; //time between events in microseconds
if (nextEventRegistered(next.getAmp(), delay)) {
//just register new event
registered.add(next);
lastRegisteredTime = next.getTimeOffset();
pileupFlag = false;
} else if (pileup(delay)) {
if (pileupFlag) {
//increase double pileup stack
doublePileup.incrementAndGet();
} else {
//pileup event
short newChannel = pileupChannel(delay, next.getAmp(), next.getAmp());
NumassEvent newEvent = new NumassEvent(newChannel, next.getBlockTime(), next.getTimeOffset());
//replace already registered event by event with new channel
registered.remove(registered.size() - 1);
registered.add(newEvent);
pileup.add(newEvent);
//do not change DAQ close time
pileupFlag = true; // up the flag to avoid secondary pileup
}
} else {
// second event not registered, DAQ closed
pileupFlag = false;
}
} else {
//register first event
registered.add(next);
lastRegisteredTime = next.getTimeOffset();
}
}
return this;
}
}

View File

@ -101,7 +101,7 @@ object NumassUtils {
}
fun wrap(obj: Markedup, meta: Meta = Meta.empty()): EnvelopeBuilder {
return wrap(obj,meta){SimpleMarkupRenderer(this).render(it.markup(meta))}
return wrap(obj, meta) { SimpleMarkupRenderer(this).render(it.markup(meta)) }
}
@ -280,7 +280,7 @@ fun FitResult.display(context: Context, stage: String = "fit") {
val func = { x: Double -> model.spectrum.value(x, parameters) }
val fit = XYFunctionPlot("fit",func)
val fit = XYFunctionPlot("fit", func)
fit.density = 100
// ensuring all data points are calculated explicitly
data.rows.map { dp -> Adapters.getXValue(adapter, dp).doubleValue() }.sorted().forEach { fit.calculateIn(it) }
@ -297,7 +297,7 @@ fun FitResult.display(context: Context, stage: String = "fit") {
data.rows.forEach {
val x = Adapters.getXValue(adapter, it).doubleValue()
val y = Adapters.getYValue(adapter, it).doubleValue()
val err = Adapters.optYError(adapter,it).orElse(1.0)
val err = Adapters.optYError(adapter, it).orElse(1.0)
residual += Adapters.buildXYDataPoint(x, (y - func(x)) / err, 1.0)
}

View File

@ -40,7 +40,7 @@ class MergeDataAction : ManyToOneAction<Table, Table>() {
private val parnames = arrayOf(NumassPoint.HV_KEY, NumassPoint.LENGTH_KEY, NumassAnalyzer.COUNT_KEY, NumassAnalyzer.COUNT_RATE_KEY, NumassAnalyzer.COUNT_RATE_ERROR_KEY)
override fun buildGroups(context: Context, input: DataNode<Table>, actionMeta: Meta): List<DataNode<Table>> {
val meta = inputMeta(context, input.getMeta(), actionMeta)
val meta = inputMeta(context, input.meta, actionMeta)
val groups: List<DataNode<Table>>
if (meta.hasValue("grouping.byValue")) {
groups = super.buildGroups(context, input, actionMeta)

View File

@ -40,7 +40,7 @@ import java.util.*
class SummaryAction : ManyToOneAction<FitState, Table>() {
protected override fun buildGroups(context: Context, input: DataNode<FitState>, actionMeta: Meta): List<DataNode<FitState>> {
val meta = inputMeta(context, input.getMeta(), actionMeta)
val meta = inputMeta(context, input.meta, actionMeta)
val groups: List<DataNode<FitState>>
if (meta.hasValue("grouping.byValue")) {
groups = super.buildGroups(context, input, actionMeta)

View File

@ -4,12 +4,14 @@ import hep.dataforge.maths.chain.Chain
import hep.dataforge.maths.chain.MarkovChain
import hep.dataforge.maths.chain.StatefulChain
import hep.dataforge.stat.defaultGenerator
import inr.numass.data.api.NumassBlock
import inr.numass.data.api.NumassEvent
import inr.numass.data.api.SimpleBlock
import hep.dataforge.tables.Table
import inr.numass.data.analyzers.NumassAnalyzer.Companion.CHANNEL_KEY
import inr.numass.data.analyzers.NumassAnalyzer.Companion.COUNT_RATE_KEY
import inr.numass.data.api.*
import kotlinx.coroutines.experimental.channels.map
import kotlinx.coroutines.experimental.channels.takeWhile
import kotlinx.coroutines.experimental.channels.toList
import kotlinx.coroutines.experimental.runBlocking
import org.apache.commons.math3.distribution.EnumeratedRealDistribution
import org.apache.commons.math3.random.RandomGenerator
import java.time.Duration
import java.time.Instant
@ -22,24 +24,50 @@ private fun RandomGenerator.nextDeltaTime(cr: Double): Long {
return (nextExp(1.0 / cr) * 1e9).toLong()
}
fun generateBlock(start: Instant, length: Long, chain: Chain<NumassEvent>): NumassBlock {
val events = runBlocking { chain.channel.takeWhile { it.timeOffset < length }.toList()}
return SimpleBlock(start, Duration.ofNanos(length), events)
fun generateBlock(start: Instant, length: Long, chain: Chain<OrphanNumassEvent>): NumassBlock {
return SimpleBlock(start, Duration.ofNanos(length)) { parent ->
chain.channel.map { it.adopt(parent) }.takeWhile { it.timeOffset < length }.toList()
}
}
internal val defaultAmplitudeGenerator: RandomGenerator.(NumassEvent?, Long) -> Short = { _, _ -> ((nextDouble() + 2.0) * 100).toShort() }
internal val defaultAmplitudeGenerator: RandomGenerator.(OrphanNumassEvent?, Long) -> Short = { _, _ -> ((nextDouble() + 2.0) * 100).toShort() }
fun buildSimpleEventChain(
/**
* Generate an event chain with fixed count rate
* @param cr = count rate in Hz
* @param rnd = random number generator
* @param amp amplitude generator for the chain. The receiver is rng, first argument is the previous event and second argument
* is the delay between the next event. The result is the amplitude in channels
*/
fun generateEvents(
cr: Double,
rnd: RandomGenerator = defaultGenerator,
amp: RandomGenerator.(NumassEvent?, Long) -> Short = defaultAmplitudeGenerator): Chain<NumassEvent> {
return MarkovChain(NumassEvent(rnd.amp(null, 0), Instant.now(), 0)) { event ->
amp: RandomGenerator.(OrphanNumassEvent?, Long) -> Short = defaultAmplitudeGenerator): Chain<OrphanNumassEvent> {
return MarkovChain(OrphanNumassEvent(rnd.amp(null, 0), 0)) { event ->
val deltaT = rnd.nextDeltaTime(cr)
NumassEvent(rnd.amp(event, deltaT), event.blockTime, event.timeOffset + deltaT)
OrphanNumassEvent(rnd.amp(event, deltaT), event.timeOffset + deltaT)
}
}
/**
* Generate a chain using provided spectrum for amplitudes
*/
fun generateEvents(
cr: Double,
rnd: RandomGenerator = defaultGenerator,
spectrum: Table): Chain<OrphanNumassEvent> {
val channels = DoubleArray(spectrum.size())
val values = DoubleArray(spectrum.size())
for (i in 0 until spectrum.size()) {
channels[i] = spectrum.get(CHANNEL_KEY, i).doubleValue()
values[i] = spectrum.get(COUNT_RATE_KEY, i).doubleValue()
}
val distribution = EnumeratedRealDistribution(channels, values)
return generateEvents(cr, rnd) { _, _ -> distribution.sample().toShort() }
}
private data class BunchState(var bunchStart: Long = 0, var bunchEnd: Long = 0)
/**
@ -53,24 +81,24 @@ fun buildBunchChain(
bunchRate: Double,
bunchLength: Double,
rnd: RandomGenerator = defaultGenerator,
amp: RandomGenerator.(NumassEvent?, Long) -> Short = defaultAmplitudeGenerator
): Chain<NumassEvent> {
amp: RandomGenerator.(OrphanNumassEvent?, Long) -> Short = defaultAmplitudeGenerator
): Chain<OrphanNumassEvent> {
return StatefulChain(
BunchState(0, 0),
NumassEvent(rnd.amp(null, 0), Instant.now(), 0)) { event ->
OrphanNumassEvent(rnd.amp(null, 0), 0)) { event ->
if (event.timeOffset >= bunchEnd) {
bunchStart = bunchEnd + (rnd.nextDeltaTime(bunchRate)).toLong()
bunchEnd = bunchStart + (bunchLength*1e9).toLong()
NumassEvent(rnd.amp(null, 0), Instant.EPOCH, bunchStart)
bunchEnd = bunchStart + (bunchLength * 1e9).toLong()
OrphanNumassEvent(rnd.amp(null, 0), bunchStart)
} else {
val deltaT = rnd.nextDeltaTime(cr)
NumassEvent(rnd.amp(event, deltaT), event.blockTime, event.timeOffset + deltaT)
OrphanNumassEvent(rnd.amp(event, deltaT), event.timeOffset + deltaT)
}
}
}
private class MergingState(private val chains: List<Chain<NumassEvent>>) {
suspend fun poll(): NumassEvent {
private class MergingState(private val chains: List<Chain<OrphanNumassEvent>>) {
suspend fun poll(): OrphanNumassEvent {
val next = chains.minBy { it.value.timeOffset } ?: chains.first()
val res = next.value
next.next()
@ -79,8 +107,8 @@ private class MergingState(private val chains: List<Chain<NumassEvent>>) {
}
fun mergeEventChains(vararg chains: Chain<NumassEvent>): Chain<NumassEvent> {
return StatefulChain(MergingState(listOf(*chains)),NumassEvent(0, Instant.now(), 0)){
fun mergeEventChains(vararg chains: Chain<OrphanNumassEvent>): Chain<OrphanNumassEvent> {
return StatefulChain(MergingState(listOf(*chains)), OrphanNumassEvent(0, 0)) {
poll()
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright 2018 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package inr.numass.data
import hep.dataforge.maths.chain.Chain
import inr.numass.data.api.*
import kotlinx.coroutines.experimental.runBlocking
import org.apache.commons.math3.random.RandomGenerator
import java.lang.Math.max
import java.time.Duration
import java.time.Instant
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
/**
* @author [Alexander Nozik](mailto:altavir@gmail.com)
*/
class PileUpSimulator {
private val pointLength: Long
private val rnd: RandomGenerator
private val generated = ArrayList<OrphanNumassEvent>()
private val pileup = ArrayList<OrphanNumassEvent>()
private val registered = ArrayList<OrphanNumassEvent>()
private var generator: Chain<OrphanNumassEvent>
//private double uSet = 0;
private val doublePileup = AtomicInteger(0)
constructor(length: Long, rnd: RandomGenerator, countRate: Double) {
this.rnd = rnd
generator = generateEvents(countRate, rnd)
this.pointLength = length
}
constructor(pointLength: Long, rnd: RandomGenerator, generator: Chain<OrphanNumassEvent>) {
this.rnd = rnd
this.pointLength = pointLength
this.generator = generator
}
// fun withUset(uset: Double): PileUpSimulator {
// this.uSet = uset
// return this
// }
fun generated(): NumassBlock {
return SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength)) { parent -> generated.map { it.adopt(parent) } }
}
fun registered(): NumassBlock {
return SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength)) { parent -> registered.map { it.adopt(parent) } }
}
fun pileup(): NumassBlock {
return SimpleBlock(Instant.EPOCH, Duration.ofNanos(pointLength)) { parent -> pileup.map { it.adopt(parent) } }
}
/**
* The amplitude for pileup event
*
* @param x
* @return
*/
private fun pileupChannel(x: Double, prevChanel: Short, nextChanel: Short): Short {
assert(x > 0)
//эмпирическая формула для канала
val coef = max(0.0, 0.99078 + 0.05098 * x - 0.45775 * x * x + 0.10962 * x * x * x)
if (coef < 0 || coef > 1) {
throw Error()
}
return (prevChanel + coef * nextChanel).toShort()
}
/**
* pileup probability
*
* @param delay
* @return
*/
private fun pileup(delay: Double): Boolean {
val prob = 1.0 / (1.0 + Math.pow(delay / (2.5 + 0.2), 42.96))
return random(prob)
}
/**
* Probability for next event to register
*
* @param delay
* @return
*/
private fun nextEventRegistered(prevChanel: Short, delay: Double): Boolean {
val average = 6.76102 - 4.31897E-4 * prevChanel + 7.88429E-8 * prevChanel.toDouble() * prevChanel.toDouble() + 0.2
val prob = 1.0 - 1.0 / (1.0 + Math.pow(delay / average, 75.91))
return random(prob)
}
private fun random(prob: Double): Boolean {
return rnd.nextDouble() <= prob
}
@Synchronized
fun generate() {
var next: OrphanNumassEvent
var lastRegisteredTime = 0.0 // Time of DAQ closing
//flag that shows that previous event was pileup
var pileupFlag = false
runBlocking {
next = generator.next()
while (next.timeOffset <= pointLength) {
generated.add(next)
//not counting double pileups
if (generated.size > 1) {
val delay = (next.timeOffset - lastRegisteredTime) / us //time between events in microseconds
if (nextEventRegistered(next.amp, delay)) {
//just register new event
registered.add(next)
lastRegisteredTime = next.timeOffset.toDouble()
pileupFlag = false
} else if (pileup(delay)) {
if (pileupFlag) {
//increase double pileup stack
doublePileup.incrementAndGet()
} else {
//pileup event
val newChannel = pileupChannel(delay, next.amp, next.amp)
val newEvent = OrphanNumassEvent(newChannel, next.timeOffset)
//replace already registered event by event with new channel
registered.removeAt(registered.size - 1)
registered.add(newEvent)
pileup.add(newEvent)
//do not change DAQ close time
pileupFlag = true // up the flag to avoid secondary pileup
}
} else {
// second event not registered, DAQ closed
pileupFlag = false
}
} else {
//register first event
registered.add(next)
lastRegisteredTime = next.timeOffset.toDouble()
}
next = generator.next()
}
}
}
companion object {
private const val us = 1e-6//microsecond
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2018 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inr.numass.data
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.fx.plots.FXPlotManager
import hep.dataforge.kodex.KMetaBuilder
import hep.dataforge.kodex.buildMeta
import hep.dataforge.kodex.configure
import hep.dataforge.plots.data.DataPlot
import hep.dataforge.tables.Adapters
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.SmartAnalyzer
import inr.numass.data.api.NumassBlock
fun NumassBlock.plotAmplitudeSpectrum(plotName: String = "spectrum", frameName: String = "", context: Context = Global, metaAction: KMetaBuilder.() -> Unit) {
val meta = buildMeta("meta", metaAction)
val plotManager = context.load(FXPlotManager::class)
val data = SmartAnalyzer().getAmplitudeSpectrum(this, meta.getMetaOrEmpty("spectrum"))
plotManager.display(name = frameName) {
val valueAxis = if (meta.getBoolean("normalize",true)) {
NumassAnalyzer.COUNT_RATE_KEY
} else {
NumassAnalyzer.COUNT_KEY
}
val plot = DataPlot.plot(
plotName,
Adapters.buildXYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis),
data
).configure {
"connectionType" to "step"
"thickness" to 2
"showLine" to true
"showSymbol" to false
"showErrors" to false
"JFreeChart.cache" to true
}
add(plot)
}
}

View File

@ -5,8 +5,8 @@ import hep.dataforge.kodex.buildMeta
import inr.numass.actions.TimeAnalyzerAction
import inr.numass.data.api.SimpleNumassPoint
import inr.numass.data.buildBunchChain
import inr.numass.data.buildSimpleEventChain
import inr.numass.data.generateBlock
import inr.numass.data.generateEvents
import inr.numass.data.mergeEventChains
import java.time.Instant
@ -19,7 +19,7 @@ fun main(args: Array<String>) {
val num = 60;
val blocks = (1..num).map {
val regularChain = buildSimpleEventChain(cr)
val regularChain = generateEvents(cr)
val bunchChain = buildBunchChain(40.0, 0.01, 5.0)
val generator = mergeEventChains(regularChain, bunchChain)

View File

@ -1,34 +1,11 @@
package inr.numass.scripts.tristan
import hep.dataforge.meta.Meta
import hep.dataforge.tables.Table
import hep.dataforge.values.Values
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.api.NumassBlock
import inr.numass.data.api.NumassEvent
import inr.numass.data.api.NumassSet
import inr.numass.data.storage.ProtoNumassPoint
import java.util.stream.Stream
fun main(args: Array<String>) {
val analyzer = object : NumassAnalyzer{
override fun analyze(block: NumassBlock, config: Meta): Values {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun getEvents(block: NumassBlock, meta: Meta): Stream<NumassEvent> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun analyzeSet(set: NumassSet, config: Meta): Table {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
val file = ProtoNumassPoint.readFile("D:\\Work\\Numass\\data\\TRISTAN_11_2017\\df\\gun_16_19.df ")
val events = Sequence { file.events.iterator() }.sortedBy { it.time }
val filtered = file.filter { it.channel == 4 }
}

View File

@ -30,7 +30,9 @@ class NumassFitScanSummaryTask : AbstractTask<Table>() {
val builder = DataSet.builder(Table::class.java)
val action = FitSummaryAction()
val input = data.checked(FitResult::class.java)
input.nodeStream().filter { it -> it.getSize(false) > 0 }.forEach { node -> builder.putData(node.name, action.run(model.context, node, model.getMeta()).data) }
input.nodeStream()
.filter { it -> it.getSize(false) > 0 }
.forEach { node -> builder.putData(node.name, action.run(model.context, node, model.meta).data) }
return builder.build()
}

View File

@ -25,7 +25,7 @@ class NumassFitScanTask : AbstractTask<FitResult>() {
override fun run(model: TaskModel, data: DataNode<*>): DataNode<FitResult> {
val config = model.getMeta()
val config = model.meta
val scanParameter = config.getString("parameter", "msterile2")
val scanValues: Value = if (config.hasValue("masses")) {
@ -57,7 +57,8 @@ class NumassFitScanTask : AbstractTask<FitResult>() {
overrideMeta.setValue("params.$scanParameter.value", `val`)
} else {
overrideMeta.getMetaList("params.param").stream()
.filter { par -> par.getString("name") == scanParameter }.forEach { par -> par.setValue("value", `val`) }
.filter { par -> par.getString("name") == scanParameter }
.forEach { it.setValue("value", `val`) }
}
// Data<Table> newData = new Data<Table>(data.getGoal(),data.type(),overrideMeta);
val node = action.run(model.context, DataNode.of(resultName, table, Meta.empty()), overrideMeta)

View File

@ -42,7 +42,7 @@ class NumassFitSummaryTask : SingleActionTask<FitState, Table>() {
}
override fun transformMeta(model: TaskModel): Meta {
return model.getMeta().getMeta("summary")
return model.meta.getMeta("summary")
}
override fun buildModel(model: TaskModel.Builder, meta: Meta) {

View File

@ -14,28 +14,20 @@ import java.util.stream.Stream
*/
class NumassDataCache(val data: NumassSet) : NumassSet {
//private val cachedDescription: String by lazy { data.description }
private val cachedMeta: Meta by lazy { data.meta }
override val meta: Meta by lazy { data.meta }
private val cachedPoints: List<NumassPoint> by lazy { data.points.collect(Collectors.toList()) }
private val hv: Optional<Table> by lazy { data.hvData }
override val hvData: Optional<Table> by lazy { data.hvData }
override fun getPoints(): Stream<NumassPoint> {
return cachedPoints.stream();
}
override val points: Stream<NumassPoint>
get() = cachedPoints.stream()
// override fun getDescription(): String {
// return cachedDescription
// }
override fun getMeta(): Meta {
return cachedMeta
}
override fun getName(): String {
return data.name;
}
override fun getHvData(): Optional<Table> {
return hv;
}
}