Separate generator for each track

This commit is contained in:
Alexander Nozik 2018-12-06 17:32:01 +03:00
parent 839526c7af
commit 02a0624b32
3 changed files with 43 additions and 47 deletions

View File

@ -1,14 +1,17 @@
package inr.numass.trapping
import org.apache.commons.math3.analysis.interpolation.LinearInterpolator
import org.apache.commons.math3.random.SynchronizedRandomGenerator
import org.apache.commons.math3.random.JDKRandomGenerator
import org.apache.commons.rng.UniformRandomProvider
import org.apache.commons.rng.simple.RandomSource
import java.io.File
import java.io.PrintStream
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.util.stream.Stream
import java.util.stream.LongStream
private val seedGenerator = JDKRandomGenerator()
/**
* Created by darksnake on 04-Jun-16.
@ -20,7 +23,10 @@ class SimulationManager() {
var comment = ""
var generator: UniformRandomProvider = RandomSource.create(RandomSource.SPLIT_MIX_64)
/**
* A supplier for random generator. Each track has its own generator
*/
var generatorFactory: (Long) -> UniformRandomProvider = { RandomSource.create(RandomSource.MT_64, seedGenerator.nextInt()) }
var reportFilter: (Simulator.SimulationResult) -> Boolean = { it.state == Simulator.EndState.ACCEPTED }
var initialE = 18000.0
@ -85,8 +91,8 @@ class SimulationManager() {
outputDirectory.mkdirs()
}
val generator = SynchronizedRandomGenerator(RandomGeneratorBridge(generator))
val simulator = Simulator(eLow, thetaTransport, thetaPinch, gasDensity, bSource, magneticField, generator)
val simulator = Simulator(eLow, thetaTransport, thetaPinch, gasDensity, bSource, magneticField)
val counter = Counter()
@ -100,18 +106,19 @@ class SimulationManager() {
val outputPath = outputDirectory.toPath().resolve("$fileName.out")
PrintStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)).use { output ->
output.println("# " + header.replace("\n", "\n# "))//adding comment symbols
System.out.printf("%nStarting simulation with initial energy %g and %d electrons.%n%n", initialE, num.toLong())
output.printf("%s\t%s\t%s\t%s\t%s\t%s%n", "E", "theta", "theta_start", "colNum", "L", "state")
Stream
.generate {
output.printf("%s\t%s\t%s\t%s\t%s\t%s\t%s%n", "id", "E", "theta", "theta_start", "colNum", "L", "state")
LongStream.rangeClosed(1, num.toLong()).parallel()
.mapToObj { id ->
val generator = RandomGeneratorBridge(generatorFactory(id))
val theta = Math.acos(1 - 2 * generator.nextDouble())// from 0 to Pi
val z = (generator.nextDouble() - 0.5) * Simulator.SOURCE_LENGTH
simulator.simulate(initialE, theta, z).also { counter.count(it) }
simulator.simulate(id, generator, initialE, theta, z).also { counter.count(it) }
}
.limit(num.toLong()).parallel()
.filter(reportFilter)
.forEach { res ->
printOne(output, res)
@ -148,7 +155,7 @@ class SimulationManager() {
}
private fun printOne(out: PrintStream, res: Simulator.SimulationResult) {
out.printf("%g\t%g\t%g\t%d\t%g\t%s%n", res.E, res.theta * 180 / Math.PI, res.initTheta * 180 / Math.PI, res.collisionNumber, res.l, res.state.toString())
out.printf("%d\t%g\t%g\t%g\t%d\t%g\t%s%n", res.id, res.E, res.theta * 180 / Math.PI, res.initTheta * 180 / Math.PI, res.collisionNumber, res.l, res.state.toString())
out.flush()
}

View File

@ -23,8 +23,8 @@ class Simulator(
val thetaPinch: Double,
val gasDensity: Double,// m^-3
val bSource: Double,
val magneticField: ((Double) -> Double)?,
val generator: RandomGenerator) {
val magneticField: ((Double) -> Double)?
) {
enum class EndState {
@ -55,20 +55,12 @@ class Simulator(
//проверяем нормировку
assert(Precision.equals(sigmaEl + sigmaExc + sigmaIon, 1.0, 1e-2))
val alpha = generator.nextDouble()
val alpha = pos.generator.nextDouble()
val delta: Pair<Double, Double>
if (alpha > sigmaEl) {
if (alpha > sigmaEl + sigmaExc) {
//ionization case
delta = Scatter.randomion(pos.e, generator)
} else {
//excitation case
delta = Scatter.randomexc(pos.e, generator)
}
} else {
// elastic
delta = Scatter.randomel(pos.e, generator)
val delta: Pair<Double, Double> = when {
alpha < sigmaEl -> Scatter.randomel(pos.e, pos.generator)// elastic
alpha > sigmaEl + sigmaExc -> Scatter.randomion(pos.e, pos.generator)//ionization case
else -> Scatter.randomexc(pos.e, pos.generator)//excitation case
}
//Обновляем значени угла и энергии независимо ни от чего
@ -85,7 +77,7 @@ class Simulator(
* @param e
* @return
*/
private fun freePath(e: Double): Double {
private fun freePath(generator: RandomGenerator, e: Double): Double {
//FIXME redundant cross-section calculation
//All cross-sections are in m^2
return ExponentialDistribution(generator, 1.0 / Scatter.sigmaTotal(e) / gasDensity).sample()
@ -196,15 +188,15 @@ class Simulator(
* Симулируем один пробег электрона от начального значения и до вылетания из
* иточника или до того момента, как энергия становится меньше eLow.
*/
fun simulate(initEnergy: Double, initTheta: Double, initZ: Double): SimulationResult {
fun simulate(id: Long, generator: RandomGenerator, initEnergy: Double, initTheta: Double, initZ: Double): SimulationResult {
assert(initEnergy > 0)
assert(initTheta > 0 && initTheta < Math.PI)
assert(abs(initZ) <= SOURCE_LENGTH / 2.0)
val pos = State(initEnergy, initTheta, initZ)
val pos = State(generator, initEnergy, initTheta, initZ)
while (!pos.isFinished) {
val dl = freePath(pos.e) // path to next scattering
val dl = freePath(generator, pos.e) // path to next scattering
// propagate to next scattering position
propagate(pos, dl)
@ -220,7 +212,7 @@ class Simulator(
}
}
return SimulationResult(pos.endState, pos.e, pos.theta, initTheta, pos.colNum, pos.l)
return SimulationResult(id, pos.endState, pos.e, pos.theta, initTheta, pos.colNum, pos.l)
}
fun resetDebugCounters() {
@ -236,7 +228,7 @@ class Simulator(
}
}
class SimulationResult(var state: EndState, var E: Double, var theta: Double, var initTheta: Double, var collisionNumber: Int, var l: Double)
data class SimulationResult(val id: Long, val state: EndState, val E: Double, val theta: Double, val initTheta: Double, val collisionNumber: Int, val l: Double)
/**
* Current electron position in simulation. Not thread safe!
@ -245,7 +237,7 @@ class Simulator(
* @property theta Current theta recalculated to the field in the center of the source
* @property z current z. Zero is the center of the source
*/
private inner class State(internal var e: Double, internal var theta: Double, internal var z: Double) {
private inner class State(val generator: RandomGenerator, internal var e: Double, internal var theta: Double, internal var z: Double) {
/**
* Current total path
*/
@ -349,7 +341,7 @@ class Simulator(
/**
* Reverse electron direction
*/
internal fun flip() {
fun flip() {
if (theta < 0 || theta > PI) {
throw Error()
}
@ -361,7 +353,7 @@ class Simulator(
*
* @return
*/
internal fun field(): Double {
fun field(): Double {
return this@Simulator.field(z)
}
@ -370,7 +362,7 @@ class Simulator(
*
* @return
*/
internal fun realTheta(): Double {
fun realTheta(): Double {
if (magneticField == null) {
return theta
} else {
@ -391,7 +383,7 @@ class Simulator(
* @param dTheta
* @return resulting angle
*/
internal fun addTheta(dTheta: Double): Double {
fun addTheta(dTheta: Double): Double {
//Генерируем случайный фи
val phi = generator.nextDouble() * 2.0 * Math.PI
@ -406,7 +398,7 @@ class Simulator(
val result = rot.applyTo(init.cartesian)
val newtheta = acos(result.z)
val newTheta = acos(result.z)
// //следим чтобы угол был от 0 до Pi
// if (newtheta < 0) {
@ -418,10 +410,10 @@ class Simulator(
//change back to virtual angles
if (magneticField == null) {
theta = newtheta
theta = newTheta
} else {
theta = asin(sin(newtheta) * sqrt(bSource / field()))
if (newtheta > PI / 2) {
theta = asin(sin(newTheta) * sqrt(bSource / field()))
if (newTheta > PI / 2) {
theta = PI - theta
}
}
@ -435,8 +427,8 @@ class Simulator(
companion object {
val SOURCE_LENGTH = 3.0
private val DELTA_L = 0.1 //step for propagate calculation
const val SOURCE_LENGTH = 3.0
private const val DELTA_L = 0.1 //step for propagate calculation
}

View File

@ -1,7 +1,5 @@
package inr.numass.trapping
import org.apache.commons.rng.simple.RandomSource
import java.io.File
import java.time.Duration
import java.time.Instant
@ -21,12 +19,11 @@ fun main(args: Array<String>) {
SimulationManager().apply {
comment = "Out of the box cross-sections"
fileName = "trap[$e]"
generator = RandomSource.create(RandomSource.MWC_256)
setFields(0.6, 3.7, 7.2)
gasDensity = 1e19
initialE = e
range = 4000.0
}.simulateAll(1e7)
}.simulateAll(1_000_000)
}
val finishTime = Instant.now()