Skip to content
422 changes: 165 additions & 257 deletions emod_api/demographics/demographics.py

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions emod_api/demographics/demographics_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,7 @@ def _verify_node_name_uniqueness(self):

@property
def _all_nodes(self) -> List[Node]:
# only HIV is using a default node object right now, malaria currently uses self.raw
# None protection if users are using self.raw default node access
default_node = [] if self.default_node is None else [self.default_node]
all_nodes = self.nodes + default_node
all_nodes = self.nodes + [self.default_node]
return all_nodes

@property
Expand Down
34 changes: 18 additions & 16 deletions emod_api/demographics/demographics_overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,30 @@ class DemographicsOverlay(DemographicsBase):
"""

def __init__(self,
default_node: OverlayNode,
nodes: list[OverlayNode] = None,
idref: str = None,
default_node: OverlayNode = None):
idref: str = None):
"""
A class to create demographic overlays.
Args:
nodes: Overlay is applied to these nodes.
idref: a name used to indicate files (demographics, climate, and migration) are used together
individual_attributes: Object of type
:py:obj:`emod_api:emod_api.demographics.PropertiesAndAttributes.IndividualAttributes
to overwrite individual attributes
node_attributes: Object of type
:py:obj:`emod_api:emod_api.demographics.PropertiesAndAttributes.NodeAttributes
to overwrite individual attributes
default_node: (OverlayNode) An optional node to use for default settings.
An object representation of an EMOD demographics overlay input (file). The contents are interpreted by EMOD
at runtime as overrides to the canonical/primary demographics input file.

Args:
default_node: (OverlayNode) Contains default settings for nodes in the overlay.
nodes (List[OverlayNode]): Overlay is applied to these nodes. Default is no nodes.
idref (str): a name used to indicate files (demographics, climate, and migration) are used together
"""
super(DemographicsOverlay, self).__init__(nodes=nodes, idref=idref, default_node=default_node)
nodes = [] if nodes is None else nodes
super().__init__(nodes=nodes, idref=idref, default_node=default_node)

def to_file(self, file_name="demographics_overlay.json"):
def to_file(self, file_name: str = "demographics_overlay.json") -> None:
"""
Write the contents of the instance to an EMOD-compatible (JSON) file.
Writes the DemographicsOverlay to an EMOD-compatible json file.

Args:
file_name (str): The filepath to write to.

Returns:
Nothing
"""
with open(file_name, "w") as demo_override_f:
json.dump(self.to_dict(), demo_override_f)
8 changes: 3 additions & 5 deletions emod_api/demographics/properties_and_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ class NoSuchIndividualPropertyException(Exception):
# TODO: this constructor call is WEIRD. it should take a list of IndividualProperties instead and remove
# "if is None" checks on self.individual_properties (None should auto goes to [] in constructor)
# https://github.com/InstituteforDiseaseModeling/emod-api/issues/685
def __init__(self, individual_property: IndividualProperty = None):
def __init__(self, individual_properties: List[IndividualProperty] = None):
"""
https://docs.idmod.org/projects/emod-generic/en/latest/model-properties.html

Args:
individual_property:
individual_properties:
"""
super().__init__()
self.individual_properties = [individual_property] if individual_property else None
self.individual_properties = individual_properties if individual_properties else []

def add(self, individual_property: IndividualProperty, overwrite=False) -> None:
if self.individual_properties is None:
Expand Down Expand Up @@ -207,8 +207,6 @@ def __getitem__(self, index: int):
return self.individual_properties[index]

def __len__(self):
if not self.individual_properties:
return 0
return len(self.individual_properties)


Expand Down
8 changes: 4 additions & 4 deletions tests/test_config_demog.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def get_config_as_object(self):
def reset_config(self):
self.config = self.get_config_as_object()

# TODO: restore when working on issue #43 dealing with moving emodpy distribuiton classes down to emod-api
# TODO: restore when working on issue #43 dealing with moving emodpy distribution classes down to emod-api
# def test_set_birth_rate_config(self):
# demog = Demographics.from_template_node()
# self.config.parameters.Enable_Birth = 0 # since it is 1 by default
Expand All @@ -28,15 +28,15 @@ def reset_config(self):
# demog.implicits[-1](self.config)
# self.assertEqual(self.config.parameters.Birth_Rate_Dependence, "POPULATION_DEP_RATE")

# TODO: restore when working on issue #43 dealing with moving emodpy distribuiton classes down to emod-api
# TODO: restore when working on issue #43 dealing with moving emodpy distribution classes down to emod-api
# def test_set_mortality_rate_config(self):
# for index in range(2):
# demog = Demographics.from_template_node()
# if index:
# demog.SetMortalityRate(0.75)
# demog.implicits[-1](self.config)

# TODO: restore when working on issue #43 dealing with moving emodpy distribuiton classes down to emod-api
# TODO: restore when working on issue #43 dealing with moving emodpy distribution classes down to emod-api
# def test_set_mortality_distribution(self):
# demog = Demographics.from_template_node()
#
Expand All @@ -47,7 +47,7 @@ def reset_config(self):
# demog.implicits[-2](self.config)
# self.assertEqual(self.config.parameters.Death_Rate_Dependence, "NONDISEASE_MORTALITY_BY_AGE_AND_GENDER")

# TODO: restore when working on issue #43 dealing with moving emodpy distribuiton classes down to emod-api
# TODO: restore when working on issue #43 dealing with moving emodpy distribution classes down to emod-api
# def test_set_age_distribution(self):
# demog = Demographics.from_template_node()
# self.assertEqual(self.config.parameters.Age_Initialization_Distribution_Type, "DISTRIBUTION_OFF")
Expand Down
11 changes: 6 additions & 5 deletions tests/test_demog_send_over_pipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import json
import unittest
import emod_api.demographics.demographics as Demog
from emod_api.demographics.demographics import Demographics
import emod_api.demographics.pre_defined_distributions as Distributions
import tempfile
import copy

from emod_api.demographics.node import Node

thisplatform = sys.platform


Expand All @@ -19,11 +22,10 @@ def setUpClass(cls):
cls.create_large_demog(cls)

def create_large_demog(cls):
print('called once before any tests in class')
# Large demographics object.
num_node = 1000
print("Creating big complex demographics.")
cls.large_demog = Demog.from_params(tot_pop=10000000, num_nodes=num_node)
nodes = [Node(lat=0, lon=0, pop=10000, forced_id=i+1) for i in range(num_node)]
cls.large_demog = Demographics(nodes=nodes)


for node_id in range(num_node):
cls.large_demog.SetFertilityOverTimeFromParams(years_region1=100, years_region2=10, start_rate=0.3, inflection_rate=0.25, end_rate=0.1, node_ids=[node_id + 1])
Expand All @@ -32,7 +34,6 @@ def create_large_demog(cls):

# Large Expected Results Dictionary
cls.large_expected = cls.large_demog.to_dict()
print(f"Total of nodes: {cls.large_expected['Metadata']['NodeCount']} ... file size is aprox 54 MB")

@unittest.skipIf(thisplatform.startswith("win"), "Not valid on Windows")
def test_linux_send_over_file(self):
Expand Down
Loading