public Radial(Radial radial) : this()
 {
     IsCalculated = radial.IsCalculated;
     CalculationStarted = new DateTime(radial.CalculationStarted.Ticks);
     CalculationCompleted = new DateTime(radial.CalculationCompleted.Ticks);
     Bearing = radial.Bearing;
     Length = radial.Length;
 }
        /// <summary>
        /// Update a TransmissionLoss to reflect the modes that it currently contains.
        /// If there are no modes associated with the TransmissionLoss, delete it.
        /// If the max number of radials among the associated modes has changed, 
        /// or if the max calculation radius of the associated modes is larger than the existing radials, 
        /// recalculate the TransmissionLoss
        /// </summary>
        /// <param name="transmissionLoss"></param>
        static void Update(this TransmissionLoss transmissionLoss)
        {
            if (transmissionLoss.Modes == null || transmissionLoss.Modes.Count == 0)
            {
                // If there are no modes, the TransmissionLoss is not needed
                transmissionLoss.Delete();
                return;
            }

            // Get the maximum radial count called for by the associated modes in the TransmissionLoss
            var modeRadialCount = transmissionLoss.Modes.Max(m => m.RadialCount);
            // Get the maximum propagation radius called for by the associated modes in the TransmissionLoss
            var modeMaxRadius = transmissionLoss.Modes.Max(m => m.MaxPropagationRadius);

            // Check to see if none of the modes have defined a radial count.  If not, use the default values
            modeRadialCount = modeRadialCount > 0 ? modeRadialCount : modeMaxRadius <= 10000 ? 8 : 16;

            // If there are any radials already defined, and if the count of those radials is the same as the max radial count
            // and the length of all radials is greater than or equal to the required max radius, this TransmissionLoss is valid
            if (transmissionLoss.Radials != null && transmissionLoss.Radials.Count == modeRadialCount && transmissionLoss.Radials.All(radial => radial.Length >= modeMaxRadius))
            {
                // Redraw the map layers to display the new TransmissionLoss as it should be
                Globals.Dispatcher.InvokeIfRequired(transmissionLoss.UpdateMapLayers);
                return;
            }

            // If the list of radials is null, create a list
            if (transmissionLoss.Radials == null) transmissionLoss.Radials = new ObservableList<Radial>();

            // Create a list of required bearings so we can delete any existing radials that are no longer required
            var requiredBearings = new List<double>();
            // Loop through all the radials we will need to satisfy the current mode requirements
            for (var radialIndex = 0; radialIndex < modeRadialCount; radialIndex++)
            {
                // Calculate the bearing of the next radial we will need to have
                var radialBearing = (360.0 / modeRadialCount) * radialIndex;
                // Add the current bearing to the list of required bearings
                requiredBearings.Add(radialBearing);
                // Find the radial in the current set that matches the bearing, if there is one
                var currentRadial = transmissionLoss.Radials.FirstOrDefault(r => Math.Abs(r.Bearing - radialBearing) < double.Epsilon);
                // If there is a radial that matches the bearing AND that radial is too short to meet the current length requirements
                if (currentRadial != null && currentRadial.Length < modeMaxRadius)
                {
                    //Debug.WriteLine(string.Format("Deleting too-short radial [{0}] from mode [{1}] at {2}", currentRadial, transmissionLoss.Modes.First(), (Geo)transmissionLoss.AnalysisPoint.Geo));
                    // Delete the radial
                    currentRadial.Delete();
                    // Pretend that we didn't find a matching radial, so we will create one below
                    currentRadial = null;
                }
                // If we found a radial that matches the bearing and length requirements, check the next one
                if (currentRadial != null) continue;

                // We didn't find a radial that matches the bearing and length requirements, so create one now
                var radial = new Radial
                {
                    TransmissionLoss = transmissionLoss,
                    CalculationCompleted = DateTime.MaxValue,
                    CalculationStarted = DateTime.MaxValue,
                    Bearing = radialBearing,
                    Length = modeMaxRadius,
                    IsCalculated = false,
                };
                //Debug.WriteLine(string.Format("Adding new radial [{0}] to mode [{1}] at {2}", radial, transmissionLoss.Modes.First(), (Geo)transmissionLoss.AnalysisPoint.Geo));
                // Add the new Radial to the TransmissionLoss
                //transmissionLoss.Radials.Add(radial);
                // Queue the new Radial for calculation
                Globals.TransmissionLossCalculatorService.Add(radial);
            }
            // Delete any radials in the current TransmissionLoss that are no longer required
            //  Debug.WriteLine(string.Format("Deleting now-unused radial [{0}] from mode [{1}] at {2}", r.ToString(), transmissionLoss.Modes.First(), (Geo)transmissionLoss.AnalysisPoint.Geo));
            transmissionLoss.Radials.FindAll(r => !requiredBearings.Contains(r.Bearing)).ForEach(r => r.Delete());
            // Redraw the map layers to display the new TransmissionLoss as it should be
            Globals.Dispatcher.InvokeIfRequired(transmissionLoss.UpdateMapLayers);
        }
 void Copy(Scenario scenario)
 {
     Name = scenario.Name;
     Comments = scenario.Comments;
     ShowAllAnalysisPoints = scenario.ShowAllAnalysisPoints;
     ShowAllPerimeters = scenario.ShowAllPerimeters;
     ShowAllSpecies = scenario.ShowAllSpecies;
     StartTime = new TimeSpan(scenario.StartTime.Ticks);
     Duration = new TimeSpan(scenario.Duration.Ticks);
     TimePeriod = (TimePeriod)scenario.TimePeriod;
     Location = scenario.Location;
     Wind = scenario.Wind;
     SoundSpeed = scenario.SoundSpeed;
     Sediment = scenario.Sediment;
     Bathymetry = scenario.Bathymetry;
     // Here we map the old perimeter to the new perimeter so that the copied platform gets the proper perimeter
     var perimeterMap = new Dictionary<Guid, Guid>();
     foreach (var perimeter in scenario.Perimeters)
     {
         var newPerimeter = new Perimeter(perimeter) { Scenario = this };
         perimeterMap.Add(perimeter.Guid, newPerimeter.Guid);
         Perimeters.Add(newPerimeter);
     }
     var modeMap = new Dictionary<Guid, Guid>();
     var allModes = new List<Mode>();
     foreach (var platform in scenario.Platforms)
     {
         var newPlatform = new Platform(platform) { Scenario = this };
         // Make sure the new perimeter gets the proper copied perimeter from the original scenario
         if (platform.Perimeter != null) newPlatform.Perimeter = Perimeters.Find(p => p.Guid == perimeterMap[platform.Perimeter.Guid]);
         Platforms.Add(newPlatform);
         foreach (var source in platform.Sources)
         {
             var newSource = new Source(source) { Platform = newPlatform };
             newPlatform.Sources.Add(newSource);
             foreach (var mode in source.Modes)
             {
                 var newMode = new Mode(mode) { Source = newSource };
                 modeMap.Add(mode.Guid, newMode.Guid);
                 newSource.Modes.Add(newMode);
                 allModes.Add(newMode);
             }
         }
     }
     foreach (var analysisPoint in scenario.AnalysisPoints)
     {
         var newAnalysisPoint = new AnalysisPoint(analysisPoint) { Scenario = this };
         AnalysisPoints.Add(newAnalysisPoint);
         foreach (var transmissionLoss in analysisPoint.TransmissionLosses)
         {
             var newTransmissionLoss = new TransmissionLoss { AnalysisPoint = newAnalysisPoint, LayerSettings = new LayerSettings(transmissionLoss.LayerSettings) };
             foreach (var mode in transmissionLoss.Modes) 
                 newTransmissionLoss.Modes.Add(allModes.Find(m => m.Guid == modeMap[mode.Guid]));
             newAnalysisPoint.TransmissionLosses.Add(newTransmissionLoss);
             foreach (var radial in transmissionLoss.Radials)
             {
                 var newRadial = new Radial(radial) { TransmissionLoss = newTransmissionLoss };
                 newTransmissionLoss.Radials.Add(newRadial);
                 newRadial.CopyFiles(radial);
             }
         }
     }
     foreach (var species in scenario.ScenarioSpecies)
     {
         var newSpecies = new ScenarioSpecies(species) { Scenario = this };
         ScenarioSpecies.Add(newSpecies);
         newSpecies.CopyFiles(species);
     }
 }
 public abstract void CalculateTransmissionLoss(Platform platform,
                                       Mode mode,
                                       Radial radial,
                                       BottomProfile bottomProfile,
                                       SedimentType sedimentType,
                                       double windSpeed,
                                       IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial);
        protected void CalculateTransmissionLossInternal(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial, bool createArrivalsFile)
        {
            var depthCellCount = (int)Math.Ceiling(bottomProfile.MaxDepth / DepthCellSize);
            var rangeCellCount = (int)Math.Ceiling(mode.MaxPropagationRadius / RangeCellSize);
            var startProfile = soundSpeedProfilesAlongRadial[0].Item2;
            var sourceDepth = platform.Depth;
            var frequency = (float)Math.Sqrt(mode.HighFrequency * mode.LowFrequency);
            if (mode.Depth.HasValue) sourceDepth += mode.Depth.Value;
            var maxCalculationDepthMeters = bottomProfile.MaxDepth * 1.01;
            var directoryPath = Path.GetDirectoryName(radial.BasePath);
            if (directoryPath == null) throw new NullReferenceException("radial.BasePath does not point to a valid directory");
            if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);

            using (var envFile = new StreamWriter(radial.BasePath + ".env", false))
            {
                envFile.WriteLine("Scenario: '{0}' Mode: '{2}' Analysis point: {1} Bearing: {3}",
                                  radial.TransmissionLoss.AnalysisPoint.Scenario.Name,
                                  radial.TransmissionLoss.AnalysisPoint.Geo,
                                  radial.TransmissionLoss.Modes[0].ModeName,
                                  radial.Bearing);
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", frequency));
                envFile.WriteLine("1"); // was NMEDIA in gui_genbellhopenv.m
                envFile.WriteLine(UseSurfaceReflection ? "'QFLT'" : "'QVLT'");

                //if (depthCellCount < 5) throw new BathymetryTooShallowException("Error: Maximum depth of transect (" + maxCalculationDepthMeters + " meters) less than minimum required for transmission loss calculations.\nPlease choose a different location for this transect.");

                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0, 0.0, {0}", startProfile.Data[startProfile.Data.Count - 1].Depth));
                foreach (var soundSpeedSample in startProfile.Data)
                    envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} 0.0 1.0 0.0 0.0", soundSpeedSample.Depth, soundSpeedSample.SoundSpeed));

                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "'A*' 0.0")); // A = Acoustic halfspace, * = read bathymetry file 'BTYFIL', 0.0 = bottom roughness (currently ignored)
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3} {4} {5} /", maxCalculationDepthMeters, sedimentType.CompressionWaveSpeed, sedimentType.ShearWaveSpeed, sedimentType.Density, sedimentType.LossParameter, 0));
                // Source and Receiver Depths and Ranges
                envFile.WriteLine("1"); // Number of Source Depths
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} /", sourceDepth)); // source depth
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", depthCellCount)); // Number of Receiver Depths
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} /", maxCalculationDepthMeters));
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", rangeCellCount)); // Number of receiver ranges
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} /", mode.MaxPropagationRadius / 1000.0));

                envFile.WriteLine(createArrivalsFile ? "'a'" : "'I'");
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", RayCount)); // Number of beams
                var verticalHalfAngle = mode.VerticalBeamWidth / 2;
                var angle1 = mode.DepressionElevationAngle - verticalHalfAngle;
                var angle2 = mode.DepressionElevationAngle + verticalHalfAngle;
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} /", angle1, angle2)); // Beam fan half-angles (negative angles are toward the surface
                envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} {1}", maxCalculationDepthMeters, (mode.MaxPropagationRadius / 1000.0) * 1.01)); // step zbox(meters) rbox(km)
            }
            using (var sspFile = new StreamWriter(radial.BasePath + ".ssp", false))
            {
                if (soundSpeedProfilesAlongRadial.Count == 1) soundSpeedProfilesAlongRadial.Add(Tuple.Create(Geo.RadiansToKilometers(radial.Segment.LengthRadians), new SoundSpeedProfile(soundSpeedProfilesAlongRadial[0].Item2)));
                sspFile.WriteLine("{0}", soundSpeedProfilesAlongRadial.Count);
                foreach (var rangeProfileTuple in soundSpeedProfilesAlongRadial) sspFile.Write(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}", rangeProfileTuple.Item1));
                sspFile.WriteLine();
                //sspFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}{1,-10:0.###}{2,-10:0.###}", 0.0, bottomProfile.Profile[bottomProfile.Profile.Count / 2].Range, bottomProfile.Profile[bottomProfile.Profile.Count - 1].Range));
                for (var depthIndex = 0; depthIndex < startProfile.Data.Count; depthIndex++)
                {
                    foreach (var rangeProfileTuple in soundSpeedProfilesAlongRadial) sspFile.Write(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}", rangeProfileTuple.Item2.Data[depthIndex].SoundSpeed));
                    sspFile.WriteLine();
                }
                //sspFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}{1,-10:0.###}{2,-10:0.###}", startProfile.Data[depthIndex].SoundSpeed, middleProfile.Data[depthIndex].SoundSpeed, endProfile.Data[depthIndex].SoundSpeed));
            }
            using (var trcFile = new StreamWriter(radial.BasePath + ".trc", false))
            {
                var topReflectionCoefficients = GenerateReflectionCoefficients(windSpeed, frequency);
                trcFile.WriteLine(topReflectionCoefficients.GetLength(0));
                for (var rowIndex = 0; rowIndex < topReflectionCoefficients.GetLength(0); rowIndex++)
                    trcFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} ", topReflectionCoefficients[rowIndex, 0], topReflectionCoefficients[rowIndex, 1], topReflectionCoefficients[rowIndex, 2]));
            }
            using (var writer = new StreamWriter(radial.BasePath + ".bty")) writer.Write(bottomProfile.ToBellhopString());

            // Now that we've got the files ready to go, we can launch bellhop to do the actual calculations
            var bellhopProcess = new Process
            {
                StartInfo = new ProcessStartInfo(Path.Combine(AssemblyLocation, "bellhop.exe"), radial.Filename)
                {
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardInput = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    WorkingDirectory = directoryPath
                }
            };
            if (radial.IsDeleted) throw new RadialDeletedByUserException();
            bellhopProcess.Start();
            try
            {
                bellhopProcess.PriorityClass = ProcessPriorityClass.Idle;
            }
            catch (InvalidOperationException) {}
            //bellhopProcess.BeginOutputReadLine();
            while (!bellhopProcess.HasExited)
            {
                if (radial.IsDeleted)
                {
                    bellhopProcess.Kill();
                    throw new RadialDeletedByUserException();
                }
                Thread.Sleep(20);
            }
            if (bellhopProcess.ExitCode == 0) return;
            var bellhopOutput = bellhopProcess.StandardOutput.ReadToEnd();
            var bellhopError = bellhopProcess.StandardError.ReadToEnd();
            Debug.WriteLine("Bellhop process for radial {0} exited with error code {1:X}", radial.BasePath, bellhopProcess.ExitCode);
            Debug.WriteLine("Bellhop stdout: " + bellhopOutput);
            Debug.WriteLine("Bellhop stderr: " + bellhopError);
            radial.CleanupFiles();
        }
 public override void CalculateTransmissionLoss(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial)
 {
     CalculateTransmissionLossInternal(platform, mode, radial, bottomProfile, sedimentType, windSpeed, soundSpeedProfilesAlongRadial, false);
 }
 public void CopyFiles(Radial radial)
 {
     var files = Directory.GetFiles(Path.GetDirectoryName(radial.BasePath), Path.GetFileNameWithoutExtension(radial.BasePath) + ".*");
     foreach (var file in files) File.Copy(file, BasePath + Path.GetExtension(file));
 }