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,456 @@
1
+ import math
2
+ from typing import Callable, List, Tuple
3
+
4
+ from emod_api.demographics.age_distribution import AgeDistribution
5
+ from emod_api.demographics.fertility_distribution import FertilityDistribution
6
+ from emod_api.demographics.mortality_distribution import MortalityDistribution
7
+ from emod_api.demographics.susceptibility_distribution import SusceptibilityDistribution
8
+ from emod_api.demographics.updateable import Updateable
9
+ from emod_api.demographics.properties_and_attributes import IndividualAttributes, IndividualProperty, IndividualProperties, NodeAttributes
10
+
11
+
12
+ class Node(Updateable):
13
+ # ability to resolve between Nodes
14
+ res_in_degrees = 2.5 / 60
15
+
16
+ def __init__(self,
17
+ lat: float,
18
+ lon: float,
19
+ pop: float,
20
+ name: str = None,
21
+ area: float = None,
22
+ # TODO: consider deprecating 'forced_id' as an argument and renaming it to simply 'id' for clarity and
23
+ # brevity.
24
+ forced_id: int = None,
25
+ individual_attributes: IndividualAttributes = None,
26
+ individual_properties: IndividualProperties = None,
27
+ node_attributes: NodeAttributes = None,
28
+ meta: dict = None):
29
+ """
30
+ A Node in EMOD is synonymous with a group of people (model agents). A node typically represents a spatial
31
+ geography (for example, a city, province, or country), but does not have to. Arguments to Node construction
32
+ define states of the agents (individual_properties), their initial attributes and distributions
33
+ (initial_attributes), and attributes of the node (group) as a whole (node_attributes).
34
+
35
+ Args:
36
+ lat (float): Latitude of the node in degrees.
37
+ lon (float): Longitude of the node in degrees.
38
+ pop (int): Initial population of the node.
39
+ name (str): Name of the node.
40
+ area (float): Area of the node. # TODO: units unknown
41
+ forced_id (int): ID of the node
42
+ individual_attributes (IndividualAttributes): Specifies initial distributions of various agent attributes,
43
+ such as age, fertility, and mortality.
44
+ individual_properties (IndividualProperties): Specifies the utilized IndividualProperty objects for the
45
+ node, which define properties of individual model agents.
46
+ node_attributes (NodeAttributes): Specifies node-specific attributes.
47
+ meta (dict): A metadata dictionary for a Node. Entries in here are effectively comments as EMOD
48
+ binaries do not recognize node-level metadata.
49
+ """
50
+ super().__init__()
51
+ self.forced_id = forced_id
52
+ self.meta = meta if meta else {}
53
+ self.individual_attributes = individual_attributes if individual_attributes else IndividualAttributes()
54
+ self.individual_properties = individual_properties if individual_properties else IndividualProperties()
55
+
56
+ self.node_attributes = NodeAttributes(latitude=lat, longitude=lon, initial_population=pop, area=area)
57
+ if node_attributes is not None:
58
+ self.node_attributes.update(node_attributes)
59
+
60
+ if name is None:
61
+ # if no node name was explicitly provided, we need to figure out how to name the node
62
+ if node_attributes is None or node_attributes.name is None:
63
+ # if no node_attributes object was provided with a name, use a standard default name
64
+ name = f"node{str(self.id)}"
65
+ else:
66
+ # if a name was specified for use via the node_attributes parameter, use it
67
+ name = node_attributes.name
68
+ self.name = name
69
+
70
+ @property
71
+ def name(self):
72
+ return self.node_attributes.name
73
+
74
+ @name.setter
75
+ def name(self, value):
76
+ self.node_attributes.name = value
77
+
78
+ def __repr__(self):
79
+ return f"{self.node_attributes.name} - ({self.node_attributes.latitude},{self.node_attributes.longitude})"
80
+
81
+ def has_individual_property(self, property_key: str) -> bool:
82
+ return self.individual_properties.has_individual_property(property_key=property_key)
83
+
84
+ def get_individual_property(self, property_key: str) -> IndividualProperty:
85
+ if not self.has_individual_property(property_key=property_key):
86
+ raise Exception(f"No such individual property {property_key} exists in node: {self.id}")
87
+ ip_by_name = {ip.property: ip for ip in self.individual_properties}
88
+ return ip_by_name[property_key]
89
+
90
+ def to_dict(self) -> dict:
91
+ """
92
+ Translate node structure to a dictionary for EMOD
93
+ """
94
+ d = {"NodeID": self.id,
95
+ "NodeAttributes": self.node_attributes.to_dict()}
96
+
97
+ if self.individual_attributes:
98
+ d["IndividualAttributes"] = self.individual_attributes.to_dict()
99
+
100
+ if self.individual_properties:
101
+ ip_dict = {"IndividualProperties": []}
102
+ for ip in self.individual_properties:
103
+ ip_dict["IndividualProperties"].append(ip.to_dict())
104
+ d.update(ip_dict)
105
+
106
+ d.update(self.meta)
107
+ return d
108
+
109
+ def to_tuple(self):
110
+ """
111
+ Returns a tuple of (latitude, longitude, and initial population)
112
+ """
113
+ return self.node_attributes.latitude, self.node_attributes.longitude, self.node_attributes.initial_population
114
+
115
+ @property
116
+ def id(self):
117
+ """ Returns the node ID"""
118
+ return (
119
+ self.forced_id
120
+ if self.forced_id is not None
121
+ else nodeid_from_lat_lon(self.node_attributes.latitude, self.node_attributes.longitude, self.res_in_degrees)
122
+ )
123
+
124
+ @classmethod
125
+ def init_resolution_from_file(cls, fn):
126
+ if "30arcsec" in fn:
127
+ cls.res_in_degrees = 30 / 3600.0
128
+ elif "2_5arcmin" in fn:
129
+ cls.res_in_degrees = 2.5 / 30
130
+ else:
131
+ raise Exception("Don't recognize resolution from demographics filename")
132
+
133
+ @classmethod
134
+ def from_data(cls,
135
+ data: dict) -> Tuple["Node", List[Callable]]:
136
+ """
137
+ Function used to create the node object from data (most likely coming from a demographics file)
138
+
139
+ Args:
140
+ data (dict): Contains the node definitions
141
+
142
+ Returns:
143
+ A New Node object and a list of known implicit functions needed for config compatibility.
144
+ """
145
+ implicit_functions = []
146
+
147
+ nodeid = data["NodeID"]
148
+ node_attributes_dict = dict(data.get("NodeAttributes"))
149
+ attributes = data["NodeAttributes"]
150
+ name = attributes.pop("FacilityName", nodeid)
151
+ individual_attributes_dict = data.get("IndividualAttributes")
152
+ individual_properties_dict = data.get("IndividualProperties")
153
+
154
+ individual_properties = IndividualProperties()
155
+ if individual_properties_dict:
156
+ for ip in individual_properties_dict:
157
+ individual_properties.add(IndividualProperty(property=ip["Property"],
158
+ values=ip["Values"],
159
+ transitions=ip["Transitions"],
160
+ initial_distribution=ip["Initial_Distribution"]))
161
+ individual_attributes = None
162
+ if individual_attributes_dict:
163
+ individual_attributes, implicit_functions = IndividualAttributes().from_dict(individual_attributes_dict)
164
+
165
+ node_attributes = None
166
+ if node_attributes_dict:
167
+ node_attributes = NodeAttributes().from_dict(node_attributes_dict)
168
+
169
+ # Create the node and return plus any known necessary implicit functions
170
+ node = cls(node_attributes.latitude, node_attributes.longitude, node_attributes.initial_population,
171
+ name=name, forced_id=nodeid, individual_attributes=individual_attributes,
172
+ individual_properties=individual_properties, node_attributes=node_attributes)
173
+ return node, implicit_functions
174
+
175
+ @property
176
+ def pop(self):
177
+ """ initial population """
178
+ return self.node_attributes.initial_population
179
+
180
+ @pop.setter
181
+ def pop(self, value):
182
+ self.node_attributes.initial_population = value
183
+
184
+ @property
185
+ def lon(self):
186
+ """ longitude """
187
+ return self.node_attributes.longitude
188
+
189
+ @lon.setter
190
+ def lon(self, value):
191
+ self.node_attributes.longitude = value
192
+
193
+ @property
194
+ def lat(self):
195
+ """ latitude """
196
+ return self.node_attributes.latitude
197
+
198
+ @lat.setter
199
+ def lat(self, value):
200
+ self.node_attributes.latitude = value
201
+
202
+ @property
203
+ def birth_rate(self):
204
+ """ birth rate in births per person per day"""
205
+ return self.node_attributes.birth_rate
206
+
207
+ @birth_rate.setter
208
+ def birth_rate(self, value):
209
+ self.node_attributes.birth_rate = value
210
+
211
+ def _set_individual_attributes(self, ind_attribute: IndividualAttributes):
212
+ self.individual_attributes = ind_attribute
213
+
214
+ def _set_individual_properties(self, ind_properties: IndividualProperties):
215
+ self.individual_properties = ind_properties
216
+
217
+ def _add_individual_property(self, ind_property: IndividualProperty):
218
+ self.individual_properties.add(ind_property)
219
+
220
+ def _set_node_attributes(self, node_attributes: NodeAttributes):
221
+ self.node_attributes = node_attributes
222
+
223
+ #
224
+ # Any of the following _set_*() functions that appear to be missing are not valid (e.g. prevalence complex dist)
225
+ #
226
+
227
+ def _set_age_complex_distribution(self, distribution: AgeDistribution):
228
+ """
229
+ Properly sets a complex age distribution and unsets a simple one for consistency (just in case one was set).
230
+ For details on complex distributions, see:
231
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
232
+
233
+ Args:
234
+ distribution: The complex age distribution to set
235
+
236
+ Returns:
237
+ Nothing
238
+ """
239
+ self.individual_attributes.age_distribution_flag = None
240
+ self.individual_attributes.age_distribution1 = None
241
+ self.individual_attributes.age_distribution2 = None
242
+ self.individual_attributes.age_distribution = distribution
243
+
244
+ def _set_age_simple_distribution(self, flag: int, value1: float, value2: float):
245
+ """
246
+ Properly sets a simple age distribution and unsets a complex one for consistency (just in case one was set).
247
+ For details on the simple distribution flag and value meanings, see:
248
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
249
+
250
+ Args:
251
+ flag: simple distribution flag determines the type of simple distribution to use
252
+ value1: simple distribution type-dependent parameter number 1
253
+ value2: simple distribution type-dependent parameter number 2
254
+
255
+ Returns:
256
+ Nothing
257
+ """
258
+ self.individual_attributes.age_distribution_flag = flag
259
+ self.individual_attributes.age_distribution1 = value1
260
+ self.individual_attributes.age_distribution2 = value2
261
+ self.individual_attributes.age_distribution = None
262
+
263
+ def _set_susceptibility_complex_distribution(self, distribution: SusceptibilityDistribution):
264
+ """
265
+ Properly sets a complex susceptibility distribution and unsets a simple one for consistency (just in case one
266
+ was set). For details on complex distributions, see:
267
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
268
+
269
+ Args:
270
+ distribution: The complex susceptibility distribution to set
271
+
272
+ Returns:
273
+ Nothing
274
+ """
275
+ self.individual_attributes.susceptibility_distribution_flag = None
276
+ self.individual_attributes.susceptibility_distribution1 = None
277
+ self.individual_attributes.susceptibility_distribution2 = None
278
+ self.individual_attributes.susceptibility_distribution = distribution
279
+
280
+ def _set_susceptibility_simple_distribution(self, flag: int, value1: float, value2: float):
281
+ """
282
+ Properly sets a simple susceptibility distribution and unsets a complex one for consistency (just in case one
283
+ was set). For details on the simple distribution flag and value meanings, see:
284
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
285
+
286
+ Args:
287
+ flag: simple distribution flag determines the type of simple distribution to use
288
+ value1: simple distribution type-dependent parameter number 1
289
+ value2: simple distribution type-dependent parameter number 2
290
+
291
+ Returns:
292
+ Nothing
293
+ """
294
+ self.individual_attributes.susceptibility_distribution_flag = flag
295
+ self.individual_attributes.susceptibility_distribution1 = value1
296
+ self.individual_attributes.susceptibility_distribution2 = value2
297
+ self.individual_attributes.susceptibility_distribution = None
298
+
299
+ def _set_prevalence_simple_distribution(self, flag: int, value1: float, value2: float):
300
+ """
301
+ Properly sets a simple prevalence distribution. For details on the simple distribution flag and value meanings,
302
+ see:
303
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
304
+
305
+ Args:
306
+ flag: simple distribution flag determines the type of simple distribution to use
307
+ value1: simple distribution type-dependent parameter number 1
308
+ value2: simple distribution type-dependent parameter number 2
309
+
310
+ Returns:
311
+ Nothing
312
+ """
313
+ self.individual_attributes.prevalence_distribution_flag = flag
314
+ self.individual_attributes.prevalence_distribution1 = value1
315
+ self.individual_attributes.prevalence_distribution2 = value2
316
+
317
+ def _set_migration_heterogeneity_simple_distribution(self, flag: int, value1: float, value2: float):
318
+ """
319
+ Properly sets a simple migration heterogeneity distribution. For details on the simple distribution flag and
320
+ value meanings, see:
321
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
322
+
323
+ Args:
324
+ flag: simple distribution flag determines the type of simple distribution to use
325
+ value1: simple distribution type-dependent parameter number 1
326
+ value2: simple distribution type-dependent parameter number 2
327
+
328
+ Returns:
329
+ Nothing
330
+ """
331
+ self.individual_attributes.migration_heterogeneity_distribution_flag = flag
332
+ self.individual_attributes.migration_heterogeneity_distribution1 = value1
333
+ self.individual_attributes.migration_heterogeneity_distribution2 = value2
334
+
335
+ def _set_mortality_complex_distribution(self, distribution: MortalityDistribution):
336
+ """
337
+ Properly sets a complex mortality distribution. For details on complex distributions, see:
338
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
339
+
340
+ Args:
341
+ distribution: The complex mortality distribution to set
342
+
343
+ Returns:
344
+ Nothing
345
+ """
346
+ self.individual_attributes.mortality_distribution = distribution
347
+
348
+ def _set_mortality_female_complex_distribution(self, distribution: MortalityDistribution):
349
+ """
350
+ Properly sets a complex female mortality distribution. For details on complex distributions, see:
351
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
352
+
353
+ Args:
354
+ distribution: The complex female mortality distribution to set
355
+
356
+ Returns:
357
+ Nothing
358
+ """
359
+ self.individual_attributes.mortality_distribution_female = distribution
360
+
361
+ def _set_mortality_male_complex_distribution(self, distribution: MortalityDistribution):
362
+ """
363
+ Properly sets a complex male mortality distribution. For details on complex distributions, see:
364
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
365
+
366
+ Args:
367
+ distribution: The complex male mortality distribution to set
368
+
369
+ Returns:
370
+ Nothing
371
+ """
372
+ self.individual_attributes.mortality_distribution_male = distribution
373
+
374
+ # malaria only
375
+ # TODO: Move to emodpy-malaria?
376
+ # https://github.com/InstituteforDiseaseModeling/emodpy-malaria-old/issues/707
377
+ def _set_innate_immune_simple_distribution(self, flag: int, value1: float, value2: float):
378
+ """
379
+ Properly sets a simple innate immune distribution. For details on the simple distribution flag and value
380
+ meanings, see:
381
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
382
+
383
+ Args:
384
+ flag: simple distribution flag determines the type of simple distribution to use
385
+ value1: simple distribution type-dependent parameter number 1
386
+ value2: simple distribution type-dependent parameter number 2
387
+
388
+ Returns:
389
+ Nothing
390
+ """
391
+ self.individual_attributes.innate_immune_distribution_flag = flag
392
+ self.individual_attributes.innate_immune_distribution1 = value1
393
+ self.individual_attributes.innate_immune_distribution2 = value2
394
+
395
+ # malaria only
396
+ # TODO: Move to emodpy-malaria?
397
+ # https://github.com/InstituteforDiseaseModeling/emodpy-malaria-old/issues/707
398
+ def _set_risk_simple_distribution(self, flag: int, value1: float, value2: float):
399
+ """
400
+ Properly sets a simple risk distribution. For details on the simple distribution flag and value meanings, see:
401
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#simple-distributions
402
+
403
+ Args:
404
+ flag: simple distribution flag determines the type of simple distribution to use
405
+ value1: simple distribution type-dependent parameter number 1
406
+ value2: simple distribution type-dependent parameter number 2
407
+
408
+ Returns:
409
+ Nothing
410
+ """
411
+ self.individual_attributes.risk_distribution_flag = flag
412
+ self.individual_attributes.risk_distribution1 = value1
413
+ self.individual_attributes.risk_distribution2 = value2
414
+
415
+ # HIV only
416
+ def _set_fertility_complex_distribution(self, distribution: FertilityDistribution):
417
+ """
418
+ Properly sets a complex fertility distribution. For details on complex distributions, see:
419
+ https://docs.idmod.org/projects/emod-generic/en/latest/parameter-demographics.html#complex-distributions
420
+
421
+ Args:
422
+ distribution: The complex fertility distribution to set
423
+
424
+ Returns:
425
+ Nothing
426
+ """
427
+ self.individual_attributes.fertility_distribution = distribution
428
+
429
+
430
+ def get_xpix_ypix(nodeid):
431
+ """ Get pixel position from nodid. Inverse of :py:func:`nodeid_from_lat_lon` """
432
+ ypix = (nodeid - 1) & 2 ** 16 - 1
433
+ xpix = (nodeid - 1) >> 16 # shift bits to the right
434
+ return (xpix, ypix)
435
+
436
+
437
+ def lat_lon_from_nodeid(nodeid, res_in_deg=Node.res_in_degrees):
438
+ """ Inverse of :py:func:`nodeid_from_lat_lon` """
439
+ xpix, ypix = get_xpix_ypix(nodeid)
440
+ lat = (0.5 + ypix) * res_in_deg - 90.0
441
+ lon = (0.5 + xpix) * res_in_deg - 180.0
442
+ return (lat, lon)
443
+
444
+
445
+ def xpix_ypix_from_lat_lon(lat, lon, res_in_deg=Node.res_in_degrees):
446
+ """ Pixel position (origin is -90°N and -180°E). No modular arithmentic is done."""
447
+ xpix = int(math.floor((lon + 180.0) / res_in_deg))
448
+ ypix = int(math.floor((lat + 90.0) / res_in_deg))
449
+ return xpix, ypix
450
+
451
+
452
+ def nodeid_from_lat_lon(lat, lon, res_in_deg=Node.res_in_degrees):
453
+ """ Generate unique identifier from lat, lon. Inverse of :py:func:`lat_lon_from_nodeid` """
454
+ xpix, ypix = xpix_ypix_from_lat_lon(lat, lon, res_in_deg)
455
+ nodeid = (xpix << 16) + ypix + 1
456
+ return nodeid
@@ -0,0 +1,16 @@
1
+ from emod_api.demographics.node import Node
2
+
3
+
4
+ class OverlayNode(Node):
5
+ """
6
+ Node that only requires an ID. Use to overlay a Node.
7
+ """
8
+
9
+ def __init__(self,
10
+ node_id: int,
11
+ latitude: float = None,
12
+ longitude: float = None,
13
+ initial_population: int = None,
14
+ **kwargs
15
+ ):
16
+ super().__init__(lat=latitude, lon=longitude, pop=initial_population, forced_id=node_id, **kwargs)