/// <summary>
        /// Performs a wave conditions calculation based on the supplied <see cref="WaveConditionsInput"/>
        /// and returns the output. Error and status information is logged during the execution
        /// of the operation.
        /// </summary>
        /// <param name="waveConditionsInput">The <see cref="WaveConditionsInput"/> that holds all the information
        /// required to perform the calculation.</param>
        /// <param name="assessmentLevel">The assessment level to use for determining water levels.</param>
        /// <param name="a">The 'a' factor decided on failure mechanism level.</param>
        /// <param name="b">The 'b' factor decided on failure mechanism level.</param>
        /// <param name="c">The 'c' factor decided on failure mechanism level.</param>
        /// <param name="targetProbability">The target probability to use.</param>
        /// <param name="hydraulicBoundaryDatabase">The hydraulic boundary database to perform the calculations with.</param>
        /// <returns>An <see cref="IEnumerable{T}"/> of <see cref="WaveConditionsOutput"/>.</returns>
        /// <remarks>Preprocessing is disabled when the preprocessor directory equals <see cref="string.Empty"/>.</remarks>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="waveConditionsInput"/> or
        /// <paramref name="hydraulicBoundaryDatabase"/> 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="HydraRingCalculationException">Thrown when an error occurs during
        /// the calculations.</exception>
        protected IEnumerable <WaveConditionsOutput> CalculateWaveConditions(WaveConditionsInput waveConditionsInput,
                                                                             RoundedDouble assessmentLevel,
                                                                             RoundedDouble a,
                                                                             RoundedDouble b,
                                                                             RoundedDouble c,
                                                                             double targetProbability,
                                                                             HydraulicBoundaryDatabase hydraulicBoundaryDatabase)
        {
            if (waveConditionsInput == null)
            {
                throw new ArgumentNullException(nameof(waveConditionsInput));
            }

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

            var calculationsFailed = 0;
            var outputs            = new List <WaveConditionsOutput>();

            RoundedDouble[] waterLevels = waveConditionsInput.GetWaterLevels(assessmentLevel).ToArray();
            foreach (RoundedDouble waterLevel in waterLevels.TakeWhile(waterLevel => !Canceled))
            {
                try
                {
                    log.Info(string.Format(CultureInfo.CurrentCulture,
                                           Resources.WaveConditionsCalculationService_OnRun_Calculation_for_waterlevel_0_started,
                                           waterLevel));

                    NotifyProgress(waterLevel, currentStep++, TotalWaterLevelCalculations);

                    WaveConditionsOutput output = CalculateWaterLevel(waterLevel,
                                                                      a, b, c, targetProbability,
                                                                      waveConditionsInput,
                                                                      HydraulicBoundaryCalculationSettingsFactory.CreateSettings(hydraulicBoundaryDatabase));

                    if (output != null)
                    {
                        outputs.Add(output);
                    }
                    else
                    {
                        calculationsFailed++;
                        outputs.Add(WaveConditionsOutputFactory.CreateFailedOutput(waterLevel, targetProbability));
                    }
                }
                finally
                {
                    log.Info(string.Format(CultureInfo.CurrentCulture,
                                           Resources.WaveConditionsCalculationService_OnRun_Calculation_for_waterlevel_0_ended,
                                           waterLevel));
                }
            }

            if (calculationsFailed == waterLevels.Length)
            {
                string message = string.Format(CultureInfo.CurrentCulture,
                                               Resources.WaveConditionsCalculationService_CalculateWaterLevel_Error_in_wave_conditions_calculation_for_all_waterlevels);
                log.Error(message);
                throw new HydraRingCalculationException(message);
            }

            return(outputs);
        }