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,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)