emod-api 3.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. emod_api/__init__.py +1 -0
  2. emod_api/campaign.py +170 -0
  3. emod_api/channelreports/__init__.py +0 -0
  4. emod_api/channelreports/channels.py +433 -0
  5. emod_api/channelreports/icj_to_csv.py +65 -0
  6. emod_api/channelreports/plot_icj_means.py +149 -0
  7. emod_api/channelreports/plot_prop_report.py +205 -0
  8. emod_api/channelreports/utils.py +326 -0
  9. emod_api/config/__init__.py +0 -0
  10. emod_api/config/default_from_schema.py +16 -0
  11. emod_api/config/default_from_schema_no_validation.py +177 -0
  12. emod_api/config/from_overrides.py +135 -0
  13. emod_api/demographics/__init__.py +0 -0
  14. emod_api/demographics/age_distribution.py +163 -0
  15. emod_api/demographics/base_input_file.py +28 -0
  16. emod_api/demographics/calculators.py +159 -0
  17. emod_api/demographics/demographic_exceptions.py +54 -0
  18. emod_api/demographics/demographics.py +249 -0
  19. emod_api/demographics/demographics_base.py +752 -0
  20. emod_api/demographics/demographics_overlay.py +41 -0
  21. emod_api/demographics/fertility_distribution.py +235 -0
  22. emod_api/demographics/implicit_functions.py +112 -0
  23. emod_api/demographics/mortality_distribution.py +227 -0
  24. emod_api/demographics/node.py +456 -0
  25. emod_api/demographics/overlay_node.py +16 -0
  26. emod_api/demographics/properties_and_attributes.py +737 -0
  27. emod_api/demographics/service/__init__.py +0 -0
  28. emod_api/demographics/service/grid_construction.py +143 -0
  29. emod_api/demographics/service/service.py +55 -0
  30. emod_api/demographics/susceptibility_distribution.py +170 -0
  31. emod_api/demographics/updateable.py +58 -0
  32. emod_api/legacy/__init__.py +0 -0
  33. emod_api/legacy/plotAllCharts.py +230 -0
  34. emod_api/migration/__init__.py +0 -0
  35. emod_api/migration/__main__.py +22 -0
  36. emod_api/migration/migration.py +782 -0
  37. emod_api/multidim_plotter.py +80 -0
  38. emod_api/schema_to_class.py +440 -0
  39. emod_api/serialization/__init__.py +0 -0
  40. emod_api/serialization/census_and_mod_pop.py +48 -0
  41. emod_api/serialization/dtk_file_support.py +61 -0
  42. emod_api/serialization/dtk_file_tools.py +1378 -0
  43. emod_api/serialization/dtk_file_utility.py +141 -0
  44. emod_api/serialization/serialized_population.py +205 -0
  45. emod_api/spatialreports/__init__.py +0 -0
  46. emod_api/spatialreports/__main__.py +67 -0
  47. emod_api/spatialreports/plot_spat_means.py +99 -0
  48. emod_api/spatialreports/spatial.py +210 -0
  49. emod_api/utils/__init__.py +26 -0
  50. emod_api/utils/distributions/__init__.py +0 -0
  51. emod_api/utils/distributions/base_distribution.py +38 -0
  52. emod_api/utils/distributions/bimodal_distribution.py +64 -0
  53. emod_api/utils/distributions/constant_distribution.py +58 -0
  54. emod_api/utils/distributions/demographic_distribution_flag.py +16 -0
  55. emod_api/utils/distributions/distribution_type.py +15 -0
  56. emod_api/utils/distributions/dual_constant_distribution.py +68 -0
  57. emod_api/utils/distributions/dual_exponential_distribution.py +75 -0
  58. emod_api/utils/distributions/exponential_distribution.py +63 -0
  59. emod_api/utils/distributions/gaussian_distribution.py +69 -0
  60. emod_api/utils/distributions/log_normal_distribution.py +61 -0
  61. emod_api/utils/distributions/poisson_distribution.py +59 -0
  62. emod_api/utils/distributions/uniform_distribution.py +70 -0
  63. emod_api/utils/distributions/weibull_distribution.py +69 -0
  64. emod_api/utils/str_enum.py +6 -0
  65. emod_api/weather/__init__.py +0 -0
  66. emod_api/weather/weather.py +428 -0
  67. emod_api-3.0.2.dist-info/METADATA +131 -0
  68. emod_api-3.0.2.dist-info/RECORD +71 -0
  69. emod_api-3.0.2.dist-info/WHEEL +5 -0
  70. emod_api-3.0.2.dist-info/licenses/LICENSE +21 -0
  71. emod_api-3.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ emod-api Weather module - Weather, Metadata, and WeatherNode objects along with IDREF and CLIMATE_UPDATE constants.
5
+ """
6
+
7
+ from collections import namedtuple
8
+ from functools import reduce
9
+ import csv
10
+ from datetime import datetime
11
+ import getpass
12
+ import json
13
+ import numpy as np
14
+
15
+
16
+ IDREF_LEGACY = "Legacy"
17
+ IDREF_GRUMP30ARCSEC = "Gridded world grump30arcsec"
18
+ IDREF_GRUMP2PT5ARCMIN = "Gridded world grump2.5arcmin"
19
+ IDREF_GRUMP1DEGREE = "Gridded world grump1degree"
20
+
21
+ CLIMATE_UPDATE_YEAR = "CLIMATE_UPDATE_YEAR"
22
+ CLIMATE_UPDATE_MONTH = "CLIMATE_UPDATE_MONTH"
23
+ CLIMATE_UPDATE_WEEK = "CLIMATE_UPDATE_WEEK"
24
+ CLIMATE_UPDATE_DAY = "CLIMATE_UPDATE_DAY"
25
+ CLIMATE_UPDATE_HOUR = "CLIMATE_UPDATE_HOUR"
26
+
27
+
28
+ class WeatherNode(object):
29
+ """Represents information for a single node: ID and timeseries data."""
30
+
31
+ def __init__(self, node_id: int, data):
32
+
33
+ self._id = node_id
34
+ self._data = data
35
+
36
+ return
37
+
38
+ @property
39
+ def id(self) -> int:
40
+ """Node ID"""
41
+ return self._id
42
+
43
+ @property
44
+ def data(self):
45
+ """Time series data for this node."""
46
+ return self._data
47
+
48
+ # index into node by time step
49
+ def __getitem__(self, item: int) -> float:
50
+ return self._data[item]
51
+
52
+ def __setitem__(self, key: int, value: float) -> None:
53
+ self._data[key] = value
54
+ return
55
+
56
+
57
+ def _is_iterable(obj) -> bool:
58
+ try:
59
+ _ = iter(obj)
60
+ return True
61
+ except TypeError:
62
+ return False
63
+
64
+
65
+ def _isinteger(obj) -> bool:
66
+ return obj and isinstance(obj, (int, np.integer))
67
+
68
+
69
+ class Metadata(object):
70
+
71
+ """
72
+ Metadata:
73
+
74
+ * [DateCreated]
75
+ * [Author]
76
+ * [OriginalDataYears]
77
+ * [StartDayOfYear]
78
+ * [DataProvenance]
79
+ * IdReference
80
+ * NodeCount
81
+ * DatavalueCount
82
+ * UpdateResolution
83
+ * NodeOffsets
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ node_ids: list[int],
89
+ datavalue_count: int,
90
+ author: str = None,
91
+ created: datetime = None,
92
+ frequency: str = None,
93
+ provenance: str = None,
94
+ reference: str = None,
95
+ ):
96
+
97
+ assert int(datavalue_count) > 0, "datavalue_count must be > 0"
98
+ self._data_count = int(datavalue_count)
99
+ self._author = f"{author}" if author else getpass.getuser()
100
+ self._creation = (
101
+ created if created and isinstance(created, datetime) else datetime.now()
102
+ )
103
+ self._id_reference = f"{reference}" if reference else IDREF_LEGACY
104
+ self._provenance = f"{provenance}" if provenance else "unknown"
105
+ self._update_frequency = f"{frequency}" if frequency else CLIMATE_UPDATE_DAY
106
+
107
+ assert _is_iterable(node_ids), "node_ids must be iterable"
108
+ concrete = list(node_ids) # if node_ids is a generator, make a concrete list
109
+ assert len(concrete) > 0, "node_ids must not be empty"
110
+ assert all(
111
+ map(lambda i: isinstance(i, int), concrete)
112
+ ), "node_ids must be integers"
113
+ assert len(set(concrete)) == len(concrete), "node_ids must be unique"
114
+ sorted_ids = sorted(concrete)
115
+ self._nodes = {
116
+ node_id: datavalue_count * sorted_ids.index(node_id) * 4
117
+ for node_id in sorted_ids
118
+ }
119
+
120
+ return
121
+
122
+ @property
123
+ def author(self) -> str:
124
+ """Author of this file."""
125
+ return self._author
126
+
127
+ @property
128
+ def creation_date(self) -> datetime:
129
+ """Creation date of this file."""
130
+ return self._creation
131
+
132
+ @property
133
+ def datavalue_count(self) -> int:
134
+ """Number of data values in each timeseries, should be > 0."""
135
+ return self._data_count
136
+
137
+ @property
138
+ def id_reference(self) -> str:
139
+ """
140
+ 'Schema' for node IDs. Commonly `Legacy`, `Gridded world grump2.5arcmin`, and `Gridded world grump30arcsec`.
141
+
142
+ `Legacy` usually indicates a 0 or 1 based scheme with increasing ID numbers.
143
+
144
+ `Gridded world grump2.5arcmin` and `Gridded world grump30arcsec` encode latitude and longitude values in the node ID with the following formula::
145
+
146
+ latitude = (((nodeid - 1) & 0xFFFF) * resolution) - 90
147
+ longitude = ((nodeid >> 16) * resolution) - 180
148
+ # nodeid = 90967271 @ 2.5 arcmin resolution
149
+ # longitude = -122.1667, latitude = 47.5833
150
+ """
151
+ return self._id_reference
152
+
153
+ @property
154
+ def node_count(self) -> int:
155
+ return len(self.nodes)
156
+
157
+ @property
158
+ def node_ids(self) -> list[int]:
159
+ return sorted(self.nodes.keys())
160
+
161
+ @property
162
+ def provenance(self) -> str:
163
+ return self._provenance
164
+
165
+ @property
166
+ def update_resolution(self) -> str:
167
+ return self._update_frequency
168
+
169
+ @property
170
+ def nodes(self) -> dict[int, int]:
171
+ """WeatherNodes offsets keyed by node id."""
172
+ return self._nodes
173
+
174
+ def write_file(self, filename: str) -> None:
175
+
176
+ metadata = dict(
177
+ DateCreated=f"{self.creation_date:%a %B %d %Y %H:%M:%S}",
178
+ Author=self.author,
179
+ DataProvenance=self.provenance,
180
+ IdReference=self.id_reference,
181
+ NodeCount=len(self.nodes),
182
+ DatavalueCount=self.datavalue_count,
183
+ UpdateResolution=self.update_resolution,
184
+ )
185
+
186
+ node_offsets = reduce(
187
+ lambda s, n: s + f"{n:08X}{self.nodes[n]:08X}", self.node_ids, ""
188
+ )
189
+
190
+ jason = dict(Metadata=metadata, NodeOffsets=node_offsets)
191
+
192
+ with open(filename, "wt") as file:
193
+ json.dump(jason, file, indent=2, separators=(",", ": "))
194
+
195
+ return
196
+
197
+ @classmethod
198
+ def from_file(cls, filename: str):
199
+ """
200
+ Read weather metadata file.
201
+ Metadata' and 'NodeOffsets' keys required.
202
+ DatavalueCount', 'UpdateResolution', and 'IdReference' required in 'Metadata'.
203
+ """
204
+ with open(filename, "rb") as file:
205
+ jason = json.load(file)
206
+
207
+ meta = jason["Metadata"]
208
+ offsets = jason["NodeOffsets"]
209
+ node_ids = sorted(
210
+ [int(offsets[(i * 16):(i * 16 + 8)], 16) for i in range(len(offsets) // 16)]
211
+ )
212
+
213
+ metadata = Metadata(
214
+ node_ids,
215
+ meta["DatavalueCount"],
216
+ author=meta["Author"] if "Author" in meta else None,
217
+ created=meta["DateCreated"] if "DateCreated" in meta else None,
218
+ frequency=meta["UpdateResolution"],
219
+ provenance=meta["DataProvenance"] if "DataProvenance" in meta else None,
220
+ reference=meta["IdReference"],
221
+ )
222
+
223
+ return metadata
224
+
225
+
226
+ class Weather(object):
227
+ def __init__(
228
+ self,
229
+ filename: str = None,
230
+ node_ids: list[int] = None,
231
+ datavalue_count: int = None,
232
+ author: str = None,
233
+ created: datetime = None,
234
+ frequency: str = None,
235
+ provenance: str = None,
236
+ reference: str = None,
237
+ data: np.array = None,
238
+ ):
239
+
240
+ if filename and isinstance(filename, str):
241
+ self._from_file(filename)
242
+ else:
243
+ # create "empty" Weather object
244
+ assert _is_iterable(node_ids), "node_ids must be provided and be iterable"
245
+ assert _isinteger(datavalue_count), "datavalue_count must be provided"
246
+ assert datavalue_count > 0, "datavalue_count must be >= 1"
247
+
248
+ self._metadata = Metadata(
249
+ node_ids,
250
+ datavalue_count,
251
+ author,
252
+ created,
253
+ frequency,
254
+ provenance,
255
+ reference,
256
+ )
257
+ node_ids = self._metadata.node_ids
258
+ self._data = (
259
+ data
260
+ if data is not None
261
+ else np.zeros(
262
+ (len(node_ids), self._metadata.datavalue_count), dtype=np.float32
263
+ )
264
+ )
265
+ self._nodes_and_map()
266
+
267
+ return
268
+
269
+ def _nodes_and_map(self):
270
+ node_ids = self._metadata.node_ids
271
+ self._node_id_to_index_map = {node_ids[n]: n for n in range(len(node_ids))}
272
+ self._nodes = {
273
+ node_id: WeatherNode(
274
+ node_id, self._data[self._node_id_to_index_map[node_id], :]
275
+ )
276
+ for node_id in node_ids
277
+ }
278
+ return
279
+
280
+ @property
281
+ def data(self) -> np.array:
282
+ """Raw data as numpy array[node index, time step]."""
283
+ return self._data
284
+
285
+ @property
286
+ def metadata(self) -> Metadata:
287
+ return self._metadata
288
+
289
+ # begin pass-through
290
+
291
+ @property
292
+ def author(self) -> str:
293
+ return self._metadata.author
294
+
295
+ @property
296
+ def creation_date(self) -> datetime:
297
+ return self._metadata.creation_date
298
+
299
+ @property
300
+ def datavalue_count(self) -> int:
301
+ """>= 1"""
302
+ return self._metadata.datavalue_count
303
+
304
+ @property
305
+ def id_reference(self) -> str:
306
+ return self._metadata.id_reference
307
+
308
+ @property
309
+ def node_count(self) -> int:
310
+ """>= 1"""
311
+ return self._metadata.node_count
312
+
313
+ @property
314
+ def node_ids(self) -> list[int]:
315
+ return self._metadata.node_ids
316
+
317
+ @property
318
+ def provenance(self) -> str:
319
+ return self._metadata.provenance
320
+
321
+ @property
322
+ def update_resolution(self) -> str:
323
+ return self._metadata.update_resolution
324
+
325
+ # end pass-through
326
+
327
+ @property
328
+ def nodes(self) -> dict[int, WeatherNode]:
329
+ """WeatherNodes indexed by node id."""
330
+ return self._nodes
331
+
332
+ # retrieve node by node id
333
+ def __getitem__(self, item: int):
334
+ return self._nodes[item]
335
+
336
+ def write_file(self, filename: str) -> None:
337
+ """Writes data to filename and metadata to filename.json."""
338
+ self.metadata.write_file(filename + ".json")
339
+
340
+ with open(filename, "wb") as file:
341
+ self._data.tofile(file)
342
+
343
+ return
344
+
345
+ def _from_file(self, filename: str):
346
+ """Reads metadata from filename.json and data from filename."""
347
+ self._metadata = Metadata.from_file(filename + ".json")
348
+ data = np.fromfile(filename, dtype=np.float32)
349
+ expected = self._metadata.node_count * self._metadata.datavalue_count
350
+ assert (
351
+ len(data) == expected
352
+ ), f"length of data ({len(data)}) != #nodes * #values ({expected})"
353
+ self._data = data.reshape(
354
+ self._metadata.node_count, self._metadata.datavalue_count
355
+ )
356
+ self._nodes_and_map()
357
+
358
+ return
359
+
360
+ @classmethod
361
+ def from_csv(
362
+ cls,
363
+ filename: str,
364
+ var_column: str = "airtemp",
365
+ id_column: str = "node_id",
366
+ step_column: str = "step",
367
+ author: str = None,
368
+ provenance: str = None,
369
+ ):
370
+ """
371
+ Create weather from CSV file with specified variable column, node id column, and time step column.
372
+
373
+ Note:
374
+ * Column order in the CSV file is not significant, but columns names must match what is passed to this function.
375
+ * Because a CSV might hold air temperature (may be negative and well outside 0-1 values), relative humidity (must _not_ be negative, must be in the interval [0-1]), or rainfall (must _not_ be negative, likely > 1), this function does not validate incoming data.
376
+ """
377
+ Entry = namedtuple("Entry", ["id", "step", "value"])
378
+
379
+ with open(filename) as csv_file:
380
+ reader = csv.DictReader(csv_file)
381
+ entries = [
382
+ Entry(
383
+ int(row[id_column]), int(row[step_column]), float(row[var_column])
384
+ )
385
+ for row in reader
386
+ ]
387
+
388
+ node_ids = set([entry.id for entry in entries])
389
+ steps_list = [
390
+ sorted([entry.step for entry in entries if entry.id == node_id])
391
+ for node_id in sorted(node_ids)
392
+ ]
393
+
394
+ for i in range(1, len(steps_list)):
395
+ test = len(steps_list[i])
396
+ expected = len(steps_list[0])
397
+ assert (
398
+ test == expected
399
+ ), f"number of data values for nodes is not consistent ({len(steps_list[i]) != {len(steps_list[0])}})"
400
+ test = steps_list[i]
401
+ expected = steps_list[0]
402
+ assert (
403
+ test == expected
404
+ ), f"time steps for node {sorted(node_ids)[i]} != time steps for node {sorted(node_ids)[0]}"
405
+
406
+ steps = sorted(steps_list[0])
407
+ expected = [i for i in range(1, len(steps) + 1)]
408
+ assert steps == expected, f"time steps do not cover all values 1...{len(steps)}"
409
+
410
+ data_count = len(steps)
411
+
412
+ w = Weather(
413
+ node_ids=node_ids,
414
+ datavalue_count=data_count,
415
+ author=author,
416
+ provenance=provenance,
417
+ )
418
+ for node_id in node_ids:
419
+
420
+ sorted_entries_for_node = sorted(
421
+ [entry for entry in entries if entry.id == node_id],
422
+ key=lambda e: e.step,
423
+ )
424
+ data_for_node = [entry.value for entry in sorted_entries_for_node]
425
+
426
+ w.nodes[node_id][:] = data_for_node
427
+
428
+ return w
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: emod-api
3
+ Version: 3.0.2
4
+ Summary: Core tools for modeling using EMOD
5
+ Author-email: Sharon Chen <sharon.chen@gatesfoundation.org>, Zhaowei Du <zhaowei.du@gatesfoundation.org>, Clark Kirkman IV <clark.kirkmand@gatesfoundation.org>, Daniel Bridenbecker <daniel.bridenbecker@gatesfoundation.org>, Svetlana Titova <svetlana.titova@gatesfoundation.org>, Ye Chen <ye.chen@gatesfoundation.org>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/EMOD-Hub/emod-api
8
+ Project-URL: Issues, https://github.com/EMOD-Hub/emod-api/issues
9
+ Keywords: modeling,IDM
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: OS Independent
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: matplotlib
22
+ Requires-Dist: scipy
23
+ Requires-Dist: pandas
24
+ Requires-Dist: numpy
25
+ Requires-Dist: shapely
26
+ Requires-Dist: pyproj
27
+ Requires-Dist: geographiclib
28
+ Requires-Dist: scikit-learn
29
+ Requires-Dist: lz4
30
+ Provides-Extra: docs
31
+ Requires-Dist: mkdocs-material; extra == "docs"
32
+ Requires-Dist: mkdocs-include-markdown-plugin; extra == "docs"
33
+ Requires-Dist: mkdocstrings-python; extra == "docs"
34
+ Requires-Dist: mkdocs-autoapi; extra == "docs"
35
+ Requires-Dist: mkdocs-glightbox; extra == "docs"
36
+ Provides-Extra: lint
37
+ Requires-Dist: flake8; extra == "lint"
38
+ Provides-Extra: test
39
+ Requires-Dist: emod-common; extra == "test"
40
+ Requires-Dist: emod-generic; extra == "test"
41
+ Requires-Dist: emod-hiv; extra == "test"
42
+ Requires-Dist: emod-malaria; extra == "test"
43
+ Requires-Dist: pytest; extra == "test"
44
+ Requires-Dist: pytest-cov; extra == "test"
45
+ Requires-Dist: pytest-xdist; extra == "test"
46
+ Dynamic: license-file
47
+
48
+ # emod-api
49
+
50
+ [![Build docs and deploy to GH Pages](https://github.com/EMOD-Hub/emod-api/actions/workflows/mkdocs_deploy.yml/badge.svg)](https://github.com/EMOD-Hub/emod-api/actions/workflows/mkdocs_deploy.yml)
51
+ [![Lint](https://github.com/EMOD-Hub/emod-api/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/EMOD-Hub/emod-api/actions/workflows/lint.yml)
52
+ [![Test and update version](https://github.com/EMOD-Hub/emod-api/actions/workflows/test_and_bump_version.yml/badge.svg)](https://github.com/EMOD-Hub/emod-api/actions/workflows/test_and_bump_version.yml)
53
+
54
+ ## Python Version
55
+
56
+ Python 3.13 is the recommended and supported version.
57
+
58
+ ## Documentation
59
+
60
+ Documentation available at https://emod-hub.github.io/emod-api/.
61
+
62
+ To build the documentation locally, do the following:
63
+
64
+ 1. Create and activate a venv.
65
+ 2. Navigate to the root directory of the repo.
66
+ ```
67
+ python -m pip install .[docs]
68
+ ```
69
+
70
+ ## Dependencies
71
+
72
+ ### Linux
73
+
74
+ emod-api can use Snappy [de]compression (python-snappy) as necessary if it is installed which requires libdev-snappy (Debian/Ubuntu) or snappy-devel (RedHat/CentOS) on Linux.
75
+
76
+ Ubuntu: ```[sudo] apt install libdev-snappy```
77
+
78
+ CentOS: ```[sudo] yum install snappy-devel``` (not yet tested)
79
+
80
+ NOTE: The python-snappy version needs to be 0.6.1. Newer versions have problems
81
+ working correctly with emod-api.
82
+
83
+
84
+ ## User Stories
85
+
86
+ Input
87
+ - User wants to be able to create a minimal working config.json for any sim type guaranteed to work with a given Eradication binary.
88
+ - User wants to be able to create config.json from large static 'defaults' file and small variable parameters-of-interest file.
89
+ - User wants to be able to create guaranteed to work campaigns without having to interact with campaign.json files.
90
+ - User wants to create a migration file without having to grok our custom binary migration format.
91
+ - User wants to be able to create large multi-node demographics files programatically.
92
+
93
+ Output
94
+ - User wants to be able to get post-processed (cleaned up) schema.
95
+ - User wants to be able to get data from InsetChart.json without worrying about exact file format of the files.
96
+ - User wants to be able to extract data of interest from spatial binary files.
97
+ - User wants to be able to work easily with serialization files.
98
+ - User wants to be able to work easily with (pending) events.sql file.
99
+
100
+ ## Dev Tips
101
+
102
+ - To build package:
103
+ `python -m build --wheel`
104
+
105
+ - To install package (fill in actual version number in filename):
106
+ `python -m pip install dist/emod_api...whl`
107
+
108
+ ## Capability Wishlist
109
+
110
+ - Migration files: users should never have to edit migration binary or header files.
111
+ - Serialization: Population manipulation, such as adding IPs or adding risk factors.
112
+ - Demographics: HINT matrices should not be created directly in demographics.
113
+ - Demographics: Population demographic initalization should be easier and reliable.
114
+ - Config: param_overrides and custom events.
115
+
116
+ ## Running tests
117
+
118
+ Please see the documentation for [testing](/tests/README.md).
119
+
120
+ ## Community
121
+
122
+ The EMOD Community is made up of researchers and software developers, primarily focused on malaria and HIV research.
123
+ We value mutual respect, openness, and a collaborative spirit. If these values resonate with you, we invite you to join our EMOD Slack Community by completing this form:
124
+
125
+ https://forms.office.com/r/sjncGvBjvZ
126
+
127
+ ## Disclaimer
128
+
129
+ The code in this repository was developed by IDM and other collaborators to support our joint research on flexible agent-based modeling.
130
+ We've made it publicly available under the MIT License to provide others with a better understanding of our research and an opportunity to build upon it for their own work. We make no representations that the code works as intended or that we will provide support, address issues that are found, or accept pull requests.
131
+ You are welcome to create your own fork and modify the code to suit your own modeling needs as permitted under the MIT License.
@@ -0,0 +1,71 @@
1
+ emod_api/__init__.py,sha256=YKXrr5J7dV2n7ZhaRv0tigylRDtfOuvJC9Y4ouFZpzo,22
2
+ emod_api/campaign.py,sha256=adfUzobd3bjQ_0xIJiYXEGYs-149V185uWoLepB9XAc,5334
3
+ emod_api/multidim_plotter.py,sha256=iFulG4v_C1EP7JaHqV_Z5LQaNgtEPNxqQTqpHASQ1p0,2871
4
+ emod_api/schema_to_class.py,sha256=S9ytQQyTFRadmiYujhoF4Jv5lx2Cs_GcsHwTxg7NUKA,19784
5
+ emod_api/channelreports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ emod_api/channelreports/channels.py,sha256=HqO3Wxj1uERA9xFfjFi_ren7YEIbDKVQPFB2DWVz56M,12867
7
+ emod_api/channelreports/icj_to_csv.py,sha256=5Gcnqthc1HSde0q-Pejbd1EaD_6rqmLcUdTMvh79GCY,1994
8
+ emod_api/channelreports/plot_icj_means.py,sha256=sgPKrQsJuX7-TK4WLFdfxzCj-NPN8aAlf-jWKwqNzkc,6038
9
+ emod_api/channelreports/plot_prop_report.py,sha256=MCJDSQ6AwfmP6hPbKW_py3wtX3qqkilVYN6sQnhHRIU,7916
10
+ emod_api/channelreports/utils.py,sha256=4uBALlotAMQESjL_IqkXirQgF6m7eCaMFdPeO26EHC0,10571
11
+ emod_api/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ emod_api/config/default_from_schema.py,sha256=K6spHA019V0CM_G1O-ezyjmERLB_bmlv39HeMDPdvu0,507
13
+ emod_api/config/default_from_schema_no_validation.py,sha256=z3Glyn0qN3l-AAVSNMsT95JC_GlzL13NAJNNg8WVAaE,7164
14
+ emod_api/config/from_overrides.py,sha256=SyFNRL9NRcSRJEjEEPoLhUatzHkg5PusM5BIKjQpn2s,5799
15
+ emod_api/demographics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ emod_api/demographics/age_distribution.py,sha256=dyIPe1DkJZPilJwbwrEKR32JB0TyLdZHeKyc6aiH70w,8645
17
+ emod_api/demographics/base_input_file.py,sha256=6EZtBpAukjWIE6y1W_2wA-5qKFlejc0KUqYs-phZ-XQ,728
18
+ emod_api/demographics/calculators.py,sha256=4a7L73qk_5opEV9ww8YhtL2tZ87GMYmw9iuCmGgPi_Q,6307
19
+ emod_api/demographics/demographic_exceptions.py,sha256=NyOIYxGPZmvlqXVOQI441LAANJMxl_zy92YBAcPGUUE,825
20
+ emod_api/demographics/demographics.py,sha256=uybCTzu6zvjekivife4LjQpIgoF2i0NTgVWzbhdbD0s,11972
21
+ emod_api/demographics/demographics_base.py,sha256=m2ZwWo4LgMaDf5a9F2Pr_Az4TSMB2Ecm7NRqpdD_oCc,35329
22
+ emod_api/demographics/demographics_overlay.py,sha256=Ren7jNHaeb8vt6K1Fog6NfhNWulhjy3kBSW5RVC7A8o,1752
23
+ emod_api/demographics/fertility_distribution.py,sha256=tXMcgfI-875onmMptwzGkBlU2iTH-vuyXCKdbs9WeNM,13187
24
+ emod_api/demographics/implicit_functions.py,sha256=dVq36_0xU_A5pVHlIPp3WfNmURmEAZlIlpRbAwNsZO8,2860
25
+ emod_api/demographics/mortality_distribution.py,sha256=lTAwIlijHI98ANFFXplgJW_9Zn6HARp_bjXx-VE3m2k,12349
26
+ emod_api/demographics/node.py,sha256=2qudJSuJNEbjfq_A22kh0Gqmj4cq9jJ3kffJeN8i5xM,20130
27
+ emod_api/demographics/overlay_node.py,sha256=H1b7wNu7sEbTRTAvOimtBjEWYq9HBLWoniSHHMY1_Yw,485
28
+ emod_api/demographics/properties_and_attributes.py,sha256=Bdgi3x_o1jiA37yjC0BIF2Gso4qT7BefazngrfhndTg,42600
29
+ emod_api/demographics/susceptibility_distribution.py,sha256=YyqI-0R-K8DvVLekAQh_5b4oh-J9jBMXU4gJQJxc2T8,9140
30
+ emod_api/demographics/updateable.py,sha256=0R_FcCX1T_AtSmJTJhfVsld-eQ8lChlOHVdZ2fkIf8g,2171
31
+ emod_api/demographics/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ emod_api/demographics/service/grid_construction.py,sha256=JAlHTlaseKJT-VN9CRDqBsDpi6iKVSio_kZtzkS68rw,3594
33
+ emod_api/demographics/service/service.py,sha256=L_UPcS7Wc6RuMyzaFqYyMSvUjuQsQCfmDdIzW70Llwk,2574
34
+ emod_api/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ emod_api/legacy/plotAllCharts.py,sha256=TzhpslzIYQUD6nDEZarthd2BHDDqTP-oA2zLy25YQcE,6997
36
+ emod_api/migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ emod_api/migration/__main__.py,sha256=soJRb_ocmw3bZ5ZFvyZY9ImF64npdt3RCn4tKMtqm6k,758
38
+ emod_api/migration/migration.py,sha256=JIoKOsSqLRV_QzU00ILVDtXPP6_KxC_3kmjSibKVQkE,30504
39
+ emod_api/serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ emod_api/serialization/census_and_mod_pop.py,sha256=hQwAwmwrjTsCR2-r_pqYOTqqY0ASG8EDqr0_4rnrPsg,1957
41
+ emod_api/serialization/dtk_file_support.py,sha256=kXvLPmPd_b2SwYW5yGLKxIYuwwV-aHAEj_-2QnIGJbU,1312
42
+ emod_api/serialization/dtk_file_tools.py,sha256=4lIEV8CtZC5n68j426520y941BstOXQXPg_eIIeF84s,50967
43
+ emod_api/serialization/dtk_file_utility.py,sha256=R9E1rh0YRFobbg3zmwLJZDXM7z5N2SMavIPrBhyluNY,5441
44
+ emod_api/serialization/serialized_population.py,sha256=vAshs2PuW2zYcUUeDkJMJEUJj1YVKzlnXKwXlAghg-A,7487
45
+ emod_api/spatialreports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ emod_api/spatialreports/__main__.py,sha256=8eN8Q1EsL2woDcVhMhBzuM10lCZGOlenDJLxp73f-to,1510
47
+ emod_api/spatialreports/plot_spat_means.py,sha256=KS4oOenpfpwGEffIaSjCp51Qf_3KYmvNJuo5apeJCR4,4047
48
+ emod_api/spatialreports/spatial.py,sha256=M-c9hE56TMI8O-SDyxMtAbDu7wxJW976WRmE_BybzDM,7019
49
+ emod_api/utils/__init__.py,sha256=y8Hf-Xrga4PXwezk2Q5Wfcuf17u5UfKHb2vZQq4N_lo,887
50
+ emod_api/utils/str_enum.py,sha256=drehV-Mvg_DTNRYvuxYhhGn6T7SpUNlhM0gfKel7eTo,106
51
+ emod_api/utils/distributions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ emod_api/utils/distributions/base_distribution.py,sha256=0UAHdsUwCHndfj5t6t5WXmil-_4VDPq42UZn46zXYos,1263
53
+ emod_api/utils/distributions/bimodal_distribution.py,sha256=AVejJlql6rrnyHx7XtvPcZswBUusaOHQZXN8WjXYfIQ,2695
54
+ emod_api/utils/distributions/constant_distribution.py,sha256=8-VdBZSAEfSOAKiSJucf5ziIMh5qAIxUvRejEiu3djE,2278
55
+ emod_api/utils/distributions/demographic_distribution_flag.py,sha256=E1lk5e6Ys7D-KAxAVNEV8PIkbsUS19udJlVfcJvOlxw,293
56
+ emod_api/utils/distributions/distribution_type.py,sha256=n-ScBqhoXVOqaJRevtCpbftjb7qI3YkIi6GKTK8p8FU,667
57
+ emod_api/utils/distributions/dual_constant_distribution.py,sha256=hUL3WGcpAH4ytIYO_ANE3z--p9GUxbQTt6__rudLMnQ,2913
58
+ emod_api/utils/distributions/dual_exponential_distribution.py,sha256=CmBww3i0bnBA15UhkoE8A3T_j7aLO6x1kthdC5K4Kkw,3273
59
+ emod_api/utils/distributions/exponential_distribution.py,sha256=g4Hr1AFLYdlcnZeGnRfs3WYtWsAQG1ZL_tXtZ90vvW4,2583
60
+ emod_api/utils/distributions/gaussian_distribution.py,sha256=h_bsSxyxFrVRk_Zn3v_gygX8ddRtggQXcLpAjhVAsK0,2764
61
+ emod_api/utils/distributions/log_normal_distribution.py,sha256=hTQSp_6b82zsWs4_qyEr-Uj_njt9DAPwAXmvk_wSQkE,2408
62
+ emod_api/utils/distributions/poisson_distribution.py,sha256=rncSCJpmLD8EqJbcIqYMJ3tcv0R2Yx6FRxH2Y_UpEvI,2290
63
+ emod_api/utils/distributions/uniform_distribution.py,sha256=LYdDIGbQIj7IU1Y6zbvXKGRuCdK8u2kgjXKX-WB6yTE,2944
64
+ emod_api/utils/distributions/weibull_distribution.py,sha256=DTuya1NIahQTmtxkEN-PgBx1Jbe7ww7RZ7ALgHs4EO0,2946
65
+ emod_api/weather/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ emod_api/weather/weather.py,sha256=Jf5AUS8ytcMlwsTb4E9XUKswF1LNBx5H73z9boDKK-g,12950
67
+ emod_api-3.0.2.dist-info/licenses/LICENSE,sha256=7Yw18dJ5Tr9BDVK7GGzOX5cPbozqgGn_FC1iV-XlvLc,1080
68
+ emod_api-3.0.2.dist-info/METADATA,sha256=lUR1lekE3yEkw-xQKjRMJfqqsacGUXCw9SM79YAfCxE,5914
69
+ emod_api-3.0.2.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
70
+ emod_api-3.0.2.dist-info/top_level.txt,sha256=NOmgWa8LR-Kl-ZkOuKhdeD6eUFBmthsGu25xjBQ0D4Q,9
71
+ emod_api-3.0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 - 2025 Gates Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ emod_api