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.
- emod_api/__init__.py +1 -0
- emod_api/campaign.py +170 -0
- emod_api/channelreports/__init__.py +0 -0
- emod_api/channelreports/channels.py +433 -0
- emod_api/channelreports/icj_to_csv.py +65 -0
- emod_api/channelreports/plot_icj_means.py +149 -0
- emod_api/channelreports/plot_prop_report.py +205 -0
- emod_api/channelreports/utils.py +326 -0
- emod_api/config/__init__.py +0 -0
- emod_api/config/default_from_schema.py +16 -0
- emod_api/config/default_from_schema_no_validation.py +177 -0
- emod_api/config/from_overrides.py +135 -0
- emod_api/demographics/__init__.py +0 -0
- emod_api/demographics/age_distribution.py +163 -0
- emod_api/demographics/base_input_file.py +28 -0
- emod_api/demographics/calculators.py +159 -0
- emod_api/demographics/demographic_exceptions.py +54 -0
- emod_api/demographics/demographics.py +249 -0
- emod_api/demographics/demographics_base.py +752 -0
- emod_api/demographics/demographics_overlay.py +41 -0
- emod_api/demographics/fertility_distribution.py +235 -0
- emod_api/demographics/implicit_functions.py +112 -0
- emod_api/demographics/mortality_distribution.py +227 -0
- emod_api/demographics/node.py +456 -0
- emod_api/demographics/overlay_node.py +16 -0
- emod_api/demographics/properties_and_attributes.py +737 -0
- emod_api/demographics/service/__init__.py +0 -0
- emod_api/demographics/service/grid_construction.py +143 -0
- emod_api/demographics/service/service.py +55 -0
- emod_api/demographics/susceptibility_distribution.py +170 -0
- emod_api/demographics/updateable.py +58 -0
- emod_api/legacy/__init__.py +0 -0
- emod_api/legacy/plotAllCharts.py +230 -0
- emod_api/migration/__init__.py +0 -0
- emod_api/migration/__main__.py +22 -0
- emod_api/migration/migration.py +782 -0
- emod_api/multidim_plotter.py +80 -0
- emod_api/schema_to_class.py +440 -0
- emod_api/serialization/__init__.py +0 -0
- emod_api/serialization/census_and_mod_pop.py +48 -0
- emod_api/serialization/dtk_file_support.py +61 -0
- emod_api/serialization/dtk_file_tools.py +1378 -0
- emod_api/serialization/dtk_file_utility.py +141 -0
- emod_api/serialization/serialized_population.py +205 -0
- emod_api/spatialreports/__init__.py +0 -0
- emod_api/spatialreports/__main__.py +67 -0
- emod_api/spatialreports/plot_spat_means.py +99 -0
- emod_api/spatialreports/spatial.py +210 -0
- emod_api/utils/__init__.py +26 -0
- emod_api/utils/distributions/__init__.py +0 -0
- emod_api/utils/distributions/base_distribution.py +38 -0
- emod_api/utils/distributions/bimodal_distribution.py +64 -0
- emod_api/utils/distributions/constant_distribution.py +58 -0
- emod_api/utils/distributions/demographic_distribution_flag.py +16 -0
- emod_api/utils/distributions/distribution_type.py +15 -0
- emod_api/utils/distributions/dual_constant_distribution.py +68 -0
- emod_api/utils/distributions/dual_exponential_distribution.py +75 -0
- emod_api/utils/distributions/exponential_distribution.py +63 -0
- emod_api/utils/distributions/gaussian_distribution.py +69 -0
- emod_api/utils/distributions/log_normal_distribution.py +61 -0
- emod_api/utils/distributions/poisson_distribution.py +59 -0
- emod_api/utils/distributions/uniform_distribution.py +70 -0
- emod_api/utils/distributions/weibull_distribution.py +69 -0
- emod_api/utils/str_enum.py +6 -0
- emod_api/weather/__init__.py +0 -0
- emod_api/weather/weather.py +428 -0
- emod_api-3.0.2.dist-info/METADATA +131 -0
- emod_api-3.0.2.dist-info/RECORD +71 -0
- emod_api-3.0.2.dist-info/WHEEL +5 -0
- emod_api-3.0.2.dist-info/licenses/LICENSE +21 -0
- 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)
|