/// <summary>Writes an organization reports.</summary> /// <param name="orgId"> The organization.</param> /// <param name="dir"> The dir.</param> /// <param name="dateTime"> The period.</param> /// <param name="deviceData"> Information describing the device.</param> /// <param name="patientData">Information describing the patient.</param> /// <param name="testData"> Information describing the test.</param> /// <param name="workerData"> [out] Information describing the worker.</param> /// <param name="step"> Amount to increment by.</param> private static void WriteOrgReportBundle( string orgId, string dir, DateTime dateTime, OrgDeviceData deviceData, OrgPatientData patientData, OrgTestData testData, OrgWorkerData workerData, int step) { Dictionary <string, FieldValue> fields = OrgUtils.BuildFieldDict( deviceData, patientData, testData, workerData); ReportData data = new ReportData( dateTime, _orgById[orgId], _rootLocationByOrgId[orgId], fields); string filename = _outputFlat ? Path.Combine(dir, $"{orgId}-{_filenameAdditionForMeasureReports}-{step:0000}{_extension}") : Path.Combine(dir, $"{orgId}-{_filenameAdditionForMeasureReports}{_extension}"); WriteBundle( filename, SanerMeasureReport.GetBundle(data, null)); }
/// <summary>Main entry-point for this application.</summary> /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception> /// <param name="outputDirectory"> Directory to write files.</param> /// <param name="dataDirectory"> Allow passing in of data directory.</param> /// <param name="outputFormat"> The output format, JSON or XML (default: JSON).</param> /// <param name="state"> State to restrict generation to (default: none).</param> /// <param name="postalCode"> Postal code to restrict generation to (default: none).</param> /// <param name="facilityCount"> Number of facilities to generate.</param> /// <param name="timeSteps"> Number of time-step updates to generate.</param> /// <param name="timePeriodHours"> Time-step period in hours (default: 24).</param> /// <param name="seed"> Starting seed to use in generation, 0 for none (default: 0).</param> /// <param name="recordsToSkip"> Number of records to skip before starting generation (default: 0).</param> /// <param name="orgSource"> Source for organization records: generate|csv|connectathon (default: connectathon).</param> /// <param name="prettyPrint"> If output files should be formatted for display.</param> /// <param name="bedTypes"> Bar separated bed types: ICU|ER... (default: ICU|ER|HU).</param> /// <param name="operationalStatuses"> Bar separated operational status: U|O|K (default: O|U).</param> /// <param name="minBedsPerOrg"> The minimum number of beds per hospital (default: 10).</param> /// <param name="maxBedsPerOrg"> The maximum number of beds per hospital (default: 1000).</param> /// <param name="changeFactor"> The amount of change in bed state per step (default 0.2).</param> /// <param name="minIcuPercent"> Minimum percentage of beds for an org which are ICU type.</param> /// <param name="maxIcuPercent"> Maximum percentage of beds for an org which are ICU type.</param> /// <param name="ventilatorsPerIcu"> Average number of ventilators per ICU bed.</param> /// <param name="initialOccupancy"> Initial occupancy of bed percentage.</param> /// <param name="positiveTestRate"> Rate of people being tested returning positive.</param> /// <param name="hospitalizationRate"> Rate of people testing positive requiring hospitalization.</param> /// <param name="patientToIcuRate"> Rate of people hospitalized requiring ICU.</param> /// <param name="icuToVentilatorRate"> Rate of people in ICU requiring ventilators.</param> /// <param name="recoveryRate"> Rate of people recovering during hospitalization.</param> /// <param name="deathRate"> Rate of people dying in hospitalization, when care is available.</param> /// <param name="outputBundles"> True to output Bundles, false to output raw resources.</param> /// <param name="outputFlat"> True to output into a single directory, false to nest.</param> /// <param name="measureMarkdown"> If specified, the filename of the Measure Markdown file.</param> public static void Main( string outputDirectory, string dataDirectory = null, string outputFormat = "JSON", string state = null, string postalCode = null, int facilityCount = 10, int timeSteps = 2, int timePeriodHours = 24, int seed = 0, int recordsToSkip = 0, string orgSource = "connectathon", bool prettyPrint = true, string bedTypes = "ICU|ER|HU", string operationalStatuses = "O|U", int minBedsPerOrg = 10, int maxBedsPerOrg = 1000, double changeFactor = 0.2, double minIcuPercent = 0.05, double maxIcuPercent = 0.20, double ventilatorsPerIcu = 0.20, double initialOccupancy = 0.20, double positiveTestRate = 0.5, double hospitalizationRate = 0.30, double patientToIcuRate = 0.30, double icuToVentilatorRate = 0.70, double recoveryRate = 0.1, double deathRate = 0.05, bool outputBundles = true, bool outputFlat = false, string measureMarkdown = "") { // sanity checks if (string.IsNullOrEmpty(outputDirectory)) { throw new ArgumentNullException(nameof(outputDirectory)); } if (string.IsNullOrEmpty(outputFormat)) { throw new ArgumentNullException(nameof(outputFormat)); } if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } _changeFactor = changeFactor; _minIcuPercent = minIcuPercent; _maxIcuPercent = maxIcuPercent; _ventilatorsPerIcu = ventilatorsPerIcu; _initialOccupancy = initialOccupancy; _positiveTestRate = positiveTestRate; _hospitalizationRate = hospitalizationRate; _patientToIcuRate = patientToIcuRate; _icuToVentilatorRate = icuToVentilatorRate; _recoveryRate = recoveryRate; _noResourceRecoveryRate = recoveryRate * 0.1; _deathRate = deathRate; _noResourceDeathRate = Math.Min(deathRate * 10, 1.0); _outputBundles = outputBundles; _outputFlat = outputFlat; _useJson = outputFormat.ToUpperInvariant().Equals("JSON", StringComparison.Ordinal); if (_useJson) { SerializerSettings settings = new SerializerSettings() { Pretty = prettyPrint, }; _jsonSerializer = new FhirJsonSerializer(settings); _extension = ".json"; } else { SerializerSettings settings = new SerializerSettings() { Pretty = prettyPrint, }; _xmlSerializer = new FhirXmlSerializer(settings); _extension = ".xml"; } _useLookup = (!string.IsNullOrEmpty(orgSource)) || (orgSource.ToUpperInvariant() != "GENERATE"); _connectathon = string.IsNullOrEmpty(orgSource) || (orgSource.ToUpperInvariant() == "CONNECTATHON"); if (seed == 0) { _rand = new Random(); } else { _rand = new Random(seed); } // always need the geo manager GeoManager.Init(seed, minBedsPerOrg, maxBedsPerOrg, dataDirectory); OrgWorkerData.Init(seed); // only need hospital manager if we are using lookup (avoid loading otherwise) if (_useLookup || _connectathon) { HospitalManager.Init( seed, minBedsPerOrg, maxBedsPerOrg, dataDirectory, _connectathon); } string dir; // create our time step directories if (!_outputFlat) { for (int step = 0; step < timeSteps; step++) { dir = Path.Combine(outputDirectory, $"t{step}"); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } } } // create our organization records CreateOrgs(facilityCount, state, postalCode, recordsToSkip); ExportAggregate(outputDirectory, timeSteps, timePeriodHours); if (!string.IsNullOrEmpty(measureMarkdown)) { File.WriteAllText(measureMarkdown, SanerMeasure.GetMarkdown()); } }
/// <summary>Creates aggregate data.</summary> /// <param name="orgId"> The organization.</param> /// <param name="deviceData"> [out] Information describing the device.</param> /// <param name="patientData">[out] Information describing the patient.</param> /// <param name="testData"> [out] Information describing the test.</param> /// <param name="workerData"> [out] Information describing the worker.</param> private static void CreateAggregateData( string orgId, out OrgDeviceData deviceData, out OrgPatientData patientData, out OrgTestData testData, out OrgWorkerData workerData) { int initialBedCount; if (_useLookup) { initialBedCount = HospitalManager.BedsForHospital(orgId); } else { initialBedCount = GeoManager.BedsForHospital(); } double icuRate = (_rand.NextDouble() * (_maxIcuPercent - _minIcuPercent)) + _minIcuPercent; int icuBeds = (int)(initialBedCount * icuRate); if (icuBeds < 1) { icuBeds = 1; } int inpatientBeds = initialBedCount - icuBeds; int ventilators = (int)(icuBeds * _ventilatorsPerIcu); if (ventilators < 1) { ventilators = 1; } // create device data for this org deviceData = new OrgDeviceData( initialBedCount, inpatientBeds, icuBeds, ventilators); // figure out patient numbers based on initial capacity int patients = (int)(initialBedCount * _initialOccupancy); int positive = (int)(patients * _positiveTestRate); int positiveNeedIcu = (int)(positive * _patientToIcuRate); int positiveNeedVent = (int)(positive * _icuToVentilatorRate); int negative = patients - positive; int negativeNeedIcu = 0; // (int)(negative * _patientToIcuRate); int negativeNeedVent = 0; // (int)(negativeNeedIcu * _icuToVentilatorRate); int onsetInCare = 0; int recovered = 0; int dead = 0; // create patient data patientData = new OrgPatientData( patients, positive, positiveNeedIcu, positiveNeedVent, negative, negativeNeedIcu, negativeNeedVent, onsetInCare, recovered, dead); // extrapolate test data int performedTests = (int)(patients / _hospitalizationRate); int positiveTests = (int)(performedTests * _positiveTestRate); int negativeTests = performedTests - positive; int pendingTests = _rand.Next(0, 10); performedTests += pendingTests; // create test data record testData = new OrgTestData( performedTests, positiveTests, negativeTests, pendingTests); workerData = new OrgWorkerData(); }
/// <summary>Updates the aggregate data for step.</summary> /// <param name="deviceData"> [in,out] Information describing the device.</param> /// <param name="patientData">[in,out] Information describing the patient.</param> /// <param name="testData"> [in,out] Information describing the test.</param> /// <param name="workerData"> [out] Information describing the worker.</param> private static void UpdateAggregateDataForStep( ref OrgDeviceData deviceData, ref OrgPatientData patientData, ref OrgTestData testData, ref OrgWorkerData workerData) { // increase testing int testDelta = (int)(testData.Performed * _changeFactor); if (testDelta == 0) { testDelta = 1; } int testPositiveDelta = (int)(testDelta * _positiveTestRate); int testNegativeDelta = testDelta - testPositiveDelta; int testPending = _rand.Next(0, 10); int patientsShortCare = Math.Max(0, patientData.PositiveNeedVent - deviceData.Ventilators) + Math.Max(0, patientData.PositiveNeedIcu - deviceData.ICU) + Math.Max(0, patientData.PositiveNonIcu - deviceData.Inpatient); int patientsWithCare = patientData.Positive - patientsShortCare; double effectiveRecoveryRate = (patientData.Positive == 0) ? 0 : ((_recoveryRate * patientsWithCare) + (_noResourceRecoveryRate * patientsShortCare)) / ((double)patientsWithCare + (double)patientsShortCare); double effectiveDeathRate = (patientData.Positive == 0) ? 0 : ((_deathRate * patientsWithCare) + (_noResourceDeathRate * patientsShortCare)) / ((double)patientsWithCare + (double)patientsShortCare); double patientRemovalRate = effectiveDeathRate + effectiveRecoveryRate; int patientsRemoved = (int)(patientData.Positive * patientRemovalRate); int patientsDied; int patientsRecovered; if (_rand.NextDouble() < 0.5) { patientsDied = (int)((double)patientData.Positive * effectiveDeathRate); patientsRecovered = patientsRemoved - patientsDied; } else { patientsRecovered = (int)((double)patientData.Positive * effectiveRecoveryRate); patientsDied = patientsRemoved - patientsRecovered; } int positive = patientData.Positive - patientsRemoved + Math.Max((int)(testPositiveDelta * _hospitalizationRate), 1); int patients = patientData.Total - patientData.Positive + positive; int positiveNeedIcu = (int)(positive * _patientToIcuRate); int positiveNeedVent = (int)(positive * _icuToVentilatorRate); int negative = patients - positive; int negativeNeedIcu = 0; // (int)(negative * _patientToIcuRate); int negativeNeedVent = 0; // (int)(negativeNeedIcu * _icuToVentilatorRate); int dead = patientData.Died + patientsDied; int recovered = patientData.Recovered + patientsRecovered; // update records testData.Update( testDelta, testPositiveDelta, testNegativeDelta, testPending); patientData.Update( patients, positive, positiveNeedIcu, positiveNeedVent, negative, negativeNeedIcu, negativeNeedVent, 0, recovered, dead); workerData.Update(); }