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,54 @@
1
+ class InvalidFixedValueException(ValueError):
2
+ pass
3
+
4
+
5
+ class InvalidPopulationGroupLengthException(ValueError):
6
+ pass
7
+
8
+
9
+ class InvalidDataDimensionality(ValueError):
10
+ pass
11
+
12
+
13
+ class InvalidDataDimensionLength(ValueError):
14
+ pass
15
+
16
+
17
+ class InvalidDataDimensionDim0Exception(ValueError):
18
+ pass
19
+
20
+
21
+ class InvalidDataDimensionDim1Exception(ValueError):
22
+ pass
23
+
24
+
25
+ class AgeOutOfRangeException(ValueError):
26
+ pass
27
+
28
+
29
+ class TimeOutOfRangeException(ValueError):
30
+ pass
31
+
32
+
33
+ class DistributionOutOfRangeException(ValueError):
34
+ pass
35
+
36
+
37
+ class NonMonotonicAgeException(ValueError):
38
+ pass
39
+
40
+
41
+ class NonMonotonicTimeException(ValueError):
42
+ pass
43
+
44
+
45
+ class NonMonotonicDistributionException(ValueError):
46
+ pass
47
+
48
+
49
+ class InvalidNodeIdException(ValueError):
50
+ pass
51
+
52
+
53
+ class ConflictingDistributionsException(ValueError):
54
+ pass
@@ -0,0 +1,249 @@
1
+ import json
2
+ import numpy as np
3
+ import pandas as pd
4
+
5
+ from pathlib import Path
6
+ from typing import Union
7
+
8
+ from emod_api.demographics.demographics_base import DemographicsBase
9
+ from emod_api.demographics.node import Node
10
+ from emod_api.demographics.properties_and_attributes import NodeAttributes
11
+ from emod_api.demographics.service import service
12
+
13
+
14
+ class Demographics(DemographicsBase):
15
+ """
16
+ This class is a container of data necessary to produce a EMOD-valid demographics input file.
17
+ """
18
+ def __init__(self, nodes: list[Node], idref: str = None, default_node: Node = None, set_defaults: bool = True):
19
+ """
20
+ Object representation of an EMOD Demographics input (json) file.
21
+
22
+ Args:
23
+ nodes: list(Node) nodes to include in the Demographics object.
24
+ idref: (string, optional) an identifier for the Demographics file. Used to co-identify sets of
25
+ Demographics/overlay files. No value will utilize a default (via inheritance).
26
+ default_node: (Node, optional) Represents default values for all nodes, unless overridden on a per-node
27
+ basis. If not provided, one will be generated by the superclass.
28
+ set_defaults: (bool) Whether to set default node attributes on the default node. Defaults to True. Should
29
+ always be True unless loading via Demographics.from_file() (to replicate in-file data fully).
30
+ """
31
+ super().__init__(nodes=nodes, idref=idref, default_node=default_node)
32
+
33
+ # set some standard EMOD defaults. set_defaults should always be True unless reading from a demographics file,
34
+ # as False allows setting default_node.node_attributes exactly as they are in the file. Loading via
35
+ # Demographics.from_file() is deprecated, see below.
36
+ if set_defaults:
37
+ self.default_node.node_attributes.airport = 1
38
+ self.default_node.node_attributes.seaport = 1
39
+ self.default_node.node_attributes.region = 1
40
+
41
+ def to_file(self, path: Union[str, Path] = "demographics.json", indent: int = 4) -> None:
42
+ """
43
+ Write the Demographics object to an EMOD demograhpics json file.
44
+
45
+ Args:
46
+ path: (str) the filepath to write the file to. Default is "demographics.json".
47
+ indent: (int, optional) The number of spaces to indent for nested JSON elements (Default is 4, None means
48
+ no nesting (one line printing)).
49
+ Returns:
50
+ Nothing
51
+ """
52
+ with open(path, "w") as output:
53
+ if indent is None:
54
+ json.dump(self.to_dict(), output, sort_keys=True)
55
+ else:
56
+ json.dump(self.to_dict(), output, indent=indent, sort_keys=True)
57
+
58
+ def generate_file(self, path: Union[str, Path] = "demographics.json", indent: int = 4):
59
+ import warnings
60
+ warnings.warn("generate_file() is deprecated. Please use to_file()", DeprecationWarning, stacklevel=2)
61
+ self.to_file(path=path, indent=indent)
62
+
63
+ @classmethod
64
+ def from_file(cls, path: str) -> "Demographics":
65
+ """
66
+ Create a Demographics object from an EMOD-compatible demographics json file.
67
+
68
+ Args:
69
+ path (str): the file path to read from.:
70
+
71
+ Returns:
72
+ a Demographics object
73
+ """
74
+ import warnings
75
+ warnings.warn("Loading Demographics from JSON files is deprecated. Objects should be created via Python code "
76
+ "whenever possible as that route is by far the most tested for modern EMOD compatibility. Please "
77
+ "ensure, for example, that the read-in json file (or resultant demographics object) does not "
78
+ "contain conflicting distributions for IndividualAttributes that can be represented with simple "
79
+ "or complex distributions (use of only one at a time is valid).",
80
+ DeprecationWarning, stacklevel=2)
81
+
82
+ with open(path, "r") as src:
83
+ demographics_dict = json.load(src)
84
+ demographics_dict["Defaults"]["NodeID"] = 0 # This is a requirement of all emod-api Demographics objects
85
+ implicit_functions = []
86
+ nodes = []
87
+ for node_dict in demographics_dict["Nodes"]:
88
+ node, implicits = Node.from_data(data=node_dict)
89
+ implicit_functions.extend(implicits)
90
+ nodes.append(node)
91
+ default_node, implicits = Node.from_data(data=demographics_dict["Defaults"])
92
+ implicit_functions.extend(implicits)
93
+ metadata = demographics_dict["Metadata"]
94
+ idref = demographics_dict["Metadata"]["IdReference"]
95
+
96
+ demographics = cls(nodes=nodes, default_node=default_node, idref=idref, set_defaults=False)
97
+ demographics.metadata = metadata
98
+ demographics.implicits.extend(implicit_functions)
99
+ return demographics
100
+
101
+ @classmethod
102
+ def from_template_node(cls,
103
+ lat: float = 0,
104
+ lon: float = 0,
105
+ pop: int = 1000000,
106
+ name: str = "Erewhon",
107
+ forced_id: int = 1) -> "Demographics":
108
+ """
109
+ Creates a basic, single-node Demographics object.
110
+
111
+ Args:
112
+ lat: (float, optional) latitude of node to be created. Default is 0.
113
+ lon: (float, optional) longitude of node to be created. Default is 0.
114
+ pop: (int, optional) number of people in the node to be created. Default is 1000000.
115
+ name: (str, optional) name of node to be created. Default is Erewhon.
116
+ forced_id: (int, optional) id of node to be created. Default is 1.
117
+
118
+ Returns:
119
+ A Demographics object
120
+ """
121
+ new_nodes = [Node(lat=lat, lon=lon, pop=pop, forced_id=forced_id, name=name)]
122
+ return cls(nodes=new_nodes)
123
+
124
+ # The below implements the standard naming convention for DTK nodes based on latitude and longitude.
125
+ # The node ID encodes both lat and long at a specified pixel resolution, and I've maintained this
126
+ # convention even when running on spatial setups that are not non-uniform grids.
127
+ @staticmethod
128
+ def _node_id_from_lat_lon_res(lat: float, lon: float, res: float = 30 / 3600) -> int:
129
+ node_id = int((np.floor((lon + 180) / res) * (2 ** 16)).astype(np.uint) + (np.floor((lat + 90) / res) + 1).astype(np.uint))
130
+ return node_id
131
+
132
+ @classmethod
133
+ def from_csv(cls,
134
+ input_file: str,
135
+ res: float = 30 / 3600,
136
+ id_ref: str = "from_csv") -> "Demographics":
137
+ """
138
+ Create an EMOD-compatible :py:class:`Demographics` instance from a csv population-by-node file.
139
+
140
+ Args:
141
+ input_file (str): the csv filepath to read from.
142
+ res (float, optional): spatial resolution of the nodes in arc-seconds
143
+ id_ref (str, optional): Description of the file source for co-identification of demographics objects/files.
144
+
145
+ Returns:
146
+ A Demographics object
147
+ """
148
+ def get_value(row, headers):
149
+ for h in headers:
150
+ if row.get(h) is not None:
151
+ return float(row.get(h))
152
+ return None
153
+
154
+ print(f"{input_file} found and being read for demographics.json file creation.")
155
+ node_info = pd.read_csv(input_file, encoding='iso-8859-1')
156
+ out_nodes = []
157
+ for index, row in node_info.iterrows():
158
+ if 'under5_pop' in row:
159
+ pop = int(6 * row['under5_pop'])
160
+ if pop < 25000:
161
+ continue
162
+ else:
163
+ pop = int(row['pop'])
164
+
165
+ latitude_headers = ["lat", "latitude", "LAT", "LATITUDE", "Latitude", "Lat"]
166
+ lat = get_value(row, latitude_headers)
167
+
168
+ longitude_headers = ["lon", "longitude", "LON", "LONGITUDE", "Longitude", "Lon"]
169
+ lon = get_value(row, longitude_headers)
170
+
171
+ birth_rate_headers = ["birth", "Birth", "birth_rate", "birthrate", "BirthRate", "Birth_Rate", "BIRTH",
172
+ "birth rate", "Birth Rate"]
173
+ birth_rate = get_value(row, birth_rate_headers)
174
+ if birth_rate is not None and birth_rate < 0.0:
175
+ raise ValueError("Birth rate defined in " + input_file + " must be greater 0.")
176
+
177
+ node_id = row.get('node_id')
178
+ if node_id is not None and int(node_id) == 0:
179
+ raise ValueError("Node ids can not be '0'.")
180
+
181
+ forced_id = int(cls._node_id_from_lat_lon_res(lat=lat, lon=lon, res=res)) if node_id is None else int(node_id)
182
+
183
+ if 'loc' in row:
184
+ place_name = str(row['loc'])
185
+ else:
186
+ place_name = None
187
+ meta = {}
188
+ """
189
+ meta = {'dot_name': (row['ADM0_NAME']+':'+row['ADM1_NAME']+':'+row['ADM2_NAME']),
190
+ 'GUID': row['GUID'],
191
+ 'density': row['under5_pop_weighted_density']}
192
+ """
193
+ node_attributes = NodeAttributes(name=place_name, birth_rate=birth_rate)
194
+ node = Node(lat, lon, pop,
195
+ node_attributes=node_attributes,
196
+ forced_id=forced_id, meta=meta)
197
+
198
+ out_nodes.append(node)
199
+ return cls(nodes=out_nodes, idref=id_ref)
200
+
201
+ # This will be the long-term API for this function.
202
+ @classmethod
203
+ def from_pop_raster_csv(cls,
204
+ pop_filename_in: str,
205
+ res: float = 1 / 120,
206
+ id_ref: str = "from_raster",
207
+ pop_dirname_out: str = "spatial_gridded_pop_dir",
208
+ site: str = "No_Site") -> "Demographics":
209
+ """
210
+ Take a csv of a population-counts raster and build a grid for use with EMOD simulations.
211
+ Grid size is specified by grid resolution in arcs or in kilometers. The population counts
212
+ from the raster csv are then assigned to their nearest grid center and a new intermediate
213
+ grid file is generated with latitude, longitude and population. This file is then fed to
214
+ from_csv to generate a demographics object.
215
+
216
+ Args:
217
+ pop_filename_in (str): The filepath of the population-counts raster in CSV format.
218
+ res (float, optional): The grid resolution in arcs or kilometers. Default is 1/120.
219
+ id_ref (str, optional): Identifier reference for the grid. Default is "from_raster".
220
+ pop_dirname_out (str, optional): The output directory name to hold the intermediate grid file.
221
+ Default is "spatial_gridded_pop_dir".
222
+ site (str, optional): The site name or identifier. Default is "No_Site".
223
+
224
+ Returns:
225
+ A Demographics object based on the input grid file.
226
+
227
+ Raises:
228
+
229
+ """
230
+ grid_file_path = service._create_grid_files(point_records_file_in=pop_filename_in,
231
+ final_grid_files_dir=pop_dirname_out,
232
+ site=site)
233
+ print(f"{grid_file_path} grid file created.")
234
+ return cls.from_csv(input_file=grid_file_path, res=res, id_ref=id_ref)
235
+
236
+ @classmethod
237
+ def from_pop_csv(cls,
238
+ pop_filename_in: str,
239
+ res: float = 1 / 120,
240
+ id_ref: str = "from_raster",
241
+ pop_dirname_out: str = "spatial_gridded_pop_dir",
242
+ site: str = "No_Site") -> "Demographics":
243
+ import warnings
244
+ warnings.warn("from_pop_csv is deprecated. Please use from_pop_csv.", DeprecationWarning, stacklevel=2)
245
+ return cls.from_pop_raster_csv(pop_filename_in=pop_filename_in,
246
+ res=res,
247
+ id_ref=id_ref,
248
+ pop_dirname_out=pop_dirname_out,
249
+ site=site)