diff --git a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs
index 46a25526..2c408611 100644
--- a/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs
+++ b/src/Vts.Test/MonteCarlo/DataStructuresValidation/SimulationInputValidationTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using NUnit.Framework;
using Vts.Common;
using Vts.MonteCarlo;
@@ -12,133 +13,305 @@ 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_null_detector_input_is_invalid_when_no_database_specified()
+ 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
+ ///
+ [Test]
+ 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);
}
+
+ ///
+ /// 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()
+ 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 {}
+ 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);
}
+
+ ///
+ /// 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()
+ 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 to verify that input with two detectors with the same Name is invalid
+ ///
[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 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
+ ///
[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()
+ 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, 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 issues warning
+ /// but continues as valid input
+ ///
[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()
+ 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() }
};
+ // 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 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 bounded tissue without ATotalBoundingVolume detector
+ /// is invalid
+ ///
[Test]
- public void validate_angled_source_and_cylindrical_detectors_are_not_defined_together()
+ 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
+ ///
+ [Test]
+ 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),
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: 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
+ ///
[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()
+ 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() }
};
+ // 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: 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 outputs 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()
+ 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() }
};
+ // 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: 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()
+ 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[]
@@ -157,5 +330,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);
+ }
}
}
diff --git a/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs b/src/Vts/MonteCarlo/DataStructuresValidation/SimulationInputValidation.cs
index 7ae39437..1a0e9803 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,
@@ -28,16 +27,14 @@ public static ValidationResult ValidateInput(SimulationInput input)
si => ValidateTissueInput(si.TissueInput),
ValidateDetectorInput,
ValidateCombinedInputParameters,
- ValidateCurrentIncapabilities
+ ValidateCurrentCapabilities
};
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");
@@ -116,8 +113,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
@@ -137,7 +134,12 @@ 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. 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
@@ -145,7 +147,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,
@@ -153,6 +155,37 @@ 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 tempResult = ValidateTissueCombinedWithDetectors(input);
+ if (!tempResult.IsValid) return tempResult;
+
+ // 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,
@@ -160,98 +193,93 @@ private static ValidationResult ValidateCombinedInputParameters(SimulationInput
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:
- 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,
- "R(fx) tallies assume a homogeneous or layered tissue geometry",
- "Change tissue type to be homogeneous or layered");
- }
- }
-
+ var tempResult = ValidateSingleEllipsoidTissueCombinedWithDetectors(input, ellipsoid);
+ if (!tempResult.IsValid) return tempResult;
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)
- {
- 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)
{
+ 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(
- false,
- "R(fx) tallies assume a homogeneous or layered tissue geometry",
- "Change tissue type to be homogeneous or layered");
+ 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))
- {
- return new ValidationResult(
false,
- "If source is angled, cannot define cylindrically symmetric detectors",
- "Change detector to Cartesian equivalent or define source to be normal");
+ "BoundingCylinderTissueInput needs associated detector ATotalBoundingVolume to be defined",
+ "Add ATotalBoundingVolumeDetectorInput to detector inputs");
}
- if (input.DetectorInputs.Where(detectorInput => detectorInput.TallyDetails.IsTransmittanceTally &&
- input.TissueInput is MultiLayerTissueInput).Any(detectorInput => ((dynamic)detectorInput).FinalTissueRegionIndex == 0))
+ return new ValidationResult(
+ true,
+ "Input options or tissue/detector combinations are valid",
+ "");
+ }
+
+ 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(
- false,
- "Transmittance detectors with MultiLayerTissues cannot detect in tissue region 0",
- "Change FinalTissueRegionIndex to be index of air below tissue (index >= 2)");
+ 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.
+ /// 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)
{
@@ -278,7 +306,18 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in
default:
throw new ArgumentOutOfRangeException(typeof(AbsorptionWeightingType).ToString());
}
+ // check current detector capabilities
+ var tempResult = ValidateCurrentDetectorCapabilities(input);
+ if (!tempResult.IsValid) return tempResult;
+ return new ValidationResult(
+ true,
+ "Detector definitions are consistent with current capabilities");
+ }
+
+ private static ValidationResult ValidateCurrentDetectorCapabilities(
+ SimulationInput input)
+ {
foreach (var detectorInput in input.DetectorInputs)
{
// can only run dMC detectors with 1 perturbed region for the present
@@ -290,23 +329,12 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in
{
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.Count());
- }
- if (detectorInput.TallyType.Contains("ReflectedDynamicMTOfXAndYAndSubregionHist"))
- {
- return ReflectedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count());
- }
- if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfRhoAndSubregionHist"))
- {
- return TransmittedDynamicMTOfRhoAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count());
- }
- if (detectorInput.TallyType.Contains("TransmittedDynamicMTOfXAndYAndSubregionHist"))
- {
- return TransmittedDynamicMTOfXAndYAndSubregionHistDetectorInputValidation.ValidateInput(detectorInput, input.TissueInput.Regions.Count());
- }
+ var tempResult = ValidateBloodVolumeDetectorConsistencies(input, detectorInput);
+ if (!tempResult.IsValid) return tempResult;
+
+ // check fiber consistencies
if (detectorInput.TallyType.Contains("SurfaceFiber"))
{
return SurfaceFiberDetectorInputValidation.ValidateInput(detectorInput);
@@ -321,10 +349,39 @@ private static ValidationResult ValidateCurrentIncapabilities(SimulationInput in
{
return RecessedDetectorInputValidation.ValidateInput(detectorInput);
}
- }
+ }
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");
+
+ }
}
}