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,737 @@
|
|
|
1
|
+
from typing import Union, Optional, Callable, Tuple
|
|
2
|
+
|
|
3
|
+
from emod_api.demographics.age_distribution import AgeDistribution
|
|
4
|
+
from emod_api.demographics.demographic_exceptions import ConflictingDistributionsException
|
|
5
|
+
from emod_api.demographics.fertility_distribution import FertilityDistribution
|
|
6
|
+
from emod_api.demographics.implicit_functions import _set_age_simple, _set_age_complex, _set_suscept_simple, \
|
|
7
|
+
_set_suscept_complex, _set_init_prev, _set_migration_model_fixed_rate, _set_enable_migration_model_heterogeneity, \
|
|
8
|
+
_set_enable_natural_mortality, _set_mortality_age_gender_year, _set_mortality_age_gender, _set_enable_demog_risk, \
|
|
9
|
+
_set_fertility_age_year
|
|
10
|
+
from emod_api.demographics.mortality_distribution import MortalityDistribution
|
|
11
|
+
from emod_api.demographics.susceptibility_distribution import SusceptibilityDistribution
|
|
12
|
+
from emod_api.demographics.updateable import Updateable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# TODO: most of the documentation in this file consists of stand-in stubs. Needs to be filled in.
|
|
16
|
+
# https://github.com/InstituteforDiseaseModeling/emod-api/issues/695
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IndividualProperty(Updateable):
|
|
20
|
+
def __init__(self,
|
|
21
|
+
property: str,
|
|
22
|
+
values: Union[list[float], list[str]],
|
|
23
|
+
initial_distribution: list[float] = None,
|
|
24
|
+
transitions: list[dict] = None,
|
|
25
|
+
transmission_matrix: list[list[float]] = None,
|
|
26
|
+
transmission_route: str = "Contact"):
|
|
27
|
+
"""
|
|
28
|
+
Add Individual Properties, including an optional HINT configuration matrix.
|
|
29
|
+
|
|
30
|
+
Individual properties act as 'labels' on model agents that can be used for identifying and targeting
|
|
31
|
+
subpopulations in campaign elements and reports. E.g. model agents may be given a property ('Accessibility')
|
|
32
|
+
that labels them as either having access to health care (value: 'Yes') or not (value: 'No').
|
|
33
|
+
|
|
34
|
+
Property-based heterogeneous disease transmission (HINT) is available for generic, environmental, typhoid,
|
|
35
|
+
airborne, or TBHIV simulations as other simulation types have parameters for modeling the heterogeneity of
|
|
36
|
+
transmission. By default, transmission is assumed to occur homogeneously among the population within a node.
|
|
37
|
+
|
|
38
|
+
Note: EMOD requires individual property key and values (property and values args) to be the same across all
|
|
39
|
+
nodes. The individual distributions of individual properties (initial_distribution) can vary acros nodes.
|
|
40
|
+
|
|
41
|
+
Note: For HINT, you will also need to set config parameter `Enable_Heterogeneous_Intranode_Transmission` to 1
|
|
42
|
+
likely with config.parameters.Enable_Heterogeneous_Intranode_Transmission = 1
|
|
43
|
+
|
|
44
|
+
Documentation of individual properties and HINT:
|
|
45
|
+
For malaria, see :doc:`emod-malaria:emod/model-properties`
|
|
46
|
+
and for HIV, see :doc:`emod-hiv:emod/model-properties`.
|
|
47
|
+
For malaria, see :doc:`emod-malaria:emod/model-hint`
|
|
48
|
+
and for HIV, see :doc:`emod-hiv:emod/model-hint`.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
property: a new individual property key to add. If property already exists an exception is raised
|
|
52
|
+
unless overwrite_existing is True.
|
|
53
|
+
values: A list of valid values for the property, or, if creating age-based transmission, age edges for
|
|
54
|
+
the 'Age_Bin' property. E.g. ['Yes', 'No'] for an 'Accessibility' property.
|
|
55
|
+
initial_distribution: The fractional, between 0 and 1, initial distribution of each valid values entry.
|
|
56
|
+
Order must match values argument. The values must add up to 1.
|
|
57
|
+
transmission_matrix: HINT transmission matrix. For malaria, see :doc:`emod-malaria:emod/model-hint`
|
|
58
|
+
and for HIV, see :doc:`emod-hiv:emod/model-hint`.
|
|
59
|
+
transmission_route: The route of transmission. Default is 'Contact'. Available routes are 'Contact' and
|
|
60
|
+
'Environmental'.
|
|
61
|
+
transitions: A list of dictionaries that each define how an individual transitions
|
|
62
|
+
from one property value to another. For malaria, see :doc:`emod-malaria:emod/parameter-demographics`
|
|
63
|
+
and for HIV, see :doc:`emod-hiv:emod/parameter-demographics`.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__()
|
|
66
|
+
if property == "Age_Bin":
|
|
67
|
+
if not isinstance(values, list) or not all(isinstance(i, float) or isinstance(i, int) for i in values):
|
|
68
|
+
raise ValueError("For property 'Age_Bin' values must be a list of floats representing "
|
|
69
|
+
"age bin edges in years.")
|
|
70
|
+
if values[0] != 0 or values[-1] != -1:
|
|
71
|
+
raise ValueError("For property 'Age_Bin', first value must be 0 and last value must be -1.")
|
|
72
|
+
if not transmission_matrix:
|
|
73
|
+
raise ValueError("For property 'Age_Bin', transmission_matrix and transmission_routes must be defined.")
|
|
74
|
+
num_age_buckets = len(values) - 1
|
|
75
|
+
if len(transmission_matrix) != num_age_buckets:
|
|
76
|
+
raise ValueError("For property 'Age_Bin', transmission_matrix must match number of age buckets, which "
|
|
77
|
+
" is number of edges in 'values' - 1.")
|
|
78
|
+
for mtx_row in transmission_matrix:
|
|
79
|
+
if len(mtx_row) != num_age_buckets:
|
|
80
|
+
raise ValueError("For property 'Age_Bin', each row of transmission_matrix must match number of age "
|
|
81
|
+
"buckets, which is number of edges in 'values' - 1.")
|
|
82
|
+
if initial_distribution:
|
|
83
|
+
for i in initial_distribution:
|
|
84
|
+
if i < 0 or i > 1:
|
|
85
|
+
raise ValueError("initial_distribution values must be between 0 and 1.")
|
|
86
|
+
if sum(initial_distribution) != 1:
|
|
87
|
+
raise ValueError("initial_distribution values must sum to 1.")
|
|
88
|
+
if len(initial_distribution) != len(values):
|
|
89
|
+
raise ValueError("initial_distribution must have the same number of entries as values.")
|
|
90
|
+
|
|
91
|
+
if transmission_matrix and transmission_route not in ["Contact", "Environmental"]:
|
|
92
|
+
raise ValueError(f"Invalid transmission route: {transmission_route}. "
|
|
93
|
+
f"Valid routes are 'Contact' and 'Environmental'.")
|
|
94
|
+
if transmission_matrix and property != "Age_Bin":
|
|
95
|
+
if len(transmission_matrix) != len(values):
|
|
96
|
+
raise ValueError("For property other than 'Age_Bin', size of transmission_matrix must match number "
|
|
97
|
+
"of values.")
|
|
98
|
+
for mtx_row in transmission_matrix:
|
|
99
|
+
if len(mtx_row) != len(values):
|
|
100
|
+
raise ValueError("For property other than 'Age_Bin', each row of transmission_matrix must match "
|
|
101
|
+
"number of values.")
|
|
102
|
+
for transition in transitions or []:
|
|
103
|
+
if not isinstance(transition, dict):
|
|
104
|
+
raise ValueError("Transitions must be a list of dictionaries. Please see the documentation for correct "
|
|
105
|
+
"format: ")
|
|
106
|
+
|
|
107
|
+
self.initial_distribution = initial_distribution
|
|
108
|
+
self.property = property
|
|
109
|
+
self.values = values
|
|
110
|
+
self.transitions = transitions
|
|
111
|
+
self.transmission_matrix = transmission_matrix
|
|
112
|
+
self.transmission_route = transmission_route
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> dict:
|
|
115
|
+
individual_property = self.parameter_dict
|
|
116
|
+
individual_property.update({"Property": self.property})
|
|
117
|
+
if self.property == "Age_Bin":
|
|
118
|
+
individual_property.update({"Age_Bin_Edges_In_Years": self.values})
|
|
119
|
+
else:
|
|
120
|
+
individual_property.update({"Values": self.values})
|
|
121
|
+
|
|
122
|
+
if self.initial_distribution:
|
|
123
|
+
individual_property.update({"Initial_Distribution": self.initial_distribution})
|
|
124
|
+
|
|
125
|
+
if self.transitions is not None:
|
|
126
|
+
individual_property.update({"Transitions": self.transitions})
|
|
127
|
+
|
|
128
|
+
if self.transmission_matrix is not None:
|
|
129
|
+
individual_property.update({"TransmissionMatrix": {"Route": self.transmission_route,
|
|
130
|
+
"Matrix": self.transmission_matrix}})
|
|
131
|
+
return individual_property
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_dict(cls, ip_dict: dict) -> '__class__':
|
|
135
|
+
available_args = ['initial_distribution', 'property', 'values', 'transitions',
|
|
136
|
+
'transmission_matrix', 'transmission_route']
|
|
137
|
+
args = {key: ip_dict[key] for key in available_args if key in ip_dict}
|
|
138
|
+
return cls(**args)
|
|
139
|
+
|
|
140
|
+
def __eq__(self, other) -> bool:
|
|
141
|
+
return self.to_dict() == other.to_dict()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class IndividualProperties(Updateable):
|
|
145
|
+
"""
|
|
146
|
+
A container class for holding IndividualProperty objects used by Node objects. It simply contains functionality for
|
|
147
|
+
adding, removing, and retrieving contained IndividualProperty objects with some light consistency checking
|
|
148
|
+
(preventing duplicate-named IndividualProperties).
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
class DuplicateIndividualPropertyException(Exception):
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
class NoSuchIndividualPropertyException(Exception):
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
def __init__(self, individual_properties: list[IndividualProperty] = None):
|
|
158
|
+
"""
|
|
159
|
+
https://docs.idmod.org/projects/emod-generic/en/latest/model-properties.html
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
individual_properties (list[IndividualProperty]): list of individual properties to include. Default is
|
|
163
|
+
no individual_properties.
|
|
164
|
+
"""
|
|
165
|
+
super().__init__()
|
|
166
|
+
self.individual_properties = [] if individual_properties is None else individual_properties
|
|
167
|
+
|
|
168
|
+
def add(self, individual_property: IndividualProperty, overwrite=False) -> None:
|
|
169
|
+
has_ip = self.has_individual_property(property_key=individual_property.property)
|
|
170
|
+
if has_ip:
|
|
171
|
+
if overwrite:
|
|
172
|
+
# remove existing then add
|
|
173
|
+
self.remove_individual_property(property_key=individual_property.property)
|
|
174
|
+
else:
|
|
175
|
+
msg = f"Property {individual_property.property} already present in IndividualProperties"
|
|
176
|
+
raise self.DuplicateIndividualPropertyException(msg)
|
|
177
|
+
self.individual_properties.append(individual_property)
|
|
178
|
+
|
|
179
|
+
def add_parameter(self, key, value):
|
|
180
|
+
raise NotImplementedError("A parameter cannot be added to IndividualProperties.")
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def ip_by_name(self):
|
|
184
|
+
return {ip.property: ip for ip in self.individual_properties}
|
|
185
|
+
|
|
186
|
+
def has_individual_property(self, property_key: str) -> bool:
|
|
187
|
+
return property_key in self.ip_by_name.keys()
|
|
188
|
+
|
|
189
|
+
def get_individual_property(self, property_key: str) -> IndividualProperty:
|
|
190
|
+
ip = self.ip_by_name.get(property_key, None)
|
|
191
|
+
if ip is None:
|
|
192
|
+
msg = f"No IndividualProperty exists with the property key: {property_key}"
|
|
193
|
+
raise self.NoSuchIndividualPropertyException(msg)
|
|
194
|
+
return ip
|
|
195
|
+
|
|
196
|
+
def remove_individual_property(self, property_key: str):
|
|
197
|
+
ips_to_keep = [ip for ip in self.individual_properties if ip.property != property_key]
|
|
198
|
+
self.individual_properties = ips_to_keep
|
|
199
|
+
|
|
200
|
+
def to_dict(self) -> list[dict]:
|
|
201
|
+
data = [ip.to_dict() for ip in self.individual_properties]
|
|
202
|
+
return data
|
|
203
|
+
|
|
204
|
+
def __getitem__(self, index: int):
|
|
205
|
+
return self.individual_properties[index]
|
|
206
|
+
|
|
207
|
+
def __len__(self):
|
|
208
|
+
return len(self.individual_properties)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class IndividualAttributes(Updateable):
|
|
212
|
+
# TODO: consider refactoring to use objects instead of a big list of potential parameters here:
|
|
213
|
+
# https://github.com/InstituteforDiseaseModeling/emod-api-old/issues/750
|
|
214
|
+
def __init__(self,
|
|
215
|
+
age_distribution_flag: int = None,
|
|
216
|
+
age_distribution1: int = None,
|
|
217
|
+
age_distribution2: int = None,
|
|
218
|
+
age_distribution: AgeDistribution = None,
|
|
219
|
+
susceptibility_distribution_flag: int = None,
|
|
220
|
+
susceptibility_distribution1: int = None,
|
|
221
|
+
susceptibility_distribution2: int = None,
|
|
222
|
+
susceptibility_distribution: SusceptibilityDistribution = None,
|
|
223
|
+
prevalence_distribution_flag: int = None,
|
|
224
|
+
prevalence_distribution1: int = None,
|
|
225
|
+
prevalence_distribution2: int = None,
|
|
226
|
+
risk_distribution_flag: int = None,
|
|
227
|
+
risk_distribution1: int = None,
|
|
228
|
+
risk_distribution2: int = None,
|
|
229
|
+
migration_heterogeneity_distribution_flag: int = None,
|
|
230
|
+
migration_heterogeneity_distribution1: int = None,
|
|
231
|
+
migration_heterogeneity_distribution2: int = None,
|
|
232
|
+
fertility_distribution: FertilityDistribution = None,
|
|
233
|
+
mortality_distribution_male: MortalityDistribution = None,
|
|
234
|
+
mortality_distribution_female: MortalityDistribution = None,
|
|
235
|
+
innate_immune_distribution_flag: int = None,
|
|
236
|
+
innate_immune_distribution1: int = None,
|
|
237
|
+
innate_immune_distribution2: int = None
|
|
238
|
+
):
|
|
239
|
+
"""
|
|
240
|
+
Defines the initial distribution of attributes for model agents for all disease setups. These are used by Node
|
|
241
|
+
objects and can be defined separately per-node. Some attributes utilize simple distributions, some utilize
|
|
242
|
+
complex distributions, some can utilize either simple or complex. For those that can utilize simple or complex
|
|
243
|
+
distributions, only one may be specified (it is a user choice). It is highly unlikely a user will utilize this
|
|
244
|
+
class directly, as it exists primarily for ensuring proper serialization to JSON for EMOD input file
|
|
245
|
+
representation. The standard, user-facing interface for updating the distributions used is in the Demographics
|
|
246
|
+
class in emodpy.demographics .
|
|
247
|
+
|
|
248
|
+
Supported simple distributions and the meaning of their parameters are defined in the
|
|
249
|
+
emod_api.utils.distributions submodule.
|
|
250
|
+
|
|
251
|
+
Further information can be found at:
|
|
252
|
+
https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#individual-attributes
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
age_distribution_flag (int, optional): Toggles the type of simple distribution for representing age,
|
|
256
|
+
determining the distribution-specific interpretation of age_distribution1 and age_distribution2.
|
|
257
|
+
Mutually exclusive with a complex age distribution (age_distribution).
|
|
258
|
+
age_distribution1 (int, optional): If age_distribution_flag is not None, the specified simple
|
|
259
|
+
distribution-dependent first argument.
|
|
260
|
+
age_distribution2 (int, optional): If age_distribution_flag is not None, the specified simple
|
|
261
|
+
distribution-dependent second argument (if any).
|
|
262
|
+
age_distribution (AgeDistribution, optional): If provided, defines a complex age distribution. Mutually
|
|
263
|
+
exclusive with a simple age distribution (age_distribution_flag).
|
|
264
|
+
|
|
265
|
+
susceptibility_distribution_flag (int, optional): Toggles the type of simple distribution for representing
|
|
266
|
+
susceptibility, determining the distribution-specific interpretation of susceptibility_distribution1
|
|
267
|
+
and susceptibility_distribution2. Mutually exclusive with a complex susceptibility distribution
|
|
268
|
+
(susceptibility_distribution).
|
|
269
|
+
susceptibility_distribution1 (int, optional): If susceptibility_distribution_flag is not None, the
|
|
270
|
+
specified simple distribution-dependent first argument.
|
|
271
|
+
susceptibility_distribution2 (int, optional): If susceptibility_distribution_flag is not None, the
|
|
272
|
+
specified simple distribution-dependent second argument (if any).
|
|
273
|
+
susceptibility_distribution (SusceptibilityDistribution, optional): If provided, defines a complex
|
|
274
|
+
susceptibility distribution. Mutually exclusive with a simple susceptibility distribution
|
|
275
|
+
(susceptibility_distribution_flag).
|
|
276
|
+
|
|
277
|
+
prevalence_distribution_flag (int, optional): Toggles the type of simple distribution for representing
|
|
278
|
+
prevalence, determining the distribution-specific interpretation of prevalence_distribution1 and
|
|
279
|
+
prevalence_distribution2.
|
|
280
|
+
prevalence_distribution1 (int, optional): If prevalence_distribution_flag is not None, the specified simple
|
|
281
|
+
distribution-dependent first argument.
|
|
282
|
+
prevalence_distribution2 (int, optional): If prevalence_distribution_flag is not None, the specified simple
|
|
283
|
+
distribution-dependent second argument (if any).
|
|
284
|
+
|
|
285
|
+
risk_distribution_flag (int, optional): Toggles the type of simple distribution for representing risk,
|
|
286
|
+
determining the distribution-specific interpretation of risk_distribution1 and risk_distribution2.
|
|
287
|
+
risk_distribution1 (int, optional): If risk_distribution_flag is not None, the specified simple
|
|
288
|
+
distribution-dependent first argument.
|
|
289
|
+
risk_distribution2 (int, optional): If risk_distribution_flag is not None, the specified simple
|
|
290
|
+
distribution-dependent second argument (if any).
|
|
291
|
+
|
|
292
|
+
migration_heterogeneity_distribution_flag (int, optional): Toggles the type of simple distribution for
|
|
293
|
+
representing migration heterogeneity, determining the distribution-specific interpretation of
|
|
294
|
+
migration_heterogeneity_distribution1 and migration_heterogeneity_distribution2.
|
|
295
|
+
migration_heterogeneity_distribution1 (int, optional): If migration_heterogeneity_distribution_flag is not
|
|
296
|
+
None, the specified simple distribution-dependent first argument.
|
|
297
|
+
migration_heterogeneity_distribution2 (int, optional): If migration_heterogeneity_distribution_flag is not
|
|
298
|
+
None, the specified simple distribution-dependent second argument (if any).
|
|
299
|
+
|
|
300
|
+
fertility_distribution (FertilityDistribution, optional): If provided, defines a complex fertility
|
|
301
|
+
distribution for females.
|
|
302
|
+
|
|
303
|
+
mortality_distribution_male (MortalityDistribution, optional): If provided, defines a complex mortality
|
|
304
|
+
distribution for males.
|
|
305
|
+
mortality_distribution_female (MortalityDistribution, optional): If provided, defines a complex mortality
|
|
306
|
+
distribution for females.
|
|
307
|
+
|
|
308
|
+
innate_immune_distribution_flag (int, optional): Toggles the type of simple distribution for representing
|
|
309
|
+
innate immunity, determining the distribution-specific interpretation of innate_immune_distribution1
|
|
310
|
+
and innate_immune_distribution2.
|
|
311
|
+
innate_immune_distribution1 (int, optional): If innate immune_distribution_flag is not None, the specified
|
|
312
|
+
simple distribution-dependent first argument.
|
|
313
|
+
innate_immune_distribution2 (int, optional): If innate immune_distribution_flag is not None, the specified
|
|
314
|
+
simple distribution-dependent second argument (if any).
|
|
315
|
+
"""
|
|
316
|
+
super().__init__()
|
|
317
|
+
|
|
318
|
+
# users can either use a simple age distribution (toggled with age_distribution_flag, defined by
|
|
319
|
+
# age_distribution1 and age_distribution2) OR a complex one (passed in via age_distribution)
|
|
320
|
+
if (age_distribution is not None) and (age_distribution_flag is not None):
|
|
321
|
+
raise ValueError("Cannot set both a simple age distribution via age_distribution_flag AND a complex "
|
|
322
|
+
"age distribution via age_distribution. Must choose one or the other. Or choose neither "
|
|
323
|
+
"to get default age distribution behavior.")
|
|
324
|
+
self.age_distribution_flag = age_distribution_flag
|
|
325
|
+
self.age_distribution1 = age_distribution1
|
|
326
|
+
self.age_distribution2 = age_distribution2
|
|
327
|
+
self.age_distribution = age_distribution
|
|
328
|
+
|
|
329
|
+
# users can either use a simple susceptibility distribution (toggled with susceptibility_distribution_flag,
|
|
330
|
+
# defined by susceptibility_distribution1 and susceptibility_distribution2) OR a complex one (passed in via
|
|
331
|
+
# susceptibility_distribution)
|
|
332
|
+
if (susceptibility_distribution is not None) and (susceptibility_distribution_flag is not None):
|
|
333
|
+
raise ValueError("Cannot set both a simple susceptibility distribution via "
|
|
334
|
+
"susceptibility_distribution_flag AND a complex susceptibility distribution via "
|
|
335
|
+
"susceptibility_distribution. Must choose one or the other. Or choose neither to get "
|
|
336
|
+
"default susceptibility distribution behavior.")
|
|
337
|
+
self.susceptibility_distribution_flag = susceptibility_distribution_flag
|
|
338
|
+
self.susceptibility_distribution1 = susceptibility_distribution1
|
|
339
|
+
self.susceptibility_distribution2 = susceptibility_distribution2
|
|
340
|
+
self.susceptibility_distribution = susceptibility_distribution
|
|
341
|
+
|
|
342
|
+
self.prevalence_distribution_flag = prevalence_distribution_flag
|
|
343
|
+
self.prevalence_distribution1 = prevalence_distribution1
|
|
344
|
+
self.prevalence_distribution2 = prevalence_distribution2
|
|
345
|
+
|
|
346
|
+
self.migration_heterogeneity_distribution_flag = migration_heterogeneity_distribution_flag
|
|
347
|
+
self.migration_heterogeneity_distribution1 = migration_heterogeneity_distribution1
|
|
348
|
+
self.migration_heterogeneity_distribution2 = migration_heterogeneity_distribution2
|
|
349
|
+
|
|
350
|
+
self.mortality_distribution_male = mortality_distribution_male
|
|
351
|
+
self.mortality_distribution_female = mortality_distribution_female
|
|
352
|
+
self.mortality_distribution = None # This should ONLY be set via from_dict() loading (deprecated).
|
|
353
|
+
# fertility is only used by HIV
|
|
354
|
+
|
|
355
|
+
self.fertility_distribution = fertility_distribution
|
|
356
|
+
|
|
357
|
+
# risk and innate_immune are only used by malaria
|
|
358
|
+
|
|
359
|
+
self.risk_distribution_flag = risk_distribution_flag
|
|
360
|
+
self.risk_distribution1 = risk_distribution1
|
|
361
|
+
self.risk_distribution2 = risk_distribution2
|
|
362
|
+
|
|
363
|
+
self.innate_immune_distribution_flag = innate_immune_distribution_flag
|
|
364
|
+
self.innate_immune_distribution1 = innate_immune_distribution1
|
|
365
|
+
self.innate_immune_distribution2 = innate_immune_distribution2
|
|
366
|
+
|
|
367
|
+
# New names for by-gender mortality distributions to support emodpy Demographics setting of all distributions
|
|
368
|
+
# using the same code (see properties here).
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def mortality_male_distribution(self):
|
|
372
|
+
return self.mortality_distribution_male
|
|
373
|
+
|
|
374
|
+
@mortality_male_distribution.setter
|
|
375
|
+
def mortality_male_distribution(self, value):
|
|
376
|
+
self.mortality_distribution_male = value
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def mortality_female_distribution(self):
|
|
380
|
+
return self.mortality_distribution_female
|
|
381
|
+
|
|
382
|
+
@mortality_female_distribution.setter
|
|
383
|
+
def mortality_female_distribution(self, value):
|
|
384
|
+
self.mortality_distribution_female = value
|
|
385
|
+
|
|
386
|
+
@staticmethod
|
|
387
|
+
def _ensure_valid_value2_value(distribution_dict: dict, value2_key: str):
|
|
388
|
+
# change any None to 0 for value2. Demographics demands it or EMOD fails.
|
|
389
|
+
distribution_dict[value2_key] = 0 if distribution_dict[value2_key] is None else distribution_dict[value2_key]
|
|
390
|
+
|
|
391
|
+
def to_dict(self) -> dict:
|
|
392
|
+
# TODO: Consider updating how/where we check for consistency of attributes of IndividualProperties objects,
|
|
393
|
+
# as a user MAY alter validity after constructor call which currently enforces consistency:
|
|
394
|
+
# https://github.com/InstituteforDiseaseModeling/emod-api-old/issues/751
|
|
395
|
+
individual_attributes = self.parameter_dict
|
|
396
|
+
|
|
397
|
+
# Set age distribution as complex or simple if specified, but not both.
|
|
398
|
+
both_types_selected = ((self.age_distribution is not None) and (self.age_distribution_flag is not None))
|
|
399
|
+
if both_types_selected:
|
|
400
|
+
raise ConflictingDistributionsException('Both a simple and complex distribution for age has been set. '
|
|
401
|
+
'Only type is allowed.')
|
|
402
|
+
if self.age_distribution is not None:
|
|
403
|
+
# complex distribution
|
|
404
|
+
age_distribution_dict = {"AgeDistribution": self.age_distribution.to_dict()}
|
|
405
|
+
individual_attributes.update(age_distribution_dict)
|
|
406
|
+
elif self.age_distribution_flag is not None:
|
|
407
|
+
# simple distribution
|
|
408
|
+
age_distribution_dict = {
|
|
409
|
+
"AgeDistributionFlag": self.age_distribution_flag,
|
|
410
|
+
"AgeDistribution1": self.age_distribution1,
|
|
411
|
+
"AgeDistribution2": self.age_distribution2
|
|
412
|
+
}
|
|
413
|
+
self._ensure_valid_value2_value(distribution_dict=age_distribution_dict, value2_key="AgeDistribution2")
|
|
414
|
+
individual_attributes.update(age_distribution_dict)
|
|
415
|
+
|
|
416
|
+
# Set susceptibility distribution as complex or simple if specified, but not both.
|
|
417
|
+
both_types_selected = ((self.susceptibility_distribution is not None) and (self.susceptibility_distribution_flag is not None))
|
|
418
|
+
if both_types_selected:
|
|
419
|
+
raise ConflictingDistributionsException('Both a simple and complex distribution for susceptibility has '
|
|
420
|
+
'been set. Only type is allowed.')
|
|
421
|
+
if self.susceptibility_distribution is not None:
|
|
422
|
+
# complex distribution
|
|
423
|
+
susceptibility_distribution_dict = {"SusceptibilityDistribution": self.susceptibility_distribution.to_dict()}
|
|
424
|
+
individual_attributes.update(susceptibility_distribution_dict)
|
|
425
|
+
elif self.susceptibility_distribution_flag is not None:
|
|
426
|
+
# simple distribution
|
|
427
|
+
susceptibility_distribution_dict = {
|
|
428
|
+
"SusceptibilityDistributionFlag": self.susceptibility_distribution_flag,
|
|
429
|
+
"SusceptibilityDistribution1": self.susceptibility_distribution1,
|
|
430
|
+
"SusceptibilityDistribution2": self.susceptibility_distribution2
|
|
431
|
+
}
|
|
432
|
+
self._ensure_valid_value2_value(distribution_dict=susceptibility_distribution_dict,
|
|
433
|
+
value2_key="SusceptibilityDistribution2")
|
|
434
|
+
individual_attributes.update(susceptibility_distribution_dict)
|
|
435
|
+
|
|
436
|
+
# The following distributions can only be simple, not complex
|
|
437
|
+
|
|
438
|
+
if self.prevalence_distribution_flag is not None:
|
|
439
|
+
prevalence_distribution_dict = {
|
|
440
|
+
"PrevalenceDistributionFlag": self.prevalence_distribution_flag,
|
|
441
|
+
"PrevalenceDistribution1": self.prevalence_distribution1,
|
|
442
|
+
"PrevalenceDistribution2": self.prevalence_distribution2
|
|
443
|
+
}
|
|
444
|
+
self._ensure_valid_value2_value(distribution_dict=prevalence_distribution_dict,
|
|
445
|
+
value2_key="PrevalenceDistribution2")
|
|
446
|
+
individual_attributes.update(prevalence_distribution_dict)
|
|
447
|
+
|
|
448
|
+
if self.migration_heterogeneity_distribution_flag is not None:
|
|
449
|
+
migration_heterogeneity_distribution_dict = {
|
|
450
|
+
"MigrationHeterogeneityDistributionFlag": self.migration_heterogeneity_distribution_flag,
|
|
451
|
+
"MigrationHeterogeneityDistribution1": self.migration_heterogeneity_distribution1,
|
|
452
|
+
"MigrationHeterogeneityDistribution2": self.migration_heterogeneity_distribution2
|
|
453
|
+
}
|
|
454
|
+
self._ensure_valid_value2_value(distribution_dict=migration_heterogeneity_distribution_dict,
|
|
455
|
+
value2_key="MigrationHeterogeneityDistribution2")
|
|
456
|
+
individual_attributes.update(migration_heterogeneity_distribution_dict)
|
|
457
|
+
|
|
458
|
+
# malaria only - possible to move this to emodpy-malaria in the future if desired.
|
|
459
|
+
if self.risk_distribution_flag is not None:
|
|
460
|
+
risk_distribution_dict = {
|
|
461
|
+
"RiskDistributionFlag": self.risk_distribution_flag,
|
|
462
|
+
"RiskDistribution1": self.risk_distribution1,
|
|
463
|
+
"RiskDistribution2": self.risk_distribution2
|
|
464
|
+
}
|
|
465
|
+
self._ensure_valid_value2_value(distribution_dict=risk_distribution_dict, value2_key="RiskDistribution2")
|
|
466
|
+
individual_attributes.update(risk_distribution_dict)
|
|
467
|
+
|
|
468
|
+
# malaria only - possible to move this to emodpy-malaria in the future if desired.
|
|
469
|
+
if self.innate_immune_distribution_flag is not None:
|
|
470
|
+
innate_immune_distribution_dict = {
|
|
471
|
+
"InnateImmuneDistributionFlag": self.innate_immune_distribution_flag,
|
|
472
|
+
"InnateImmuneDistribution1": self.innate_immune_distribution1,
|
|
473
|
+
"InnateImmuneDistribution2": self.innate_immune_distribution2
|
|
474
|
+
}
|
|
475
|
+
self._ensure_valid_value2_value(distribution_dict=innate_immune_distribution_dict,
|
|
476
|
+
value2_key="InnateImmuneDistribution2")
|
|
477
|
+
individual_attributes.update(innate_immune_distribution_dict)
|
|
478
|
+
|
|
479
|
+
# The following distributions can only be complex, not simple
|
|
480
|
+
|
|
481
|
+
if self.fertility_distribution is not None:
|
|
482
|
+
individual_attributes.update({"FertilityDistribution": self.fertility_distribution.to_dict()})
|
|
483
|
+
|
|
484
|
+
if self.mortality_distribution_male is not None:
|
|
485
|
+
individual_attributes.update({"MortalityDistributionMale": self.mortality_distribution_male.to_dict()})
|
|
486
|
+
|
|
487
|
+
if self.mortality_distribution_female is not None:
|
|
488
|
+
individual_attributes.update({"MortalityDistributionFemale": self.mortality_distribution_female.to_dict()})
|
|
489
|
+
|
|
490
|
+
# # This should ONLY be set via from_dict() loading (deprecated).
|
|
491
|
+
if self.mortality_distribution is not None:
|
|
492
|
+
individual_attributes.update({"MortalityDistribution": self.mortality_distribution.to_dict()})
|
|
493
|
+
|
|
494
|
+
return individual_attributes
|
|
495
|
+
|
|
496
|
+
def from_dict(self, individual_attributes: dict) -> Tuple["IndividualAttributes", list[Callable]]:
|
|
497
|
+
implicit_functions = []
|
|
498
|
+
|
|
499
|
+
age_distribution_dict = individual_attributes.get("AgeDistribution", None)
|
|
500
|
+
if age_distribution_dict is None:
|
|
501
|
+
self.age_distribution = None
|
|
502
|
+
self.age_distribution_flag = individual_attributes.get("AgeDistributionFlag", None)
|
|
503
|
+
self.age_distribution1 = individual_attributes.get("AgeDistribution1", None)
|
|
504
|
+
self.age_distribution2 = individual_attributes.get("AgeDistribution2", None)
|
|
505
|
+
implicit_functions.append(_set_age_simple)
|
|
506
|
+
else:
|
|
507
|
+
self.age_distribution = AgeDistribution.from_dict(distribution_dict=age_distribution_dict)
|
|
508
|
+
self.age_distribution_flag = None
|
|
509
|
+
self.age_distribution1 = None
|
|
510
|
+
self.age_distribution2 = None
|
|
511
|
+
implicit_functions.append(_set_age_complex)
|
|
512
|
+
|
|
513
|
+
susceptibility_distribution_dict = individual_attributes.get("SusceptibilityDistribution", None)
|
|
514
|
+
if susceptibility_distribution_dict is None:
|
|
515
|
+
self.susceptibility_distribution = None
|
|
516
|
+
self.susceptibility_distribution_flag = individual_attributes.get("SusceptibilityDistributionFlag", None)
|
|
517
|
+
self.susceptibility_distribution1 = individual_attributes.get("SusceptibilityDistribution1", None)
|
|
518
|
+
self.susceptibility_distribution2 = individual_attributes.get("SusceptibilityDistribution2", None)
|
|
519
|
+
implicit_functions.append(_set_suscept_simple)
|
|
520
|
+
else:
|
|
521
|
+
self.susceptibility_distribution = SusceptibilityDistribution.from_dict(
|
|
522
|
+
distribution_dict=susceptibility_distribution_dict)
|
|
523
|
+
self.susceptibility_distribution_flag = None
|
|
524
|
+
self.susceptibility_distribution1 = None
|
|
525
|
+
self.susceptibility_distribution2 = None
|
|
526
|
+
implicit_functions.append(_set_suscept_complex)
|
|
527
|
+
|
|
528
|
+
self.prevalence_distribution_flag = individual_attributes.get("PrevalenceDistributionFlag", None)
|
|
529
|
+
self.prevalence_distribution1 = individual_attributes.get("PrevalenceDistribution1", None)
|
|
530
|
+
self.prevalence_distribution2 = individual_attributes.get("PrevalenceDistribution2", None)
|
|
531
|
+
if self.prevalence_distribution_flag is not None:
|
|
532
|
+
implicit_functions.append(_set_init_prev)
|
|
533
|
+
|
|
534
|
+
self.migration_heterogeneity_distribution_flag = individual_attributes.get(
|
|
535
|
+
"MigrationHeterogeneityDistributionFlag", None)
|
|
536
|
+
self.migration_heterogeneity_distribution1 = individual_attributes.get("MigrationHeterogeneityDistribution1",
|
|
537
|
+
None)
|
|
538
|
+
self.migration_heterogeneity_distribution2 = individual_attributes.get("MigrationHeterogeneityDistribution2",
|
|
539
|
+
None)
|
|
540
|
+
if self.migration_heterogeneity_distribution_flag is not None:
|
|
541
|
+
implicit_functions.extend([_set_migration_model_fixed_rate, _set_enable_migration_model_heterogeneity])
|
|
542
|
+
|
|
543
|
+
loaded_mortality = False
|
|
544
|
+
distribution_dict = individual_attributes.get("MortalityDistributionMale", None)
|
|
545
|
+
if distribution_dict is None:
|
|
546
|
+
self.mortality_distribution_male = None
|
|
547
|
+
else:
|
|
548
|
+
self.mortality_distribution_male = MortalityDistribution.from_dict(distribution_dict=distribution_dict)
|
|
549
|
+
loaded_mortality = True
|
|
550
|
+
|
|
551
|
+
distribution_dict = individual_attributes.get("MortalityDistributionFemale", None)
|
|
552
|
+
if distribution_dict is None:
|
|
553
|
+
self.mortality_distribution_female = None
|
|
554
|
+
else:
|
|
555
|
+
self.mortality_distribution_female = MortalityDistribution.from_dict(distribution_dict=distribution_dict)
|
|
556
|
+
loaded_mortality = True
|
|
557
|
+
|
|
558
|
+
if loaded_mortality:
|
|
559
|
+
implicit_functions.extend([_set_enable_natural_mortality, _set_mortality_age_gender_year])
|
|
560
|
+
|
|
561
|
+
# Even though we do NOT support NEW CREATION of all-gender mortality distributions, they are still valid in
|
|
562
|
+
# deprecated "from_dict()"(files)-type demographics loading. This is the only way self.mortality_distribution
|
|
563
|
+
# can/should be set in this class.
|
|
564
|
+
distribution_dict = individual_attributes.get("MortalityDistribution", None)
|
|
565
|
+
if distribution_dict is None:
|
|
566
|
+
self.mortality_distribution = None
|
|
567
|
+
else:
|
|
568
|
+
self.mortality_distribution = MortalityDistribution.from_dict(distribution_dict=distribution_dict)
|
|
569
|
+
implicit_functions.extend([_set_enable_natural_mortality, _set_mortality_age_gender])
|
|
570
|
+
|
|
571
|
+
# malaria only - possible to move this to emodpy-malaria in the future if desired.
|
|
572
|
+
self.innate_immune_distribution_flag = individual_attributes.get("InnateImmuneDistributionFlag", None)
|
|
573
|
+
self.innate_immune_distribution1 = individual_attributes.get("InnateImmuneDistribution1", None)
|
|
574
|
+
self.innate_immune_distribution2 = individual_attributes.get("InnateImmuneDistribution2", None)
|
|
575
|
+
if self.innate_immune_distribution_flag is not None:
|
|
576
|
+
import warnings
|
|
577
|
+
warnings.warn("InnateImmuneDistribution loaded by file. Pyrogenic vs. cytokine-killing vs NONE (ignore) is "
|
|
578
|
+
"unknown. Config may need updating to ensure parameter Innate_Immune_Variation_Type is set "
|
|
579
|
+
"properly.",
|
|
580
|
+
Warning, stacklevel=2)
|
|
581
|
+
|
|
582
|
+
# malaria only - possible to move this to emodpy-malaria in the future if desired.
|
|
583
|
+
self.risk_distribution_flag = individual_attributes.get("RiskDistributionFlag", None)
|
|
584
|
+
self.risk_distribution1 = individual_attributes.get("RiskDistribution1", None)
|
|
585
|
+
self.risk_distribution2 = individual_attributes.get("RiskDistribution2", None)
|
|
586
|
+
if self.risk_distribution_flag is not None:
|
|
587
|
+
implicit_functions.append(_set_enable_demog_risk)
|
|
588
|
+
|
|
589
|
+
distribution_dict = individual_attributes.get("FertilityDistribution", None)
|
|
590
|
+
if distribution_dict is None:
|
|
591
|
+
self.fertility_distribution = None
|
|
592
|
+
else:
|
|
593
|
+
self.fertility_distribution = FertilityDistribution.from_dict(distribution_dict)
|
|
594
|
+
implicit_functions.append(_set_fertility_age_year)
|
|
595
|
+
|
|
596
|
+
return self, implicit_functions
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class NodeAttributes(Updateable):
|
|
600
|
+
def __init__(self,
|
|
601
|
+
airport: int = None,
|
|
602
|
+
altitude: float = None,
|
|
603
|
+
area: float = None,
|
|
604
|
+
birth_rate: float = None,
|
|
605
|
+
country: str = None,
|
|
606
|
+
growth_rate: float = None,
|
|
607
|
+
name: str = None,
|
|
608
|
+
latitude: float = None,
|
|
609
|
+
longitude: float = None,
|
|
610
|
+
metadata: dict = None,
|
|
611
|
+
initial_population: int = None,
|
|
612
|
+
region: int = None,
|
|
613
|
+
seaport: int = None,
|
|
614
|
+
larval_habitat_multiplier: Optional[list[float]] = None,
|
|
615
|
+
initial_vectors_per_species: Union[dict, int, None] = None,
|
|
616
|
+
infectivity_multiplier: float = None,
|
|
617
|
+
extra_attributes: dict = None):
|
|
618
|
+
"""
|
|
619
|
+
Defines node-specific attributes for all disease setups, utilized by Node objects.
|
|
620
|
+
|
|
621
|
+
Further information can be found at:
|
|
622
|
+
https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#nodeattributes
|
|
623
|
+
https://docs.idmod.org/projects/emod-malaria/en/latest/parameter-demographics.html#nodeattributes
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
airport (int, optional): Whether the node has an airport (1 for true, 0 for false).
|
|
627
|
+
altitude (float, optional): Altitude of the node (in meters).
|
|
628
|
+
area (float, optional): Spatial size of the node (TODO: unknown units)
|
|
629
|
+
birth_rate (float, optional): The birth rate in births/day/woman .
|
|
630
|
+
country (str, optional): Name of the country the node is in.
|
|
631
|
+
growth_rate (float, optional): TODO: unknown
|
|
632
|
+
name (str, optional): Name of the node
|
|
633
|
+
latitude (float, optional): Latitude of the node in degrees.
|
|
634
|
+
longitude (float, optional): Longitude of the node in degrees.
|
|
635
|
+
metadata (dict, optional): An arbitrary dict of metaaata key/values to add to the node for notation.
|
|
636
|
+
initial_population (int, optional): The initial number of people/agents in the node.
|
|
637
|
+
region (int, optional): Whether the node has a road network (1 for true, 0 for false).
|
|
638
|
+
seaport (int, optional): Whether the node has a seaport (1 for true, 0 for false).
|
|
639
|
+
larval_habitat_multiplier (list(float), optional): The value(s) by which to scale the larval habitat
|
|
640
|
+
availability specified in the configuration file with Larval_Habitat_Types.
|
|
641
|
+
initial_vectors_per_species ((dict or int), optional): The initial number of vectors per species in the
|
|
642
|
+
node.
|
|
643
|
+
infectivity_multiplier (float, optional): TODO: unknown
|
|
644
|
+
extra_attributes (dict, optional): An arbitrary dict of attribute key/values to add to the node.
|
|
645
|
+
"""
|
|
646
|
+
super().__init__()
|
|
647
|
+
self.airport = airport
|
|
648
|
+
self.altitude = altitude
|
|
649
|
+
self.area = area
|
|
650
|
+
self.birth_rate = birth_rate
|
|
651
|
+
self.country = country
|
|
652
|
+
self.growth_rate = growth_rate
|
|
653
|
+
self.initial_population = initial_population
|
|
654
|
+
self.initial_vectors_per_species = initial_vectors_per_species
|
|
655
|
+
self.larval_habitat_multiplier = larval_habitat_multiplier
|
|
656
|
+
self.latitude = latitude
|
|
657
|
+
self.longitude = longitude
|
|
658
|
+
self.metadata = metadata
|
|
659
|
+
self.name = name
|
|
660
|
+
self.region = region
|
|
661
|
+
self.seaport = seaport
|
|
662
|
+
self.infectivity_multiplier = infectivity_multiplier
|
|
663
|
+
self.extra_attributes = extra_attributes
|
|
664
|
+
|
|
665
|
+
def from_dict(self, node_attributes: dict):
|
|
666
|
+
self.airport = node_attributes.get("Airport")
|
|
667
|
+
self.altitude = node_attributes.get("Altitude")
|
|
668
|
+
self.area = node_attributes.get("Area")
|
|
669
|
+
self.country = node_attributes.get("country")
|
|
670
|
+
self.growth_rate = node_attributes.get("GrowthRate")
|
|
671
|
+
self.name = node_attributes.get("FacilityName")
|
|
672
|
+
self.latitude = node_attributes.get("Latitude")
|
|
673
|
+
self.longitude = node_attributes.get("Longitude")
|
|
674
|
+
self.metadata = node_attributes.get("Metadata")
|
|
675
|
+
self.initial_population = node_attributes.get("InitialPopulation")
|
|
676
|
+
self.larval_habitat_multiplier = node_attributes.get("LarvalHabitatMultiplier")
|
|
677
|
+
self.initial_vectors_per_species = node_attributes.get("InitialVectorsPerSpecies")
|
|
678
|
+
self.birth_rate = node_attributes.get("BirthRate")
|
|
679
|
+
self.seaport = node_attributes.get("Seaport")
|
|
680
|
+
self.region = node_attributes.get("Region")
|
|
681
|
+
self.infectivity_multiplier = node_attributes.get("InfectivityMultiplier")
|
|
682
|
+
return self
|
|
683
|
+
|
|
684
|
+
def to_dict(self) -> dict:
|
|
685
|
+
node_attributes = self.parameter_dict
|
|
686
|
+
if self.birth_rate is not None:
|
|
687
|
+
node_attributes.update({"BirthRate": self.birth_rate})
|
|
688
|
+
|
|
689
|
+
if self.area is not None:
|
|
690
|
+
node_attributes.update({"Area": self.area})
|
|
691
|
+
|
|
692
|
+
if self.latitude is not None:
|
|
693
|
+
node_attributes.update({"Latitude": self.latitude})
|
|
694
|
+
|
|
695
|
+
if self.longitude is not None:
|
|
696
|
+
node_attributes.update({"Longitude": self.longitude})
|
|
697
|
+
|
|
698
|
+
if self.initial_population is not None:
|
|
699
|
+
node_attributes.update({"InitialPopulation": int(self.initial_population)})
|
|
700
|
+
|
|
701
|
+
if self.name:
|
|
702
|
+
node_attributes.update({"FacilityName": self.name})
|
|
703
|
+
|
|
704
|
+
if self.larval_habitat_multiplier is not None:
|
|
705
|
+
node_attributes.update({"LarvalHabitatMultiplier": self.larval_habitat_multiplier})
|
|
706
|
+
|
|
707
|
+
if self.initial_vectors_per_species:
|
|
708
|
+
node_attributes.update({"InitialVectorsPerSpecies": self.initial_vectors_per_species})
|
|
709
|
+
|
|
710
|
+
if self.airport is not None:
|
|
711
|
+
node_attributes.update({"Airport": self.airport})
|
|
712
|
+
|
|
713
|
+
if self.altitude is not None:
|
|
714
|
+
node_attributes.update({"Altitude": self.altitude})
|
|
715
|
+
|
|
716
|
+
if self.seaport is not None:
|
|
717
|
+
node_attributes.update({"Seaport": self.seaport})
|
|
718
|
+
|
|
719
|
+
if self.region is not None:
|
|
720
|
+
node_attributes.update({"Region": self.region})
|
|
721
|
+
|
|
722
|
+
if self.country is not None:
|
|
723
|
+
node_attributes.update({"country": self.country})
|
|
724
|
+
|
|
725
|
+
if self.growth_rate is not None:
|
|
726
|
+
node_attributes.update({"GrowthRate": self.growth_rate})
|
|
727
|
+
|
|
728
|
+
if self.metadata is not None:
|
|
729
|
+
node_attributes.update({"Metadata": self.metadata})
|
|
730
|
+
|
|
731
|
+
if self.infectivity_multiplier is not None:
|
|
732
|
+
node_attributes.update({"InfectivityMultiplier": self.infectivity_multiplier})
|
|
733
|
+
|
|
734
|
+
if self.extra_attributes is not None:
|
|
735
|
+
node_attributes.update(self.extra_attributes)
|
|
736
|
+
|
|
737
|
+
return node_attributes
|