StarSim

StarSim.git
git clone git://git.lenczewski.org/StarSim.git
Log | Files | Refs | README | LICENSE

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:
MStarSim/StarSimLib/Constants.cs | 5+++++
MStarSim/StarSimLib/StarSimLib.xml | 27+++++++++++++++++++++++++++
MStarSim/StarSimLib/UI/SimulationScreen.cs | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
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