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,80 @@
1
+ import os
2
+ import matplotlib.pyplot as plt
3
+ from matplotlib import cm
4
+ from mpl_toolkits.mplot3d import Axes3D
5
+ import sqlite3
6
+
7
+ """
8
+ Plot EMOD output data from an experiment found in an SQLite db created by emodpy.
9
+ """
10
+
11
+
12
+ def plot_from_sql(x_tag: str,
13
+ y_tag: str,
14
+ output: str,
15
+ label: str,
16
+ exp_id: str = None):
17
+ """
18
+ Plot colormap/3D figure from data in <experiment_id>/results.db.
19
+
20
+ Args:
21
+ x_tag: Tag to use as x axis.
22
+ y_tag: Tag to use as y axis.
23
+ output: String to use as output, needs to correspond to one of the output cols in the db.
24
+ label: Figure needs a label.
25
+ exp_id: Optional experiment id. If omitted, 'latest_experiment' is used.
26
+ """
27
+
28
+ fig = plt.figure()
29
+ ax = Axes3D(fig)
30
+
31
+ if exp_id:
32
+ db = os.path.join(str(exp_id), "results.db")
33
+ else:
34
+ db = os.path.join("latest_experiment", "results.db")
35
+ con = sqlite3.connect(db)
36
+ cur = con.cursor()
37
+ x_tag = x_tag.replace(' ', '_').replace('-', '_')
38
+ y_tag = y_tag.replace(' ', '_').replace('-', '_')
39
+ query = f"select {x_tag}, {y_tag}, avg({output}) from results group by {x_tag}, {y_tag};"
40
+ try:
41
+ cur.execute(query)
42
+ results = cur.fetchall()
43
+ except Exception as ex:
44
+ print(f"Encountered fatal exception {ex} when executing query {query} on db {db}.")
45
+ return
46
+
47
+ x = []
48
+ y = []
49
+ z = []
50
+ for result in results:
51
+ x.append(result[0])
52
+ y.append(result[1])
53
+ z.append(result[2])
54
+ surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1)
55
+ ax.set_xlabel(f"{x_tag} rate")
56
+ ax.set_ylabel(f"{y_tag} rate")
57
+ fig.colorbar(surf, shrink=0.5, aspect=5)
58
+ ax.view_init(elev=90, azim=0)
59
+ plt.title(label)
60
+ plt.show()
61
+
62
+
63
+ if __name__ == "__main__":
64
+ import argparse
65
+ parser = argparse.ArgumentParser(description='Spatial Report Plotting')
66
+ parser.add_argument('-x', '--xtag', action='store', default="", help='X tag (must be in db)')
67
+ parser.add_argument('-y', '--ytag', action='store', default="", help='Y tag (must be in db')
68
+ parser.add_argument('-o', '--output', action='store', default="", help='Single value output file')
69
+ parser.add_argument('-t', '--title', action='store', default="", help='Graph title')
70
+ parser.add_argument('-e', '--experiment_id', action='store', default=None, help='experiment id to plot, uses latest_experiment folder if omitted (not used yet)')
71
+ args = parser.parse_args()
72
+ if not args.experiment_id:
73
+ with open("COMPS_ID", "r") as fp:
74
+ args.experiment_id = fp.read()
75
+
76
+ # check that folder with name experiment_id exists
77
+ if not os.path.exists(str(args.experiment_id)):
78
+ raise ValueError(f"Don't see folder for {args.experiment_id}.")
79
+
80
+ plot_from_sql(args.xtag, args.ytag, args.output, args.title)
@@ -0,0 +1,440 @@
1
+ import json
2
+ import os
3
+
4
+ from collections import OrderedDict
5
+
6
+ schema_cache = None
7
+ _schema_path = None
8
+
9
+
10
+ class ReadOnlyDict(OrderedDict):
11
+ def __missing__(self, key):
12
+ raise KeyError(f"'{key}' not found in this object. List of keys = {self.keys()}.")
13
+
14
+ def __getattr__(self, item):
15
+ try:
16
+ return self[item]
17
+ except KeyError:
18
+ raise AttributeError(item) # to allow deepcopy (from s/o)
19
+
20
+ def __setattr__(self, key, value):
21
+ # if key not in self and "Config" not in key and "List" not in key:
22
+ if key not in self: # these are lame; find way in schema to initialize complex types {}, [], or null
23
+ self.__missing__(key) # this should not be necessary
24
+
25
+ if value is None:
26
+ return
27
+
28
+ if "schema" not in self:
29
+ print(f"DEBUG: No schema in node for param {key}.")
30
+ else:
31
+ if "type" in self["schema"][key]:
32
+ if self["schema"][key]["type"] in ["integer", "float"]:
33
+ if type(value) is str:
34
+ raise ValueError(f"{value} is string when needs to be {self['schema'][key]['type']} "
35
+ f"for parameter {key}.")
36
+ elif value < self["schema"][key]["min"]:
37
+ raise ValueError(f"{value} is below minimum {self['schema'][key]['min']} for parameter {key}.")
38
+ elif value > self["schema"][key]["max"]:
39
+ raise ValueError(f"{value} is above maximum {self['schema'][key]['max']} "
40
+ f"for parameter {key}.")
41
+
42
+ elif self["schema"][key]["type"] == "enum":
43
+ if value not in self["schema"][key]["enum"]:
44
+ raise ValueError(f"{value} is not list of possible values {self['schema'][key]['enum']} "
45
+ f"for parameter {key}.")
46
+ elif self["schema"][key]["type"] == "bool":
47
+ if not any([value is False, value is True, value == 1, value == 0]):
48
+ raise ValueError(f"value needs to be a bool for parameter {key}.")
49
+ if value is False:
50
+ value = 0
51
+ elif value is True:
52
+ value = 1
53
+ elif "Vector" in self["schema"][key]["type"] and "idmType" not in self['schema'][key]['type']:
54
+ if type(value) is not list:
55
+ raise ValueError(f"Value needs to be a list for parameter {key}.")
56
+
57
+ # if param is dependent on a param, set that param to the value. but needs to be recursive.
58
+ if "depends-on" in self["schema"][key]:
59
+ # set this value BUT NOT IF key == Simulation_Type!
60
+ for k, v in dict(self["schema"][key]["depends-on"]).items():
61
+ if k == "Simulation_Type":
62
+ if k not in self.keys():
63
+ # not supported yet for campaigns; need to provide campaign blobs access to config...
64
+ continue
65
+ elif self["Simulation_Type"] in [x.strip() for x in v.split(',')]:
66
+ pass
67
+ else:
68
+ raise ValueError(f"ERROR: Simulation_Type needs to be one of {v} for you to be able to "
69
+ f"set {key} to {value} but it seems to be {self.Simulation_Type}.")
70
+ else:
71
+ if type(v) is str and len(v.split(',')) > 1:
72
+ v = v.split(',')[0] # pretty arbitrary but least arbitrary of options it seems
73
+ if self["schema"][k]['default'] == self[k]:
74
+ # only set implicit value if default (i.e. user didn't set it)
75
+ self.__setattr__(k, v)
76
+ if "implicits" not in self: # This should NOT be needed
77
+ self["implicits"] = []
78
+ self["implicits"].append(key)
79
+
80
+ if "default" in self["schema"][key] and self["schema"][key]["default"] == value and value == self[key]:
81
+ return
82
+
83
+ self[key] = value
84
+
85
+ if "explicits" not in self: # This should NOT be needed
86
+ self["explicits"] = []
87
+
88
+ self["explicits"].append(key)
89
+ return
90
+
91
+ def set_schema(self, schema):
92
+ """
93
+ Add schema node.
94
+ """
95
+ self["schema"] = schema
96
+
97
+ def to_file(self, config_name="config.json"):
98
+ """
99
+ Write 'clean' config file out to disk as json.
100
+ Param: config_name (defaults to 'config.json')
101
+ """
102
+ config = self.finalize()
103
+ with open(config_name, "w") as config_file:
104
+ json.dump(config, config_file, indent=4, sort_keys=True)
105
+
106
+ def finalize(self, show_warnings: bool = False):
107
+ """
108
+ Remove all params that are disabled by depends-on param being off and schema node.
109
+ """
110
+ nuke_list = []
111
+ for key, v in self.items():
112
+ finalized_keys = []
113
+ if type(v) is ReadOnlyDict and "schema" in v.keys():
114
+ v.finalize() # experimental recursive code
115
+ elif type(v) is list and len(v) > 0 and type(v[0]) is ReadOnlyDict and "schema" in v[0].keys():
116
+ for elem in v:
117
+ elem.finalize() # experimental recursive code
118
+
119
+ if key in ["schema", "explicits", "implicits"]:
120
+ continue
121
+ elif key not in self["schema"]:
122
+ if show_warnings:
123
+ print(f"WARNING: During schema-based param purge, {key} not in schema.")
124
+ elif "depends-on" in self["schema"][key]:
125
+ def purge_key(key):
126
+ if key not in self.keys() or "depends-on" not in self["schema"][key]:
127
+ return
128
+ for dep_k, dep_v in dict(self["schema"][key]["depends-on"]).items():
129
+ if dep_k not in nuke_list and dep_k not in finalized_keys:
130
+ purge_key(dep_k) # careful
131
+ if type(dep_v) is str:
132
+ vals = [x.strip() for x in str(dep_v).split(',')]
133
+ if dep_k in self and self[dep_k] not in vals:
134
+ nuke_list.append(key)
135
+ else:
136
+ if self[dep_k] != dep_v or dep_k in nuke_list:
137
+ nuke_list.append(key)
138
+
139
+ finalized_keys.append(key)
140
+ purge_key(key)
141
+ elif v == "UNINITIALIZED STRING": # work around current schema string defaults
142
+ self[key] = ""
143
+
144
+ # Logging parameters are implicit and only need to be retained if other than default
145
+ if "logLevel_default" in self.keys():
146
+ ll_default = self["logLevel_default"]
147
+ for key, val in self.items():
148
+ if key.startswith("logLevel_") and val == ll_default and key != "logLevel_default":
149
+ nuke_list.append(key)
150
+
151
+ if "Actual_IndividualIntervention_Config" in self.keys() and "Actual_NodeIntervention_Config" in self.keys():
152
+ # Need to purge one of these; yes this could be done cleverer but this is easy to follow and maintain
153
+ ind_len = len(self["Actual_IndividualIntervention_Config"])
154
+ nod_len = len(self["Actual_NodeIntervention_Config"])
155
+ if ind_len > 0 and nod_len == 0:
156
+ self.pop("Actual_NodeIntervention_Config")
157
+ elif nod_len > 0 and ind_len == 0:
158
+ self.pop("Actual_IndividualIntervention_Config")
159
+ else:
160
+ raise ValueError("We have both Actual_IndividualIntervention_Config "
161
+ "and Actual_NodeIntervention_Config set.")
162
+
163
+ # Note that nuke_list is not a set and is typically full of duplicates. There are many ways to de-dupe.
164
+ for nuke_key in nuke_list:
165
+ if nuke_key in self.keys():
166
+ if "explicits" in self and nuke_key in self['explicits']:
167
+ raise ValueError(f"You set param {nuke_key} but it was disabled and is not being used.")
168
+ self.pop(nuke_key)
169
+ if "implicits" in self:
170
+ self.pop("implicits")
171
+ if "explicits" in self:
172
+ self.pop("explicits")
173
+ try:
174
+ self.pop("schema")
175
+ except Exception as ex:
176
+ raise ValueError(f"ERROR: Something bad happened during finalize: {ex}.")
177
+ return self
178
+
179
+
180
+ def clear_schema_cache():
181
+ """
182
+ Clear cached version of the schema.
183
+ """
184
+ global schema_cache
185
+ schema_cache = None
186
+
187
+ return None
188
+
189
+
190
+ def get_class_with_defaults(classname, schema_path=None, schema_json=None):
191
+ """
192
+ Return the default parameter values for a datatype defined in schema.
193
+
194
+ Args:
195
+ classname (str): Name of target datatype
196
+ schema_path (str): Filename of schema: DEPRECATED, use schema_json instead
197
+ schema_json (dict): Contents of schema file
198
+ Returns:
199
+ (dict): Default parameter values for requested datatype
200
+ """
201
+
202
+ # Function should be removed after schema_path argument is removed
203
+ def get_schema(schema_path=None, schema_json=None):
204
+ global schema_cache
205
+ global _schema_path
206
+ schema_ret = None
207
+
208
+ # Prefer schema-as-dict if provided
209
+ if schema_json:
210
+ schema_ret = schema_json
211
+ # Then check file path
212
+ elif schema_path:
213
+ if not os.path.exists(schema_path):
214
+ raise ValueError(f"ERROR: No file found at {schema_path}. "
215
+ f"A valid schema path needs to exist at the path specified.")
216
+
217
+ if schema_cache is None or _schema_path != schema_path:
218
+ with open(schema_path) as file:
219
+ schema_val = json.load(file)
220
+ schema_cache = schema_val
221
+ schema_ret = schema_val
222
+ _schema_path = schema_path
223
+ else:
224
+ schema_ret = schema_cache
225
+ else:
226
+ raise ValueError("A valid schema_path or schema_json needs to be specified.")
227
+
228
+ return schema_ret
229
+
230
+ # Evaluate default value; assumes default containers are empty
231
+ def eval_default(default_val):
232
+ if (type(default_val) is dict):
233
+ return dict()
234
+ elif (type(default_val) is list):
235
+ return list()
236
+ else:
237
+ return default_val
238
+
239
+ # Assign default value from schema
240
+ def get_default(schema_obj, schema):
241
+ default = None
242
+ try:
243
+ if ("default" in schema_obj):
244
+ default = eval_default(schema_obj["default"])
245
+ elif ("type" in schema_obj):
246
+ default = get_class_with_defaults(schema_obj["type"], schema_json=schema)
247
+ else:
248
+ raise ValueError("No 'default' or 'type' in object.")
249
+ except Exception as ex:
250
+ raise ValueError(f"ERROR for object: {schema_obj}: {ex}")
251
+ return default
252
+
253
+ # Depending on the schema, a WaningEffect may be an abstract type or a
254
+ # concrete type. If the text 'WaningEffect' is part of any of the keys in
255
+ # idmType:WaningEffect, then the schema is using WaningEffect as an
256
+ # abstract type, and uses_old_waning should return True.
257
+ def uses_old_waning(schema_idm):
258
+ if ("idmType:WaningEffect" not in schema_idm):
259
+ return True
260
+ waning_effects = schema_idm["idmType:WaningEffect"].keys()
261
+ return any(["WaningEffect" in k for k in waning_effects])
262
+
263
+ schema = get_schema(schema_path, schema_json)
264
+ schema_blob = None
265
+ ret_json = dict()
266
+
267
+ schema_idm = schema["idmTypes"]
268
+ abstract_key1 = "idmAbstractType:CampaignEvent"
269
+ abstract_key2 = "idmAbstractType:EventCoordinator"
270
+ abstract_key3a = "idmAbstractType:IReport"
271
+ abstract_key3b = "idmType:IReport"
272
+ abstract_key4 = "idmAbstractType:NodeSet"
273
+ abstract_key5a = "idmAbstractType:WaningEffect"
274
+ abstract_key5b = "idmType:WaningEffect"
275
+ abstract_key6a = "idmAbstractType:AdditionalRestrictions"
276
+ abstract_key6b = "idmType:AdditionalRestrictions"
277
+ abstract_key7 = "idmAbstractType:IndividualIntervention"
278
+ abstract_key8 = "idmAbstractType:NodeIntervention"
279
+ abstract_key9 = "idmAbstractType:Intervention"
280
+
281
+ # Abstract types get initialized to empty object
282
+ if (classname.startswith("idmAbstractType")):
283
+ ret_json = dict()
284
+
285
+ # IReport is an abstract type, but incorrectly named
286
+ # abstract_key3b = "idmType:IReport"
287
+ elif (classname == abstract_key3b):
288
+ ret_json = dict()
289
+
290
+ # Waning effect (old style) is an abstract type, but incorrectly named
291
+ # abstract_key5b = "idmType:WaningEffect"
292
+ elif (classname == abstract_key5b and uses_old_waning(schema_idm)):
293
+ ret_json = dict()
294
+
295
+ # AdditionalRestriction is an abstract type, but incorrectly named
296
+ # abstract_key6b = "idmType:AdditionalRestrictions"
297
+ elif (classname == abstract_key6b):
298
+ ret_json = dict()
299
+
300
+ # Check if class is CampaignEvent type
301
+ # abstract_key1 = "idmAbstractType:CampaignEvent"
302
+ elif (abstract_key1 in schema_idm and classname in schema_idm[abstract_key1]):
303
+ schema_blob = schema_idm[abstract_key1][classname]
304
+ ret_json["class"] = schema_blob["class"]
305
+ for key_str in schema_blob.keys():
306
+ if key_str in ["class", "Sim_Types"]:
307
+ continue
308
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
309
+
310
+ # Check if class is EventCoordinator type
311
+ # abstract_key2 = "idmAbstractType:EventCoordinator"
312
+ elif (abstract_key2 in schema_idm and classname in schema_idm[abstract_key2]):
313
+ schema_blob = schema_idm[abstract_key2][classname]
314
+ ret_json["class"] = schema_blob["class"]
315
+ for key_str in schema_blob.keys():
316
+ if key_str in ["class", "Sim_Types"]:
317
+ continue
318
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
319
+
320
+ # Check if class is IReport type
321
+ # abstract_key3a = "idmAbstractType:IReport"
322
+ elif (abstract_key3a in schema_idm and classname in schema_idm[abstract_key3a]):
323
+ schema_blob = schema_idm[abstract_key3a][classname]
324
+ ret_json["class"] = schema_blob["class"]
325
+ for key_str in schema_blob.keys():
326
+ if key_str in ["class", "Sim_Types"]:
327
+ continue
328
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
329
+
330
+ # abstract_key3b = "idmType:IReport"
331
+ elif (abstract_key3b in schema_idm and classname in schema_idm[abstract_key3b]):
332
+ schema_blob = schema_idm[abstract_key3b][classname]
333
+ ret_json["class"] = schema_blob["class"]
334
+ for key_str in schema_blob.keys():
335
+ if key_str in ["class", "Sim_Types"]:
336
+ continue
337
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
338
+
339
+ # Check if class is NodeSet type
340
+ # abstract_key4 = "idmAbstractType:NodeSet"
341
+ elif (abstract_key4 in schema_idm and classname in schema_idm[abstract_key4]):
342
+ schema_blob = schema_idm[abstract_key4][classname]
343
+ ret_json["class"] = schema_blob["class"]
344
+ for key_str in schema_blob.keys():
345
+ if key_str in ["class", "Sim_Types"]:
346
+ continue
347
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
348
+
349
+ # Check if class is WaningEffect (old style) type
350
+ # abstract_key5a = "idmAbstractType:WaningEffect"
351
+ elif (abstract_key5a in schema_idm and classname in schema_idm[abstract_key5a]):
352
+ schema_blob = schema_idm[abstract_key5a][classname]
353
+ ret_json["class"] = schema_blob["class"]
354
+ for key_str in schema_blob.keys():
355
+ if key_str in ["class", "Sim_Types"]:
356
+ continue
357
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
358
+
359
+ # abstract_key5b = "idmType:WaningEffect"
360
+ elif (abstract_key5b in schema_idm and classname in schema_idm[abstract_key5b]):
361
+ schema_blob = schema_idm[abstract_key5b][classname]
362
+ ret_json["class"] = schema_blob["class"]
363
+ for key_str in schema_blob.keys():
364
+ if key_str in ["class", "Sim_Types"]:
365
+ continue
366
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
367
+
368
+ # Check if class is AdditionalRestriction type
369
+ # abstract_key6a = "idmAbstractType:AdditionalRestrictions"
370
+ elif (abstract_key6a in schema_idm and classname in schema_idm[abstract_key6a]):
371
+ schema_blob = schema_idm[abstract_key6a][classname]
372
+ ret_json["class"] = schema_blob["class"]
373
+ for key_str in schema_blob.keys():
374
+ if key_str in ["class", "Sim_Types"]:
375
+ continue
376
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
377
+
378
+ # abstract_key6b = "idmType:AdditionalRestrictions"
379
+ elif (abstract_key6b in schema_idm and classname in schema_idm[abstract_key6b]):
380
+ schema_blob = schema_idm[abstract_key6b][classname]
381
+ ret_json["class"] = schema_blob["class"]
382
+ for key_str in schema_blob.keys():
383
+ if key_str in ["class", "Sim_Types"]:
384
+ continue
385
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
386
+
387
+ # abstract_key7 = "idmAbstractType:IndividualIntervention"
388
+ elif (abstract_key7 in schema_idm and classname in schema_idm[abstract_key7]):
389
+ schema_blob = schema_idm[abstract_key7][classname]
390
+ ret_json["class"] = schema_blob["class"]
391
+ for key_str in schema_blob.keys():
392
+ if key_str in ["class", "Sim_Types"]:
393
+ continue
394
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
395
+
396
+ # abstract_key8 = "idmAbstractType:NodeIntervention"
397
+ elif (abstract_key8 in schema_idm and classname in schema_idm[abstract_key8]):
398
+ schema_blob = schema_idm[abstract_key8][classname]
399
+ ret_json["class"] = schema_blob["class"]
400
+ for key_str in schema_blob.keys():
401
+ if key_str in ["class", "Sim_Types"]:
402
+ continue
403
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
404
+
405
+ # Check if class is an idmType
406
+ elif (classname.startswith("idmType:")):
407
+ if classname in schema_idm:
408
+ schema_blob = schema_idm[classname]
409
+ if ("default" in schema_blob):
410
+ ret_json = eval_default(schema_blob["default"])
411
+ else:
412
+ for key_str in schema_blob.keys():
413
+ if key_str in ["class", "Sim_Types"]:
414
+ continue
415
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
416
+ else:
417
+ raise ValueError(f"ERROR: '{classname}' not found in schema.")
418
+
419
+ # Looking for NodeIntervention or IndividualIntervention
420
+ # abstract_key9 = "idmAbstractType:Intervention"
421
+ else:
422
+ for iv_type in schema_idm[abstract_key9].keys():
423
+ if classname in schema_idm[abstract_key9][iv_type].keys():
424
+ schema_blob = schema_idm[abstract_key9][iv_type][classname]
425
+ ret_json["class"] = schema_blob["class"]
426
+ for key_str in schema_blob.keys():
427
+ if key_str in ["class", "Sim_Types"]:
428
+ continue
429
+ ret_json[key_str] = get_default(schema_blob[key_str], schema)
430
+
431
+ if bool(ret_json) is False:
432
+ raise ValueError(f"Failed to find {classname} in schema.")
433
+
434
+ ret_this = ret_json
435
+ # If non-empty dict, add schema
436
+ if (type(ret_json) is dict and ret_json):
437
+ ret_this = ReadOnlyDict(ret_json)
438
+ ret_this.set_schema(schema_blob)
439
+
440
+ return ret_this
File without changes
@@ -0,0 +1,48 @@
1
+ import emod_api.serialization.serialized_population as sp
2
+
3
+
4
+ def change_ser_pop(input_serpop_path, mod_fn=None, save_file_path=None):
5
+ """
6
+ This function loads a serialization population file, iterates over each person, calls a
7
+ user-provided callback with each individuals, and saves the population as manipulated by
8
+ the user.
9
+
10
+ The mod function can act at will on the population object. There are no checks.
11
+
12
+ The new file is saved to a name provided by user. Interactive if none provided to function.
13
+
14
+ Assuming a single node file for now.
15
+ """
16
+ # print( f"change_ser_pop called with 'path'={input_serpop_path}, 'save_file_path'={save_file_path}." )
17
+
18
+ """
19
+ if not os.path.exists( input_serpop_path ):
20
+ print( f"Couldn't find specified serialized population file: {input_serpop_path}." )
21
+ sys.exit(0)
22
+ """
23
+
24
+ if mod_fn is None:
25
+ print("Calling with no mod_fn serves to test whether the .dtk input file can be loaded, but makes no change.")
26
+
27
+ ser_pop = sp.SerializedPopulation(input_serpop_path)
28
+
29
+ if len(ser_pop.nodes) > 1:
30
+ print("This code currently operates properly on single-node population files. This file has {len(ser_pop)} nodes.")
31
+ return
32
+
33
+ node_0 = ser_pop.nodes[0]
34
+ pop_size = len(node_0["individualHumans"])
35
+ print(f"Found {pop_size} people -- or agents -- in serialized population file.")
36
+
37
+ for person in range(len(node_0["individualHumans"])):
38
+ # 1) Figure out what age bucket this person is in.
39
+ if "individualHumans" not in node_0:
40
+ print("ERROR: Failed to find 'individualHumans' in node_0 serialized population input.")
41
+
42
+ if mod_fn:
43
+ node_0["individualHumans"][person] = mod_fn(node_0["individualHumans"][person])
44
+
45
+ if mod_fn:
46
+ if not save_file_path:
47
+ save_file_path = input("Enter filename of new serialized population (e.g., my_sp_file.dtk): ")
48
+ ser_pop.write(save_file_path)
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/python
2
+
3
+ import lz4.block
4
+
5
+ try:
6
+ import snappy
7
+ SNAPPY_SUPPORT = True
8
+ except Exception:
9
+ SNAPPY_SUPPORT = False
10
+
11
+
12
+ # noinspection PyCamelCase
13
+ class Uncompressed(object):
14
+
15
+ @classmethod
16
+ def compress(cls, data):
17
+ return data
18
+
19
+ @classmethod
20
+ def uncompress(cls, data):
21
+ return data
22
+
23
+
24
+ class EllZeeFour(object):
25
+
26
+ @classmethod
27
+ def compress(cls, data):
28
+ return lz4.block.compress(data if type(data) is bytes else data.encode())
29
+
30
+ @classmethod
31
+ def uncompress(cls, data):
32
+ return lz4.block.decompress(data)
33
+
34
+
35
+ class Snappy(object):
36
+
37
+ @classmethod
38
+ def compress(cls, data):
39
+ if SNAPPY_SUPPORT:
40
+ return snappy.compress(data)
41
+ raise UserWarning("Snappy [de]compression not available.")
42
+
43
+ @classmethod
44
+ def uncompress(cls, data):
45
+ if SNAPPY_SUPPORT:
46
+ return snappy.uncompress(data)
47
+ raise UserWarning("Snappy [de]compression not available.")
48
+
49
+
50
+ class SerialObject(dict):
51
+ # noinspection PyDefaultArgument
52
+ def __init__(self, dictionary={}):
53
+ super(SerialObject, self).__init__(dictionary)
54
+ self.__dict__ = self
55
+ return
56
+
57
+
58
+ class NullPtr(SerialObject):
59
+ def __init__(self):
60
+ nullptr = {'__class__': 'nullptr'}
61
+ super(NullPtr, self).__init__(nullptr)