From fdb09c838bdc4ea832ffc8a9b3de84d67b9f764e Mon Sep 17 00:00:00 2001 From: hayakawa Date: Wed, 13 Sep 2023 16:24:43 -0700 Subject: [PATCH 01/13] Modified the SimulationInputValidation code to allow user specifying inhomogeneous tissue and ROfFx detector. Also performed code cleaned up on these two files. Since the ValidationResult.IsValid now returns true, simulation will proceed without interruption. This means that the ValidationRule set to "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry" and the Remarks set to "User discretion advised" are not put to the screen since only results with IsValid=false put out these statements. --- .../SimulationInputValidationTests.cs | 70 +++--- .../SimulationInputValidation.cs | 223 ++++++++---------- 2 files changed, 132 insertions(+), 161 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index 46a255263..dcd62336b 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -13,7 +13,7 @@ namespace Vts.Test.MonteCarlo.DataStructuresValidation public class SimulationInputValidationTests { [Test] - public void validate_null_detector_input_is_invalid_when_no_database_specified() + public void Validate_null_detector_input_is_invalid_when_no_database_specified() { // generate input without any detector inputs and no database specified var input = new SimulationInput() // default constructor has empty list of databases @@ -24,10 +24,10 @@ public void validate_null_detector_input_is_invalid_when_no_database_specified() Assert.IsFalse(result.IsValid); } [Test] - public void validate_null_detector_input_is_valid_when_database_specified() + public void Validate_null_detector_input_is_valid_when_database_specified() { // generate input without any detector inputs but with database specified - var input = new SimulationInput() + var input = new SimulationInput { DetectorInputs = new List {} }; @@ -36,40 +36,40 @@ public void validate_null_detector_input_is_valid_when_database_specified() Assert.IsTrue(result.IsValid); } [Test] - public void validate_detector_input_not_implemented_is_invalid() + public void Validate_detector_input_not_implemented_is_invalid() { // generate input with detector input not implemented yet - var input = new SimulationInput() + var input = new SimulationInput { - Options = new SimulationOptions() { AbsorptionWeightingType = AbsorptionWeightingType.Continuous}, + Options = new SimulationOptions { AbsorptionWeightingType = AbsorptionWeightingType.Continuous}, DetectorInputs = new List { new RadianceOfXAndYAndZAndThetaAndPhiDetectorInput()} }; var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } [Test] - public void validate_duplicate_detector_name_is_invalid() + public void Validate_duplicate_detector_name_is_invalid() { // generate input with detector input with duplicate names - var input = new SimulationInput() + var input = new SimulationInput { DetectorInputs = new List { - new ROfRhoDetectorInput() {Name = "ROfRho1"}, - new ROfRhoDetectorInput() {Name = "ROfRho1"} + new ROfRhoDetectorInput {Name = "ROfRho1"}, + new ROfRhoDetectorInput {Name = "ROfRho1"} } }; var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } [Test] - public void validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_detectors_are_not_defined_together() { // generate input embedded ellipsoid tissue and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { - TissueInput = new SingleEllipsoidTissueInput() + TissueInput = new SingleEllipsoidTissueInput { - EllipsoidRegion = new EllipsoidTissueRegion() {Center = new Position(1, 1, 0)} + EllipsoidRegion = new EllipsoidTissueRegion {Center = new Position(1, 1, 0)} }, DetectorInputs = new List {new ROfRhoDetectorInput()} }; @@ -77,14 +77,14 @@ public void validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_dete Assert.IsFalse(result.IsValid); } [Test] - public void validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_are_not_defined_together() { // generate input embedded ellipsoid tissue and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { - TissueInput = new SingleEllipsoidTissueInput() + TissueInput = new SingleEllipsoidTissueInput { - EllipsoidRegion = new EllipsoidTissueRegion() { Dx = 1.0, Dy = 2.0 } + EllipsoidRegion = new EllipsoidTissueRegion { Dx = 1.0, Dy = 2.0 } }, DetectorInputs = new List { new ROfRhoDetectorInput() } }; @@ -92,10 +92,10 @@ public void validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindric Assert.IsFalse(result.IsValid); } [Test] - public void validate_angled_source_and_cylindrical_detectors_are_not_defined_together() + public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_together() { // generate input with angled source and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { SourceInput = new DirectionalPointSourceInput( new Position(0,0,0), new Direction(1.0/Math.Sqrt(2), 0, 1.0/Math.Sqrt(2)),1), @@ -105,40 +105,46 @@ public void validate_angled_source_and_cylindrical_detectors_are_not_defined_tog Assert.IsFalse(result.IsValid); } [Test] - public void validate_ellipsoid_tissue_and_ROfFx_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_and_ROfFx_detectors_are_not_defined_together() { // generate input embedded ellipsoid tissue and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { - TissueInput = new SingleEllipsoidTissueInput() + TissueInput = new SingleEllipsoidTissueInput { - EllipsoidRegion = new EllipsoidTissueRegion() { Dx = 1.0, Dy = 2.0 } + EllipsoidRegion = new EllipsoidTissueRegion { Dx = 1.0, Dy = 2.0 } }, DetectorInputs = new List { new ROfFxDetectorInput() } }; var result = SimulationInputValidation.ValidateInput(input); - Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.IsValid); // only warning } [Test] - public void validate_voxel_tissue_and_ROfFx_detectors_are_not_defined_together() + public void Validate_voxel_tissue_and_ROfFx_detectors_are_not_defined_together() { // generate input embedded ellipsoid tissue and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { - TissueInput = new SingleVoxelTissueInput() + TissueInput = new SingleVoxelTissueInput { - VoxelRegion = new VoxelTissueRegion() + // make sure voxel defined so validation doesn't error on voxel geometry + VoxelRegion = new VoxelTissueRegion + { + X = new DoubleRange(-5,5,2), + Y = new DoubleRange(-5,5,2), + Z = new DoubleRange(1,6,2) + } }, DetectorInputs = new List { new ROfFxDetectorInput() } }; var result = SimulationInputValidation.ValidateInput(input); - Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.IsValid); // only warning } [Test] - public void validate_tissue_optical_properties_are_non_negative() + public void Validate_tissue_optical_properties_are_non_negative() { // generate input embedded ellipsoid tissue and cylindrical detector - var input = new SimulationInput() + var input = new SimulationInput { TissueInput = new MultiLayerTissueInput( new ITissueRegion[] diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index 3bbbf6fc8..7279f607d 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -61,18 +61,15 @@ private static ValidationResult ValidateSourceInput(ISourceInput sourceInput, IT "Source input not valid given tissue definition", "Alter sourceInput.InitialTissueRegionIndex to be consistent with tissue definition"); } - if (sourceInput is FluorescenceEmissionAOfRhoAndZSourceInput) - { - return FluorescenceEmissionAOfRhoAndZSourceInputValidation.ValidateInput(sourceInput); - } - if (sourceInput is FluorescenceEmissionAOfXAndYAndZSourceInput) - { - return FluorescenceEmissionAOfXAndYAndZSourceInputValidation.ValidateInput(sourceInput); - } - return new ValidationResult( - true, - "Starting photons in region " + sourceInput.InitialTissueRegionIndex); + return sourceInput switch + { + FluorescenceEmissionAOfRhoAndZSourceInput => FluorescenceEmissionAOfRhoAndZSourceInputValidation + .ValidateInput(sourceInput), + FluorescenceEmissionAOfXAndYAndZSourceInput => FluorescenceEmissionAOfXAndYAndZSourceInputValidation + .ValidateInput(sourceInput), + _ => new ValidationResult(true, "Starting photons in region " + sourceInput.InitialTissueRegionIndex) + }; } private static ValidationResult ValidateTissueInput(ITissueInput tissueInput) @@ -88,32 +85,17 @@ private static ValidationResult ValidateTissueInput(ITissueInput tissueInput) "Please check optical properties"); } - if (tissueInput is MultiLayerTissueInput) - { - return MultiLayerTissueInputValidation.ValidateInput(tissueInput); - } - if (tissueInput is SingleEllipsoidTissueInput) + return tissueInput switch { - return SingleEllipsoidTissueInputValidation.ValidateInput(tissueInput); - } - if (tissueInput is SingleVoxelTissueInput) - { - return SingleVoxelTissueInputValidation.ValidateInput(tissueInput); - } - if (tissueInput is MultiConcentricInfiniteCylinderTissueInput) - { - return MultiConcentricInfiniteCylinderTissueInputValidation.ValidateInput(tissueInput); - } - if (tissueInput is BoundingCylinderTissueInput) - { - return BoundingCylinderTissueInputValidation.ValidateInput(tissueInput); - } - - return new ValidationResult( - true, - "Tissue input must be valid", - "Validation skipped for tissue input " + tissueInput + - ". No matching validation rules were found."); + MultiLayerTissueInput => MultiLayerTissueInputValidation.ValidateInput(tissueInput), + SingleEllipsoidTissueInput => SingleEllipsoidTissueInputValidation.ValidateInput(tissueInput), + SingleVoxelTissueInput => SingleVoxelTissueInputValidation.ValidateInput(tissueInput), + MultiConcentricInfiniteCylinderTissueInput => MultiConcentricInfiniteCylinderTissueInputValidation + .ValidateInput(tissueInput), + BoundingCylinderTissueInput => BoundingCylinderTissueInputValidation.ValidateInput(tissueInput), + _ => new ValidationResult(true, "Tissue input must be valid", + "Validation skipped for tissue input " + tissueInput + ". No matching validation rules were found.") + }; } private static ValidationResult ValidateDetectorInput(SimulationInput si) { @@ -167,72 +149,74 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput "Russian Roulette cannot be employed with Analog absorption weighting is specified", "With Analog absorption weighting, set Russian Roulette weight threshold = 0.0"); } - // check that if single ellipsoid tissue specified and (r,z) detector specified, - // that (1) ellipsoid is centered at x=0, y=0, (2) ellipsoid is cylindrically symmetric (dx=dy) - var tissueWithEllipsoid = input.TissueInput as SingleEllipsoidTissueInput; - if (tissueWithEllipsoid != null) + + switch (input.TissueInput) { - var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; - foreach (var detectorInput in input.DetectorInputs) + // check that if single ellipsoid tissue specified and (r,z) detector specified, + // that (1) ellipsoid is centered at x=0, y=0, (2) ellipsoid is cylindrically symmetric (dx=dy) + case SingleEllipsoidTissueInput tissueWithEllipsoid: { - switch (detectorInput.TallyDetails.IsCylindricalTally) + var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; + foreach (var detectorInput in input.DetectorInputs) { - case true when - ellipsoid.Center.X != 0.0 && ellipsoid.Center.Y != 0.0: - return new ValidationResult( - false, - "Ellipsoid must be centered at (x,y)=(0,0) for cylindrical tallies", - "Change ellipsoid center to (0,0) or specify non-cylindrical type tally"); - case true when ellipsoid.Dx != ellipsoid.Dy: + switch (detectorInput.TallyDetails.IsCylindricalTally) + { + case true when + ellipsoid.Center.X != 0.0 && ellipsoid.Center.Y != 0.0: + return new ValidationResult( + false, + "Ellipsoid must be centered at (x,y)=(0,0) for cylindrical tallies", + "Change ellipsoid center to (0,0) or specify non-cylindrical type tally"); + case true when ellipsoid.Dx != ellipsoid.Dy: + return new ValidationResult( + false, + "Ellipsoid must have Dx=Dy for cylindrical tallies", + "Change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + } + + if (detectorInput.TallyType == TallyType.ROfFx) + { return new ValidationResult( - false, - "Ellipsoid must have Dx=Dy for cylindrical tallies", - "Change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); + } } - if (detectorInput.TallyType == TallyType.ROfFx) - { - return new ValidationResult( - false, - "R(fx) tallies assume a homogeneous or layered tissue geometry", - "Change tissue type to be homogeneous or layered"); - } + break; } - } - // check that if single voxel or single infinite cylinder tissue specified, - // cannot specify (r,z) detector - if (input.TissueInput is SingleVoxelTissueInput || - input.TissueInput is SingleInfiniteCylinderTissueInput) - { - foreach (var detectorInput in input.DetectorInputs) + // check that if single voxel or single infinite cylinder tissue specified, + // cannot specify (r,z) detector + case SingleVoxelTissueInput: + case SingleInfiniteCylinderTissueInput: { - if (detectorInput.TallyDetails.IsCylindricalTally) - { - return new ValidationResult( - false, - "Cannot use Single Voxel Tissue for cylindrical tallies", - "Change detector inputs to specify non-cylindrical type tallies"); - } - if (detectorInput.TallyType == TallyType.ROfFx) + foreach (var detectorInput in input.DetectorInputs) { - return new ValidationResult( - false, - "R(fx) tallies assume a homogeneous or layered tissue geometry", - "Change tissue type to be homogeneous or layered"); + if (detectorInput.TallyDetails.IsCylindricalTally) + { + return new ValidationResult( + false, + "Cannot use Single Voxel Tissue for cylindrical tallies", + "Change detector inputs to specify non-cylindrical type tallies"); + } + if (detectorInput.TallyType == TallyType.ROfFx) + { + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); + } } + + break; } - } - // check that if bounding volume tissue specified, the ATotalBoundingVolumeTissueInput detector needs - // to be specified - if (input.TissueInput is BoundingCylinderTissueInput) - { - if (input.DetectorInputs.All(d => d.TallyType != TallyType.ATotalBoundingVolume)) - { + // check that if bounding volume tissue specified, the ATotalBoundingVolumeTissueInput detector needs + // to be specified + case BoundingCylinderTissueInput when input.DetectorInputs.All(d => d.TallyType != TallyType.ATotalBoundingVolume): return new ValidationResult( false, "BoundingCylinderTissueInput needs associated detector ATotalBoundingVolume to be defined", "Add ATotalBoundingVolumeDetectorInput to detector inputs"); - } } var source = input.SourceInput as DirectionalPointSourceInput; @@ -240,30 +224,22 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput { if (source.Direction != new Direction(0,0,1)) { - foreach (var detectorInput in input.DetectorInputs) + if (input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsCylindricalTally)) { - if (detectorInput.TallyDetails.IsCylindricalTally) - { - return new ValidationResult( - false, - "If source is angled, cannot define cylindrically symmetric detectors", - "Change detector to Cartesian equivalent or define source to be normal"); - } + return new ValidationResult( + false, + "If source is angled, cannot define cylindrically symmetric detectors", + "Change detector to Cartesian equivalent or define source to be normal"); } } } - foreach (var detectorInput in input.DetectorInputs) + if (input.DetectorInputs.Where(detectorInput => detectorInput.TallyDetails.IsTransmittanceTally && + input.TissueInput is MultiLayerTissueInput).Any(detectorInput => ((dynamic)detectorInput).FinalTissueRegionIndex == 0)) { - if (detectorInput.TallyDetails.IsTransmittanceTally && input.TissueInput is MultiLayerTissueInput) - { - if (((dynamic)detectorInput).FinalTissueRegionIndex == 0) - { - return new ValidationResult( - false, - "Transmittance detectors with MultiLayerTissues cannot detect in tissue region 0", - "Change FinalTissueRegionIndex to be index of air below tissue (index >= 2)"); - } - } + return new ValidationResult( + false, + "Transmittance detectors with MultiLayerTissues cannot detect in tissue region 0", + "Change FinalTissueRegionIndex to be index of air below tissue (index >= 2)"); } return new ValidationResult( true, @@ -278,32 +254,21 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput /// An instance of the ValidationResult class private static ValidationResult ValidateCurrentIncapabilities(SimulationInput input) { - if (input.Options.AbsorptionWeightingType == AbsorptionWeightingType.Continuous) + if (input.Options.AbsorptionWeightingType == AbsorptionWeightingType.Continuous && + input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsNotImplementedForCAW)) { - foreach (var detectorInput in input.DetectorInputs) - { - if (detectorInput.TallyDetails.IsNotImplementedForCAW) - { - return new ValidationResult( - false, - "The use of Continuous Absorption Weighting is not implemented for one of the infile detectors", - "Modify AbsorptionWeightingType to Discrete"); - } - } - + return new ValidationResult( + false, + "The use of Continuous Absorption Weighting is not implemented for one of the infile detectors", + "Modify AbsorptionWeightingType to Discrete"); } - if (input.Options.AbsorptionWeightingType == AbsorptionWeightingType.Discrete) + if (input.Options.AbsorptionWeightingType == AbsorptionWeightingType.Discrete && + input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsNotImplementedForDAW)) { - foreach (var detectorInput in input.DetectorInputs) - { - if (detectorInput.TallyDetails.IsNotImplementedForDAW) - { - return new ValidationResult( - false, - "The use of Discrete Absorption Weighting with path length type detectors not implemented yet", - "Modify AbsorptionWeightingType to Continuous"); - } - } + return new ValidationResult( + false, + "The use of Discrete Absorption Weighting with path length type detectors not implemented yet", + "Modify AbsorptionWeightingType to Continuous"); } foreach (var detectorInput in input.DetectorInputs) { From 59f858b9ddcd6b4a5a23efecf265d24f606a1f7f Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 14 Sep 2023 17:10:40 -0700 Subject: [PATCH 02/13] Some more code cleanup. --- .../SimulationInputValidationTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index dcd62336b..9af018aac 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -18,7 +18,7 @@ public void Validate_null_detector_input_is_invalid_when_no_database_specified() // generate input without any detector inputs and no database specified var input = new SimulationInput() // default constructor has empty list of databases { - DetectorInputs = new List {} + DetectorInputs = new List() }; var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); @@ -29,7 +29,7 @@ public void Validate_null_detector_input_is_valid_when_database_specified() // generate input without any detector inputs but with database specified var input = new SimulationInput { - DetectorInputs = new List {} + DetectorInputs = new List() }; input.Options.Databases = new List {DatabaseType.DiffuseReflectance}; var result = SimulationInputValidation.ValidateInput(input); From 0b3839fb5329807bea36dd4513db4c7ed5fa4ff1 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 8 Aug 2024 12:44:57 -0700 Subject: [PATCH 03/13] Modified validation code check of non-layered or non-homogeneous tissue and R(fx) detector specification to issue Warning and continue with simulation. --- .../SimulationInputValidationTests.cs | 57 ++++++++++++++++++- .../SimulationInputValidation.cs | 27 +++++---- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index 9af018aac..bb06cad24 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.IO; using NUnit.Framework; using Vts.Common; +using Vts.Modeling.ForwardSolvers; using Vts.MonteCarlo; using Vts.MonteCarlo.Detectors; using Vts.MonteCarlo.Sources; @@ -12,6 +14,9 @@ namespace Vts.Test.MonteCarlo.DataStructuresValidation [TestFixture] public class SimulationInputValidationTests { + /// + /// Test to verify input with no detectors nor database specified is invalid + /// [Test] public void Validate_null_detector_input_is_invalid_when_no_database_specified() { @@ -23,6 +28,10 @@ public void Validate_null_detector_input_is_invalid_when_no_database_specified() var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify input with database specified and no detectors specified is valid + /// [Test] public void Validate_null_detector_input_is_valid_when_database_specified() { @@ -35,6 +44,11 @@ public void Validate_null_detector_input_is_valid_when_database_specified() var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); } + + /// + /// Test to verify input with detector, Radiance(x,y,z,theta,phi) and CAW is invalid + /// because not implemented + /// [Test] public void Validate_detector_input_not_implemented_is_invalid() { @@ -47,6 +61,10 @@ public void Validate_detector_input_not_implemented_is_invalid() var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify that input with two detectors with the same Name is invalid + /// [Test] public void Validate_duplicate_detector_name_is_invalid() { @@ -61,6 +79,10 @@ public void Validate_duplicate_detector_name_is_invalid() var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify input with cylindrical detector and off axis ellipsoid in tissue is invalid + /// [Test] public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_detectors_are_not_defined_together() { @@ -76,6 +98,10 @@ public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_dete var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify input cylindrical detector and ellipsoid in tissue is invalid + /// [Test] public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_are_not_defined_together() { @@ -91,6 +117,10 @@ public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindric var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify input with angled source and cylindrical detectors is invalid + /// [Test] public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_together() { @@ -104,8 +134,13 @@ public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_tog var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify input with ellipsoid in tissue and R(fx) detector puts out warning + /// but continues as valid input + /// [Test] - public void Validate_ellipsoid_tissue_and_ROfFx_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_and_ROfFx_detectors_defined_together_issues_warning() { // generate input embedded ellipsoid tissue and cylindrical detector var input = new SimulationInput @@ -116,11 +151,20 @@ public void Validate_ellipsoid_tissue_and_ROfFx_detectors_are_not_defined_togeth }, DetectorInputs = new List { new ROfFxDetectorInput() } }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised\r\n")); } + + /// + /// Test to verify input with voxel in tissue and R(fx) detector puts out warning + /// but continues as valid input + /// [Test] - public void Validate_voxel_tissue_and_ROfFx_detectors_are_not_defined_together() + public void Validate_voxel_tissue_and_ROfFx_detectors_defined_together_issues_warning() { // generate input embedded ellipsoid tissue and cylindrical detector var input = new SimulationInput @@ -137,9 +181,18 @@ public void Validate_voxel_tissue_and_ROfFx_detectors_are_not_defined_together() }, DetectorInputs = new List { new ROfFxDetectorInput() } }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised\r\n")); + } + + /// + /// Test to verify input with negative optical properties is invalid + /// [Test] public void Validate_tissue_optical_properties_are_non_negative() { diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index a92d706e2..d2e08b915 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -177,13 +177,12 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput "Change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); } - if (detectorInput.TallyType == TallyType.ROfFx) - { - return new ValidationResult( - true, - "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", - "User discretion advised"); - } + if (detectorInput.TallyType != TallyType.ROfFx) continue; + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised"); + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); } break; @@ -202,13 +201,13 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput "Cannot use Single Voxel Tissue for cylindrical tallies", "Change detector inputs to specify non-cylindrical type tallies"); } - if (detectorInput.TallyType == TallyType.ROfFx) - { - return new ValidationResult( - true, - "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", - "User discretion advised"); - } + + if (detectorInput.TallyType != TallyType.ROfFx) continue; + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised"); + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); } break; From 5306b4a0f00ba0f350a92bf7e5869c245bd553e5 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 8 Aug 2024 16:07:32 -0700 Subject: [PATCH 04/13] Downgraded three more validation checks from an exception that stops the code, to issuing a warning and continuing on with the simulation.The philosophy here is that if combination of inputs is non-standard but the transport will not error, a warning is issued and the validation result remains true. This allows users to specify inconsistent combinations, e.g. angled source and cylindrical coordinate detectors, receive a warning and have the simulation proceed. If the combination of inputs will cause the transport to error, e.g. defining an embedded ellipsoid that overlaps with a tissue layer, then the input validation fails and the simulation stopped. --- .../SimulationInputValidationTests.cs | 45 +++++++++++++------ .../SimulationInputValidation.cs | 32 +++++++------ 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index bb06cad24..2ad8afb3f 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -38,9 +38,12 @@ public void Validate_null_detector_input_is_valid_when_database_specified() // generate input without any detector inputs but with database specified var input = new SimulationInput { - DetectorInputs = new List() + DetectorInputs = new List(), + Options = + { + Databases = new List {DatabaseType.DiffuseReflectance} + } }; - input.Options.Databases = new List {DatabaseType.DiffuseReflectance}; var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); } @@ -81,29 +84,34 @@ public void Validate_duplicate_detector_name_is_invalid() } /// - /// Test to verify input with cylindrical detector and off axis ellipsoid in tissue is invalid + /// Test to verify input with cylindrical detector and off axis ellipsoid in tissue outputs warning + /// but continues as valid input /// [Test] - public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_detectors_issues_warning() { // generate input embedded ellipsoid tissue and cylindrical detector var input = new SimulationInput { TissueInput = new SingleEllipsoidTissueInput { - EllipsoidRegion = new EllipsoidTissueRegion {Center = new Position(1, 1, 0)} + EllipsoidRegion = new EllipsoidTissueRegion {Center = new Position(1, 1, 5)} }, DetectorInputs = new List {new ROfRhoDetectorInput()} }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); - Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: off center ellipsoid in tissue with cylindrical detector defined: user discretion advised\r\n")); } /// /// Test to verify input cylindrical detector and ellipsoid in tissue is invalid /// [Test] - public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_are_not_defined_together() + public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_issues_warning() { // generate input embedded ellipsoid tissue and cylindrical detector var input = new SimulationInput @@ -114,12 +122,17 @@ public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindric }, DetectorInputs = new List { new ROfRhoDetectorInput() } }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); - Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised\r\n")); } /// - /// Test to verify input with angled source and cylindrical detectors is invalid + /// Test to verify input with angled source and cylindrical detectors outputs warning + /// but continues as valid input /// [Test] public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_together() @@ -131,12 +144,16 @@ public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_tog new Position(0,0,0), new Direction(1.0/Math.Sqrt(2), 0, 1.0/Math.Sqrt(2)),1), DetectorInputs = new List { new ROfRhoDetectorInput() } }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); - Assert.IsFalse(result.IsValid); + Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: Angled source and cylindrical coordinate detector defined: user discretion advised\r\n")); } /// - /// Test to verify input with ellipsoid in tissue and R(fx) detector puts out warning + /// Test to verify input with ellipsoid in tissue and R(fx) detector outputs warning /// but continues as valid input /// [Test] @@ -156,11 +173,11 @@ public void Validate_ellipsoid_tissue_and_ROfFx_detectors_defined_together_issue Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); // only warning - Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised\r\n")); + Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised\r\n")); } /// - /// Test to verify input with voxel in tissue and R(fx) detector puts out warning + /// Test to verify input with voxel in tissue and R(fx) detector outputs warning /// but continues as valid input /// [Test] @@ -186,7 +203,7 @@ public void Validate_voxel_tissue_and_ROfFx_detectors_defined_together_issues_wa Console.SetOut(output); var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); // only warning - Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised\r\n")); + Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised\r\n")); } diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index d2e08b915..2bfa6be97 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -137,7 +137,10 @@ private static ValidationResult ValidateDetectorInput(SimulationInput si) /// /// This method checks the input against combined combinations of options - /// and source, tissue, detector definitions. + /// and source, tissue, detector definitions. The philosophy here is that if the transport will + /// not error, a warning is issued and the validation result remains true. This allows users to + /// specify inconsistent combinations, e.g. angled source and cylindrical coordinate detectors, + /// receive a warning and have the simulation proceed. /// /// input to be validated /// An instance of ValidationResult with IsValid set and error message if false @@ -166,19 +169,21 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput { case true when ellipsoid.Center.X != 0.0 && ellipsoid.Center.Y != 0.0: - return new ValidationResult( - false, - "Ellipsoid must be centered at (x,y)=(0,0) for cylindrical tallies", - "Change ellipsoid center to (0,0) or specify non-cylindrical type tally"); + Console.WriteLine("Warning: off center ellipsoid in tissue with cylindrical detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: off center ellipsoid in tissue with cylindrical detector defined", + "User discretion advised: change ellipsoid center to (0,0) or specify non-cylindrical type tally"); case true when ellipsoid.Dx != ellipsoid.Dy: + Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); return new ValidationResult( - false, - "Ellipsoid must have Dx=Dy for cylindrical tallies", - "Change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + true, + "Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined", + "User discretion advised: change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); } if (detectorInput.TallyType != TallyType.ROfFx) continue; - Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised"); + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); return new ValidationResult( true, "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", @@ -203,7 +208,7 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput } if (detectorInput.TallyType != TallyType.ROfFx) continue; - Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry.User discretion advised"); + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); return new ValidationResult( true, "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", @@ -225,10 +230,11 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput source.Direction != new Direction(0,0,1) && input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsCylindricalTally)) { + Console.WriteLine("Warning: Angled source and cylindrical coordinate detector defined: user discretion advised"); return new ValidationResult( - false, - "If source is angled, cannot define cylindrically symmetric detectors", - "Change detector to Cartesian equivalent or define source to be normal"); + true, + "Warning: Angled source and cylindrical coordinate detector defined", + "User discretion advised: change detector to Cartesian equivalent or define source to be normal"); } if (input.DetectorInputs.Where(detectorInput => detectorInput.TallyDetails.IsTransmittanceTally && input.TissueInput is MultiLayerTissueInput).Any(detectorInput => ((dynamic)detectorInput).FinalTissueRegionIndex == 0)) From 9331e5a2405d4c1d8f2f4e37ee9de304a6ca3dda Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 8 Aug 2024 16:33:01 -0700 Subject: [PATCH 05/13] Found another validation check that could be downgraded to warning. Added unit test. --- .../SimulationInputValidationTests.cs | 32 ++++++++++++++++++- .../SimulationInputValidation.cs | 13 +++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index 2ad8afb3f..cb0ff09c2 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -108,7 +108,8 @@ public void Validate_ellipsoid_tissue_with_off_zaxis_center_and_cylindrical_dete } /// - /// Test to verify input cylindrical detector and ellipsoid in tissue is invalid + /// Test to verify input cylindrical detector and ellipsoid in tissue issues warning + /// but continues as valid input /// [Test] public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindrical_detectors_issues_warning() @@ -130,6 +131,35 @@ public void Validate_ellipsoid_tissue_without_cylindrical_symmetry_and_cylindric Assert.That(output.ToString(), Is.EqualTo("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised\r\n")); } + /// + /// Test to verify input cylindrical detector and voxel in tissue issues warning + /// but continues as valid input + /// + [Test] + public void Validate_voxel_tissue_and_cylindrical_detectors_issues_warning() + { + // generate input embedded ellipsoid tissue and cylindrical detector + var input = new SimulationInput + { + TissueInput = new SingleVoxelTissueInput + { + VoxelRegion = new VoxelTissueRegion + { + X = new DoubleRange(-1.0, 1.0, 2), + Y = new DoubleRange(-1.0, 1.0,2), + Z = new DoubleRange(0.01, 1, 2) + } + }, + DetectorInputs = new List { new ROfRhoDetectorInput() } + }; + // set to catch Console output + var output = new StringWriter(); + Console.SetOut(output); + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsTrue(result.IsValid); // only warning + Assert.That(output.ToString(), Is.EqualTo("Warning: voxel in tissue with cylindrical detector defined: user discretion advised\r\n")); + } + /// /// Test to verify input with angled source and cylindrical detectors outputs warning /// but continues as valid input diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index 2bfa6be97..a5a4d3490 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -140,7 +140,9 @@ private static ValidationResult ValidateDetectorInput(SimulationInput si) /// and source, tissue, detector definitions. The philosophy here is that if the transport will /// not error, a warning is issued and the validation result remains true. This allows users to /// specify inconsistent combinations, e.g. angled source and cylindrical coordinate detectors, - /// receive a warning and have the simulation proceed. + /// receive a warning and have the simulation proceed. However, if the transport will error then + /// the validation result will be false, the validationRule and remarks output and simulation stops, + /// e.g. embedded ellipsoid in tissue that overlaps tissue layer. /// /// input to be validated /// An instance of ValidationResult with IsValid set and error message if false @@ -201,10 +203,11 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput { if (detectorInput.TallyDetails.IsCylindricalTally) { - return new ValidationResult( - false, - "Cannot use Single Voxel Tissue for cylindrical tallies", - "Change detector inputs to specify non-cylindrical type tallies"); + Console.WriteLine("Warning: voxel in tissue with cylindrical detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: voxel in tissue with cylindrical detector defined", + "User discretion advised: change detector inputs to specify non-cylindrical type tallies"); } if (detectorInput.TallyType != TallyType.ROfFx) continue; From 89e1b62e127b50148a152c5d61411cad7cb542c1 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Wed, 21 Aug 2024 18:31:54 -0700 Subject: [PATCH 06/13] Modfied one of the floating point comparisons using a range. The other should not have a range. --- .../DataStructuresValidation/SimulationInputValidation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index a5a4d3490..83c1abf29 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -176,7 +176,7 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput true, "Warning: off center ellipsoid in tissue with cylindrical detector defined", "User discretion advised: change ellipsoid center to (0,0) or specify non-cylindrical type tally"); - case true when ellipsoid.Dx != ellipsoid.Dy: + case true when Math.Abs(ellipsoid.Dx - ellipsoid.Dy) > 1e-6: Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); return new ValidationResult( true, From 08b3354cfba8ada2a8bbf967dc42f933d40d4f33 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 22 Aug 2024 15:28:08 -0700 Subject: [PATCH 07/13] Fixed unused usings, code cleanup, cognitive complexity in one place and floating point check. --- .../SimulationInputValidation.cs | 132 ++++++++++-------- 1 file changed, 71 insertions(+), 61 deletions(-) diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index 83c1abf29..c6b173b21 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -2,7 +2,6 @@ using System.Linq; using Vts.Common; using Vts.MonteCarlo.DataStructuresValidation; -using Vts.MonteCarlo.Interfaces; using Vts.MonteCarlo.Sources; using Vts.MonteCarlo.Tissues; @@ -11,7 +10,7 @@ namespace Vts.MonteCarlo /// /// This sanity checks SimulationInput /// - public class SimulationInputValidation + public static class SimulationInputValidation { /// /// Master of call validation methods. Calls methods to validate source, @@ -116,8 +115,8 @@ private static ValidationResult ValidateDetectorInput(SimulationInput si) { return new ValidationResult( false, - "DetectorInput not implemented yet:" + detectorInput.ToString(), - "Please omit " + detectorInput.ToString() + " from DetectorInput list"); + "DetectorInput not implemented yet:" + detectorInput, + "Please omit " + detectorInput + " from DetectorInput list"); } } // make sure all detectors have unique Names @@ -150,7 +149,7 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput { // check that absorption weighting type set to analog and RR weight threshold != 0.0 if (input.Options.AbsorptionWeightingType == AbsorptionWeightingType.Analog && - input.Options.RussianRouletteWeightThreshold != 0.0) + input.Options.RussianRouletteWeightThreshold > 0.0) { return new ValidationResult( false, @@ -158,102 +157,113 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput "With Analog absorption weighting, set Russian Roulette weight threshold = 0.0"); } + // check combination of tissue definition with detector definition + var result = ValidateTissueCombinedWithDetectors(input); + if (!result.IsValid) return result; + + // check combination of source definition with detector definition + if (input.SourceInput is DirectionalPointSourceInput source && + source.Direction != new Direction(0,0,1) && + input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsCylindricalTally)) + { + Console.WriteLine("Warning: Angled source and cylindrical coordinate detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: Angled source and cylindrical coordinate detector defined", + "User discretion advised: change detector to Cartesian equivalent or define source to be normal"); + } + if (input.DetectorInputs.Where(detectorInput => detectorInput.TallyDetails.IsTransmittanceTally && + input.TissueInput is MultiLayerTissueInput).Any(detectorInput => ((dynamic)detectorInput).FinalTissueRegionIndex == 0)) + { + return new ValidationResult( + false, + "Transmittance detectors with MultiLayerTissues cannot detect in tissue region 0", + "Change FinalTissueRegionIndex to be index of air below tissue (index >= 2)"); + } + return new ValidationResult( + true, + "Input options or tissue/detector combinations are valid", + ""); + } + + private static ValidationResult ValidateTissueCombinedWithDetectors(SimulationInput input) + { switch (input.TissueInput) { // check that if single ellipsoid tissue specified and (r,z) detector specified, // that (1) ellipsoid is centered at x=0, y=0, (2) ellipsoid is cylindrically symmetric (dx=dy) case SingleEllipsoidTissueInput tissueWithEllipsoid: - { - var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; - foreach (var detectorInput in input.DetectorInputs) { - switch (detectorInput.TallyDetails.IsCylindricalTally) + var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; + foreach (var detectorInput in input.DetectorInputs) { - case true when - ellipsoid.Center.X != 0.0 && ellipsoid.Center.Y != 0.0: + switch (detectorInput.TallyDetails.IsCylindricalTally) + { + case true when + ellipsoid.Center.X != 0.0 || ellipsoid.Center.Y != 0.0: Console.WriteLine("Warning: off center ellipsoid in tissue with cylindrical detector defined: user discretion advised"); return new ValidationResult( true, "Warning: off center ellipsoid in tissue with cylindrical detector defined", "User discretion advised: change ellipsoid center to (0,0) or specify non-cylindrical type tally"); - case true when Math.Abs(ellipsoid.Dx - ellipsoid.Dy) > 1e-6: - Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); - return new ValidationResult( - true, - "Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined", - "User discretion advised: change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + case true when Math.Abs(ellipsoid.Dx - ellipsoid.Dy) > 1e-6: + Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined", + "User discretion advised: change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + } + + if (detectorInput.TallyType != TallyType.ROfFx) continue; + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); } - if (detectorInput.TallyType != TallyType.ROfFx) continue; - Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); - return new ValidationResult( - true, - "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", - "User discretion advised"); + break; } - - break; - } // check that if single voxel or single infinite cylinder tissue specified, // cannot specify (r,z) detector case SingleVoxelTissueInput: case SingleInfiniteCylinderTissueInput: - { - foreach (var detectorInput in input.DetectorInputs) { - if (detectorInput.TallyDetails.IsCylindricalTally) + foreach (var detectorInput in input.DetectorInputs) { + if (detectorInput.TallyDetails.IsCylindricalTally) + { Console.WriteLine("Warning: voxel in tissue with cylindrical detector defined: user discretion advised"); return new ValidationResult( true, "Warning: voxel in tissue with cylindrical detector defined", "User discretion advised: change detector inputs to specify non-cylindrical type tallies"); - } + } - if (detectorInput.TallyType != TallyType.ROfFx) continue; - Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); - return new ValidationResult( - true, - "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", - "User discretion advised"); + if (detectorInput.TallyType != TallyType.ROfFx) continue; + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); + } + break; } - - break; - } // check that if bounding volume tissue specified, the ATotalBoundingVolumeTissueInput detector needs // to be specified case BoundingCylinderTissueInput when input.DetectorInputs.All(d => d.TallyType != TallyType.ATotalBoundingVolume): return new ValidationResult( - false, - "BoundingCylinderTissueInput needs associated detector ATotalBoundingVolume to be defined", - "Add ATotalBoundingVolumeDetectorInput to detector inputs"); - } - - if (input.SourceInput is DirectionalPointSourceInput source && - source.Direction != new Direction(0,0,1) && - input.DetectorInputs.Any(detectorInput => detectorInput.TallyDetails.IsCylindricalTally)) - { - Console.WriteLine("Warning: Angled source and cylindrical coordinate detector defined: user discretion advised"); - return new ValidationResult( - true, - "Warning: Angled source and cylindrical coordinate detector defined", - "User discretion advised: change detector to Cartesian equivalent or define source to be normal"); - } - if (input.DetectorInputs.Where(detectorInput => detectorInput.TallyDetails.IsTransmittanceTally && - input.TissueInput is MultiLayerTissueInput).Any(detectorInput => ((dynamic)detectorInput).FinalTissueRegionIndex == 0)) - { - return new ValidationResult( false, - "Transmittance detectors with MultiLayerTissues cannot detect in tissue region 0", - "Change FinalTissueRegionIndex to be index of air below tissue (index >= 2)"); + "BoundingCylinderTissueInput needs associated detector ATotalBoundingVolume to be defined", + "Add ATotalBoundingVolumeDetectorInput to detector inputs"); } return new ValidationResult( true, "Input options or tissue/detector combinations are valid", ""); - } + /// /// Method checks SimulationInput against current in-capabilities of the code. /// From ecc056f0e35a8ed0b1f069f8f36dce96d1cef12d Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 22 Aug 2024 16:02:06 -0700 Subject: [PATCH 08/13] Reduced complexity some more. Gave in and modified floating point check. Replaced Count() with Length and reduced long lines. --- .../SimulationInputValidation.cs | 86 +++++++++++-------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index c6b173b21..2c3d9b2c9 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -193,37 +193,12 @@ private static ValidationResult ValidateTissueCombinedWithDetectors(SimulationIn // check that if single ellipsoid tissue specified and (r,z) detector specified, // that (1) ellipsoid is centered at x=0, y=0, (2) ellipsoid is cylindrically symmetric (dx=dy) case SingleEllipsoidTissueInput tissueWithEllipsoid: - { - var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; - foreach (var detectorInput in input.DetectorInputs) - { - switch (detectorInput.TallyDetails.IsCylindricalTally) - { - case true when - ellipsoid.Center.X != 0.0 || ellipsoid.Center.Y != 0.0: - Console.WriteLine("Warning: off center ellipsoid in tissue with cylindrical detector defined: user discretion advised"); - return new ValidationResult( - true, - "Warning: off center ellipsoid in tissue with cylindrical detector defined", - "User discretion advised: change ellipsoid center to (0,0) or specify non-cylindrical type tally"); - case true when Math.Abs(ellipsoid.Dx - ellipsoid.Dy) > 1e-6: - Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); - return new ValidationResult( - true, - "Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined", - "User discretion advised: change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); - } - - if (detectorInput.TallyType != TallyType.ROfFx) continue; - Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); - return new ValidationResult( - true, - "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", - "User discretion advised"); - } - - break; - } + { + var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; + var result = ValidateSingleEllipsoidTissueCombinedWithDetectors(input, ellipsoid); + if (!result.IsValid) return result; + break; + } // check that if single voxel or single infinite cylinder tissue specified, // cannot specify (r,z) detector case SingleVoxelTissueInput: @@ -263,6 +238,43 @@ private static ValidationResult ValidateTissueCombinedWithDetectors(SimulationIn ""); } + private static ValidationResult ValidateSingleEllipsoidTissueCombinedWithDetectors( + SimulationInput input, EllipsoidTissueRegion ellipsoid) + { + foreach (var detectorInput in input.DetectorInputs) + { + switch (detectorInput.TallyDetails.IsCylindricalTally) + { + // check if ellipsoid off center, then not cylindrically symmetric, continue with warning + case true when + Math.Abs(ellipsoid.Center.X) > 1e-6 || Math.Abs(ellipsoid.Center.Y) > 1e-6: + Console.WriteLine("Warning: off center ellipsoid in tissue with cylindrical detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: off center ellipsoid in tissue with cylindrical detector defined", + "User discretion advised: change ellipsoid center to (0,0) or specify non-cylindrical type tally"); + // check if Dx != Dy, then not cylindrically symmetric, continue with warning + case true when Math.Abs(ellipsoid.Dx - ellipsoid.Dy) > 1e-6: + Console.WriteLine("Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined: user discretion advised"); + return new ValidationResult( + true, + "Warning: ellipsoid with Dx != Dy in tissue with cylindrical detector defined", + "User discretion advised: change ellipsoid.Dx to be = to Dy or specify non-cylindrical type tally"); + } + + // theory assumes homogeneous tissue for R(fx), however users can continue with warning + if (detectorInput.TallyType != TallyType.ROfFx) continue; + Console.WriteLine("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised"); + return new ValidationResult( + true, + "Warning: R(fx) theory assumes a homogeneous or layered tissue geometry", + "User discretion advised"); + } + return new ValidationResult( + true, + "Input options or tissue/detector combinations are valid", + ""); + } /// /// Method checks SimulationInput against current in-capabilities of the code. @@ -311,19 +323,23 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in // check that number in blood volume list matches number of tissue subregions if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfRhoAndSubregionHist")) { - return ReflectedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count()); + return ReflectedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); } if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfXAndYAndSubregionHist")) { - return ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count()); + return ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); } if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfRhoAndSubregionHist")) { - return TransmittedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count()); + return TransmittedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); } if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfXAndYAndSubregionHist")) { - return TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count()); + return TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); } if (detectorInput.TallyType.Contains("SurfaceFiber")) { From 08daa7adffb27e56789c814e972cb2cc395c8197 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 22 Aug 2024 16:30:44 -0700 Subject: [PATCH 09/13] Reduced complexity in another place. --- .../SimulationInputValidation.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index 2c3d9b2c9..c258a9c04 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -33,10 +33,8 @@ public static ValidationResult ValidateInput(SimulationInput input) foreach (var validation in validations) { var tempResult = validation(input); - if (!tempResult.IsValid) - { - return tempResult; - } + if (!tempResult.IsValid) return tempResult; + } return new ValidationResult( true, "Simulation input is valid"); @@ -158,8 +156,8 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput } // check combination of tissue definition with detector definition - var result = ValidateTissueCombinedWithDetectors(input); - if (!result.IsValid) return result; + var tempResult = ValidateTissueCombinedWithDetectors(input); + if (!tempResult.IsValid) return tempResult; // check combination of source definition with detector definition if (input.SourceInput is DirectionalPointSourceInput source && @@ -195,8 +193,8 @@ private static ValidationResult ValidateTissueCombinedWithDetectors(SimulationIn case SingleEllipsoidTissueInput tissueWithEllipsoid: { var ellipsoid = (EllipsoidTissueRegion)tissueWithEllipsoid.EllipsoidRegion; - var result = ValidateSingleEllipsoidTissueCombinedWithDetectors(input, ellipsoid); - if (!result.IsValid) return result; + var tempResult = ValidateSingleEllipsoidTissueCombinedWithDetectors(input, ellipsoid); + if (!tempResult.IsValid) return tempResult; break; } // check that if single voxel or single infinite cylinder tissue specified, @@ -308,7 +306,17 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in default: throw new ArgumentOutOfRangeException(typeof(AbsorptionWeightingType).ToString()); } + // check current detector capabilities + var tempResult = ValidateCurrentDetectorIncapabilities(input); + if (!tempResult.IsValid) return tempResult; + + return new ValidationResult( + true, + "Detector definitions are consistent with current capabilities"); + } + private static ValidationResult ValidateCurrentDetectorIncapabilities(SimulationInput input) + { foreach (var detectorInput in input.DetectorInputs) { // can only run dMC detectors with 1 perturbed region for the present @@ -355,10 +363,11 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in { return RecessedDetectorInputValidation.ValidateInput(detectorInput); } - } + } return new ValidationResult( true, "Detector definitions are consistent with current capabilities"); + } } } From 16412e2d68161fb95ad28f1b448db54c87b70c56 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 22 Aug 2024 16:57:17 -0700 Subject: [PATCH 10/13] Reduced complexity some more. And renamed Incapabilities to Capabilities in method names because not just incapabiities being checked, consistencies being checked too. --- .../SimulationInputValidation.cs | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index c258a9c04..72f7658cc 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -2,6 +2,7 @@ using System.Linq; using Vts.Common; using Vts.MonteCarlo.DataStructuresValidation; +using Vts.MonteCarlo.Detectors; using Vts.MonteCarlo.Sources; using Vts.MonteCarlo.Tissues; @@ -27,7 +28,7 @@ public static ValidationResult ValidateInput(SimulationInput input) si => ValidateTissueInput(si.TissueInput), ValidateDetectorInput, ValidateCombinedInputParameters, - ValidateCurrentIncapabilities + ValidateCurrentCapabilities }; foreach (var validation in validations) @@ -275,11 +276,11 @@ private static ValidationResult ValidateSingleEllipsoidTissueCombinedWithDetecto } /// - /// Method checks SimulationInput against current in-capabilities of the code. + /// Method checks SimulationInput against current capabilities of the code. /// /// SimulationInput /// An instance of the ValidationResult class - private static ValidationResult ValidateCurrentIncapabilities(SimulationInput input) + private static ValidationResult ValidateCurrentCapabilities(SimulationInput input) { switch (input.Options.AbsorptionWeightingType) { @@ -307,7 +308,7 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in throw new ArgumentOutOfRangeException(typeof(AbsorptionWeightingType).ToString()); } // check current detector capabilities - var tempResult = ValidateCurrentDetectorIncapabilities(input); + var tempResult = ValidateCurrentDetectorCapabilities(input); if (!tempResult.IsValid) return tempResult; return new ValidationResult( @@ -315,7 +316,8 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in "Detector definitions are consistent with current capabilities"); } - private static ValidationResult ValidateCurrentDetectorIncapabilities(SimulationInput input) + private static ValidationResult ValidateCurrentDetectorCapabilities( + SimulationInput input) { foreach (var detectorInput in input.DetectorInputs) { @@ -328,27 +330,12 @@ private static ValidationResult ValidateCurrentDetectorIncapabilities(Simulation { return dMCdROfRhodMusDetectorInputValidation.ValidateInput(detectorInput); } + // check that number in blood volume list matches number of tissue subregions - if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfRhoAndSubregionHist")) - { - return ReflectedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( - detectorInput, input.TissueInput.Regions.Length); - } - if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfXAndYAndSubregionHist")) - { - return ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( - detectorInput, input.TissueInput.Regions.Length); - } - if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfRhoAndSubregionHist")) - { - return TransmittedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( - detectorInput, input.TissueInput.Regions.Length); - } - if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfXAndYAndSubregionHist")) - { - return TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( - detectorInput, input.TissueInput.Regions.Length); - } + var tempResult = ValidateBloodVolumeDetectorConsistencies(input, detectorInput); + if (!tempResult.IsValid) return tempResult; + + // check fiber consistencies if (detectorInput.TallyType.Contains("SurfaceFiber")) { return SurfaceFiberDetectorInputValidation.ValidateInput(detectorInput); @@ -367,6 +354,34 @@ private static ValidationResult ValidateCurrentDetectorIncapabilities(Simulation return new ValidationResult( true, "Detector definitions are consistent with current capabilities"); + } + + private static ValidationResult ValidateBloodVolumeDetectorConsistencies( + SimulationInput input, IDetectorInput detectorInput) + { + if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfRhoAndSubregionHist")) + { + return ReflectedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); + } + if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfXAndYAndSubregionHist")) + { + return ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); + } + if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfRhoAndSubregionHist")) + { + return TransmittedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); + } + if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfXAndYAndSubregionHist")) + { + return TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput( + detectorInput, input.TissueInput.Regions.Length); + } + return new ValidationResult( + true, + "Detector definitions are consistent with current capabilities"); } } From b084325b12905a1b6eac402a89ff04680116fda3 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Thu, 22 Aug 2024 18:36:29 -0700 Subject: [PATCH 11/13] Added unit test for existing code that wasn't covered before. --- .../SimulationInputValidationTests.cs | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index cb0ff09c2..60746b4f0 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -3,7 +3,6 @@ using System.IO; using NUnit.Framework; using Vts.Common; -using Vts.Modeling.ForwardSolvers; using Vts.MonteCarlo; using Vts.MonteCarlo.Detectors; using Vts.MonteCarlo.Sources; @@ -263,5 +262,143 @@ public void Validate_tissue_optical_properties_are_non_negative() var result = SimulationInputValidation.ValidateInput(input); Assert.IsFalse(result.IsValid); } + + /// + /// Test to verify blood volume detectors + /// + [Test] + public void Validate_blood_volume_detectors() + { + // generate multilayer tissue + var input = new SimulationInput + { + TissueInput = 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, 1.0), + new OpticalProperties(0.01, 1.0, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(1.0, 10.0), + new OpticalProperties(0.01, 1.0, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(10.0, double.PositiveInfinity), + new OpticalProperties(0.0, 1e-10, 1.0, 1.0)) + } + ), + DetectorInputs = new List + { + new ReflectedDynamicMTOfRhoAndSubregionHistDetectorInput + { + Rho=new DoubleRange(0.0, 10.0, 21), // rho bins MAKE SURE AGREES with ROfRho rho specification for unit test below + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0}, + TallySecondMoment = true + }, + new TransmittedDynamicMTOfRhoAndSubregionHistDetectorInput + { + Rho=new DoubleRange(0.0, 10.0, 21), // rho bins MAKE SURE AGREES with TOfRho rho specification for unit test below + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0 }, + TallySecondMoment = true + }, + new ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInput + { + X = new DoubleRange(-10.0, 10.0, 21), + Y = new DoubleRange(-10.0, 10.0, 21), + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0}, + TallySecondMoment = true + }, + new TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInput + { + X = new DoubleRange(-10.0, 10.0, 21), + Y = new DoubleRange(-10.0, 10.0, 21), + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0}, + TallySecondMoment = true + } + } + }; + // verify when blood volume fraction size is same as tissue + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsTrue(result.IsValid); + // create case when validation fails + input = new SimulationInput + { + TissueInput = 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, 1.0), + new OpticalProperties(0.01, 1.0, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(1.0, 10.0), + new OpticalProperties(0.01, 1.0, 0.8, 1.4)), + new LayerTissueRegion( + new DoubleRange(10.0, double.PositiveInfinity), + new OpticalProperties(0.0, 1e-10, 1.0, 1.0)) + } + ), + DetectorInputs = new List + { + new ReflectedDynamicMTOfRhoAndSubregionHistDetectorInput + { + Rho=new DoubleRange(0.0, 10.0, 21), // rho bins MAKE SURE AGREES with ROfRho rho specification for unit test below + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0}, // FAILURE + TallySecondMoment = true + }, + new TransmittedDynamicMTOfRhoAndSubregionHistDetectorInput + { + Rho=new DoubleRange(0.0, 10.0, 21), // rho bins MAKE SURE AGREES with TOfRho rho specification for unit test below + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0 }, + TallySecondMoment = true + }, + new ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInput + { + X = new DoubleRange(-10.0, 10.0, 21), + Y = new DoubleRange(-10.0, 10.0, 21), + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0}, + TallySecondMoment = true + }, + new TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInput + { + X = new DoubleRange(-10.0, 10.0, 21), + Y = new DoubleRange(-10.0, 10.0, 21), + Z = new DoubleRange(0.0, 10.0, 21), + MTBins=new DoubleRange(0.0, 500.0, 51), // MT bins + FractionalMTBins = new DoubleRange(0.0, 1.0, 11), + BloodVolumeFraction = new List { 0, 0.5, 0.5, 0}, + TallySecondMoment = true + } + } + }; + // verify when blood volume fraction size is same as tissue + result = SimulationInputValidation.ValidateInput(input); + Assert.IsFalse(result.IsValid); + } } } From db40c980d8f7051192f7263bbb55ca55772e5acf Mon Sep 17 00:00:00 2001 From: hayakawa Date: Fri, 23 Aug 2024 11:48:09 -0700 Subject: [PATCH 12/13] Added more unit tests --- .../SimulationInputValidationTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index 60746b4f0..3a8138736 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -82,6 +82,21 @@ public void Validate_duplicate_detector_name_is_invalid() Assert.IsFalse(result.IsValid); } + /// + /// Test to verify that input with Analog specified does not also + /// specify Russian Roulette + /// + [Test] + public void Validate_Analog_with_Russian_Roulette_is_invalid() + { + // generate input with Analog + var input = new SimulationInput(); + input.Options.AbsorptionWeightingType = AbsorptionWeightingType.Analog; + input.Options.RussianRouletteWeightThreshold = 0.1; + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsFalse(result.IsValid); + } + /// /// Test to verify input with cylindrical detector and off axis ellipsoid in tissue outputs warning /// but continues as valid input @@ -181,6 +196,27 @@ public void Validate_angled_source_and_cylindrical_detectors_are_not_defined_tog Assert.That(output.ToString(), Is.EqualTo("Warning: Angled source and cylindrical coordinate detector defined: user discretion advised\r\n")); } + /// + /// Test to verify that input with transmittance detector with final tissue region=0 + /// is invalid + /// + [Test] + public void Validate_transmittance_detector_with_final_tissue_region_equal_0_is_invalid() + { + // generate input with transmittance + var input = new SimulationInput + { + DetectorInputs = new List { + new TOfRhoDetectorInput() + { + FinalTissueRegionIndex = 0 + } + } + }; + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsFalse(result.IsValid); + } + /// /// Test to verify input with ellipsoid in tissue and R(fx) detector outputs warning /// but continues as valid input From 648b186537cc445049192c293fb8471d97cb57a8 Mon Sep 17 00:00:00 2001 From: hayakawa Date: Fri, 23 Aug 2024 12:20:37 -0700 Subject: [PATCH 13/13] Removed unused usings. Wrote more unit tests. --- .../SimulationInputValidationTests.cs | 36 +++++++++++++++++-- .../SimulationInputValidation.cs | 1 - 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs index 3a8138736..2c4086110 100644 --- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs +++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs @@ -13,6 +13,22 @@ namespace Vts.Test.MonteCarlo.DataStructuresValidation [TestFixture] public class SimulationInputValidationTests { + /// + /// Test to verify input with source initial tissue region index outside number + /// of tissue regions is invalid + /// + [Test] + public void Validate_source_with_initial_tissue_region_index_outside_tissue_range_invalid() + { + // generate input without source initial tissue region in error + var input = new SimulationInput() // default constructor has empty list of databases + { + SourceInput = new CustomPointSourceInput { InitialTissueRegionIndex = 100 } + }; + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsFalse(result.IsValid); + } + /// /// Test to verify input with no detectors nor database specified is invalid /// @@ -174,6 +190,23 @@ public void Validate_voxel_tissue_and_cylindrical_detectors_issues_warning() Assert.That(output.ToString(), Is.EqualTo("Warning: voxel in tissue with cylindrical detector defined: user discretion advised\r\n")); } + /// + /// Test to verify input with bounded tissue without ATotalBoundingVolume detector + /// is invalid + /// + [Test] + public void Validate_bounded_tissue_without_ATotalBoundingVolume_detector_invalid() + { + // generate input with bounding cylinder tissue without needed detector + var input = new SimulationInput + { + TissueInput = new BoundingCylinderTissueInput(), + DetectorInputs = new List { new ROfRhoDetectorInput() } + }; + var result = SimulationInputValidation.ValidateInput(input); + Assert.IsFalse(result.IsValid); + } + /// /// Test to verify input with angled source and cylindrical detectors outputs warning /// but continues as valid input @@ -207,7 +240,7 @@ public void Validate_transmittance_detector_with_final_tissue_region_equal_0_is_ var input = new SimulationInput { DetectorInputs = new List { - new TOfRhoDetectorInput() + new TOfRhoDetectorInput { FinalTissueRegionIndex = 0 } @@ -269,7 +302,6 @@ public void Validate_voxel_tissue_and_ROfFx_detectors_defined_together_issues_wa var result = SimulationInputValidation.ValidateInput(input); Assert.IsTrue(result.IsValid); // only warning Assert.That(output.ToString(), Is.EqualTo("Warning: R(fx) theory assumes a homogeneous or layered tissue geometry: user discretion advised\r\n")); - } /// diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs index 72f7658cc..1a0e9803e 100644 --- a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs +++ b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs @@ -2,7 +2,6 @@ using System.Linq; using Vts.Common; using Vts.MonteCarlo.DataStructuresValidation; -using Vts.MonteCarlo.Detectors; using Vts.MonteCarlo.Sources; using Vts.MonteCarlo.Tissues;