diff --git a/src/Visual Studio 2022/Visualizers/attribcache140.bin b/src/Visual Studio 2022/Visualizers/attribcache140.bin new file mode 100644 index 000000000..e2dba902e Binary files /dev/null and b/src/Visual Studio 2022/Visualizers/attribcache140.bin differ diff --git a/src/Vts.MonteCarlo.CommandLineApplication.Test/ProgramTests.cs b/src/Vts.MonteCarlo.CommandLineApplication.Test/ProgramTests.cs index f466d15c4..9c2ceac5c 100644 --- a/src/Vts.MonteCarlo.CommandLineApplication.Test/ProgramTests.cs +++ b/src/Vts.MonteCarlo.CommandLineApplication.Test/ProgramTests.cs @@ -35,7 +35,8 @@ public class ProgramTests "two_layer_ROfRho", "two_layer_ROfRho_TOfRho_with_databases", "voxel_ROfXAndY_FluenceOfXAndYAndZ", - "surface_fiber_detector" + "surface_fiber_detector", + "ray_database_generator" }; private readonly List _listOfInfilesThatRequireExistingResultsToRun = new List() { diff --git a/src/Vts.MonteCarlo.PostProcessor.Test/ProgramTests.cs b/src/Vts.MonteCarlo.PostProcessor.Test/ProgramTests.cs index 172c10d6b..d75498fab 100644 --- a/src/Vts.MonteCarlo.PostProcessor.Test/ProgramTests.cs +++ b/src/Vts.MonteCarlo.PostProcessor.Test/ProgramTests.cs @@ -42,7 +42,8 @@ public class ProgramTests "two_layer_ROfRho", "two_layer_ROfRho_TOfRho_with_databases", "voxel_ROfXAndY_FluenceOfXAndYAndZ", - "surface_fiber_detector" + "surface_fiber_detector", + "ray_database_generator" }; /// diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/ProgramTests.cs b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/ProgramTests.cs new file mode 100644 index 000000000..47974645f --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/ProgramTests.cs @@ -0,0 +1,165 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System; +using Vts.IO; +using Vts.MonteCarlo.RayData; +using System.Threading.Tasks; +using System.IO; +using System.Reflection; + +namespace Vts.MonteCarlo.ZemaxDatabaseConverter.Test +{ + [TestFixture] + public class ProgramTests + { + // This tests code that would be executed to convert ZRD DB to/from MCCL compatible DB + // An actual Zemax output ZRD file was first used to verify tests, however file too large + // to add to resources. Instead this program is used to convert a MCCL DB to Zemax DB + // and that is used to test + + /// + /// list of temporary files created by these unit tests + /// + readonly List listOfTestGeneratedFiles = new List() + { + "testzrdraydatabase", + "testzrdraydatabase.txt", + "testmcclraydatabase", + "testmcclraydatabase.txt", + }; + readonly List listOfTestGeneratedFolders = new List() + { + "mcclraydatabase", + "zrdraydatabase" + }; + + + /// + /// Set up simulation specifying RayDatabase to be written + /// + [OneTimeSetUp] + public void Setup_simulation_input_components() + { + // delete previously generated files + Clear_folders_and_files(); + } + + /// + /// clear all previously generated files. + /// + [OneTimeTearDown] + public void Clear_folders_and_files() + { + // delete any previously generated files + foreach (var file in listOfTestGeneratedFiles) + { + if (File.Exists(file)) + { + FileIO.FileDelete(file); + } + } + // delete any previously generated folders + foreach (var folder in listOfTestGeneratedFolders) + { + if (Directory.Exists(folder)) + { + FileIO.DeleteDirectory(folder); + } + } + + } + /// + /// Test to verify sanity check on input works correctly + /// + [Test] + public async Task Validate_VerifyInputs_method_returns_correct_values() + { + // the following will fail because only 1 argument and file does not exist + string[] arguments = new string[] { "infile=databaseToConvert" }; + var status = await Task.Run(() => Program.Main(arguments)); + Assert.IsTrue(status == 1); + // no successful mccl-to-zrd test is tested here because tested in other tests + } + /// + /// Test that uses app to convert RayDatabase written in OneTimeSetup to a Zemax ZRD ray database + /// Validation values used from prior test + /// + [Test] + public async Task Validate_conversion_from_MCCL_RayDatabase_to_Zemax_ZrdDatabase_successful() + { + // copy MCCL RayDatabase from Resources + var folder = "mcclraydatabase"; + FileIO.CopyFolderFromEmbeddedResources(folder, "", + Assembly.GetExecutingAssembly().FullName, true); + // run database converter on MCCL Ray Database generated in OneTimeSetup + var arguments = new string[] { + "infile=mcclraydatabase/RayDiffuseReflectanceDatabase", + "infiletype=mccl", + "outfile=testzrdraydatabase" }; + await Task.Run(() => Program.Main(arguments)); + // read file written + var rayDatabase = Zemax.ZrdRayDatabase.FromFile("testzrdraydatabase"); + Assert.AreEqual(88, rayDatabase.NumberOfElements); + + // manually enumerate through the first element + var enumerator = rayDatabase.DataPoints.GetEnumerator(); + // advance to the first point and test that the point is valid + enumerator.MoveNext(); + var dp1 = enumerator.Current; + Assert.IsTrue(Math.Abs(dp1.X - 4.18911) < 1e-5); + Assert.IsTrue(Math.Abs(dp1.Y + 22.1217) < 1e-4); + Assert.IsTrue(Math.Abs(dp1.Z - 0.0) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Ux - 0.654227) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Uy - 0.223239) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Uz + 0.722600) < 1e-6); // negative because exiting + Assert.IsTrue(Math.Abs(dp1.Weight - 0.021116) < 1e-6); + // advance to the second point and test that the point is valid + enumerator.MoveNext(); + var dp2 = enumerator.Current; + Assert.IsTrue(Math.Abs(dp2.X - 0.382333) < 1e-6); + Assert.IsTrue(Math.Abs(dp2.Y + 2.13952) < 1e-5); + Assert.IsTrue(Math.Abs(dp2.Z - 0.0) < 1e-6); + Assert.IsTrue(Math.Abs(dp2.Ux + 0.711575) < 1e-6); + Assert.IsTrue(Math.Abs(dp2.Uy + 0.493464) < 1e-6); + Assert.IsTrue(Math.Abs(dp2.Uz + 0.500153) < 1e-6); + Assert.IsTrue(Math.Abs(dp2.Weight - 0.911520) < 1e-6); + enumerator.Dispose(); + } + /// + /// test to verify reading actual Zemax ZRD file on hold until we can generate + /// a small one ourselves. In the meantime, convert MCCL Ray Database generated + /// in OneTimeSetup and then convert back to ZRD formatted database + /// + [Test] + public async Task Validate_conversion_from_Zemax_ZrdDatabase_to_MCCL_RayDatabase_successful() + { + //actual ZRD DB is in @"C:\Users\hayakawa\Desktop\RP\Zemax\ZWOutput\lightfromsourcefile" + // copy ZRD RayDatabase from Resources + var folder = "zrdraydatabase"; + FileIO.CopyFolderFromEmbeddedResources(folder, "", + Assembly.GetExecutingAssembly().FullName, true); + // use actual zrd database and convert to mccl database + var arguments = new string[] { + "infile=zrdraydatabase/Lightfromsourcefile", + "infiletype=zrd", + "outfile=testmcclraydatabase" }; + await Task.Run(() => Program.Main(arguments)); + var rayDatabase = RayDatabase.FromFile("testmcclraydatabase"); + Assert.AreEqual(10, rayDatabase.NumberOfElements); + var enumerator = rayDatabase.DataPoints.GetEnumerator(); + // advance to the first point and test that the point is valid + enumerator.MoveNext(); + var dp1 = enumerator.Current; + Assert.IsTrue(Math.Abs(dp1.Position.X - 0.003760) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Position.Y - 0.001500) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Position.Z - 0.490243) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Direction.Ux - 0.005315) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Direction.Uy - 0.992280) < 1e-6); + Assert.IsTrue(Math.Abs(dp1.Direction.Uz - 0.123897) < 1e-6); // why is this neg? + Assert.IsTrue(Math.Abs(dp1.Weight - 0.000730) < 1e-6); + enumerator.Dispose(); + } + + + } +} diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase new file mode 100644 index 000000000..d612feded Binary files /dev/null and b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase differ diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase.txt b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase.txt new file mode 100644 index 000000000..29161e52d --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/mcclraydatabase/RayDiffuseReflectanceDatabase.txt @@ -0,0 +1,3 @@ +{ + "NumberOfElements": 88 +} \ No newline at end of file diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile new file mode 100644 index 000000000..fb4d44001 Binary files /dev/null and b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile differ diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile.txt b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile.txt new file mode 100644 index 000000000..41ac6a717 --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Resources/zrdraydatabase/Lightfromsourcefile.txt @@ -0,0 +1,3 @@ +{ + "NumberOfElements": 10 +} diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Vts.MonteCarlo.ZemaxDatabaseConverter.Test.csproj b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Vts.MonteCarlo.ZemaxDatabaseConverter.Test.csproj new file mode 100644 index 000000000..5a68a658d --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter.Test/Vts.MonteCarlo.ZemaxDatabaseConverter.Test.csproj @@ -0,0 +1,38 @@ + + + + net8.0 + + false + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + \ No newline at end of file diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter/DatabaseConverter.cs b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/DatabaseConverter.cs new file mode 100644 index 000000000..68eab2c4a --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/DatabaseConverter.cs @@ -0,0 +1,130 @@ +using System; +using System.IO; +using Vts.Common; +using Vts.MonteCarlo.RayData; +using Vts.Zemax; + +namespace Vts.MonteCarlo.ZemaxDatabaseConverter +{ + public static class DatabaseConverter + { + /// + /// method to verify input arguments + /// + /// Database to be converted + /// Converted database + /// Boolean indicating inputs are valid + public static bool VerifyInputs(string inputFile, string outputFile) + { + try + { + if (string.IsNullOrEmpty(inputFile)) + { + Console.WriteLine("\nNo input database file specified"); + return false; + } + + if (!string.IsNullOrEmpty(outputFile)) return true; + Console.WriteLine("\nNo output database file specified"); + return false; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return false; + } + } + + /// + /// method to convert Zemax ray database to MCCL source database + /// + /// Database to be converted + /// Converted database + public static void ConvertZemaxDatabaseToMcclSourceDatabase(string inputFile, string outputFile) + { + try + { + //get the full path for the input file + var fullFilePath = Path.GetFullPath(inputFile); + + var fileToConvert = ZrdRayDatabase.FromFile(fullFilePath); + + // enumerate through the elements + var enumerator = fileToConvert.DataPoints.GetEnumerator(); + + using (var dbWriter = new RayDatabaseWriter( + VirtualBoundaryType.DiffuseReflectance, outputFile)) + { + for (var i = 0; i < fileToConvert.NumberOfElements; i++) + { + // advance to the next ray data + enumerator.MoveNext(); + var dp = enumerator.Current; + // excise Position,Direction,Weight from Zemax struct + if (dp != null) + { + dbWriter.Write(new RayDataPoint( + new Position(dp.X, dp.Y, dp.Z), + new Direction(dp.Ux, dp.Uy, dp.Uz), + dp.Weight)); + } + } + dbWriter.Close(); + } + enumerator.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + /// + /// method to convert zemax ray database to MCCL source database + /// + /// Database to be converted + /// Converted database + public static void ConvertMcclDatabaseToZemaxSourceDatabase(string inputFile, string outputFile) + { + try + { + //get the full path for the input file + var fullFilePath = Path.GetFullPath(inputFile); + + var fileToConvert = RayDatabase.FromFile(fullFilePath); + + // enumerate through the elements + var enumerator = fileToConvert.DataPoints.GetEnumerator(); + + using (var dbWriter = new ZrdRayDatabaseWriter( + VirtualBoundaryType.DiffuseReflectance, outputFile)) + { + for (var i = 0; i < fileToConvert.NumberOfElements; i++) + { + // advance to the next ray data + enumerator.MoveNext(); + var dp = enumerator.Current; + if (dp == null) continue; + var zrdRayDataPoint = new ZrdRayDataPoint + { + // set Position,Direction,Weight in Zemax struct + X = dp.Position.X, + Y = dp.Position.Y, + Z = dp.Position.Z, + Ux = dp.Direction.Ux, + Uy = dp.Direction.Uy, + Uz = dp.Direction.Uz, + Weight = dp.Weight + }; + dbWriter.Write(zrdRayDataPoint); + } + dbWriter.Close(); + } + enumerator.Dispose(); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + } +} diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Program.cs b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Program.cs new file mode 100644 index 000000000..dc436fffb --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Program.cs @@ -0,0 +1,167 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Vts.MonteCarlo.ZemaxDatabaseConverter +{ + #region Zemax Command Line Arguments Parser + + /* Simple commandline argument parser written by Ananth B. http://www.ananthonline.net */ + static class CommandLine + { + public class Switch // Class that encapsulates switch data. + { + public Switch(string name, string shortForm, Action> handler) + { + Name = name; + ShortForm = shortForm; + Handler = handler; + } + + public Switch(string name, Action> handler) + { + Name = name; + ShortForm = null; + Handler = handler; + } + + public string Name { get; private set; } + public string ShortForm { get; private set; } + public Action> Handler { get; private set; } + + public int InvokeHandler(string[] values) + { + Handler(values); + return 1; + } + } + + /* The regex that extracts names and comma-separated values for switches + in the form ([="value 1",value2,...])+ */ + private static readonly Regex ArgRegex = + new Regex(@"(?[^=]+)=?((?\""?)(?(?(quoted)[^\""]+|[^,]+))\""?,?)*", + RegexOptions.Compiled | RegexOptions.CultureInvariant | + RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); + + private const string NameGroup = "name"; // Names of capture groups + private const string ValueGroup = "value"; + + public static void Process(this string[] args, Action printUsage, params Switch[] switches) + { + /* Run through all matches in the argument list and if any of the switches + match, get the values and invoke the handler we were given. We do a Sum() + here for 2 reasons; a) To actually run the handlers + and b) see if any were invoked at all (each returns 1 if invoked). + If none were invoked, we simply invoke the printUsage handler. */ + if ((from arg in args + from Match match in ArgRegex.Matches(arg) + from s in switches + where match.Success && + ((string.Compare(match.Groups[NameGroup].Value, s.Name, true) == 0) || + (string.Compare(match.Groups[NameGroup].Value, s.ShortForm, true) == 0)) + select s.InvokeHandler(match.Groups[ValueGroup].Value.Split(','))).Sum() == 0) + printUsage(); // We didn't find any switches + } + } + #endregion + public static class Program + { + /// + /// main Zemax-MCCL Database converter + /// + /// + /// int=0 (successful completion) + /// int=1 (infile null or missing) + public static int Main(string[] args) + { + var databaseToConvertName = ""; + var convertedDatabaseName = ""; + var infileType = ""; + var zemaxToMccl = false; + var infoOnlyOption = false; + + args.Process(() => + { + Console.WriteLine("Virtual Photonics Zemax-MCCL Database converter"); + Console.WriteLine(); + Console.WriteLine("For more information type mc_zemax help"); + Console.WriteLine(); + }, + new CommandLine.Switch("help", val => + { + ShowHelp(); + infoOnlyOption = true; + }), + new CommandLine.Switch("infile", val => + { + databaseToConvertName = val.First(); + Console.WriteLine("input file specified as {0}", databaseToConvertName); + }), + new CommandLine.Switch("outfile", val => + { + convertedDatabaseName = val.First(); + Console.WriteLine("output file specified as {0}", convertedDatabaseName); + }), + new CommandLine.Switch("infiletype", val => + { + infileType = val.First(); + switch (infileType) + { + case "zrd": + zemaxToMccl = true; + Console.WriteLine("converting Zemax database to MCCL database"); + break; + case "mccl": + zemaxToMccl = false; + Console.WriteLine("converting MCCL database to Zemax database"); + break; + default: + infoOnlyOption = true; + Console.WriteLine("infiletype either needs to be set to 'zrd' or 'mccl'"); + break; + } + }) + ); + + if (infoOnlyOption) return 0; + // check infiles exist + if (!DatabaseConverter.VerifyInputs(databaseToConvertName, convertedDatabaseName)) + return 1; // VerifyInputs returned a false + if (zemaxToMccl) + { + // conversion of Zemax output to MCCL source database process + DatabaseConverter.ConvertZemaxDatabaseToMcclSourceDatabase( + databaseToConvertName, convertedDatabaseName); + } + else + { + // conversion of MCCL reflectance to Zemax input + DatabaseConverter.ConvertMcclDatabaseToZemaxSourceDatabase( + databaseToConvertName, convertedDatabaseName); + } + return 0; + } + + /// + /// Displays the help text for detailed usage of the application + /// + private static void ShowHelp() + { + Console.WriteLine("Virtual Photonics Zemax-MCCL Database Converter"); + Console.WriteLine(); + Console.WriteLine("list of arguments:"); + Console.WriteLine(); + Console.WriteLine("infile\t\tthe input file of database to be converted"); + Console.WriteLine("outname\t\toutput name of converted database file"); + Console.WriteLine("infiletype\t\ttype of infile, either 'zrd' or 'mccl'"); + Console.WriteLine(); + Console.WriteLine("sample usage:"); + Console.WriteLine(); + Console.WriteLine("mc_zemax infile=databasetoconvert infiletype=zrd outfile=converteddatabase"); + Console.WriteLine("mc_zemax infile=databasetoconvert infiletype=mccl outfile=converteddatabase"); + + } + + } +} \ No newline at end of file diff --git a/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Vts.MonteCarlo.ZemaxDatabaseConverter.csproj b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Vts.MonteCarlo.ZemaxDatabaseConverter.csproj new file mode 100644 index 000000000..56ea4bdbb --- /dev/null +++ b/src/Vts.MonteCarlo.ZemaxDatabaseConverter/Vts.MonteCarlo.ZemaxDatabaseConverter.csproj @@ -0,0 +1,31 @@ + + + + Exe + net8.0 + Vts.MonteCarlo.ZemaxDatabaseConverter + mc_zemax + win-x64;linux-x64;osx-x64 + 7.0.0.0 + 6.2.0.0 + David Cuccia; Carole Hayakawa; Lisa Malenfant; Janaka Ranasinghesagara; Jennifer Nguyen; Adam Gardner; Michele Martinelli + Virtual Photonics Technology Initiative + MCZemax + Monte-Carlo Zemax-MCCL database converter command-line application + Copyright © 2022 Laser Microbeam and Medical Program + https://github.com/VirtualPhotonics/VTS/wiki + https://github.com/VirtualPhotonics/VTS/blob/master/license.md + http://virtualphotonics.org/Themes/VP/Content/Images/logo.png + https://github.com/VirtualPhotonics/VTS + Git + C#;Monte-Carlo + false + 7.0.0 + + + + + + + + diff --git a/src/Vts.Test/MonteCarlo/Factories/DatabaseWriterFactoryTests.cs b/src/Vts.Test/MonteCarlo/Factories/DatabaseWriterFactoryTests.cs index 9630b3aa1..2f02167b2 100644 --- a/src/Vts.Test/MonteCarlo/Factories/DatabaseWriterFactoryTests.cs +++ b/src/Vts.Test/MonteCarlo/Factories/DatabaseWriterFactoryTests.cs @@ -6,6 +6,7 @@ using Vts.MonteCarlo; using Vts.MonteCarlo.Factories; using Vts.MonteCarlo.PhotonData; +using Vts.MonteCarlo.RayData; using Vts.MonteCarlo.Tissues; namespace Vts.Test.MonteCarlo.Factories @@ -133,5 +134,50 @@ public void Demonstrate_GetCollisionInfoDatabaseWriter_successful_return() Directory.GetCurrentDirectory(), "")); } + /// + /// Test DatabaseWriterFactory GetRaySurfaceVirtualBoundaryDatabaseWriter method + /// + [Test] + public void Demonstrate_GetRaySurfaceVirtualBoundaryDatabaseWriter_successful_return() + { + // following are not RaySurfaceVBs + var diffuseReflectanceDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.DiffuseReflectance, + Directory.GetCurrentDirectory(), ""); + Assert.That(diffuseReflectanceDb, Is.Null); + var diffuseTransmittanceDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.DiffuseTransmittance, + Directory.GetCurrentDirectory(), ""); + Assert.That(diffuseTransmittanceDb, Is.Null); + var specularDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.SpecularReflectance, + Directory.GetCurrentDirectory(), ""); + Assert.That(specularDb, Is.Null); + var pMcDiffuseReflectanceDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.pMCDiffuseReflectance, + Directory.GetCurrentDirectory(), ""); + Assert.That(pMcDiffuseReflectanceDb, Is.Null); + var pMcDiffuseTransmittanceDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.pMCDiffuseTransmittance, + Directory.GetCurrentDirectory(), ""); + Assert.That(pMcDiffuseTransmittanceDb, Is.Null); + // actual RaySurfaceVB + var rayDiffuseReflectanceDb = + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType.RayDiffuseReflectance, + Directory.GetCurrentDirectory(), ""); + Assert.That(rayDiffuseReflectanceDb, Is.InstanceOf()); + rayDiffuseReflectanceDb.Close(); + // check if enum set to something out of range + var fakeDatabaseType = (DatabaseType)Enum.GetNames(typeof(DatabaseType)).Length + 1; + Assert.Throws( + () => DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriter( + fakeDatabaseType, Directory.GetCurrentDirectory(), "")); + } } } diff --git a/src/Vts.Test/MonteCarlo/RayData/RayDatabaseTests.cs b/src/Vts.Test/MonteCarlo/RayData/RayDatabaseTests.cs new file mode 100644 index 000000000..2d67fd397 --- /dev/null +++ b/src/Vts.Test/MonteCarlo/RayData/RayDatabaseTests.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Vts.Common; +using Vts.IO; +using Vts.MonteCarlo; +using Vts.MonteCarlo.Detectors; +using Vts.MonteCarlo.RayData; +using Vts.MonteCarlo.Sources; +using Vts.MonteCarlo.Tissues; + +namespace Vts.Test.MonteCarlo.RayData +{ + [TestFixture] + public class RayDatabaseTests + { + /// + /// list of temporary files created by these unit tests + /// + readonly List _listOfTestGeneratedFiles = new List() + { + "testraydatabase", + "testraydatabase.txt", + "RayDiffuseReflectanceDatabase", // name has no "test" prefix, it is generated by the code so name fixed + "RayDiffuseReflectanceDatabase.txt", + //"file.txt", // file that captures output of MC simulation that usually goes to screen + }; + + /// + /// Set up simulation specifying RayDatabase to be written + /// + [OneTimeSetUp] + public void Setup_simulation_input_components() + { + // delete previously generated files + Clear_folders_and_files(); + + var input = new SimulationInput( + 100, + "", + new SimulationOptions( + 0, + RandomNumberGeneratorType.MersenneTwister, + AbsorptionWeightingType.Continuous, + PhaseFunctionType.HenyeyGreenstein, + new List() { DatabaseType.RayDiffuseReflectance }, // SPECIFY DATABASE + false, // track statistics + 0.0, // RR threshold -> 0 = no RR performed + 1), + new DirectionalPointSourceInput( + new Position(0.0, 0.0, 0.0), + new Direction(0.0, 0.0, 1.0), + 1), + new MultiLayerTissueInput( + new ITissueRegion[] + { + new LayerTissueRegion( + new DoubleRange(double.NegativeInfinity, 0.0), + new OpticalProperties(0.0, 1e-10, 0.0, 1.0)), + new LayerTissueRegion( + new DoubleRange(0.0, 20.0), + new OpticalProperties(0.01, 1.0, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(20.0, double.PositiveInfinity), + new OpticalProperties(0.0, 1e-10, 0.0, 1.0)) + }), + new List() + { + new ROfRhoAndTimeDetectorInput() + { + Rho = new DoubleRange(0.0, 10.0, 101), + Time = new DoubleRange(0.0, 1, 101) + } + } + ); + new MonteCarloSimulation(input).Run(); + } + + /// + /// clear all previously generated files. + /// + [OneTimeTearDown] + public void Clear_folders_and_files() + { + // delete any previously generated files + foreach (var file in _listOfTestGeneratedFiles) + { + FileIO.FileDelete(file); + } + } + + /// + /// test to verify RayDatabaseWriter and RayDatabase.FromFile are working correctly. + /// + [Test] + public void Validate_RayDatabase_Writer_and_FromFile_are_correct_when_using_WriteToFile() + { + const string databaseFileName = "testraydatabase"; + var firstRayDp = new RayDataPoint( + new Position(1, 1, 0), + new Direction(0, 1 / Math.Sqrt(2), -1 / Math.Sqrt(2)), + 1.0); + var secondRayDp = new RayDataPoint( + new Position(2, 2, 0), + new Direction(1 / Math.Sqrt(2), 0, -1 / Math.Sqrt(2)), + 2.0); + using (var dbWriter = new RayDatabaseWriter( + VirtualBoundaryType.DiffuseReflectance, databaseFileName)) + { + dbWriter.Write(firstRayDp); + dbWriter.Write(secondRayDp); + dbWriter.Close(); + } + // read back file written + var rayDatabase = RayDatabase.FromFile(databaseFileName); + Assert.That(rayDatabase.NumberOfElements, Is.EqualTo(2)); + + // manually enumerate through the first two elements (same as foreach) + // PhotonDatabase is designed so you don't have to have the whole thing + // in memory, so .ToArray() loses the benefits of the lazy-load data points + var enumerator = rayDatabase.DataPoints.GetEnumerator(); + // advance to the first point and test that the point is valid + enumerator.MoveNext(); + var dp1 = enumerator.Current; + Assert.That(dp1.Position.X == firstRayDp.Position.X, Is.True); + Assert.That(dp1.Position.Y == firstRayDp.Position.Y, Is.True); + Assert.That(dp1.Position.Z == firstRayDp.Position.Z, Is.True); + Assert.That(dp1.Direction.Ux == firstRayDp.Direction.Ux, Is.True); + Assert.That(dp1.Direction.Uy == firstRayDp.Direction.Uy, Is.True); + Assert.That(dp1.Direction.Uz == firstRayDp.Direction.Uz, Is.True); + Assert.That(dp1.Weight == firstRayDp.Weight, Is.True); + // advance to the second point and test that the point is valid + enumerator.MoveNext(); + var dp2 = enumerator.Current; + Assert.That(dp2.Position.X == secondRayDp.Position.X, Is.True); + Assert.That(dp2.Position.Y == secondRayDp.Position.Y, Is.True); + Assert.That(dp2.Position.Z == secondRayDp.Position.Z, Is.True); + Assert.That(dp2.Direction.Ux == secondRayDp.Direction.Ux, Is.True); + Assert.That(dp2.Direction.Uy == secondRayDp.Direction.Uy, Is.True); + Assert.That(dp2.Direction.Uz == secondRayDp.Direction.Uz, Is.True); + Assert.That(dp2.Weight == secondRayDp.Weight, Is.True); + enumerator.Dispose(); + } + + /// + /// Test to verify RayDiffuseReflectanceDatabase generated by MCCL simulation + /// executed in OneTimeSetup is written and that it has the correct contents. + /// + [Test] + public void Validate_database_file_gets_written_by_MCCL_and_is_correct() + { + Assert.That(FileIO.FileExists("RayDiffuseReflectanceDatabase"), Is.True); + // read the database from file, and verify the correct number of photons were written + var rayDatabase = RayDatabase.FromFile("RayDiffuseReflectanceDatabase"); + + Assert.That(rayDatabase.NumberOfElements, Is.EqualTo(88)); + var enumerator = rayDatabase.DataPoints.GetEnumerator(); + // advance to the first point and test that the point is valid + enumerator.MoveNext(); + var dp1 = enumerator.Current; + Assert.That(dp1.Position.Z, Is.EqualTo(0.0)); + Assert.That(Math.Abs(dp1.Weight - 0.021116) < 0.000001, Is.True); + enumerator.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Vts.Test/MonteCarlo/Sources/FromFile/RayFileSourceTests.cs b/src/Vts.Test/MonteCarlo/Sources/FromFile/RayFileSourceTests.cs new file mode 100644 index 000000000..6f972f438 --- /dev/null +++ b/src/Vts.Test/MonteCarlo/Sources/FromFile/RayFileSourceTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Vts.IO; +using Vts.MonteCarlo; +using Vts.MonteCarlo.Tissues; +using Vts.MonteCarlo.RayData; +using Vts.MonteCarlo.Sources; +using Vts.Common; + +namespace Vts.Test.MonteCarlo.Sources +{ + /// + /// Unit tests for From File Source + /// + [TestFixture] + public class RayFileSourceTests + { + /// + /// list of temporary files created by these unit tests + /// + readonly List listOfTestGeneratedFiles = new List() + { + "inputFromFile.txt", + "testraydatabase", + "testraydatabase.txt" + }; + /// + /// clear all generated folders and files + /// + [OneTimeSetUp] + [OneTimeTearDown] + public void clear_folders_and_files() + { + foreach (var file in listOfTestGeneratedFiles) + { + FileIO.FileDelete(file); + } + } + /// + /// set up MCCL RayDatabase + /// + [OneTimeSetUp] + public void setup() + { + string databaseFileName = "testraydatabase"; + var firstRayDP = new RayDataPoint( + new Position(1, 1, 0), + new Direction(0, 1 / Math.Sqrt(2), -1 / Math.Sqrt(2)), + 1.0); + var secondRayDP = new RayDataPoint( + new Position(2, 2, 0), + new Direction(1 / Math.Sqrt(2), 0, -1 / Math.Sqrt(2)), + 2.0); + using (var dbWriter = new RayDatabaseWriter( + VirtualBoundaryType.DiffuseReflectance, databaseFileName)) + { + dbWriter.Write(firstRayDP); + dbWriter.Write(secondRayDP); + dbWriter.Close(); + } + } + + /// + /// Validate RayFileSource reads data from file correctly + /// + [Test] + public void validate_RayFileSource_reads_database_correctly() + { + var source = new RayFileSource("testraydatabase", 0); + var tissue = new MultiLayerTissue(); + Photon dp = source.GetNextPhoton(tissue); + Assert.That(dp.DP.Position.X, Is.EqualTo(1.0)); + Assert.That(dp.DP.Position.Y, Is.EqualTo(1.0)); + Assert.That(dp.DP.Position.Z, Is.EqualTo(0.0)); + Assert.That(dp.DP.Direction.Ux, Is.EqualTo(0.0)); + Assert.That(dp.DP.Direction.Uy, Is.EqualTo(1.0 / Math.Sqrt(2))); + Assert.That(dp.DP.Direction.Uz, Is.EqualTo(-1.0 / Math.Sqrt(2))); + dp = source.GetNextPhoton(tissue); + Assert.That(dp.DP.Position.X, Is.EqualTo(2.0)); + Assert.That(dp.DP.Position.Y, Is.EqualTo(2.0)); + Assert.That(dp.DP.Position.Z, Is.EqualTo(0.0)); + Assert.That(dp.DP.Direction.Ux, Is.EqualTo(1.0 / Math.Sqrt(2))); + Assert.That(dp.DP.Direction.Uy, Is.EqualTo(0.0)); + Assert.That(dp.DP.Direction.Uz, Is.EqualTo(-1.0 / Math.Sqrt(2))); + source.DatabaseEnumerator.Dispose(); + } + + } +} \ No newline at end of file diff --git a/src/Vts.Zemax.Test/Vts.Zemax.Test.csproj b/src/Vts.Zemax.Test/Vts.Zemax.Test.csproj new file mode 100644 index 000000000..c85700d82 --- /dev/null +++ b/src/Vts.Zemax.Test/Vts.Zemax.Test.csproj @@ -0,0 +1,27 @@ + + + net8.0 + false + 40 + C:\Users\hayakawa\Documents\Visual Studio 2022\Projects\vts_220810\src\Backup\Vts.Zemax.Test\ + 2.0 + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + Always + + + diff --git a/src/Vts.Zemax.Test/ZrdRayDatabaseTests.cs b/src/Vts.Zemax.Test/ZrdRayDatabaseTests.cs new file mode 100644 index 000000000..37d3af390 --- /dev/null +++ b/src/Vts.Zemax.Test/ZrdRayDatabaseTests.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Vts.IO; +using Vts.MonteCarlo; + +namespace Vts.Zemax.Test +{ + [TestFixture] + public class ZrdRayDatabaseTests + { + /// + /// list of temporary files created by these unit tests + /// + private readonly List _listOfTestGeneratedFiles = new() + { + "testzrdraydatabase", + "testzrdraydatabase.txt", + }; + /// + /// clear all previously generated files. + /// + [OneTimeSetUp] + [OneTimeTearDown] + public void Clear_folders_and_files() + { + // delete any previously generated files + foreach (var file in _listOfTestGeneratedFiles) + { + FileIO.FileDelete(file); + } + } + /// + /// test to verify ZRDRayDatabaseWriter and ZRDRayDatabase.FromFile are working correctly + /// + [Test] + public void Validate_ZrdRayDatabase_writing_and_reading_is_correct() + { + const string databaseFileName = "testzrdraydatabase"; + var firstRayDp = new ZrdRayDataPoint() + { + X = 1, + Y = 2, + Z = 3, + Ux = 0, + Uy = 1 / Math.Sqrt(2), + Uz = 1 / Math.Sqrt(2), + Weight = 1 + }; + var secondRayDp = new ZrdRayDataPoint() + { + X = 4, + Y = 5, + Z = 6, + Ux = 0, + Uy = 0, + Uz = 1, + Weight = 0.555 + }; + using (var dbWriter = new ZrdRayDatabaseWriter( + VirtualBoundaryType.DiffuseReflectance, databaseFileName)) + { + dbWriter.Write(firstRayDp); + dbWriter.Write(secondRayDp); + dbWriter.Close(); + } + // read back file written + var rayDatabase = ZrdRayDatabase.FromFile(databaseFileName); + Assert.AreEqual(2, rayDatabase.NumberOfElements); + + // manually enumerate through the first two elements + // in memory, so .ToArray() loses the benefits of the lazy-load data points + var enumerator = rayDatabase.DataPoints.GetEnumerator(); + // advance to the first point and test that the point is valid + enumerator.MoveNext(); + var dp1 = enumerator.Current; + if (dp1 != null) + { + Assert.IsTrue(dp1.X == firstRayDp.X); + Assert.IsTrue(dp1.Y == firstRayDp.Y); + Assert.IsTrue(dp1.Z == firstRayDp.Z); + Assert.IsTrue(dp1.Ux == firstRayDp.Ux); + Assert.IsTrue(dp1.Uy == firstRayDp.Uy); + Assert.IsTrue(dp1.Uz == firstRayDp.Uz); + Assert.IsTrue(dp1.Weight == firstRayDp.Weight); + } + + // advance to the second point and test that the point is valid + enumerator.MoveNext(); + var dp2 = enumerator.Current; + if (dp2 == null) return; + Assert.IsTrue(dp2.X == secondRayDp.X); + Assert.IsTrue(dp2.Y == secondRayDp.Y); + Assert.IsTrue(dp2.Z == secondRayDp.Z); + Assert.IsTrue(dp2.Ux == secondRayDp.Ux); + Assert.IsTrue(dp2.Uy == secondRayDp.Uy); + Assert.IsTrue(dp2.Uz == secondRayDp.Uz); + Assert.IsTrue(dp2.Weight == secondRayDp.Weight); + + enumerator.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Vts.Zemax/Vts.Zemax.csproj b/src/Vts.Zemax/Vts.Zemax.csproj new file mode 100644 index 000000000..dc10a750b --- /dev/null +++ b/src/Vts.Zemax/Vts.Zemax.csproj @@ -0,0 +1,11 @@ + + + + net8.0 + + + + + + + diff --git a/src/Vts.Zemax/ZemaxDatabaseType.cs b/src/Vts.Zemax/ZemaxDatabaseType.cs new file mode 100644 index 000000000..cfe1806f7 --- /dev/null +++ b/src/Vts.Zemax/ZemaxDatabaseType.cs @@ -0,0 +1,15 @@ +namespace Vts.Zemax +{ + /// + /// This should match VirtualBoundaryType one for one. Commented out ones have not made + /// it to the white list yet. + /// + public enum ZemaxDatabaseType + { + /// + /// Zemax Ray diffuse reflectance + /// + ZrdDiffuseReflectance, + + } +} \ No newline at end of file diff --git a/src/Vts.Zemax/ZrdDatabaseWriterController.cs b/src/Vts.Zemax/ZrdDatabaseWriterController.cs new file mode 100644 index 000000000..501f46d2f --- /dev/null +++ b/src/Vts.Zemax/ZrdDatabaseWriterController.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using Vts.MonteCarlo; +using Vts.MonteCarlo.PhotonData; +using Vts.MonteCarlo.RayData; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert ZRD DB to/from MCCL compatible DB + /// A controller of ZrdDatabaseWriter(s). It handles determining whether data should be written, + /// and if so, writing the data, and finally disposing of the database. + /// + public class ZrdDatabaseWriterController + { + private readonly IList _rayDatabaseWriters; + /// + /// class that controls DatabaseWriter(s). + /// + /// IList of PhotonDatabaseWriter + public ZrdDatabaseWriterController(IList rayDatabaseWriters) + { + _rayDatabaseWriters = rayDatabaseWriters; + } + /// + /// list of PhotonDatabaseWriter + /// + public IList ZrdRayDatabaseWriters { get; set; } + + /// + /// Method to write to all surface VB databases + /// + /// PhotonDataPoint + public void WriteToSurfaceVirtualBoundaryDatabases(PhotonDataPoint dp) + { + foreach (var writer in _rayDatabaseWriters) + { + if (!DpBelongsToSurfaceVirtualBoundary(dp, writer)) continue; + var ray = new ZrdRayDataPoint( + new RayDataPoint( + dp.Position, + dp.Direction, + dp.Weight)); + writer.Write(ray); + } + } + + /// + /// Method to determine if photon data point should be tallied or not + /// + /// PhotonDataPoint + /// single ZrdRayDatabaseWriter + /// + public bool DpBelongsToSurfaceVirtualBoundary(PhotonDataPoint dp, + ZrdRayDatabaseWriter rayDatabaseWriter) + { + return (dp.StateFlag.HasFlag(PhotonStateType.PseudoDiffuseReflectanceVirtualBoundary) && + rayDatabaseWriter.VirtualBoundaryType == VirtualBoundaryType.DiffuseReflectance) || + (dp.StateFlag.HasFlag(PhotonStateType.PseudoDiffuseTransmittanceVirtualBoundary) && + rayDatabaseWriter.VirtualBoundaryType == VirtualBoundaryType.DiffuseTransmittance); + } + /// + /// Method to dispose of database writer(s). Currently not used, may be needed in future. + /// + public void Dispose() + { + foreach (var writer in _rayDatabaseWriters) + { + writer.Dispose(); + } + } + + } +} diff --git a/src/Vts.Zemax/ZrdDatabaseWriterFactory.cs b/src/Vts.Zemax/ZrdDatabaseWriterFactory.cs new file mode 100644 index 000000000..26bde658e --- /dev/null +++ b/src/Vts.Zemax/ZrdDatabaseWriterFactory.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Vts.MonteCarlo; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert ZRD DB to/from MCCL compatible DB + /// Factory methods to provide the PhotonDatabaseWriter (or list of PhotonDatabaseWriters) + /// or CollisionInfoDatabaseWriter (or list). + /// + public static class ZrdDatabaseWriterFactory + { + /// + /// Static method to provide list of ZrdRayDatabaseWriters. It calls the method + /// to instantiate one CollisionInfoDatabaseWriter, GetCollisionInfoDatabaseWriter, + /// for all elements in the list of virtual boundary DatabaseType. + /// + /// list of database types + /// path string of database output + /// filename string of output file + /// a list of CollisionInfoDatabaseWriter + public static IList GetZrdSurfaceVirtualBoundaryDatabaseWriters( + IList databaseTypes, string filePath, string outputName) + { + return databaseTypes.Select(v => GetZrdSurfaceVirtualBoundaryDatabaseWriter(v, + filePath, outputName)).ToList(); + + } + /// + /// Static method to instantiate correct ZrdRayDatabaseWriter given a + /// virtual boundary DatabaseType, path to where to output database and database filename. + /// + /// database type enum + /// path string of database output + /// filename string of database file + /// a ZrdRayDatabaseWriter + public static ZrdRayDatabaseWriter GetZrdSurfaceVirtualBoundaryDatabaseWriter( + ZemaxDatabaseType databaseType, string filePath, string outputName) + { + switch (databaseType) + { + default: + case ZemaxDatabaseType.ZrdDiffuseReflectance: + return new ZrdRayDatabaseWriter(VirtualBoundaryType.DiffuseReflectance, + Path.Combine(filePath, outputName, "ZrdDiffuseReflectanceDatabase")); + } + } + + } +} diff --git a/src/Vts.Zemax/ZrdRayDataPoint.cs b/src/Vts.Zemax/ZrdRayDataPoint.cs new file mode 100644 index 000000000..308069fc0 --- /dev/null +++ b/src/Vts.Zemax/ZrdRayDataPoint.cs @@ -0,0 +1,172 @@ +using Vts.Common; +using Vts.MonteCarlo.RayData; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert ZRD DB to/from MCCL compatible DB + /// ZRDRay Data to be read in from Zemax ZRD file in uncompressed + /// full Data format (UFD) + /// + public class ZrdRayDataPoint + { + /// + /// Defines ZrdRay Data class. + /// This Zemax structure size is 208 bytes for each ray segment + /// This constructor allows for easy instantiation from MCCL local + /// class RayDataPoint. RayDataPoint contains the data from ZrdRayDataPoint + /// that is necessary to MCCL. + /// + /// ray data point + public ZrdRayDataPoint(RayDataPoint rayDataPoint) + { + X = rayDataPoint.Position.X; + Y = rayDataPoint.Position.Y; + Z = rayDataPoint.Position.Z; + Ux = rayDataPoint.Direction.Ux; + Uy = rayDataPoint.Direction.Uy; + Uz = rayDataPoint.Direction.Uz; + Weight = rayDataPoint.Weight; + } + /// + /// default constructor + /// + public ZrdRayDataPoint() : this(new RayDataPoint( + new Position(0, 0, 0), + new Direction(0, 0, 1), + 1.0)) + { } + + // Properties were defined for all field in structure, however only + // X, Y, Z, Ux, Uy, Uz and Weight currently used by code + // [StructLayout(LayoutKind.Explicit)] + // public struct ZrdRayDataPoint + // + /// + /// [FieldOffset(0)] status : bitwise flags indicating status of the ray + /// + public uint Status { get; set; } + /// + /// [FieldOffset(4)] level : number of ray segments bw ray segment and original source + /// + public int Level { get; set; } + /// + /// [FieldOffset(8)] hitObject: the object number the ray intercepted (0=hit nothing) + /// + public int HitObject { get; set; } + /// + /// [FieldOffset(12)] hitFace: the face number the ray intercepted (valid if hitObject!=0) + /// + public int HitFace { get; set; } + /// + /// [FieldOffset(16)] unused + /// + public int Unused { get; set; } + /// + /// [FieldOffset(20)] inObject: object number of ray is propagating inside of + /// + public int InObject{ get; set; } + /// + /// [FieldOffset(24)] parent: the prior ray segment from which the ray originated + /// + public int Parent { get; set; } + /// + /// [FieldOffset(28)] storage: temp buffer used by OpticStudio for buffering + /// + public int Storage { get; set; } + /// + /// [FieldOffset(32)] xyBin: pixel number on a detector object which the ray struck *spatial* + /// + public int XyBin { get; set; } + /// + /// [FieldOffset(36)] lmBin: pixel number on a detector object which the ray struck *angular* + /// + public int LmBin { get; set; } + /// + /// [FieldOffset(40)] index: index of refraction of the media + /// + public double Index { get; set; } + /// + /// [FieldOffset(48)] startingPhase: initial optical path length the ray starts with + /// + public double StartingPhase { get; set; } + /// + /// [FieldOffset(56)] x coordinate of ray position + /// + public double X { get; set; } + /// + /// [FieldOffset(64)] y coordinate of ray position + /// + public double Y { get; set; } + /// + /// [FieldOffset(72)] z coordinate of ray position + /// + public double Z { get; set; } + /// + /// [FieldOffset(80)] direction Ux + /// + public double Ux { get; set; } // this is "l" in Zemax + /// + /// [FieldOffset(88)]direction Uy + /// + public double Uy { get; set; } // this is "m" in Zemax + /// + /// [FieldOffset(96)]direction Uz + /// + public double Uz { get; set; } // this is "n" in Zemax + /// + /// [FieldOffset(104)] nx: global normal vector of the object at the intercept point + /// for segment 0 the nx values stores the wavelength of the ray being launched + /// + public double Nx { get; set; } + /// + /// [FieldOffset(112)] ny: global normal vector of the object at the intercept point + /// + public double Ny { get; set; } + /// + /// [FieldOffset(120)] nz: global normal vector of the object at the intercept point + /// + public double Nz { get; set; } + /// + /// [FieldOffset(128)] pathTo: the physical (not optical) path length of the ray segment + /// + public double PathTo { get; set; } + /// + /// [FieldOffset(136)] Weight: "intensity" in Zemax + /// + public double Weight { get; set; } + /// + /// FieldOffset(144)] phaseOf: the phase of the obect + /// + public double PhaseOf { get; set; } + /// + /// [FieldOffset(152)] phaseAt: the accumulated total phase of the ray (modulo 2pi) + /// + public double PhaseAt { get; set; } + /// + /// [FieldOffset(160)] exr: electric field in global x,y,z coordinates, real + /// + public double Exr { get; set; } + /// + /// [FieldOffset(168)] exi: electric field in global x,y,z coordinates, imaginary + /// + public double Exi { get; set; } + /// + /// [FieldOffset(176)] eyr: electric field in global x,y,z coordinates, real + /// + public double Eyr { get; set; } + /// + /// [FieldOffset(184)] eyi: electric field in global x,y,z coordinates, imaginary + /// + public double Eyi { get; set; } + /// + /// [FieldOffset(192)] ezr: electric field in global x,y,z coordinates, real + /// + public double Ezr { get; set; } + /// + /// [FieldOffset(200)] ezi: electric field in global x,y,z coordinates, imaginary + /// + public double Ezi { get; set; } + + } +} diff --git a/src/Vts.Zemax/ZrdRayDataPointSerializer.cs b/src/Vts.Zemax/ZrdRayDataPointSerializer.cs new file mode 100644 index 000000000..5a23362c2 --- /dev/null +++ b/src/Vts.Zemax/ZrdRayDataPointSerializer.cs @@ -0,0 +1,137 @@ +using System.IO; +using Vts.IO; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert Zrd DB to/from MCCL compatible DB + /// Implements ICustomBinaryReader<RayDataPoint> and + /// ICustomBinaryWriter<ZrdDataPoint>. + /// + public class ZrdRayDataPointSerializer : + ICustomBinaryReader, + ICustomBinaryWriter + { + private readonly int _count = 208; + private bool _headerIsWritten = false; + private bool _headerIsRead = false; + /// + /// Method to write ZrdDataPoint to binary. Header is written only first time through. + /// + /// BinaryWriter + /// ZrdDataPoint + public void WriteToBinary(BinaryWriter bw, ZrdRayDataPoint item) + { + if (!_headerIsWritten) + { + const int version = 2002; + bw.Write(version); + const int maxNumberOfSegments = 500; + bw.Write(maxNumberOfSegments); + _headerIsWritten = true; + } + var numSegments = 2; // number of segments in ray_i + bw.Write(numSegments); + for (var i = 0; i < numSegments; i++) // write same rayDP twice to make ray + { + var rayDp = new ZrdRayDataPoint + { + // rest of fields until x,y,z set to 0 + Status = 0 + }; + bw.Write(rayDp.Status); + bw.Write(i); // level: should match segment# + rayDp.HitObject = 0; + bw.Write(rayDp.HitObject); + rayDp.HitFace = 0; + bw.Write(rayDp.HitFace); + rayDp.Unused = 0; + bw.Write(rayDp.Unused); + rayDp.InObject = 0; + bw.Write(rayDp.InObject); + rayDp.Parent = 0; + bw.Write(rayDp.Parent); + rayDp.Storage = 0; + bw.Write(rayDp.Storage); + rayDp.XyBin = 0; + bw.Write(rayDp.XyBin); + rayDp.LmBin = 0; + bw.Write(rayDp.LmBin); + rayDp.Index = 0; + bw.Write(rayDp.Index); + rayDp.StartingPhase = 0; + bw.Write(rayDp.StartingPhase); + // write data from rayDataPointsList + bw.Write(item.X); + bw.Write(item.Y); + bw.Write(item.Z); + bw.Write(item.Ux); + bw.Write(item.Uy); + bw.Write(item.Uz); + rayDp.Nx = 0.0; + bw.Write(rayDp.Nx); + rayDp.Ny = 0.0; + bw.Write(rayDp.Ny); + rayDp.Nz = 0.0; + bw.Write(rayDp.Nz); + rayDp.PathTo = 0.0; + bw.Write(rayDp.PathTo); + // write weight into intensity field + bw.Write(item.Weight); + rayDp.PhaseOf = 0.0; + bw.Write(rayDp.PhaseOf); + rayDp.PhaseAt = 0.0; + bw.Write(rayDp.PhaseAt); + rayDp.Exr = 0.0; + bw.Write(rayDp.Exr); + rayDp.Exi = 0.0; + bw.Write(rayDp.Exi); + rayDp.Eyr = 0.0; + bw.Write(rayDp.Eyr); + rayDp.Eyi = 0.0; + bw.Write(rayDp.Eyi); + rayDp.Ezr = 0.0; + bw.Write(rayDp.Ezr); + rayDp.Ezi = 0.0; + bw.Write(rayDp.Ezi); + } + } + /// + /// Method to read PhotonDataPoint from binary. Header info is only read first time through. + /// + /// BinaryReader + /// ZrdRayDataPoint, data consistent with zrd file ray + public ZrdRayDataPoint ReadFromBinary(BinaryReader br) + { + if (!_headerIsRead) + { + br.ReadInt32(); + br.ReadInt32(); + _headerIsRead = true; + } + var numSegments = br.ReadInt32(); // number of segments in this ray + // skip until last segment + br.ReadBytes((numSegments - 1) * _count); + br.ReadBytes(56); // skip down to x,y,z,ux,uy,uz + var x = br.ReadDouble(); + var y = br.ReadDouble(); + var z = br.ReadDouble(); + var ux = br.ReadDouble(); + var uy = br.ReadDouble(); + var uz = br.ReadDouble(); + br.ReadBytes(32); // skip to intensity + var weight = br.ReadDouble(); + br.ReadBytes(64); // skip to end of ZrdRayDataPoint + return new ZrdRayDataPoint() + { + X = x, + Y = y, + Z = z, + Ux = ux, + Uy = uy, + Uz = uz, + Weight = weight + }; + } + } +} diff --git a/src/Vts.Zemax/ZrdRayDatabase.cs b/src/Vts.Zemax/ZrdRayDatabase.cs new file mode 100644 index 000000000..f94a5e66b --- /dev/null +++ b/src/Vts.Zemax/ZrdRayDatabase.cs @@ -0,0 +1,35 @@ +using Vts.MonteCarlo.IO; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert ZRD DB to/from MCCL compatible DB + /// Describes database for storing and returning source Zemax ZRD ray data points + /// (position, direction, weight). + /// The base class, Database(OfT), exposes the IEnumerable(OfT) DataPoints + /// list of RayDataPoint items + /// + public class ZrdRayDatabase : Database + { + /// + /// Returns an instance of SourceDatabase + /// + public ZrdRayDatabase() + { + } + + /// + /// method to read ZrdRayDatabase from file + /// + /// filename of ZrdRayDatabase + /// database of Zemax rays + public static ZrdRayDatabase FromFile(string fileName) + { + var dbReader = new DatabaseReader( + db => new ZrdRayDataPointSerializer()); + + return dbReader.FromFile(fileName); + } + + } +} diff --git a/src/Vts.Zemax/ZrdRayDatabaseWriter.cs b/src/Vts.Zemax/ZrdRayDatabaseWriter.cs new file mode 100644 index 000000000..a78674817 --- /dev/null +++ b/src/Vts.Zemax/ZrdRayDatabaseWriter.cs @@ -0,0 +1,28 @@ +using Vts.MonteCarlo; +using Vts.MonteCarlo.IO; + +namespace Vts.Zemax +{ + /// + /// This is code that would be executed to convert ZRD DB to/from MCCL compatible DB + /// Implements CustomBinaryStreamWriter(Of ZRDRayDataPoint). Handles writing ray + /// data to database. + /// + public class ZrdRayDatabaseWriter : DatabaseWriter + { + /// + /// constructor for photon database writer + /// + /// virtual boundary type + /// database filename + public ZrdRayDatabaseWriter(VirtualBoundaryType virtualBoundaryType, string filename) + : base(filename, new ZrdRayDatabase(), new ZrdRayDataPointSerializer()) + { + VirtualBoundaryType = virtualBoundaryType; + } + /// + /// virtual boundary type + /// + public VirtualBoundaryType VirtualBoundaryType { get; set; } + } +} diff --git a/src/Vts.sln b/src/Vts.sln index 5756d6d37..7021bcac7 100644 --- a/src/Vts.sln +++ b/src/Vts.sln @@ -23,6 +23,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.MonteCarlo.PostProcesso EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.Benchmark", "Vts.Benchmark\Vts.Benchmark.csproj", "{3B0676C4-5CF1-4A0B-97E4-0CDF2B035702}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.MonteCarlo.ZemaxDatabaseConverter", "Vts.MonteCarlo.ZemaxDatabaseConverter\Vts.MonteCarlo.ZemaxDatabaseConverter.csproj", "{1A79D2A0-0171-4D3D-92D8-57ED68743A94}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.MonteCarlo.ZemaxDatabaseConverter.Test", "Vts.MonteCarlo.ZemaxDatabaseConverter.Test\Vts.MonteCarlo.ZemaxDatabaseConverter.Test.csproj", "{C476D49D-1764-44EF-865D-2E479D7A3A89}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.Zemax.Test", "Vts.Zemax.Test\Vts.Zemax.Test.csproj", "{FC824002-2910-43DE-8633-70B1707469FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vts.Zemax", "Vts.Zemax\Vts.Zemax.csproj", "{0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Benchmark|Any CPU = Benchmark|Any CPU @@ -214,6 +222,134 @@ Global {3B0676C4-5CF1-4A0B-97E4-0CDF2B035702}.ReleaseWhiteList|Win32.Build.0 = Release|Any CPU {3B0676C4-5CF1-4A0B-97E4-0CDF2B035702}.ReleaseWhiteList|x86.ActiveCfg = Release|Any CPU {3B0676C4-5CF1-4A0B-97E4-0CDF2B035702}.ReleaseWhiteList|x86.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Any CPU.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Mixed Platforms.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Win32.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|Win32.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|x86.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Benchmark|x86.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Win32.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|Win32.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|x86.ActiveCfg = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Debug|x86.Build.0 = Debug|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Any CPU.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Win32.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|Win32.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|x86.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.Release|x86.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Any CPU.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Any CPU.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Mixed Platforms.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Mixed Platforms.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Win32.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|Win32.Build.0 = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|x86.ActiveCfg = Release|Any CPU + {1A79D2A0-0171-4D3D-92D8-57ED68743A94}.ReleaseWhiteList|x86.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Any CPU.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Mixed Platforms.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Win32.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|Win32.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|x86.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Benchmark|x86.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Win32.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|Win32.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|x86.ActiveCfg = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Debug|x86.Build.0 = Debug|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Any CPU.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Win32.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|Win32.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|x86.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.Release|x86.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Any CPU.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Any CPU.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Mixed Platforms.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Mixed Platforms.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Win32.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|Win32.Build.0 = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|x86.ActiveCfg = Release|Any CPU + {C476D49D-1764-44EF-865D-2E479D7A3A89}.ReleaseWhiteList|x86.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Any CPU.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Mixed Platforms.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Win32.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|Win32.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|x86.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Benchmark|x86.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Win32.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|Win32.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Debug|x86.Build.0 = Debug|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Any CPU.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Win32.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|Win32.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|x86.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.Release|x86.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Any CPU.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Any CPU.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Mixed Platforms.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Mixed Platforms.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Win32.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|Win32.Build.0 = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|x86.ActiveCfg = Release|Any CPU + {FC824002-2910-43DE-8633-70B1707469FC}.ReleaseWhiteList|x86.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Any CPU.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Any CPU.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Mixed Platforms.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Win32.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|Win32.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|x86.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Benchmark|x86.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Win32.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|Win32.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|x86.ActiveCfg = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Debug|x86.Build.0 = Debug|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Any CPU.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Win32.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|Win32.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|x86.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.Release|x86.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Any CPU.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Any CPU.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Mixed Platforms.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Mixed Platforms.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Win32.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|Win32.Build.0 = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|x86.ActiveCfg = Release|Any CPU + {0C84DEAC-046E-4BF3-AB91-AB37AAE6DC0F}.ReleaseWhiteList|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -228,7 +364,7 @@ Global {3B0676C4-5CF1-4A0B-97E4-0CDF2B035702} = {D6A3DE7A-7E93-4932-AA8F-C612FD97830E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.2\lib\NET35 SolutionGuid = {BB1B236A-1BE9-476A-A4B3-8C0F51F1FDA7} + EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.2\lib\NET35 EndGlobalSection EndGlobal diff --git a/src/Vts/MonteCarlo/Controllers/RayDatabaseWriterController.cs b/src/Vts/MonteCarlo/Controllers/RayDatabaseWriterController.cs new file mode 100644 index 000000000..6e40c7ab2 --- /dev/null +++ b/src/Vts/MonteCarlo/Controllers/RayDatabaseWriterController.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using Vts.MonteCarlo.PhotonData; +using Vts.MonteCarlo.RayData; + +namespace Vts.MonteCarlo.Controllers +{ + /// + /// A controller of RayDatabaseWriter(s). It handles determining whether data should be written, + /// and if so, writing the data, and finally disposing of the database. + /// + public class RayDatabaseWriterController + { + IList _rayDatabaseWriters; + /// + /// class that controls DatabaseWriter(s). + /// + /// IList of PhotonDatabaseWriter + public RayDatabaseWriterController(IList rayDatabaseWriters) + { + _rayDatabaseWriters = rayDatabaseWriters; + } + /// + /// list of PhotonDatabaseWriter + /// + public IList RayDatabaseWriters { get { return _rayDatabaseWriters; } set { _rayDatabaseWriters = value; } } + + /// + /// Method to write to all surface VB databases + /// + /// PhotonDataPoint + public void WriteToSurfaceVirtualBoundaryDatabases(PhotonDataPoint dp) + { + foreach (var writer in _rayDatabaseWriters) + { + if (DPBelongsToSurfaceVirtualBoundary(dp, writer)) + { + var ray = new RayDataPoint( + dp.Position, + dp.Direction, + dp.Weight); + writer.Write(ray); + } + }; + } + + /// + /// Method to determine if photon datapoint should be tallied or not + /// + /// PhotonDataPoint + /// single ZRDRayDatabaseWriter + /// + public bool DPBelongsToSurfaceVirtualBoundary(PhotonDataPoint dp, + RayDatabaseWriter rayDatabaseWriter) + { + if ((dp.StateFlag.HasFlag(PhotonStateType.PseudoDiffuseReflectanceVirtualBoundary) && + rayDatabaseWriter.VirtualBoundaryType == VirtualBoundaryType.DiffuseReflectance) || + (dp.StateFlag.HasFlag(PhotonStateType.PseudoDiffuseTransmittanceVirtualBoundary) && + rayDatabaseWriter.VirtualBoundaryType == VirtualBoundaryType.DiffuseTransmittance)) + { + return true; + } + return false; + } + /// + /// Method to dispose of database writer(s) + /// + public void Dispose() + { + foreach (var writer in _rayDatabaseWriters) + { + writer.Dispose(); + } + } + + } +} diff --git a/src/Vts/MonteCarlo/DataStructures/SimulationInputProvider.cs b/src/Vts/MonteCarlo/DataStructures/SimulationInputProvider.cs index aafa54002..945195673 100644 --- a/src/Vts/MonteCarlo/DataStructures/SimulationInputProvider.cs +++ b/src/Vts/MonteCarlo/DataStructures/SimulationInputProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Metrics; using Vts.Common; using Vts.MonteCarlo.Detectors; using Vts.MonteCarlo.Helpers; @@ -45,7 +46,8 @@ public static IList GenerateAllSimulationInputs() PointSourceSlantedRecessedFiberTissueAndDetector(), FluorescenceEmissionAOfXAndYAndZSourceInfiniteCylinder(), PointSourceBoundedTissue(), - ImageSourceOneLayerTissueROfXAndYDetector() + ImageSourceOneLayerTissueROfXAndYDetector(), + RayDatabaseGenerator() }; } @@ -1382,7 +1384,8 @@ public static SimulationInput PointSourceBoundedTissue() } #endregion - #region image source one layer + #region image source one layer + /// /// Bounded Cylinder Tissue /// @@ -1402,13 +1405,13 @@ public static SimulationInput ImageSourceOneLayerTissueROfXAndYDetector() 0.0, // RR threshold -> no RR performed 0), new DirectionalImageSourceInput( - "folder", // image folder - "image.png", // image name - 1280, // x-axis number of pixels - 1024, // y-axis number of pixels - 0.1, // pixel size x-axis - 0.1, // pixel size y-axis - 0.0, // conv or diverge + "folder", // image folder + "image.png", // image name + 1280, // x-axis number of pixels + 1024, // y-axis number of pixels + 0.1, // pixel size x-axis + 0.1, // pixel size y-axis + 0.0, // conv or diverge new Direction(0, 0, 1), new Position(0.0, 0.0, 0.0), new PolarAzimuthalAngles(), @@ -1424,8 +1427,7 @@ public static SimulationInput ImageSourceOneLayerTissueROfXAndYDetector() new LayerTissueRegion( new DoubleRange(100.0, double.PositiveInfinity), new OpticalProperties(0.0, 1e-10, 1.0, 1.0)) - ] - ), + ]), new List { new ROfXAndYDetectorInput() @@ -1433,5 +1435,50 @@ public static SimulationInput ImageSourceOneLayerTissueROfXAndYDetector() ); } #endregion + + #region Ray Database creation + /// + /// This infile creates a RayDiffuseReflectanceDatabase after running + /// Bounded Cylinder Tissue + /// + /// An instance of the SimulationInput class + public static SimulationInput RayDatabaseGenerator() + { + return new SimulationInput( + 100, + "ray_database_generator", + new SimulationOptions( + 0, // random number generator seed, -1=random seed, 0=fixed seed + RandomNumberGeneratorType.MersenneTwister, + AbsorptionWeightingType.Discrete, + PhaseFunctionType.HenyeyGreenstein, + new List() { DatabaseType.RayDiffuseReflectance }, // databases to be written + true, // track statistics + 0.0, // RR threshold -> no RR performed + 0), + new DirectionalPointSourceInput(), + new MultiLayerTissueInput( + new ITissueRegion[] + { + new LayerTissueRegion( + new DoubleRange(double.NegativeInfinity, 0.0), + new OpticalProperties(0.0, 1e-10, 1.0, 1.0)), + new LayerTissueRegion( + new DoubleRange(0.0, 100.0), + new OpticalProperties(0.01, 10, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(100.0, double.PositiveInfinity), + new OpticalProperties(0.0, 1e-10, 1.0, 1.0)) + } + ), + new List() + { + new ROfXAndYDetectorInput() { X=new DoubleRange(-10, 10,11), + Y=new DoubleRange(-100.0, 100.0, 2)} + } + ); + + } + #endregion } } diff --git a/src/Vts/MonteCarlo/Detectors/TallyDetails.cs b/src/Vts/MonteCarlo/Detectors/TallyDetails.cs index f17cf0da2..5bdf11d13 100644 --- a/src/Vts/MonteCarlo/Detectors/TallyDetails.cs +++ b/src/Vts/MonteCarlo/Detectors/TallyDetails.cs @@ -30,6 +30,10 @@ public class TallyDetails /// public bool IspMCTransmittanceTally { get; set; } /// + /// Boolean identifying all ray reflectance tallies + /// + public bool IsRayReflectanceTally { get; set; } + /// /// Boolean identifying all bounding volume tallies /// public bool IsLateralBoundingVolumeTally { get; set; } diff --git a/src/Vts/MonteCarlo/Extensions/DatabaseExtensionMethods.cs b/src/Vts/MonteCarlo/Extensions/DatabaseExtensionMethods.cs index 55bc04b18..213f6dfa8 100644 --- a/src/Vts/MonteCarlo/Extensions/DatabaseExtensionMethods.cs +++ b/src/Vts/MonteCarlo/Extensions/DatabaseExtensionMethods.cs @@ -32,6 +32,7 @@ public static bool IspMCDatabase(this DatabaseType databaseType) case DatabaseType.DiffuseReflectance: case DatabaseType.DiffuseTransmittance: case DatabaseType.SpecularReflectance: + case DatabaseType.RayDiffuseReflectance: return false; default: throw new ArgumentOutOfRangeException( @@ -40,6 +41,25 @@ public static bool IspMCDatabase(this DatabaseType databaseType) } } /// + /// Method to determine if ray database specified or not + /// + /// database type + /// true if ray VB, false if not + public static bool IsRayDatabase(this DatabaseType databaseType) + { + switch (databaseType) + { + case DatabaseType.RayDiffuseReflectance: + return true; + default: + case DatabaseType.DiffuseReflectance: + case DatabaseType.DiffuseTransmittance: + case DatabaseType.SpecularReflectance: + case DatabaseType.pMCDiffuseReflectance: + return false; + } + } + /// /// Method to provide VirtualBoundaryType corresponding to DatabaseType /// /// database type @@ -58,6 +78,8 @@ public static VirtualBoundaryType GetCorrespondingVirtualBoundaryType(this Datab return VirtualBoundaryType.pMCDiffuseReflectance; case DatabaseType.pMCDiffuseTransmittance: return VirtualBoundaryType.pMCDiffuseTransmittance; + case DatabaseType.RayDiffuseReflectance: + return VirtualBoundaryType.RayDiffuseReflectance; default: throw new ArgumentOutOfRangeException( "Database type not recognized: " + databaseType); diff --git a/src/Vts/MonteCarlo/Factories/DatabaseWriterFactory.cs b/src/Vts/MonteCarlo/Factories/DatabaseWriterFactory.cs index 184fe6115..fcda690bd 100644 --- a/src/Vts/MonteCarlo/Factories/DatabaseWriterFactory.cs +++ b/src/Vts/MonteCarlo/Factories/DatabaseWriterFactory.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.CompilerServices; using Vts.MonteCarlo.PhotonData; +using Vts.MonteCarlo.RayData; namespace Vts.MonteCarlo.Factories { @@ -66,6 +67,9 @@ public static PhotonDatabaseWriter GetSurfaceVirtualBoundaryDatabaseWriter( case DatabaseType.pMCDiffuseTransmittance: return new PhotonDatabaseWriter(VirtualBoundaryType.pMCDiffuseTransmittance, Path.Combine(filePath, outputName, "DiffuseTransmittanceDatabase")); + case DatabaseType.RayDiffuseReflectance: + return new PhotonDatabaseWriter(VirtualBoundaryType.RayDiffuseReflectance, + Path.Combine(filePath, outputName, "DiffuseReflectanceDatabase")); default: throw new ArgumentOutOfRangeException( "Database type not recognized: " + databaseType); @@ -113,6 +117,50 @@ public static CollisionInfoDatabaseWriter GetCollisionInfoDatabaseWriter( case DatabaseType.DiffuseReflectance: case DatabaseType.DiffuseTransmittance: case DatabaseType.SpecularReflectance: + case DatabaseType.RayDiffuseReflectance: + return null; + default: + throw new ArgumentOutOfRangeException( + "Database type not recognized: " + databaseType); + } + } + /// + /// Static method to provide list of RayDatabaseWriters. It calls the method + /// to instantiate one CollisionInfoDatabaseWriter, GetCollisionInfoDatabaseWriter, + /// for all elements in the list of virtual boundary DatabaseType. + /// + /// list of database types + /// path string of database output + /// filename string of output file + /// a list of CollisionInfoDatabaseWriter + public static IList GetRaySurfaceVirtualBoundaryDatabaseWriters( + IList databaseTypes, string filePath, string outputName) + { + return databaseTypes.Select(v => GetRaySurfaceVirtualBoundaryDatabaseWriter(v, + filePath, outputName)).ToList(); + + } + /// + /// Static method to instantiate correct RayDatabaseWriter given a + /// virtual boundary DatabaseType, path to where to output database and database filename. + /// + /// database type enum + /// path string of database output + /// filename string of database file + /// a RayDatabaseWriter + public static RayDatabaseWriter GetRaySurfaceVirtualBoundaryDatabaseWriter( + DatabaseType databaseType, string filePath, string outputName) + { + switch (databaseType) + { + case DatabaseType.RayDiffuseReflectance: + return new RayDatabaseWriter(VirtualBoundaryType.DiffuseReflectance, + Path.Combine(filePath, outputName, "RayDiffuseReflectanceDatabase")); + case DatabaseType.DiffuseReflectance: + case DatabaseType.DiffuseTransmittance: + case DatabaseType.SpecularReflectance: + case DatabaseType.pMCDiffuseReflectance: + case DatabaseType.pMCDiffuseTransmittance: return null; default: throw new ArgumentOutOfRangeException( diff --git a/src/Vts/MonteCarlo/Factories/DetectorControllerFactory.cs b/src/Vts/MonteCarlo/Factories/DetectorControllerFactory.cs index e9385f856..6b123837d 100644 --- a/src/Vts/MonteCarlo/Factories/DetectorControllerFactory.cs +++ b/src/Vts/MonteCarlo/Factories/DetectorControllerFactory.cs @@ -27,6 +27,7 @@ public static IDetectorController GetDetectorController(VirtualBoundaryType virt case VirtualBoundaryType.InternalSurface: case VirtualBoundaryType.pMCDiffuseReflectance: case VirtualBoundaryType.pMCDiffuseTransmittance: + case VirtualBoundaryType.RayDiffuseReflectance: case VirtualBoundaryType.BoundingVolume: return new DetectorController(detectors); case VirtualBoundaryType.GenericVolumeBoundary: diff --git a/src/Vts/MonteCarlo/Factories/VirtualBoundaryFactory.cs b/src/Vts/MonteCarlo/Factories/VirtualBoundaryFactory.cs index 557fc0f11..f34be4ffe 100644 --- a/src/Vts/MonteCarlo/Factories/VirtualBoundaryFactory.cs +++ b/src/Vts/MonteCarlo/Factories/VirtualBoundaryFactory.cs @@ -36,6 +36,8 @@ public static IVirtualBoundary GetVirtualBoundary( tissue, detectorController, VirtualBoundaryType.DiffuseTransmittance.ToString()), VirtualBoundaryType.BoundingVolume => new LateralBoundingVirtualBoundary( tissue, detectorController, VirtualBoundaryType.BoundingVolume.ToString()), + VirtualBoundaryType.RayDiffuseReflectance => new RayDiffuseReflectanceVirtualBoundary( + tissue, detectorController, VirtualBoundaryType.RayDiffuseReflectance.ToString()), _ => throw new ArgumentOutOfRangeException("Virtual boundary type not recognized: " + vbType), }; return vb; diff --git a/src/Vts/MonteCarlo/MonteCarloSimulation.cs b/src/Vts/MonteCarlo/MonteCarloSimulation.cs index 421118212..056095f07 100644 --- a/src/Vts/MonteCarlo/MonteCarloSimulation.cs +++ b/src/Vts/MonteCarlo/MonteCarloSimulation.cs @@ -52,7 +52,9 @@ public class MonteCarloSimulation private long _numberOfPhotons; private DatabaseWriterController _databaseWriterController; private pMCDatabaseWriterController _pMcDatabaseWriterController; + private RayDatabaseWriterController _rayDatabaseWriterController; private bool _doPmc; + private bool _doRayDb; private string _outputPath; /// @@ -229,7 +231,7 @@ protected virtual void ExecuteMCLoop() try { - if (Input.Options.Databases.Any()) InitialDatabases(_doPmc); + if (Input.Options.Databases.Any()) InitialDatabases(_doPmc, _doRayDb); var volumeVBs = _virtualBoundaryController.VirtualBoundaries.Where( v => v.VirtualBoundaryType == VirtualBoundaryType.GenericVolumeBoundary).ToList(); @@ -283,7 +285,8 @@ protected virtual void ExecuteMCLoop() } while (photon.DP.StateFlag.HasFlag(PhotonStateType.Alive)); // end do while - if (Input.Options.Databases.Any()) WriteToDatabases(_doPmc, photon); + if (Input.Options.Databases.Any()) WriteToDatabases(_doPmc, _doRayDb, photon); + // note History has possibly 2 more DPs than linux code due to // final crossing of PseudoReflectedTissueBoundary and then @@ -300,7 +303,7 @@ protected virtual void ExecuteMCLoop() } finally { - if (Input.Options.Databases.Any()) CloseDatabases(_doPmc); + if (Input.Options.Databases.Any()) CloseDatabases(_doPmc, _doRayDb); } // normalize all detectors by the total number of photons (each tally records it's own "local" count as well) @@ -368,7 +371,8 @@ private void InitializeInputs() VirtualBoundaryType.pMCDiffuseReflectance => Input.DetectorInputs.Where(d => d.TallyDetails.IspMCReflectanceTally).ToList(), VirtualBoundaryType.pMCDiffuseTransmittance => Input.DetectorInputs.Where(d => d.TallyDetails.IspMCTransmittanceTally).ToList(), VirtualBoundaryType.BoundingVolume => Input.DetectorInputs.Where(d => d.TallyDetails.IsLateralBoundingVolumeTally).ToList(), - _ => throw new ArgumentOutOfRangeException( + VirtualBoundaryType.RayDiffuseReflectance => Input.DetectorInputs.Where(d => d.TallyDetails.IsRayReflectanceTally).ToList(), + _ => throw new ArgumentOutOfRangeException( "Virtual boundary type not recognized: " + vbType), }; @@ -386,64 +390,100 @@ private void InitializeInputs() } // set _doPMC flag - if (Input.Options.Databases.Any(d => d.IspMCDatabase())) _doPmc = true; - + if (Input.Options.Databases.Any(d => d.IspMCDatabase())) + { + _doPmc = true; + } + // set do ray database flag + if (Input.Options.Databases.Any(d => d.IsRayDatabase())) + { + _doRayDb = true; + } _isCancelled = false; _isRunning = false; _resultsAvailable = false; } - private void CloseDatabases(bool doPmc) + private void CloseDatabases(bool doPMC, bool doRayDB) { - if (doPmc) + if (doPMC) { _pMcDatabaseWriterController.Dispose(); } else { - _databaseWriterController.Dispose(); + if (doRayDB) + { + _rayDatabaseWriterController.Dispose(); + } + else + { + _databaseWriterController.Dispose(); + } } } - private void WriteToDatabases(bool doPmc, Photon photon) + private void WriteToDatabases(bool doPMC, bool doRayDB, Photon photon) { - if (doPmc) + if (doPMC) { - _pMcDatabaseWriterController.WriteToSurfaceVirtualBoundaryDatabases(photon.DP, - photon.History.SubRegionInfoList); + _pMcDatabaseWriterController.WriteToSurfaceVirtualBoundaryDatabases(photon.DP, photon.History.SubRegionInfoList); } else { - _databaseWriterController.WriteToSurfaceVirtualBoundaryDatabases(photon.DP); + if (doRayDB) + { + _rayDatabaseWriterController.WriteToSurfaceVirtualBoundaryDatabases(photon.DP); + + } + else + { + _databaseWriterController.WriteToSurfaceVirtualBoundaryDatabases(photon.DP); + } } } + /// /// Initializes databases for diffuse reflectance (1 database) or perturbation /// MC (2 databases) /// - /// flag indicating whether to do pmc or not - private void InitialDatabases(bool doPmc) + /// flag to initialize perturbation Monte Carlo databaes + /// flag to initialize ray database + private void InitialDatabases(bool doPmc, bool doRayDb) { if (doPmc) { _pMcDatabaseWriterController = new pMCDatabaseWriterController( DatabaseWriterFactory.GetSurfaceVirtualBoundaryDatabaseWriters( Input.Options.Databases, - _outputPath, - Input.OutputName), + _outputPath, + Input.OutputName), DatabaseWriterFactory.GetCollisionInfoDatabaseWriters( - Input.Options.Databases, - _tissue, - _outputPath, - Input.OutputName)); + Input.Options.Databases, + _tissue, + _outputPath, + Input.OutputName)); + } else { - _databaseWriterController = new DatabaseWriterController( - DatabaseWriterFactory.GetSurfaceVirtualBoundaryDatabaseWriters( - Input.Options.Databases, - _outputPath, - Input.OutputName)); + if (doRayDb) + { + _rayDatabaseWriterController = new RayDatabaseWriterController( + DatabaseWriterFactory.GetRaySurfaceVirtualBoundaryDatabaseWriters( + Input.Options.Databases, + _outputPath, + Input.OutputName)); + + } + else + { + _databaseWriterController = new DatabaseWriterController( + DatabaseWriterFactory.GetSurfaceVirtualBoundaryDatabaseWriters( + Input.Options.Databases, + _outputPath, + Input.OutputName)); + } } } /// diff --git a/src/Vts/MonteCarlo/RayData/RayDataPoint.cs b/src/Vts/MonteCarlo/RayData/RayDataPoint.cs new file mode 100644 index 000000000..0753b75cd --- /dev/null +++ b/src/Vts/MonteCarlo/RayData/RayDataPoint.cs @@ -0,0 +1,41 @@ +using System; +using Vts.Common; + +namespace Vts.MonteCarlo.RayData +{ + /// + /// RayDataPoint is the subset of the Ray-tracing tool ray data output that + /// is necessary to MCCL. + /// + public class RayDataPoint + { + private Position _position; + private Direction _direction; + private double _weight; + + /// + /// Defines Ray Data class + /// + public RayDataPoint( + Position position, + Direction direction, + double weight) + { + _position = position; + _direction = direction; + _weight = weight; + } + /// + /// position of photon source data point + /// + public Position Position { get { return _position; } set { _position = value; } } + /// + /// direction of photon source data point + /// + public Direction Direction { get { return _direction; } set { _direction = value; } } + /// + /// photon initial weight + /// + public double Weight { get { return _weight; } set { _weight = value; } } + } +} diff --git a/src/Vts/MonteCarlo/RayData/RayDataPointSerializer.cs b/src/Vts/MonteCarlo/RayData/RayDataPointSerializer.cs new file mode 100644 index 000000000..95e26ca58 --- /dev/null +++ b/src/Vts/MonteCarlo/RayData/RayDataPointSerializer.cs @@ -0,0 +1,46 @@ +using System.IO; +using Vts.Common; +using Vts.IO; + +namespace Vts.MonteCarlo.RayData +{ + /// + /// Implements ICustomBinaryReader<RayDataPoint> and + /// ICustomBinaryWriter<DataPoint>. + /// + public class RayDataPointSerializer : + ICustomBinaryReader, + ICustomBinaryWriter + { + /// + /// Method to write RayDataPoint to binary. + /// + /// BinaryWriter + /// RayDataPoint + public void WriteToBinary(BinaryWriter bw, RayDataPoint rayDataPoint) + { + // write data from rayDataPointsList + bw.Write(rayDataPoint.Position.X); + bw.Write(rayDataPoint.Position.Y); + bw.Write(rayDataPoint.Position.Z); + bw.Write(rayDataPoint.Direction.Ux); + bw.Write(rayDataPoint.Direction.Uy); + bw.Write(rayDataPoint.Direction.Uz); + // write weight into intensity field + bw.Write(rayDataPoint.Weight); + } + /// + /// Method to read PhotonDataPoint from binary. Header info is only read first time through. + /// + /// BinaryReader + /// RayDataPoint is pertinent data from ray data that MCCL uses + public RayDataPoint ReadFromBinary(BinaryReader br) + { + var rayDataPoint = new RayDataPoint( + Position.ReadBinary(br), // Position + Direction.ReadBinary(br), // Direction + br.ReadDouble()); // Weight + return rayDataPoint; + } + } +} diff --git a/src/Vts/MonteCarlo/RayData/RayDatabase.cs b/src/Vts/MonteCarlo/RayData/RayDatabase.cs new file mode 100644 index 000000000..719903996 --- /dev/null +++ b/src/Vts/MonteCarlo/RayData/RayDatabase.cs @@ -0,0 +1,34 @@ +using Vts.MonteCarlo.IO; + +namespace Vts.MonteCarlo.RayData +{ + /// + /// Describes database for storing and returning MCCL DB source ray data points + /// (position, direction, weight). + /// The base class, Database(OfT), exposes the IEnumerable(OfT) DataPoints + /// list of RayDataPoint items + /// + public class RayDatabase : Database + { + /// + /// Returns an instance of SourceDatabase + /// + public RayDatabase() + { + } + + /// + /// method to read ZRDRayDatabase from file + /// + /// filename of RayDatabase + /// + public static RayDatabase FromFile(string fileName) + { + var dbReader = new DatabaseReader( + db => new RayDataPointSerializer()); + + return dbReader.FromFile(fileName); + } + + } +} diff --git a/src/Vts/MonteCarlo/RayData/RayDatabaseWriter.cs b/src/Vts/MonteCarlo/RayData/RayDatabaseWriter.cs new file mode 100644 index 000000000..aba8a253a --- /dev/null +++ b/src/Vts/MonteCarlo/RayData/RayDatabaseWriter.cs @@ -0,0 +1,26 @@ +using Vts.MonteCarlo.IO; + +namespace Vts.MonteCarlo.RayData +{ + /// + /// Implements CustomBinaryStreamWriter(Of RayDataPoint). Handles writing ray + /// data to database. + /// + public class RayDatabaseWriter : DatabaseWriter + { + /// + /// constructor for photon database writer + /// + /// virtual boundary type + /// database filename + public RayDatabaseWriter(VirtualBoundaryType virtualBoundaryType, string filename) + : base(filename, new RayDatabase(), new RayDataPointSerializer()) + { + VirtualBoundaryType = virtualBoundaryType; + } + /// + /// virtual boundary type + /// + public VirtualBoundaryType VirtualBoundaryType { get; set; } + } +} diff --git a/src/Vts/MonteCarlo/Sources/FromFile/FromFileSourceBase.cs b/src/Vts/MonteCarlo/Sources/FromFile/FromFileSourceBase.cs new file mode 100644 index 000000000..5c1694d2f --- /dev/null +++ b/src/Vts/MonteCarlo/Sources/FromFile/FromFileSourceBase.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Vts.MonteCarlo.PhotonData; + +namespace Vts.MonteCarlo.Sources +{ + /// + /// Abstract class for FromFileSourceBase + /// + public abstract class FromFileSourceBase : ISource + { + /// + /// enumerator through database + /// + protected IEnumerator _enumerator; + /// + /// Initial tissue region index + /// + protected int _initialTissueRegionIndex; + + /// + /// Defines FromFileSourceBase class + /// + protected FromFileSourceBase( + IEnumerator enumerator, + int initialTissueRegionIndex) + { + _enumerator = enumerator; + _initialTissueRegionIndex = initialTissueRegionIndex; + } + + /// + /// Implements Get next photon + /// + /// tissue + /// photon + public Photon GetNextPhoton(ITissue tissue) + { + // read next source data point + _enumerator.MoveNext(); + var dp = _enumerator.Current; + + var photon = new Photon(dp.Position, dp.Direction, + dp.Weight, tissue, _initialTissueRegionIndex, Rng); // Rng not used here + + return photon; + } + + #region Random number generator code (copy-paste into all sources) + /// + /// The random number generator used to create photons. If not assigned externally, + /// a Mersenne Twister (MathNet.Numerics.Random.MersenneTwister) will be created with + /// a seed of zero. + /// + public Random Rng + { + get + { + if (_rng == null) + { + _rng = new MathNet.Numerics.Random.MersenneTwister(0); + } + return _rng; + } + set { _rng = value; } + } + private Random _rng; + #endregion + + } +} diff --git a/src/Vts/MonteCarlo/Sources/FromFile/RayFileSource.cs b/src/Vts/MonteCarlo/Sources/FromFile/RayFileSource.cs new file mode 100644 index 000000000..4cd8d27c1 --- /dev/null +++ b/src/Vts/MonteCarlo/Sources/FromFile/RayFileSource.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using Vts.Common; +using Vts.MonteCarlo.RayData; + +namespace Vts.MonteCarlo.Sources +{ + /// + /// Implements ISourceInput. Defines input data for Ray FileSource implementation + /// including emitting position, direction, weight and initial tissue region index. + /// + public class RayFileSourceInput :ISourceInput + { + /// + /// Initializes a new instance of ZemaxFileSourceInput class + /// + /// Source file name + /// Initial tissue region index + public RayFileSourceInput( + string sourceFileName, + int initialTissueRegionIndex) + { + SourceType = "RayFile"; + SourceFileName = sourceFileName; + InitialTissueRegionIndex = initialTissueRegionIndex; + } + + /// + /// Initializes the default constructor of ZemaxFileSourceInput class + /// + public RayFileSourceInput() + : this("", 0) + { + } + + /// + /// Point source type + /// + public string SourceType { get; set; } + /// + /// Source file name + /// + public string SourceFileName { get; set; } + /// + /// Initial tissue region index + /// + public int InitialTissueRegionIndex { get; set; } + + /// + /// Required code to create a source based on the input values + /// + /// random number generator + /// + public ISource CreateSource(Random rng = null) + { + rng = rng ?? new Random(); + + return new RayFileSource( + this.SourceFileName, + this.InitialTissueRegionIndex) + { Rng = rng }; + } + } + + /// + /// Implements FileSource with file name, initial + /// tissue region index. + /// + public class RayFileSource : ISource //: FromFileSourceBase + { + /// + /// enumerator that iterates through database + /// + public IEnumerator DatabaseEnumerator { get; set; } + /// + /// initial tissue region index + /// + public int InitialTissueRegionIndex { get; set; } + /// + /// Returns an instance of Zemax File Source at a given location + /// + /// filename of MCCL source DB file + /// Initial tissue region index + public RayFileSource( + string sourceFileName, + int initialTissueRegionIndex = 0) + { + var sourceDatabase = RayDatabase.FromFile(sourceFileName); + DatabaseEnumerator = sourceDatabase.DataPoints.GetEnumerator(); + InitialTissueRegionIndex = initialTissueRegionIndex; + } + + /// + /// method to iterate through database and get next photon + /// + /// tissue definition + /// + public Photon GetNextPhoton(ITissue tissue) + { + // read next source data point + DatabaseEnumerator.MoveNext(); + var dp = DatabaseEnumerator.Current; + + var photon = new Photon(new Position( + dp.Position.X, + dp.Position.Y, + dp.Position.Z), + new Direction( + dp.Direction.Ux, + dp.Direction.Uy, + dp.Direction.Uz), + dp.Weight, tissue, InitialTissueRegionIndex, Rng); + + return photon; + } + + #region Random number generator code (copy-paste into all sources) + /// + /// The random number generator used to create photons. If not assigned externally, + /// a Mersenne Twister (MathNet.Numerics.Random.MersenneTwister) will be created with + /// a seed of zero. + /// + public Random Rng + { + get + { + if (_rng == null) + { + _rng = new MathNet.Numerics.Random.MersenneTwister(0); + } + return _rng; + } + set { _rng = value; } + } + private Random _rng; + #endregion + } +} diff --git a/src/Vts/MonteCarlo/Types/DatabaseType.cs b/src/Vts/MonteCarlo/Types/DatabaseType.cs index 32d2b309d..dedba1662 100644 --- a/src/Vts/MonteCarlo/Types/DatabaseType.cs +++ b/src/Vts/MonteCarlo/Types/DatabaseType.cs @@ -33,6 +33,11 @@ public enum DatabaseType /// /// pMC diffuse transmittance /// - pMCDiffuseTransmittance + pMCDiffuseTransmittance, + /// + /// MCCL Ray diffuse reflectance + /// + RayDiffuseReflectance, + } } \ No newline at end of file diff --git a/src/Vts/MonteCarlo/Types/VirtualBoundaryType.cs b/src/Vts/MonteCarlo/Types/VirtualBoundaryType.cs index b892f091f..a45a59dcd 100644 --- a/src/Vts/MonteCarlo/Types/VirtualBoundaryType.cs +++ b/src/Vts/MonteCarlo/Types/VirtualBoundaryType.cs @@ -37,6 +37,10 @@ public enum VirtualBoundaryType /// pMCDiffuseTransmittance, /// + /// Virtual Boundary used for ray diffuse reflectance detectors + /// + RayDiffuseReflectance, + /// /// Virtual boundary used to capture photons if leave this lateral boundary /// BoundingVolume, diff --git a/src/Vts/MonteCarlo/VirtualBoundaries/RayDiffuseReflectanceVirtualBoundary.cs b/src/Vts/MonteCarlo/VirtualBoundaries/RayDiffuseReflectanceVirtualBoundary.cs new file mode 100644 index 000000000..104874a59 --- /dev/null +++ b/src/Vts/MonteCarlo/VirtualBoundaries/RayDiffuseReflectanceVirtualBoundary.cs @@ -0,0 +1,78 @@ +using System; +using Vts.MonteCarlo.PhotonData; +using Vts.MonteCarlo.Tissues; + +namespace Vts.MonteCarlo.VirtualBoundaries +{ + /// + /// Implements IVirtualBoundary. Used to capture all diffuse reflectance detectors + /// + public class RayDiffuseReflectanceVirtualBoundary : IVirtualBoundary + { + private IDetectorController _detectorController; + private double _zPlanePosition; + + /// + /// class for ray MCCL diffuse reflectance virtual boundary + /// + /// ITissue + /// IDetectorController + /// string name + public RayDiffuseReflectanceVirtualBoundary(ITissue tissue, IDetectorController detectorController, string name) + { + _zPlanePosition = ((LayerTissueRegion)tissue.Regions[0]).ZRange.Stop; + + WillHitBoundary = dp => + dp.StateFlag.HasFlag(PhotonStateType.PseudoReflectedTissueBoundary) && + dp.Direction.Uz < 0 && + Math.Abs(dp.Position.Z - _zPlanePosition) < 10E-16; + + VirtualBoundaryType = VirtualBoundaryType.RayDiffuseReflectance; + PhotonStateType = PhotonStateType.PseudoDiffuseReflectanceVirtualBoundary; + + _detectorController = detectorController; + + Name = name; + } + + /// + /// VirtualBoundaryType + /// + public VirtualBoundaryType VirtualBoundaryType { get; private set; } + /// + /// PhotonStateType + /// + public PhotonStateType PhotonStateType { get; private set; } + /// + /// string Name + /// + public string Name { get; private set; } + /// + /// Predicate of PhotonDataPoint + /// + public Predicate WillHitBoundary { get; private set; } + /// + /// IDetectorController + /// + public IDetectorController DetectorController { get { return _detectorController; } } + + /// + /// Finds the distance to the virtual boundary given direction of VB and photon + /// + /// photon data point + public double GetDistanceToVirtualBoundary(PhotonDataPoint dp) + { + double distanceToBoundary = double.PositiveInfinity; + // check if VB not applied + if (!dp.StateFlag.HasFlag(PhotonStateType.PseudoReflectedTissueBoundary) || + dp.Direction.Uz >= 0.0) + { + return distanceToBoundary; + } + // VB applies + distanceToBoundary = (_zPlanePosition - dp.Position.Z) / dp.Direction.Uz; + return distanceToBoundary; + } + + } +}