/// <summary> /// Initializes a new instance of the <see cref="WorldModel"/> class. /// </summary> /// <param name="mapData">The map data.</param> /// <param name="countryData">The country data.</param> /// <param name="ortCounts">For each country, the number of Outbreak Response Teams it has, and the color they are rendered in.</param> /// <param name="execParameters">The execute parameters.</param> public WorldModel(MapData mapData, List<SimCountryData> countryData, Dictionary<string, Tuple<int,Color>> ortCounts = null, ExecParameters execParameters = null) { Executive = ExecFactory.Instance.CreateExecutive(ExecType.FullFeatured); ExecutionParameters = execParameters?? new ExecParameters(); m_controller = new Controller(this); m_countryDataByCountryCodes = new Dictionary<string, SimCountryData>(); countryData.ForEach(n=> m_countryDataByCountryCodes.Add(n.CountryCode, n)); m_ortCounts = ortCounts??new Dictionary<string, Tuple<int, Color>>(); m_mapData = mapData; m_nodes = new DiseaseNode[m_mapData.Width, m_mapData.Height]; m_size = new Size(m_mapData.Width, m_mapData.Height); for (int x = 0; x < m_mapData.Width; x++) { for (int y = 0; y < m_mapData.Height; y++) { double density = m_mapData.CellData[x, y].PopulationDensity; if (Math.Abs(density - CellData.NO_DATA) < 0.01) density = 0.0; double landArea = m_mapData.CellData[x, y].LandArea; double waterArea = m_mapData.CellData[x, y].WaterArea; Locale locale = new Locale(Locale.DEFAULT); switch (m_mapData.CellData[x, y].CellState) { case CellState.Unknown: locale.Population = 0; locale.Area = Locale.DEFAULT.Area; break; case CellState.Occupied: locale.Area = landArea + (Math.Abs(waterArea - CellData.NO_DATA) < 0.01 ? 0 : waterArea); if (locale.Area <= 0.001) locale.Area = Locale.DEFAULT.Area; locale.Population = density * landArea; break; case CellState.Unoccupied: locale.Population = 0; break; case CellState.Ocean: locale.Population = 0; break; default: break; } double lat, lon; m_mapData.DataXYToLatLon(x,y,out lat, out lon); m_nodes[x, y] = new DiseaseNode(Policy.DEFAULT, locale, Disease.DEFAULT); // Bidirectional Cross-Reference. m_nodes[x, y].MapCell = m_mapData.CellData[x, y]; m_mapData.CellData[x, y].DiseaseModel = m_nodes[x, y]; } } CreateAndAssignGovernments(); }
/// <summary> /// Updates this country's disease nodes with disease-impacting country-specific data /// such as HealthCareEffectiveness and SocialStability. /// </summary> /// <param name="initializing">if set to <c>true</c> [initializing].</param> internal void UpdateDiseaseNodes(bool initializing = false) { foreach (Coordinates coordinates in DiseaseNodeCoordinates) { if (initializing) { DiseaseNode dn = WorldModel.DiseaseNodes[coordinates.X, coordinates.Y]; dn.LocaleData.HealthCareEffectiveness = SimCountryData.HealthCareEffectiveness; dn.LocaleData.SocialStability = SimCountryData.SocialStability; dn.LocaleData.ForeignImmunizationAidPerCapita = 0.0; // Birth rate, mortality, doctors per capita... } else { // TODO: Some day, we'll have dynamic evolution of ... stuff. } } }
/// <summary> /// The national government is given an opportunity to reassess itself after each cycle. /// </summary> /// <param name="exec">The execute.</param> internal void Reassess(IExecutive exec) { double evidentlyInfected = 0; double population = 0; foreach (Coordinates dnCoordinates in DiseaseNodeCoordinates) { DiseaseNode dn = WorldModel.DiseaseNodes[dnCoordinates.X, dnCoordinates.Y]; evidentlyInfected += dn.NonContagiousInfected; evidentlyInfected += dn.ContagiousAsymptomatic; evidentlyInfected += dn.ContagiousSymptomatic; evidentlyInfected += dn.Killed; population += dn.Population; } // Once we have greater than half a percent of the // population infected, establish a quarantine on every // cell in the country. if (evidentlyInfected > (.005 * population)) { bool alreadyQuarantined = false; foreach (Coordinates dnCoordinates in DiseaseNodeCoordinates) { DiseaseNode dn = WorldModel.DiseaseNodes[dnCoordinates.X, dnCoordinates.Y]; if (dn.Quarantined) { alreadyQuarantined = true; break; } dn.Quarantined = true; } if (!alreadyQuarantined) { console.WriteLine($"{exec.Now} : {SimCountryData.Name} is quarantining all of its cells."); } } }
/// <summary> /// Updates the world model by one SD timeslice. /// </summary> /// <param name="exec">The executive under which this update is being done.</param> /// <param name="manualResetEvent1">The manual reset event1.</param> /// <param name="manualResetEvent2">The manual reset event2.</param> internal void Update(IExecutive exec, ManualResetEvent manualResetEvent1 = null, ManualResetEvent manualResetEvent2 = null) { ILineWriter console = Locator.Resolve<ILineWriter>("console"); m_governments.ForEach(n=>n.UpdateDiseaseNodes()); bool travelOnlyFromToApparentlyDiseaseFree = NoAirTravelFromToKnownInfected; manualResetEvent1?.WaitOne(); manualResetEvent2?.WaitOne(); //DateTime now = DateTime.Now; DiseaseNode[,] newNodes = new DiseaseNode[m_mapData.Width, m_mapData.Height]; // Progress the simulation one time step. int count1 = 0, count2 = 0; #if AUTO_INOCULATE if (m_nodes[0, 0].TimeSliceNdx == 10 && m_districtsOfInterest != null) { { foreach (var tuple in m_districtsOfInterest) { console.WriteLine($"Inoculating at {tuple.Item1},{tuple.Item2} with patient zero."); m_nodes[tuple.Item1, tuple.Item2].ContagiousAsymptomatic++; console.WriteLine(m_nodes[tuple.Item1, tuple.Item2]); } } } #endif #if USE_PARALLEL Parallel.For(0, m_mapData.Width, x => { #else for (int x = 0; x < m_data.Width; x++) { #endif for (int y = 0; y < m_mapData.Height; y++) { if (!m_mapData.CellData[x, y].CellState.Equals(CellState.Occupied)) { if (m_mapData.CellData[x, y].DiseaseModel.Population > 0) Debugger.Break(); newNodes[x, y] = m_nodes[x, y]; newNodes[x, y].TimeSliceNdx++; Interlocked.Increment(ref count1); } else { //EpidemicNode.m_debug = (x == 243 && y == 240); //if ( x== 243 && y == 240 && m_nodes[x, y].TimeSliceNdx == 63) Debugger.Break(); DiseaseNode oldNode = m_nodes[x, y]; DiseaseNode newNode = Behavior<DiseaseNode>.RunOneTimeslice(m_nodes[x, y]); newNodes[x, y] = newNode; if (oldNode.ContagiousAsymptomatic < EPSILON && newNode.ContagiousAsymptomatic > EPSILON) { ReportNewlyInfectedNode(new Coordinates() {X=x, Y=y}); } Interlocked.Increment(ref count2); } } #if USE_PARALLEL }); #else } // Non-parallel.
private void CreateAndAssignGovernments() { m_allORTs = new List<OutbreakResponseTeam>(); ILineWriter console = Locator.Resolve<ILineWriter>("console"); Dictionary<string, List<Coordinates>> nodeToCountryMapping = new Dictionary<string, List<Coordinates>>(); for (int x = 0; x < m_mapData.Width; x++) { for (int y = 0; y < m_mapData.Height; y++) { DiseaseNode dn = m_nodes[x, y]; string cc = dn.MapCell.CountryCode; if (cc != null && !"--".Equals(cc)) { List<Coordinates> diseaseNodeCoordinates; if (!nodeToCountryMapping.TryGetValue(cc, out diseaseNodeCoordinates)) { diseaseNodeCoordinates = new List<Coordinates>(); nodeToCountryMapping.Add(cc, diseaseNodeCoordinates); } diseaseNodeCoordinates.Add(new Coordinates {X = x, Y = y}); } } } Dictionary<string, NationalGovernment> govts = new Dictionary<string, NationalGovernment>(); for (int x = 0; x < m_mapData.Width; x++) { for (int y = 0; y < m_mapData.Height; y++) { DiseaseNode dn = m_nodes[x, y]; string countryCode = dn.MapCell.CountryCode; if ("--".Equals(countryCode)) continue; if (countryCode == null) continue; if (!m_countryDataByCountryCodes.ContainsKey(countryCode)) { //console.WriteLine($"No country data for country code \"{countryCode}\"."); continue; } SimCountryData simCountryData = m_countryDataByCountryCodes[countryCode]; if (nodeToCountryMapping.ContainsKey(dn.MapCell.CountryCode)) { if (!govts.ContainsKey(countryCode)) { NationalGovernment ng = new NationalGovernment(this) { WorldModel = this, SimCountryData = m_countryDataByCountryCodes[countryCode], DiseaseNodeCoordinates = nodeToCountryMapping[countryCode] }; if (m_ortCounts.ContainsKey(simCountryData.CountryCode)) { var ortCount = m_ortCounts[simCountryData.CountryCode]; int numberOfResponseTeams = ortCount.Item1; for (int i = 0; i < numberOfResponseTeams; i++) { int randomDiseaseNode = m_random.Next(0, ng.DiseaseNodeCoordinates.Count - 1); Coordinates initialCoordinates = ng.DiseaseNodeCoordinates[randomDiseaseNode]; OutbreakResponseTeam ort = new OutbreakResponseTeam(simCountryData.CountryCode, i, ortCount.Item2, this, initialCoordinates); ng.ResponseTeams.Add(ort); simCountryData.ResponseTeams.Add(ort); m_allORTs.Add(ort); } } simCountryData.Government = ng; govts.Add(countryCode, ng); ng.UpdateDiseaseNodes(true); } } else { console.WriteLine($"Could not find a list of nodes for country code {simCountryData.CountryCode}"); } } } m_governments.AddRange(govts.Values); }