/// <summary>
        /// Performs a piping calculation based on the supplied <see cref="ProbabilisticPipingCalculation"/> and sets <see cref="ProbabilisticPipingCalculation.Output"/>
        /// if the calculation was successful. Error and status information is logged during the execution of the operation.
        /// </summary>
        /// <param name="calculation">The <see cref="ProbabilisticPipingCalculation"/> that holds all the information required to perform the calculation.</param>
        /// <param name="generalInput">The <see cref="GeneralPipingInput"/> to derive values from during the calculation.</param>
        /// <param name="calculationSettings">The <see cref="HydraulicBoundaryCalculationSettings"/> with the
        /// hydraulic boundary calculation settings.</param>
        /// <param name="sectionLength">The length of the section the calculation belongs to.</param>
        /// <remarks>Preprocessing is disabled when the preprocessor directory equals <see cref="string.Empty"/>.</remarks>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="calculation"/>, <paramref name="generalInput"/>
        /// or <paramref name="calculationSettings"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown when the hydraulic boundary database file path
        /// contains invalid characters.</exception>
        /// <exception cref="CriticalFileReadException">Thrown when:
        /// <list type="bullet">
        /// <item>No settings database file could be found at the location of the hydraulic boundary database file path
        /// with the same name.</item>
        /// <item>Unable to open settings database file.</item>
        /// <item>Unable to read required data from database file.</item>
        /// </list></exception>
        /// <exception cref="HydraRingFileParserException">Thrown when an error occurs during parsing of the Hydra-Ring output.</exception>
        /// <exception cref="HydraRingCalculationException">Thrown when an error occurs during the calculation.</exception>
        internal void Calculate(ProbabilisticPipingCalculation calculation, GeneralPipingInput generalInput,
                                HydraulicBoundaryCalculationSettings calculationSettings, double sectionLength)
        {
            if (calculation == null)
            {
                throw new ArgumentNullException(nameof(calculation));
            }

            if (generalInput == null)
            {
                throw new ArgumentNullException(nameof(generalInput));
            }

            if (calculationSettings == null)
            {
                throw new ArgumentNullException(nameof(calculationSettings));
            }

            string hydraulicBoundaryDatabaseFilePath = calculationSettings.HydraulicBoundaryDatabaseFilePath;
            bool   usePreprocessor = !string.IsNullOrEmpty(calculationSettings.PreprocessorDirectory);

            HydraRingCalculationSettings hydraRingCalculationSettings = HydraRingCalculationSettingsFactory.CreateSettings(calculationSettings);

            profileSpecificCalculator = HydraRingCalculatorFactory.Instance.CreatePipingCalculator(
                hydraRingCalculationSettings);
            sectionSpecificCalculator = HydraRingCalculatorFactory.Instance.CreatePipingCalculator(
                hydraRingCalculationSettings);

            CalculationServiceHelper.LogCalculationBegin();

            try
            {
                IPartialProbabilisticPipingOutput profileSpecificOutput = CalculateProfileSpecific(
                    calculation, generalInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor);

                if (canceled)
                {
                    return;
                }

                IPartialProbabilisticPipingOutput sectionSpecificOutput = CalculateSectionSpecific(
                    calculation, generalInput, sectionLength, hydraulicBoundaryDatabaseFilePath, usePreprocessor);

                if (canceled)
                {
                    return;
                }

                calculation.Output = new ProbabilisticPipingOutput(sectionSpecificOutput, profileSpecificOutput);
            }
            finally
            {
                CalculationServiceHelper.LogCalculationEnd();

                profileSpecificCalculator = null;
                sectionSpecificCalculator = null;
            }
        }
        public void CreatePipingCalculator_Always_ReturnsPipingCalculator()
        {
            // Setup
            HydraRingCalculationSettings settings = HydraRingCalculationSettingsTestFactory.CreateSettings();

            // Call
            IPipingCalculator calculator = HydraRingCalculatorFactory.Instance.CreatePipingCalculator(settings);

            // Assert
            Assert.IsInstanceOf <PipingCalculator>(calculator);
        }