public void Process(SimulationTimeStepRecord record)
 {
     var actors = Simulation.Actors;
     for (var i = 0; i < record.ActorPositionRecords.Count; i++)
     {
         foreach (var t in record.ActorPositionRecords[i].Exposures) Scatterplot.Expose(actors[i], t);
     }
 }
 public void Process(SimulationTimeStepRecord record)
 {
     var actors = Simulation.Actors;
     for (var i = 0; i < record.ActorPositionRecords.Count; i++)
     {
         foreach (var t in record.ActorPositionRecords[actors[i].ID].Exposures) ModeBinnedExposureDictionary.Expose(actors[i], t);
     }
 }
        void Run(TimeSpan timeStepSize, CancellationToken token)
        {
            Geo<float> firstAnimatPosition = null;
            Task<bool> processTask = null;
            var timeStepCount = (int)Math.Round(((TimeSpan)Scenario.Duration).TotalSeconds / timeStepSize.TotalSeconds);
            if (MovingAnimats) Initialize3MB();
            PercentProgress = new PercentProgress<Simulation>(this) { MinimumValue = 0, MaximumValue = timeStepCount - 1 };
            Actors = new List<Actor>();
            foreach (var platform in Scenario.Platforms)
            {
                platform.PlatformBehavior = new PlatformBehavior(platform, timeStepSize, timeStepCount);
                var behaviors = platform.PlatformBehavior.PlatformStates.ToArray();

                _platformStates.Add(behaviors);
                var curPlatform = platform;
                Globals.Dispatcher.InvokeIfRequired(() =>
                                            {
                                                curPlatform.RemoveMapLayers();
                                                curPlatform.UpdateMapLayers();
                                                var mapLayers = CreateFootprintMapLayers(curPlatform, behaviors[0]).ToArray();
                                                _modeFootprintMapLayers.Add(mapLayers);
                                                foreach (var layer in mapLayers)
                                                {
                                                    MediatorMessage.Send(MediatorMessage.AddMapLayer, layer);
                                                    MediatorMessage.Send(MediatorMessage.HideMapLayer, layer);
                                                }
                                            });
                var actor = new Actor { ID = platform.ActorID, Platform = platform };
                Actors.Add(actor);
            }
            _exposuresBySpecies = new int[Scenario.ScenarioSpecies.Count];
            _speciesActorIDStart = new int[Scenario.ScenarioSpecies.Count];
            _speciesActorIDEnd = new int[Scenario.ScenarioSpecies.Count];
            for (var j = 0; j < Scenario.ScenarioSpecies.Count; j++)
            {
                var species = Scenario.ScenarioSpecies[j];
                _speciesActorIDStart[j] = species.StartActorID;
                _speciesActorIDEnd[j] = species.StartActorID + species.Animat.Locations.Count - 1;
                if (firstAnimatPosition == null) firstAnimatPosition = new Geo<float>(species.Animat.Locations[0].Latitude, species.Animat.Locations[0].Longitude) { Data = species.Animat.Locations[0].Data };
                for (var i = 0; i < species.Animat.Locations.Count; i++) Actors.Add(new Actor { ID = species.StartActorID + i, Species = species });
            }
            var actorCount = Scenario.ScenarioSpecies.Last().StartActorID + Scenario.ScenarioSpecies.Last().Animat.Locations.Count;
            var logBlock = new ActionBlock<SimulationTimeStepRecord>(block => SimulationLog.Add(block), new ExecutionDataflowBlockOptions { BoundedCapacity = 1, MaxDegreeOfParallelism = 1 });
            logBlock.Completion.ContinueWith(t => SimulationLog.Close());

            var logBuffer = new BufferBlock<SimulationTimeStepRecord>();
            logBuffer.LinkTo(logBlock);
            logBuffer.Completion.ContinueWith(t => logBlock.Complete());
            Task moveTask = null;
            for (var timeStepIndex = 0; timeStepIndex < timeStepCount; timeStepIndex++)
            {

                if (MovingAnimats) moveTask = MoveAnimatsAsync();
                var actorPositionRecords = new ActorPositionRecord[actorCount];
                var actionBlockCompletions = new List<Task>();
                var bufferBlocks = new List<BufferBlock<int>>();
                foreach (var platform in Scenario.Platforms)
                {
                    var platformState = _platformStates[platform.ActorID][timeStepIndex];
                    actorPositionRecords[platform.ActorID] = new ActorPositionRecord(platformState.PlatformLocation.Location, platformState.PlatformLocation.Depth);
                    foreach (var activeMode in platformState.ModeActiveTimes.Keys)
                    {
                        var platformModeLayers = _modeFootprintMapLayers[Scenario.Platforms.IndexOf(platform)];
                        var activeModeLayerName = string.Format("{0}-footprint", activeMode.Guid);
                        var curModeLayer = (from l in platformModeLayers
                                            where l.Name == activeModeLayerName
                                            select l).FirstOrDefault();
                        if (curModeLayer != null)
                        {
                            UpdateFootprintMapLayer(activeMode, platformState, curModeLayer);
                            var isActive = platformState.ModeActiveTimes[activeMode].Ticks > 0;
                            Globals.Dispatcher.InvokeIfRequired(() =>
                                                        {
                                                            MediatorMessage.Send(AnimateSimulation && isActive ? MediatorMessage.ShowMapLayer : MediatorMessage.HideMapLayer, curModeLayer);
                                                            MediatorMessage.Send(MediatorMessage.RefreshMapLayer, curModeLayer);
                                                        });
                        }
                        var mode = activeMode;
                        var platformLocation = platformState.PlatformLocation.Location;
                        var thisPlatform = platform;
                        var scenario = Scenario;
                        var geoArc = new GeoArc(platformLocation,
                                                Geo.DegreesToRadians(platformState.PlatformLocation.Course + mode.RelativeBeamAngle),
                                                Geo.DegreesToRadians(mode.HorizontalBeamWidth),
                                                Geo.MetersToRadians(mode.MaxPropagationRadius));
                        var actionBlock = new ActionBlock<int>(async index =>
                        {
                            // Don't expose a platform to itself
                            if (thisPlatform.ActorID == index) return;

                            var record = actorPositionRecords[index];
                            var actorGeo = new Geo(record.Latitude, record.Longitude);
                            var radiansToActor = platformLocation.DistanceRadians(actorGeo);
                            var azimuthToActor = platformLocation.Azimuth(actorGeo);
                            if (!geoArc.Contains(radiansToActor, azimuthToActor)) return;
                            // At this point we know the actor will be exposed to this mode
                            // Find the nearest radial
                            var closestRadial = scenario.ClosestTransmissionLoss(platformLocation, mode)
                                .ClosestRadial(Geo.RadiansToDegrees(azimuthToActor));
                            // Load it into the cache if it's not already there
                            var tlTask = _transmissionLossCache[closestRadial];
                            await tlTask;
                            // Look up the TL value at the actor's range and depth
                            var transmissionLoss = tlTask.Result.ShadeFile[Geo.RadiansToMeters(radiansToActor), -record.Depth];
                            // Only generate an exposure record if the appropriate transmissionLoss is not NaN
                            if (!float.IsNaN(transmissionLoss))
                            {
                                var peakSPL = mode.SourceLevel - transmissionLoss;
                                //Debug.Assert(platformState != null, "platformState != null");
                                //Debug.Assert(platformState.ModeActiveTimes != null, "platformState.ModeActiveTimes != null");
                                //Debug.Assert(platformState.ModeActiveTimes.ContainsKey(mode), "platformState.ModeActiveTimes does not contain key");
                                //Debug.Assert(record != null, "record != null");
                                //Debug.Assert(record.Exposures != null, "record.Exposures != null");
                                var energy = (float)(peakSPL + (10 * Math.Log10(platformState.ModeActiveTimes[mode].TotalSeconds)));
                                record.Expose(new ActorExposureRecord(index, mode, peakSPL, energy));
                                Interlocked.Increment(ref _totalExposureCount);
                                for (var i = 0; i < Scenario.ScenarioSpecies.Count; i++) if (_speciesActorIDStart[i] <= index && index <= _speciesActorIDEnd[i]) Interlocked.Increment(ref _exposuresBySpecies[i]);
                                //var actorRecord = SimulationLog.RecordFromActorID(index) as SpeciesNameGuid;
                                //if (actorRecord != null) Interlocked.Increment(ref _exposuresBySpecies[SimulationLog.SpeciesRecords.IndexOf(actorRecord)]);
                            }
                        }, new ExecutionDataflowBlockOptions { BoundedCapacity = -1, MaxDegreeOfParallelism = -1 });
                        var bufferBlock = new BufferBlock<int>();
                        bufferBlock.LinkTo(actionBlock);
                        bufferBlocks.Add(bufferBlock);
                        actionBlockCompletions.Add(actionBlock.Completion);
                        bufferBlock.Completion.ContinueWith(t => actionBlock.Complete());
                    }
                }

                foreach (var species in Scenario.ScenarioSpecies)
                {
                    for (var animatIndex = 0; animatIndex < species.Animat.Locations.Count; animatIndex++)
                    {
                        var actorID = species.StartActorID + animatIndex;
                        actorPositionRecords[actorID] = new ActorPositionRecord(species.Animat.Locations[animatIndex]);
                    }
                }
                foreach (var bufferBlock in bufferBlocks)
                {
                    //Debug.WriteLine("Sending actor IDs to an active mode");
                    for (var actorID = 0; actorID < actorCount; actorID++) bufferBlock.Post(actorID);
                    bufferBlock.Complete();
                }
                //Debug.WriteLine("Actor IDs sent.  Waiting for completion.");
                Task.WhenAll(actionBlockCompletions).Wait();
                PercentProgress.Report(timeStepIndex);
                var timeStepRecord = new SimulationTimeStepRecord();
                timeStepRecord.ActorPositionRecords.AddRange(actorPositionRecords);
                if (processTask != null) processTask.Wait();
                processTask = ModeThresholdHistogram.Process(timeStepRecord, Globals.Dispatcher);
                if (timeStepIndex % 10 == 0)
                {
                    processTask.Wait();
                    Globals.Dispatcher.InvokeIfRequired(UpdateHistogramDisplay);
                }
                //SpeciesThresholdHistogram.Process(timeStepRecord);
                logBuffer.Post(timeStepRecord);
                if (moveTask != null)
                {
                    // Wait for 3MB to finish moving the animats
                    moveTask.Wait();
                    // Pull in updated animat positions from 3MB for the next time step
                    UpdateAnimatPositions();
                }
                //var distance = Scenario.ScenarioSpecies[0].Animat.Locations[0].DistanceKilometers(firstAnimatPosition);
                //if (distance > 0.01) Debug.WriteLine(string.Format("{0}: First animat has moved {1:0.##} km from initial location", DateTime.Now, distance));
                //Debug.WriteLine(string.Format("{0}: Finished time step {1} of {2}: {3:0%} complete", DateTime.Now, timeStepIndex, timeStepCount, Math.Round((float)timeStepIndex / timeStepCount, 3)));
                if (MovingAnimats & AnimateSimulation) Globals.Dispatcher.InvokeIfRequired(() => { foreach (var species in Scenario.ScenarioSpecies) species.UpdateMapLayers(); });
                if (token.IsCancellationRequested) break;
            }
            if (processTask != null) processTask.Wait();
            Globals.Dispatcher.InvokeIfRequired(UpdateHistogramDisplay);
            foreach (var layer in _modeFootprintMapLayers.SelectMany(layerSet => layerSet))
            {
                var curLayer = layer;
                Globals.Dispatcher.InvokeIfRequired(() => MediatorMessage.Send(MediatorMessage.RemoveMapLayer, curLayer));
            }
            Globals.Dispatcher.InvokeIfRequired(() => MediatorMessage.Send(MediatorMessage.RefreshMap, true));
            logBuffer.Complete();
            logBlock.Completion.Wait();
            if (MovingAnimats) Shutdown3MB();
            Debug.WriteLine("{0}: Simulation complete. Exposure count: {1}", DateTime.Now, _totalExposureCount);
            Debug.WriteLine("{0}: Exposures by species:", DateTime.Now);
            for (var i = 0; i < _exposuresBySpecies.Length; i++) Debug.WriteLine("{0}: Species: {1}, Exposures: {2}", DateTime.Now, Scenario.ScenarioSpecies[i].LatinName, _exposuresBySpecies[i]);
            //SpeciesThresholdHistogram.Display();
            //NewModeThresholdHistogram.DebugDisplay();
        }