commit 9e6d2b930741e0d94a7b39a9ad6aeb61d685f3d9
parent ce4167e69580d92e3f80e3ca55e438828d8dd49a
Author: Mikolaj Lenczewski <33129490+EnderRifter@users.noreply.github.com>
Date: Mon, 26 Aug 2019 09:22:28 +0200
Added ability to output simulation state to file for further manual analysis and verification
Diffstat:
3 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/StarSim/StarSimLib/Constants.cs b/StarSim/StarSimLib/Constants.cs
@@ -104,6 +104,11 @@ namespace StarSimLib
};
/// <summary>
+ /// The directory path at which all the simulation output files are stored.
+ /// </summary>
+ public static readonly string OutputFileDirectory = @"./simulations/";
+
+ /// <summary>
/// The <see cref="Octant"/> instance representing the rendered universe.
/// </summary>
public static readonly Octant UniverseOctant = new Octant(new Vector4(), UniverseSize);
diff --git a/StarSim/StarSimLib/StarSimLib.xml b/StarSim/StarSimLib/StarSimLib.xml
@@ -101,6 +101,11 @@
The list of accepted email providers.
</summary>
</member>
+ <member name="F:StarSimLib.Constants.OutputFileDirectory">
+ <summary>
+ The directory path at which all the simulation output files are stored.
+ </summary>
+ </member>
<member name="F:StarSimLib.Constants.UniverseOctant">
<summary>
The <see cref="T:StarSimLib.Data_Structures.Octant"/> instance representing the rendered universe.
@@ -1946,6 +1951,11 @@
screen at the <see cref="T:StarSimLib.Data_Structures.Body"/>s position.
</summary>
</member>
+ <member name="F:StarSimLib.UI.SimulationScreen.fileWriter">
+ <summary>
+ Allows for writing out the state of the current frame to a file.
+ </summary>
+ </member>
<member name="F:StarSimLib.UI.SimulationScreen.miscTimer">
<summary>
Timer that manages FPS counter and other miscellaneous counters.
@@ -1971,6 +1981,11 @@
Counts the frames elapsed since the last timer pulse, so that the FPS can be tracked.
</summary>
</member>
+ <member name="F:StarSimLib.UI.SimulationScreen.totalFramesElapsed">
+ <summary>
+ Counts the number of frames elapsed since the beginning of the simulation.
+ </summary>
+ </member>
<member name="M:StarSimLib.UI.SimulationScreen.#ctor(SFML.Graphics.RenderWindow,StarSimLib.UI.IInputHandler,StarSimLib.Data_Structures.Body[]@,System.Collections.Generic.Dictionary{StarSimLib.Data_Structures.Body,SFML.Graphics.CircleShape}@,StarSimLib.Physics.UpdateDelegate)">
<summary>
Initialises a new instance of the <see cref="T:StarSimLib.UI.SimulationScreen"/> class.
@@ -1981,6 +1996,18 @@
<param name="bodyShapeMap">The shapes for the bodies managed by this instance.</param>
<param name="bodyPositionUpdater">The body position update delegate for this instance.</param>
</member>
+ <member name="M:StarSimLib.UI.SimulationScreen.CreateFileWriter">
+ <summary>
+ Creates a <see cref="T:System.IO.StreamWriter"/> to allow for logging the frame-by-frame state of the simulation to a file.
+ </summary>
+ </member>
+ <member name="M:StarSimLib.UI.SimulationScreen.GetSimulationOutputFileName(System.String)">
+ <summary>
+ Returns the formatted name of the simulation file to be created.
+ </summary>
+ <param name="dirPath">The path to the output directory.</param>
+ <returns>The simulation output file name, including a timestamp.</returns>
+ </member>
<member name="M:StarSimLib.UI.SimulationScreen.CleanupScreen(SFML.Graphics.RenderTarget,SFML.Graphics.RenderStates)">
<inheritdoc />
</member>
diff --git a/StarSim/StarSimLib/UI/SimulationScreen.cs b/StarSim/StarSimLib/UI/SimulationScreen.cs
@@ -1,10 +1,13 @@
-using SFML.Graphics;
+using System;
+using SFML.Graphics;
using StarSimLib.Data_Structures;
using StarSimLib.Graphics;
using StarSimLib.Physics;
using System.Collections.Generic;
+using System.IO;
+using System.Text;
using System.Timers;
namespace StarSimLib.UI
@@ -36,6 +39,11 @@ namespace StarSimLib.UI
private readonly Dictionary<Body, CircleShape> bodyShapeMap;
/// <summary>
+ /// Allows for writing out the state of the current frame to a file.
+ /// </summary>
+ private readonly StreamWriter fileWriter;
+
+ /// <summary>
/// Timer that manages FPS counter and other miscellaneous counters.
/// </summary>
private readonly Timer miscTimer;
@@ -61,6 +69,11 @@ namespace StarSimLib.UI
private uint framesElapsed;
/// <summary>
+ /// Counts the number of frames elapsed since the beginning of the simulation.
+ /// </summary>
+ private ulong totalFramesElapsed;
+
+ /// <summary>
/// Initialises a new instance of the <see cref="SimulationScreen"/> class.
/// </summary>
/// <param name="renderWindow">The render window to which to display this instance.</param>
@@ -89,14 +102,81 @@ namespace StarSimLib.UI
framesElapsed = 0;
renderWindow.SetTitle($"N-Body Simulator: FPS {fps}");
};
+
+ fileWriter = CreateFileWriter();
}
- #region Overrides of Screen
+ /// <summary>
+ /// Creates a <see cref="StreamWriter"/> to allow for logging the frame-by-frame state of the simulation to a file.
+ /// </summary>
+ private StreamWriter CreateFileWriter()
+ {
+ try
+ {
+ string dirPath = Path.GetFullPath(Constants.OutputFileDirectory);
+
+ if (!Directory.Exists(dirPath))
+ {
+ // ensure that the chosen output directory exists
+ Directory.CreateDirectory(dirPath);
+ }
+
+ string fullPath = Path.Combine(dirPath, GetSimulationOutputFileName(dirPath));
+
+ return new StreamWriter(File.Create(fullPath), Encoding.UTF8);
+ }
+ catch (DirectoryNotFoundException ex)
+ {
+ Console.WriteLine($"Selected output directory could not be found: {ex.Message}");
+ Console.WriteLine(
+ "Could not create new simulation output file. Frame-by-frame state of simulation will not be logged.");
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Returns the formatted name of the simulation file to be created.
+ /// </summary>
+ /// <param name="dirPath">The path to the output directory.</param>
+ /// <returns>The simulation output file name, including a timestamp.</returns>
+ private string GetSimulationOutputFileName(string dirPath)
+ {
+ StringBuilder fileNameBuilder = new StringBuilder("simulation-");
+
+ string timestamp = $"{DateTime.Now.Day}.{DateTime.Now.Month}.{DateTime.Now.Year}";
+ fileNameBuilder.Append(timestamp);
+
+ uint previousFileCount = 0;
+
+ foreach (string fileName in Directory.EnumerateFiles(dirPath))
+ {
+ if (fileName.Contains(timestamp))
+ {
+ previousFileCount++;
+ }
+ }
+
+ if (previousFileCount > 0)
+ {
+ fileNameBuilder.Append($"-{previousFileCount}");
+ }
+
+ fileNameBuilder.Append(".txt");
+
+ return fileNameBuilder.ToString();
+ }
/// <inheritdoc />
protected override void CleanupScreen(RenderTarget renderTarget, RenderStates renderStates)
{
miscTimer.Stop();
+
+ if (fileWriter != null)
+ {
+ fileWriter.Flush();
+ fileWriter.Close();
+ }
}
/// <inheritdoc />
@@ -109,6 +189,21 @@ namespace StarSimLib.UI
/// <inheritdoc />
protected override void DrawFrame(RenderTarget renderTarget, RenderStates renderStates)
{
+ if (fileWriter != null)
+ {
+ fileWriter.WriteLine($"FRAME: {totalFramesElapsed}, ELAPSED TIME: {totalFramesElapsed * Constants.TimeStep}");
+
+ // write out the unique id of the body and its state to the currently open file
+ foreach (Body body in bodies)
+ {
+ fileWriter.WriteLine($"{body.Generation,2:D}.{body.Id,-4:D}\t" +
+ $"{body.Mass,21}\t" +
+ $"[{body.Position.X,21}, {body.Position.Y,21}, {body.Position.Z,21}]\t" +
+ $"[{body.Velocity.X,21}, {body.Velocity.Y,21}, {body.Velocity.Z,21}]\t" +
+ $"[{body.Force.X,21}, {body.Force.Y,21}, {body.Force.Z,21}]");
+ }
+ }
+
if (!simulationInputHandler.IsSimulationPaused)
{
bodyPositionUpdater(bodies, Constants.TimeStep);
@@ -116,8 +211,9 @@ namespace StarSimLib.UI
simulationDrawer.DrawBodies();
- // increment the fps counter
+ // increment the fps counter and global frame counter
framesElapsed++;
+ totalFramesElapsed++;
}
/// <inheritdoc />
@@ -125,7 +221,5 @@ namespace StarSimLib.UI
{
miscTimer.Start();
}
-
- #endregion Overrides of Screen
}
}
\ No newline at end of file