[docs]classCensus:def__init__(self,model,verbose:bool=False)->None:self.model=modelself.verbose=verboseasserthasattr(model,"patches"),"Census: model needs to have a 'patches' attribute."model.patches.add_vector_property("N",length=model.params.nticks+1,dtype=np.int32,default=0)asserthasattr(self.model,"params"),"Census: model needs to have a 'params' attribute."return
[docs]defcheck(self):""" check() gets called after all components have been instantiated and initialized. At this point, we will dynamically generate the update() method based on which compartments we find in the model. """prolog="def update(patches, agents, tick):\n N_next = patches.N[tick + 1]\n"epilog=" return\n"body=""forcompartmentin["S","E","Isym","Iasym","R","V1","V2"]:ifhasattr(self.model.agents,compartment):body+=f" {compartment}_next = agents.{compartment}[tick + 1]\n"body+=f" N_next[:] += {compartment}_next\n"code=compile(prolog+body+epilog,filename="<string>",mode="exec",)namespace={}exec(code,namespace)# noqa: S102self.update=staticmethod(namespace["update"])# Makes me feel a little slimy, but we need to set the initial population sizeself.update(self.model.patches,self.model.agents,-1)return
def__call__(self,model,tick:int)->None:self.update(model.patches,model.agents,tick)assertnp.all(model.patches.N[tick+1]>=0),"N' should not go negative"return
[docs]defplot(self,fig:Figure=None):# pragma: no cover_fig=plt.figure(figsize=(12,9),dpi=128,num="Census (Total Population)")iffigisNoneelsefigforipatchinnp.argsort(self.model.params.S_j_initial)[-10:]:plt.plot(self.model.patches.N[:,ipatch],label=f"{self.model.params.location_name[ipatch]}")plt.xlabel("Tick")plt.ylabel("Total Population")plt.legend()yield"Census (Total Population)"return