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,141 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import emod_api.serialization.dtk_file_tools as dft
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def __do_read__(args):
|
|
11
|
+
|
|
12
|
+
if args.output is not None:
|
|
13
|
+
prefix = args.output
|
|
14
|
+
else:
|
|
15
|
+
root, _ = os.path.splitext(args.filename)
|
|
16
|
+
prefix = root
|
|
17
|
+
|
|
18
|
+
if args.raw:
|
|
19
|
+
extension = 'bin'
|
|
20
|
+
else:
|
|
21
|
+
extension = 'json'
|
|
22
|
+
|
|
23
|
+
dtk_file = dft.read(args.filename)
|
|
24
|
+
|
|
25
|
+
if args.header:
|
|
26
|
+
with open(args.header, 'w') as handle:
|
|
27
|
+
json.dump(dtk_file.header, handle, indent=2, separators=(',', ':'))
|
|
28
|
+
|
|
29
|
+
print(f'File header: {dtk_file.header}')
|
|
30
|
+
|
|
31
|
+
for index in range(len(dtk_file.chunks)):
|
|
32
|
+
if args.raw:
|
|
33
|
+
# Write raw chunks to disk
|
|
34
|
+
output = dtk_file.chunks[index]
|
|
35
|
+
else:
|
|
36
|
+
if args.unformatted:
|
|
37
|
+
# Expand compressed contents, but don't serialize and format
|
|
38
|
+
output = dtk_file.contents[index]
|
|
39
|
+
else:
|
|
40
|
+
# Expand compressed contents, serialize, write out formatted
|
|
41
|
+
obj = dtk_file.objects[index]
|
|
42
|
+
print(f'Formatting chunk {index + 1} of {len(dtk_file.chunks)}... ', end='')
|
|
43
|
+
output = json.dumps(obj, indent=2, separators=(',', ':'))
|
|
44
|
+
|
|
45
|
+
if index == 0:
|
|
46
|
+
output_filename = '.'.join([prefix, 'simulation', extension])
|
|
47
|
+
else:
|
|
48
|
+
output_filename = '.'.join([prefix, f'node-{index:0>5}', extension])
|
|
49
|
+
|
|
50
|
+
print(f"Writing file '{output_filename}'")
|
|
51
|
+
if args.raw:
|
|
52
|
+
with open(output_filename, 'wb') as handle:
|
|
53
|
+
handle.write(output)
|
|
54
|
+
else:
|
|
55
|
+
with open(output_filename, 'wt', encoding='utf-8') as handle:
|
|
56
|
+
handle.write(output)
|
|
57
|
+
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def __do_write__(args):
|
|
62
|
+
|
|
63
|
+
print(f"Writing file '{args.filename}'", file=sys.stderr)
|
|
64
|
+
print(f"Reading simulation data from '{args.simulation}'", file=sys.stderr)
|
|
65
|
+
print(f"Reading node data from {args.nodes}", file=sys.stderr)
|
|
66
|
+
print(f"Author = {args.author}", file=sys.stderr)
|
|
67
|
+
print(f"Tool = {args.tool}", file=sys.stderr)
|
|
68
|
+
print(f"{'Compressing' if args.compress else 'Not compressing'} contents", file=sys.stderr)
|
|
69
|
+
print(f"{'Verifying' if args.verify else 'Not verifying'} contents", file=sys.stderr)
|
|
70
|
+
print(f"Using compression engine '{args.engine}'", file=sys.stderr)
|
|
71
|
+
|
|
72
|
+
dtk_file = dft.DtkFileV3()
|
|
73
|
+
dtk_file.author = args.author
|
|
74
|
+
dtk_file.tool = args.tool
|
|
75
|
+
dtk_file.compression = args.engine
|
|
76
|
+
|
|
77
|
+
_prepare_simulation_data(args.simulation, dtk_file)
|
|
78
|
+
_prepare_node_data(args.nodes, dtk_file)
|
|
79
|
+
|
|
80
|
+
dft.write(dtk_file, args.filename)
|
|
81
|
+
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _prepare_simulation_data(filename, dtk_file):
|
|
86
|
+
|
|
87
|
+
with open(filename, 'rb') as handle:
|
|
88
|
+
data = handle.read()
|
|
89
|
+
# Do not use dtk_file.simulation property because this is text rather than a Python object
|
|
90
|
+
dtk_file.contents.append(data)
|
|
91
|
+
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# noinspection SpellCheckingInspection
|
|
96
|
+
def _prepare_node_data(filenames, dtk_file):
|
|
97
|
+
|
|
98
|
+
for filename in filenames:
|
|
99
|
+
with open(filename, 'rb') as handle:
|
|
100
|
+
data = handle.read()
|
|
101
|
+
# Do not use dtk_file.nodes here because this is text rather than a Python object
|
|
102
|
+
dtk_file.contents.append(data)
|
|
103
|
+
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == '__main__':
|
|
108
|
+
|
|
109
|
+
parser = argparse.ArgumentParser()
|
|
110
|
+
subparsers = parser.add_subparsers(help='add_subparsers help')
|
|
111
|
+
|
|
112
|
+
read_parser = subparsers.add_parser('read', help='read help')
|
|
113
|
+
read_parser.add_argument('filename')
|
|
114
|
+
read_parser.add_argument('--header', default=None, help='Write header to file', metavar='<filename>')
|
|
115
|
+
read_parser.add_argument('-r', '--raw', default=False, action='store_true',
|
|
116
|
+
help='Write raw contents of chunks to disk')
|
|
117
|
+
# noinspection SpellCheckingInspection
|
|
118
|
+
read_parser.add_argument('-u', '--unformatted', default=False, action='store_true',
|
|
119
|
+
help='Write unformatted (compact) JSON to disk')
|
|
120
|
+
read_parser.add_argument('-o', '--output', default=None,
|
|
121
|
+
help='Output filename prefix, defaults to input filename with .json extension')
|
|
122
|
+
read_parser.set_defaults(func=__do_read__)
|
|
123
|
+
|
|
124
|
+
username = os.environ['USERNAME'] if 'USERNAME' in os.environ else os.environ['USER']
|
|
125
|
+
tool_name = os.path.basename(__file__)
|
|
126
|
+
|
|
127
|
+
write_parser = subparsers.add_parser('write', help='write help')
|
|
128
|
+
write_parser.add_argument('filename', help='Output .dtk filename')
|
|
129
|
+
write_parser.add_argument('simulation', help='Filename for simulation JSON')
|
|
130
|
+
write_parser.add_argument('nodes', nargs='+', help='Filename(s) for node JSON')
|
|
131
|
+
write_parser.add_argument('-a', '--author', default=username, help=f'Author name for header [{username}]')
|
|
132
|
+
write_parser.add_argument('-t', '--tool', default=tool_name, help=f'Tool name for header [{tool_name}]')
|
|
133
|
+
write_parser.add_argument('-u', '--uncompressed', default=True, action='store_false', dest='compress',
|
|
134
|
+
help='Do not compress contents of new .dtk file')
|
|
135
|
+
write_parser.add_argument('-v', '--verify', default=False, action='store_true',
|
|
136
|
+
help='Verify JSON in simulation and nodes (could be slow).')
|
|
137
|
+
write_parser.add_argument('-e', '--engine', default='LZ4', help='Compression engine {NONE|LZ4|SNAPPY} [LZ4]')
|
|
138
|
+
write_parser.set_defaults(func=__do_write__)
|
|
139
|
+
|
|
140
|
+
commandline_args = parser.parse_args()
|
|
141
|
+
commandline_args.func(commandline_args)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Class to load and manipulate a saved population."""
|
|
2
|
+
import difflib
|
|
3
|
+
import emod_api.serialization.dtk_file_tools as dft
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
COUNTER = 0
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SerializedPopulation:
|
|
12
|
+
"""Opens the passed file and reads in all the nodes.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
file: serialized population file
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
Create an instance of SerializedPopulation::
|
|
19
|
+
import emod_api.serialization.SerializedPopulation as SerPop
|
|
20
|
+
ser_pop = SerPop.SerializedPopulation('state-00001.dtk')
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, file: str):
|
|
25
|
+
self.next_infection_suid = None
|
|
26
|
+
self.next_infection_suid_initialized = False
|
|
27
|
+
self.dtk = dft.read(file)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def nodes(self):
|
|
31
|
+
"""All nodes.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
Delete number_of_ind individuals from node 0::
|
|
35
|
+
node = ser_pop.nodes[0]
|
|
36
|
+
del node.individualHumans[0:number_of_ind]
|
|
37
|
+
|
|
38
|
+
Only keep individuals with a certain condition::
|
|
39
|
+
node.individualHumans = [ind for ind in node.individualHumans if keep_fct(ind)]
|
|
40
|
+
|
|
41
|
+
Change susceptibility of an individual::
|
|
42
|
+
print(node.individualHumans[0].susceptibility)
|
|
43
|
+
new_susceptibility = {"age": 101.01, "mod_acquire": 0}
|
|
44
|
+
node.individualHumans[0].susceptibility.update(new_susceptibility)
|
|
45
|
+
|
|
46
|
+
Copy individual[0] from node 0, change properties and add individual as new individual::
|
|
47
|
+
import copy
|
|
48
|
+
individual_properties = {"m_age": 1234}
|
|
49
|
+
individual = copy.deepcopy(node.individualHumans[0])
|
|
50
|
+
individual["suid"] = ser_pop.get_next_individual_suid(0)
|
|
51
|
+
individual.update(individual_properties)
|
|
52
|
+
ser_pop.nodes[0].individualHumans.append(individual)
|
|
53
|
+
|
|
54
|
+
Infect an individual with an infection copied from another individual::
|
|
55
|
+
import copy
|
|
56
|
+
individual_0 = node.individualHumans[0]
|
|
57
|
+
individual_1 = node.individualHumans[1]
|
|
58
|
+
new_infection = copy.deepcopy(individual_0.infections[0])
|
|
59
|
+
new_infection["suid"] = ser_pop.get_next_infection_suid()
|
|
60
|
+
individual_1.infections.append(new_infection)
|
|
61
|
+
individual_1.m_is_infected = True
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
return self.dtk.nodes
|
|
65
|
+
|
|
66
|
+
def flush(self):
|
|
67
|
+
"""Save all made changes to the node(s)."""
|
|
68
|
+
for idx in range(len(self.dtk.nodes)):
|
|
69
|
+
self.dtk.nodes[idx] = self.dtk.nodes[idx]
|
|
70
|
+
|
|
71
|
+
def write(self, output_file: str = "my_sp_file.dtk"):
|
|
72
|
+
"""Write the population to a file.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
output_file: output file
|
|
76
|
+
"""
|
|
77
|
+
self.flush()
|
|
78
|
+
sim = self.dtk.simulation
|
|
79
|
+
sim["infectionSuidGenerator"]["next_suid"] = self.get_next_infection_suid()
|
|
80
|
+
self.dtk.simulation = sim
|
|
81
|
+
|
|
82
|
+
print(f"Saving file {output_file}.")
|
|
83
|
+
dft.write(self.dtk, output_file)
|
|
84
|
+
|
|
85
|
+
def get_next_infection_suid(self):
|
|
86
|
+
"""Each infection needs a unique identifier, this function returns one."""
|
|
87
|
+
sim = self.dtk.simulation
|
|
88
|
+
if not self.next_infection_suid_initialized:
|
|
89
|
+
self.next_infection_suid = sim["infectionSuidGenerator"]["next_suid"]
|
|
90
|
+
self.next_infection_suid_initialized = True
|
|
91
|
+
else:
|
|
92
|
+
self.next_infection_suid["id"] = (
|
|
93
|
+
self.next_infection_suid["id"]
|
|
94
|
+
+ sim["infectionSuidGenerator"]["numtasks"]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return dict(self.next_infection_suid)
|
|
98
|
+
|
|
99
|
+
def get_next_individual_suid(self, node_id: int) -> dict:
|
|
100
|
+
"""Each individual needs a unique identifier, this function returns one.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
node_id: The first parameter.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
The return value. True for success, False otherwise.
|
|
107
|
+
|
|
108
|
+
Examples:
|
|
109
|
+
To get a unique id for an individual::
|
|
110
|
+
|
|
111
|
+
print(sp.get_next_individual_suid(0))
|
|
112
|
+
{'id': 2}
|
|
113
|
+
"""
|
|
114
|
+
suid = self.dtk.nodes[node_id]["m_IndividualHumanSuidGenerator"]["next_suid"]
|
|
115
|
+
self.dtk.nodes[node_id]["m_IndividualHumanSuidGenerator"]["id"] = (
|
|
116
|
+
suid["id"]
|
|
117
|
+
+ self.dtk.nodes[node_id]["m_IndividualHumanSuidGenerator"]["numtasks"]
|
|
118
|
+
)
|
|
119
|
+
return dict(suid)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Some useful functions
|
|
123
|
+
def find(name: str,
|
|
124
|
+
handle: Union[str, Iterable],
|
|
125
|
+
currentlevel: str = "dtk.nodes"):
|
|
126
|
+
"""Recursively searches for a paramters that matches or is close to name and prints out where to find it in the file.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
name: the paramter you are looking for e.g. "age", "gender".
|
|
130
|
+
handle: some iterable data structure, can be a list of
|
|
131
|
+
nodes, a node, list of individuals, etc
|
|
132
|
+
currentlevel: just a string to print out where the found item
|
|
133
|
+
is located e.g. "dtk.nodes" or "dtk.node.individuals"
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
What is the exact paramteter name used for the age of an individual?::
|
|
137
|
+
|
|
138
|
+
SerPop.find("age", node)
|
|
139
|
+
...
|
|
140
|
+
1998 Found in: dtk.nodes.individualHumans[999].m_age
|
|
141
|
+
1999 Found in: dtk.nodes.individualHumans[999].susceptibility.age
|
|
142
|
+
2000 Found in: dtk.nodes.m_vectorpopulations[0].EggQueues[0].age
|
|
143
|
+
2001 Found in: dtk.nodes.m_vectorpopulations[0].EggQueues[1].age
|
|
144
|
+
...
|
|
145
|
+
"""
|
|
146
|
+
global COUNTER
|
|
147
|
+
if isinstance(handle, str) and difflib.get_close_matches(name, [handle], cutoff=0.6):
|
|
148
|
+
print(COUNTER, " Found in: ", currentlevel)
|
|
149
|
+
COUNTER += 1
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
if isinstance(handle, str) or not isinstance(handle, Iterable):
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# key can be a string or on dict/list/..
|
|
156
|
+
for idx, key in enumerate(handle):
|
|
157
|
+
level = (
|
|
158
|
+
currentlevel + "." + key
|
|
159
|
+
if isinstance(key, str)
|
|
160
|
+
else currentlevel + "[" + str(idx) + "]"
|
|
161
|
+
)
|
|
162
|
+
try:
|
|
163
|
+
tmp = handle[key]
|
|
164
|
+
if isinstance(tmp, Iterable):
|
|
165
|
+
find(name, key, level + "[]")
|
|
166
|
+
else:
|
|
167
|
+
find(name, key, level)
|
|
168
|
+
except BaseException:
|
|
169
|
+
# list or keys of a dict, works in all cases but misses objects in
|
|
170
|
+
# dicts
|
|
171
|
+
find(name, key, level)
|
|
172
|
+
if isinstance(handle, dict):
|
|
173
|
+
find(name, handle[key], level) # check if string is key for a dict
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def get_parameters(handle: Union[str, Iterable],
|
|
177
|
+
currentlevel: str = "dtk.nodes"):
|
|
178
|
+
"""Return a set of all parameters in the serialized population file. Helpful to get an overview about what is in the serialized population file.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
handle: some iterable data structure, can be a list of
|
|
182
|
+
nodes, a node, list of individuals, etc
|
|
183
|
+
currentlevel: just a string to print out where the found item
|
|
184
|
+
is located e.g. "dtk.nodes" or "dtk.node.individuals
|
|
185
|
+
Examples:
|
|
186
|
+
Print all parameters in serialized population file::
|
|
187
|
+
for n in sorted(SerPop.get_parameters(node)):
|
|
188
|
+
print(n)
|
|
189
|
+
"""
|
|
190
|
+
param = set()
|
|
191
|
+
|
|
192
|
+
if isinstance(handle, str):
|
|
193
|
+
param.add(currentlevel)
|
|
194
|
+
return param
|
|
195
|
+
|
|
196
|
+
if not isinstance(handle, Iterable):
|
|
197
|
+
return param
|
|
198
|
+
|
|
199
|
+
for _, d in enumerate(handle):
|
|
200
|
+
level = currentlevel + " " + d if isinstance(d, str) else currentlevel
|
|
201
|
+
param.update(get_parameters(d, level))
|
|
202
|
+
if isinstance(handle, dict):
|
|
203
|
+
param.update(get_parameters(handle[d], level))
|
|
204
|
+
|
|
205
|
+
return param
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from argparse import ArgumentParser
|
|
5
|
+
from emod_api.spatialreports.spatial import SpatialReport
|
|
6
|
+
|
|
7
|
+
MATPLOTLIB = True
|
|
8
|
+
try:
|
|
9
|
+
import matplotlib
|
|
10
|
+
matplotlib.use('TkAgg')
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
except ModuleNotFoundError:
|
|
13
|
+
print("This example requires the matplotlib package.")
|
|
14
|
+
MATPLOTLIB = False
|
|
15
|
+
|
|
16
|
+
SCRIPT_PATH = os.path.realpath(__file__)
|
|
17
|
+
WORKING_DIRECTORY = os.path.dirname(SCRIPT_PATH)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main(filename: str):
|
|
21
|
+
|
|
22
|
+
report = SpatialReport(filename)
|
|
23
|
+
|
|
24
|
+
plt.xkcd()
|
|
25
|
+
|
|
26
|
+
y_axis_guess = os.path.basename(filename).strip("SpatialReport_").strip(".bin")
|
|
27
|
+
# Show node-wise time series data
|
|
28
|
+
for node_id in report.node_ids:
|
|
29
|
+
plt.plot(report[node_id].data, label=f"Node {node_id}")
|
|
30
|
+
plt.title(f"{y_axis_guess} for Nodes")
|
|
31
|
+
plt.legend()
|
|
32
|
+
plt.xlabel("Time Step")
|
|
33
|
+
plt.ylabel(f"{y_axis_guess}")
|
|
34
|
+
plt.show()
|
|
35
|
+
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def dump_source():
|
|
40
|
+
|
|
41
|
+
with open(SCRIPT_PATH, "r") as file:
|
|
42
|
+
print(file.read())
|
|
43
|
+
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
parser = ArgumentParser()
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"-f", "--filename", default=None, help="spatial report filename"
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"-g",
|
|
54
|
+
"--get",
|
|
55
|
+
default=False,
|
|
56
|
+
action="store_true",
|
|
57
|
+
help="Write source code for this example to stdout.",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
args = parser.parse_args()
|
|
61
|
+
|
|
62
|
+
if args.get:
|
|
63
|
+
dump_source()
|
|
64
|
+
elif args.filename and MATPLOTLIB:
|
|
65
|
+
main(args.filename)
|
|
66
|
+
else:
|
|
67
|
+
parser.print_help()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script assumes local SpatialReport_XXX.bin files which have been downloaded (from COMPS)
|
|
3
|
+
using pyCOMPS getexpout function or equivalent. Note that this interacts with files on an experiment
|
|
4
|
+
basis, not simulation basis. It assumes the files are in a subdirectory named after the experiment id,
|
|
5
|
+
and then in subdirectories of that named after the simulation id.
|
|
6
|
+
|
|
7
|
+
<exp_id>/
|
|
8
|
+
<sim1_id>/
|
|
9
|
+
SpatialReport_XXX.bin
|
|
10
|
+
<sim2_id>/
|
|
11
|
+
SpatialReport_XXX.bin
|
|
12
|
+
|
|
13
|
+
The idea is that the data is most interesting not on a simulation basis, but for an experiment,
|
|
14
|
+
especially aggregated on a certain sweep param and value. This plot calculates means and plots those.
|
|
15
|
+
|
|
16
|
+
Option 1: For each node, plot the mean of the specified channel for all files (values) found in experiment.
|
|
17
|
+
Option 2: For each node, plot the mean of the specified channel for all files (values) found in experiment _limited_ by specified tag key-value pair.
|
|
18
|
+
There is little to no assistance here so you need to specify a valid key and value.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import emod_api.spatialreports.spatial as sr
|
|
23
|
+
import matplotlib.pyplot as plt
|
|
24
|
+
import numpy as np
|
|
25
|
+
import sqlite3
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def collect(exp_id, chan="Prevalence", tag=None):
|
|
29
|
+
node_chan_data = {}
|
|
30
|
+
node_chan_means = {}
|
|
31
|
+
groupby_values = {}
|
|
32
|
+
if tag:
|
|
33
|
+
if len(tag.split("=")) == 1:
|
|
34
|
+
raise ValueError("When passing tag, has to have key=value format.")
|
|
35
|
+
|
|
36
|
+
groupby_key = tag.split("=")[0]
|
|
37
|
+
groupby_value = tag.split("=")[1]
|
|
38
|
+
db = os.path.join("latest_experiment", "results.db")
|
|
39
|
+
con = sqlite3.connect(db)
|
|
40
|
+
cur = con.cursor()
|
|
41
|
+
|
|
42
|
+
query = f"SELECT sim_id FROM results where CAST({groupby_key} AS DECIMAL)-CAST({groupby_value} AS DECIMAL)<0.0001"
|
|
43
|
+
all_results = cur.execute(query)
|
|
44
|
+
groupby_values["ref"] = list()
|
|
45
|
+
for result in all_results:
|
|
46
|
+
sim_id = result[0]
|
|
47
|
+
groupby_values["ref"].append(sim_id)
|
|
48
|
+
else:
|
|
49
|
+
groupby_values["ref"] = os.listdir(exp_id)
|
|
50
|
+
if "results.db" in groupby_values["ref"]:
|
|
51
|
+
groupby_values["ref"].remove("results.db")
|
|
52
|
+
|
|
53
|
+
for sim_id in groupby_values["ref"]:
|
|
54
|
+
report_path = os.path.join(str(exp_id), sim_id, "SpatialReport_" + chan + ".bin")
|
|
55
|
+
data = sr.SpatialReport(report_path)
|
|
56
|
+
for node_id in data.node_ids:
|
|
57
|
+
chan_data = data[node_id].data
|
|
58
|
+
if node_id not in node_chan_data:
|
|
59
|
+
node_chan_data[node_id] = list()
|
|
60
|
+
node_chan_means[node_id] = list()
|
|
61
|
+
node_chan_data[node_id].append(chan_data)
|
|
62
|
+
node_chan_means[node_id] = np.zeros(len(node_chan_data[node_id]))
|
|
63
|
+
for node in node_chan_data.keys():
|
|
64
|
+
node_chan_means[node] = np.mean(np.array(node_chan_data[node]), axis=0)
|
|
65
|
+
|
|
66
|
+
return node_chan_means
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def plot(exp_id, chan="Prevalence", tag=None):
|
|
70
|
+
|
|
71
|
+
node_chan_means = collect(exp_id, chan, tag)
|
|
72
|
+
|
|
73
|
+
for node in node_chan_means.keys():
|
|
74
|
+
plt.plot(node_chan_means[node], label=f"node={node}")
|
|
75
|
+
plt.xlabel("Timestep")
|
|
76
|
+
plt.ylabel(chan)
|
|
77
|
+
plt.title(f"Mean values of {chan} over time by node.")
|
|
78
|
+
plt.legend()
|
|
79
|
+
plt.show()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
import argparse
|
|
84
|
+
parser = argparse.ArgumentParser(description='Spatial Report Plotting')
|
|
85
|
+
parser.add_argument('-c', '--channel', action='store', default="Prevalence", help='channel(s) to display [Prevalence]')
|
|
86
|
+
parser.add_argument('-e', '--experiment_id', action='store', default=None, help='experiment id to plot, data assumed to be local')
|
|
87
|
+
parser.add_argument('-t', '--tag', action='store', default="", help='tag constraint')
|
|
88
|
+
args = parser.parse_args()
|
|
89
|
+
if not args.experiment_id:
|
|
90
|
+
with open("COMPS_ID", "r") as fp:
|
|
91
|
+
args.experiment_id = fp.read()
|
|
92
|
+
if not args.channel:
|
|
93
|
+
args.channel = 'Prevalence' # should not be necessary
|
|
94
|
+
|
|
95
|
+
# check that folder with name experiment_id exists
|
|
96
|
+
if not os.path.exists(str(args.experiment_id)):
|
|
97
|
+
raise ValueError(f"Don't see folder for {args.experiment_id}.")
|
|
98
|
+
|
|
99
|
+
plot(str(args.experiment_id), args.channel, tag=args.tag)
|