diff --git a/src/main/kotlin/ru/inr/mass/trapping/SimulationManager.kt b/src/main/kotlin/ru/inr/mass/trapping/SimulationManager.kt index 395e53c..3855875 100644 --- a/src/main/kotlin/ru/inr/mass/trapping/SimulationManager.kt +++ b/src/main/kotlin/ru/inr/mass/trapping/SimulationManager.kt @@ -26,7 +26,8 @@ class SimulationManager() { /** * A supplier for random generator. Each track has its own generator */ - var generatorFactory: (Long) -> UniformRandomProvider = { RandomSource.create(RandomSource.MT_64, seedGenerator.nextInt()) } + 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 @@ -107,26 +108,35 @@ class SimulationManager() { val outputPath = outputDirectory.toPath().resolve("$fileName.out") - PrintStream(Files.newOutputStream(outputPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)).use { output -> + 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()) + 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\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(id, generator, initialE, theta, z).also { counter.count(it) } - } - .filter(reportFilter) - .forEach { res -> - printOne(output, res) - } + 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(id, generator, initialE, theta, z).also { counter.count(it) } + }.filter(reportFilter).forEach { res -> + printOne(output, res) + } } val statisticsPath = outputDirectory.toPath().resolve("$fileName.stat") - PrintStream(Files.newOutputStream(statisticsPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)).use { + PrintStream(Files.newOutputStream(statisticsPath, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE)).use { it.println(header + "\n") printStatistics(it, simulator, counter) @@ -155,7 +165,14 @@ class SimulationManager() { } private fun printOne(out: PrintStream, res: Simulator.SimulationResult) { - 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.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() } diff --git a/src/main/kotlin/ru/inr/mass/trapping/Simulator.kt b/src/main/kotlin/ru/inr/mass/trapping/Simulator.kt index 5f38f25..fa2dfa3 100644 --- a/src/main/kotlin/ru/inr/mass/trapping/Simulator.kt +++ b/src/main/kotlin/ru/inr/mass/trapping/Simulator.kt @@ -29,11 +29,10 @@ class Simulator( ) { enum class EndState { - - ACCEPTED, //трэппинговый электрон попал в аксептанс - REJECTED, //трэппинговый электрон вылетел через заднюю пробку - LOWENERGY, //потерял слишком много энергии - PASS, //электрон никогда не запирался и прошел напрямую, нужно для нормировки + ACCEPTED, // escaped to the spectrometer + REJECTED, // escaped to the rear side + LOWENERGY, // energy below lower border + PASS, // Electron never trapped NONE } @@ -45,7 +44,7 @@ class Simulator( * @return */ private fun scatter(pos: State): State { - //Вычисляем сечения и нормируем их на полное сечение + //Computing cross-sections and normalizing them var sigmaIon = Scatter.sigmaion(pos.e) var sigmaEl = Scatter.sigmael(pos.e) var sigmaExc = Scatter.sigmaexc(pos.e) @@ -54,20 +53,19 @@ class Simulator( sigmaEl /= sigmaTotal sigmaExc /= sigmaTotal - //проверяем нормировку + //Checking norming assert(Precision.equals(sigmaEl + sigmaExc + sigmaIon, 1.0, 1e-2)) val alpha = pos.generator.nextDouble() val delta: Pair = 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 + 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 } - //Обновляем значени угла и энергии независимо ни от чего + // updating energy and angle pos.substractE(delta.first) - //Изменение угла pos.addTheta(delta.second / 180 * Math.PI) return pos @@ -389,13 +387,13 @@ class Simulator( //Генерируем случайный фи val phi = generator.nextDouble() * 2.0 * Math.PI - //change to real angles + // change to real angles val realTheta = realTheta() - //Создаем начальный вектор в сферических координатах + // Initial vector in spherical coordinates val init = SphericalCoordinates(1.0, 0.0, realTheta + dTheta) - // Задаем вращение относительно оси, перпендикулярной исходному вектору + // Rotating on an axis perpendicular to the initial vector val rotate = SphericalCoordinates(1.0, 0.0, realTheta) - // поворачиваем исходный вектор на dTheta + // Rotation the initial vector val rot = Rotation(rotate.cartesian, phi, null) val result = rot.applyTo(init.cartesian) diff --git a/src/main/kotlin/ru/inr/mass/trapping/crosssections.kt b/src/main/kotlin/ru/inr/mass/trapping/crosssections.kt index b14068b..6684070 100644 --- a/src/main/kotlin/ru/inr/mass/trapping/crosssections.kt +++ b/src/main/kotlin/ru/inr/mass/trapping/crosssections.kt @@ -18,6 +18,9 @@ infix fun ClosedFloatingPointRange.step(step: Double): Sequence } +/** + * Extract cross-sections from the code + */ fun main() { val energies = ((1.0..20.0) step 0.2).toList() // energy in keV diff --git a/src/main/kotlin/ru/inr/mass/trapping/simulateFullRange.kt b/src/main/kotlin/ru/inr/mass/trapping/simulateFullRange.kt new file mode 100644 index 0000000..3d78e17 --- /dev/null +++ b/src/main/kotlin/ru/inr/mass/trapping/simulateFullRange.kt @@ -0,0 +1,29 @@ +package ru.inr.mass.trapping + +import java.time.Duration +import java.time.Instant + + +fun main() { + //val z = doubleArrayOf(-1.736, -1.27, -0.754, -0.238, 0.278, 0.794, 1.31, 1.776) + //val b = doubleArrayOf(3.70754, 0.62786, 0.60474, 0.60325, 0.60333, 0.60503, 0.6285, 3.70478) + // System.out.println("Press any key to start..."); + // System.in.read(); + + val startTime = Instant.now() + System.out.printf("Starting at %s%n%n", startTime.toString()) + + SimulationManager().apply { + comment = "Out of the box cross-sections" + fileName = "trap-full-range" + setFields(0.6, 3.6, 7.2) + gasDensity = 1e19 // m^-3 + initialE = 18500.0 + range = 16000.0 + }.simulateAll(1_000_001) + + val finishTime = Instant.now() + System.out.printf("%nFinished at %s%n", finishTime.toString()) + System.out.printf("Calculation took %s%n", Duration.between(startTime, finishTime).toString()) +} +