blob: 306186adbce96076bde7ef60ac4cd0676c03b8eb [file] [log] [blame]
package org.eclipse.stem.diseasemodels.standard.impl;
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.stem.core.STEMObjectPool;
import org.eclipse.stem.core.graph.DynamicLabel;
import org.eclipse.stem.core.graph.Edge;
import org.eclipse.stem.core.graph.EdgeLabel;
import org.eclipse.stem.core.graph.Exchange;
import org.eclipse.stem.core.graph.ExchangeType;
import org.eclipse.stem.core.graph.Graph;
import org.eclipse.stem.core.graph.IntegrationLabel;
import org.eclipse.stem.core.graph.IntegrationLabelValue;
import org.eclipse.stem.core.graph.Node;
import org.eclipse.stem.core.graph.NodeLabel;
import org.eclipse.stem.core.math.BinomialDistributionUtil;
import org.eclipse.stem.core.model.STEMTime;
import org.eclipse.stem.core.scenario.ScenarioInitializationException;
import org.eclipse.stem.definitions.edges.MixingEdge;
import org.eclipse.stem.definitions.edges.MixingEdgeLabelValue;
import org.eclipse.stem.definitions.edges.impl.MixingEdgeLabelImpl;
import org.eclipse.stem.definitions.labels.AreaLabel;
import org.eclipse.stem.definitions.labels.CommonBorderRelationshipLabelValue;
import org.eclipse.stem.definitions.labels.PopulationLabel;
import org.eclipse.stem.definitions.labels.RoadTransportRelationshipLabelValue;
import org.eclipse.stem.definitions.labels.impl.CommonBorderRelationshipLabelImpl;
import org.eclipse.stem.definitions.labels.impl.RoadTransportRelationshipLabelImpl;
import org.eclipse.stem.definitions.nodes.Region;
import org.eclipse.stem.diseasemodels.Activator;
import org.eclipse.stem.diseasemodels.standard.SEIRLabelValue;
import org.eclipse.stem.diseasemodels.standard.SILabelValue;
import org.eclipse.stem.diseasemodels.standard.StandardDiseaseModel;
import org.eclipse.stem.diseasemodels.standard.StandardDiseaseModelLabel;
import org.eclipse.stem.diseasemodels.standard.StandardDiseaseModelLabelValue;
import org.eclipse.stem.diseasemodels.standard.StandardPackage;
import org.eclipse.stem.populationmodels.standard.StandardPopulationModelLabel;
/**
* <!-- begin-user-doc --> An implementation of the model object '
* <em><b>Disease Model</b></em>'. <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link org.eclipse.stem.diseasemodels.standard.impl.StandardDiseaseModelImpl#getReferencePopulationDensity <em>Reference Population Density</em>}</li>
* <li>{@link org.eclipse.stem.diseasemodels.standard.impl.StandardDiseaseModelImpl#getRoadNetworkInfectiousProportion <em>Road Network Infectious Proportion</em>}</li>
* <li>{@link org.eclipse.stem.diseasemodels.standard.impl.StandardDiseaseModelImpl#getCharacteristicMixingDistance <em>Characteristic Mixing Distance</em>}</li>
* </ul>
* </p>
*
* @generated
*/
public abstract class StandardDiseaseModelImpl extends DiseaseModelImpl
implements StandardDiseaseModel {
/**
* The default value of the '{@link #getReferencePopulationDensity() <em>Reference Population Density</em>}' attribute.
* <!-- begin-user-doc
* --> <!-- end-user-doc -->
* @see #getReferencePopulationDensity()
* @generated
* @ordered
*/
protected static final double REFERENCE_POPULATION_DENSITY_EDEFAULT = 100.0;
/**
* The cached value of the '{@link #getReferencePopulationDensity() <em>Reference Population Density</em>}' attribute.
* <!-- begin-user-doc
* --> <!-- end-user-doc -->
* @see #getReferencePopulationDensity()
* @generated
* @ordered
*/
protected double referencePopulationDensity = REFERENCE_POPULATION_DENSITY_EDEFAULT;
/**
* The default value of the '{@link #getRoadNetworkInfectiousProportion()
* <em>Road Network Infectious Proportion</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getRoadNetworkInfectiousProportion()
* @generated
* @ordered
*/
protected static final double ROAD_NETWORK_INFECTIOUS_PROPORTION_EDEFAULT = 0.01;
/**
* The cached value of the '{@link #getRoadNetworkInfectiousProportion()
* <em>Road Network Infectious Proportion</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getRoadNetworkInfectiousProportion()
* @generated
* @ordered
*/
protected double roadNetworkInfectiousProportion = ROAD_NETWORK_INFECTIOUS_PROPORTION_EDEFAULT;
/**
* The default value of the '{@link #getCharacteristicMixingDistance() <em>Characteristic Mixing Distance</em>}' attribute.
* <!-- begin-user-doc
* --> <!-- end-user-doc -->
* @see #getCharacteristicMixingDistance()
* @generated
* @ordered
*/
protected static final double CHARACTERISTIC_MIXING_DISTANCE_EDEFAULT = 2.25;
/**
* The cached value of the '{@link #getCharacteristicMixingDistance() <em>Characteristic Mixing Distance</em>}' attribute.
* <!-- begin-user-doc
* --> <!-- end-user-doc -->
* @see #getCharacteristicMixingDistance()
* @generated
* @ordered
*/
protected double characteristicMixingDistance = CHARACTERISTIC_MIXING_DISTANCE_EDEFAULT;
/**
* Pool of label values used to reduce object creation. Key'd by the population identifier (different types of label values for different populations are possible)
*/
protected HashMap<String, STEMObjectPool> labelValueObjectPools = new HashMap<String, STEMObjectPool>();
/**
* Pool of list to keep edges of certain type for a node
*/
protected STEMObjectPool edgeListObjectPool = new STEMObjectPool(5, 5) {
@Override
protected Object createNewObject() {
return new ArrayList<Edge>();
}
@Override
protected void resetObject(Object o) {
// Nothing to do
}
};
/**
* Determine whether to do mixing via common border or via mixing edges. If even a single mixing edge exists in the model, mixing via mixing edges are used instead
*/
protected boolean doCommonBorderMixing=true;
/**
* Set to true when mixing strategy (common border of mixing edges) is determined. This way we avoid figuring out the mixing strategy on every call.
*/
private boolean mixingStrategyDetermined = false;
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated NOT
*/
protected StandardDiseaseModelImpl() {
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass() {
return StandardPackage.Literals.STANDARD_DISEASE_MODEL;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public double getReferencePopulationDensity() {
return referencePopulationDensity;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public void setReferencePopulationDensity(
double newReferencePopulationDensity) {
referencePopulationDensity = newReferencePopulationDensity;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public double getRoadNetworkInfectiousProportion() {
return roadNetworkInfectiousProportion;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public void setRoadNetworkInfectiousProportion(
double newRoadNetworkInfectiousProportion) {
roadNetworkInfectiousProportion = newRoadNetworkInfectiousProportion;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public double getCharacteristicMixingDistance() {
return characteristicMixingDistance;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
public void setCharacteristicMixingDistance(
double newCharacteristicMixingDistance) {
characteristicMixingDistance = newCharacteristicMixingDistance;
}
@Override
public void applyExternalDeltas(STEMTime time, double t, long timeDelta,
EList<DynamicLabel> labels) {
synchronized(this) {
if(labelValueObjectPools.size() == 0)
for(final String pop:this.getAllLabelIdentifiers()) {
labelValueObjectPools.put(pop, new STEMObjectPool(5, 5) {
@Override
protected Object createNewObject() {
return StandardDiseaseModelImpl.this.createDiseaseModelLabelValue(pop);
}
@Override
protected void resetObject(Object o) {
// Nothing to do
}
});
}
}
for(int i=0;i<labels.size();++i) {
//for (final Iterator<DynamicLabel> currentStateLabelIter = labels
// .iterator(); currentStateLabelIter.hasNext();) {
final StandardDiseaseModelLabel diseaseLabel = (StandardDiseaseModelLabel) labels.get(i);
StandardDiseaseModelLabelValue myDelta = (StandardDiseaseModelLabelValue) diseaseLabel
.getDeltaValue();
Node n = diseaseLabel.getNode();
// Find other labels on the node that wants to exchange data
EList<NodeLabel> labs = n.getLabels();
for(int j=0;j<labs.size();++j) {
NodeLabel l = labs.get(j);
if (l instanceof IntegrationLabel
&& !l.equals(diseaseLabel)
&& ((IntegrationLabel) l).getIdentifier().equals(
diseaseLabel.getIdentifier())) {
IntegrationLabelValue sdeLabelValue = ((IntegrationLabel) l).getDeltaValue();
EList<Exchange> arrivals = sdeLabelValue.getArrivals();
EList<Exchange> departures = sdeLabelValue.getDepartures();
if (arrivals == null || departures == null) {
Activator.logError(
"Error, null arrivals or departures for label "
+ l, new Exception());
continue;
}
// Arrivals
for(int k=0;k<arrivals.size();++k) {
Exchange entry = arrivals.get(k);
// for(Node n2:arrivals.keySet()) {
if (entry.getType() == ExchangeType.BIRTHS_AND_DEATHS) {
EAttribute birthsCompartment = this.getBirthsCompartment();
myDelta.eSet(birthsCompartment, ((Double)myDelta.eGet(birthsCompartment))+entry.getCount()); // ToDo: Need eSet,eGetDouble...
// For stochastic modeling, add the target compartment where births goes into as well as the (disease) label
entry.setTarget(birthsCompartment);
entry.getOtherLabels().add(diseaseLabel);
} else if (entry.getType() == ExchangeType.MIGRATION) {
double inflow = entry.getCount();
// Find the corresponding disease label on the other
// node
Node otherNode = (Node) entry.getOtherLabels().get(0)
.getIdentifiable();
for(int m=0;m<otherNode.getLabels().size();++m) {
NodeLabel nl = otherNode.getLabels().get(m);
if (nl instanceof StandardDiseaseModelLabel
&& ((StandardDiseaseModelLabel) nl)
.getDecorator().equals(this)
&& ((StandardDiseaseModelLabel) nl)
.getPopulationModelLabel()
.getPopulationIdentifier()
.equals(diseaseLabel
.getIdentifier())) {
String pop = diseaseLabel.getIdentifier();
StandardDiseaseModelLabelValue value = (StandardDiseaseModelLabelValue)labelValueObjectPools.get(pop).get();
value.set(((StandardDiseaseModelLabel) nl).getTempValue());
double factor = inflow / value.getPopulationCount();
if (!Double.isInfinite(factor) && !Double.isNaN(factor)) {
value.scale(factor);
}
// Remember disease deaths since it'll be
// overwritten in the add
double diseaseDeaths = myDelta
.getDiseaseDeaths();
myDelta.add(value);
myDelta.setDiseaseDeaths(diseaseDeaths);
// Remember for stochastic modeling
entry.getOtherLabels().add(nl); // Other label
entry.getOtherLabels().add(diseaseLabel); // This label
labelValueObjectPools.get(pop).release(value);
}
}
} else if (entry.getType() == ExchangeType.AGING) {
// Find the disease label of the other age group
String otherAgeGroup = ((StandardPopulationModelLabel) entry
.getOtherLabels().get(0)).getPopulationIdentifier();
for(int m=0;m<labs.size();++m) {
NodeLabel nl = labs.get(m);
if (nl instanceof StandardDiseaseModelLabel) {
StandardDiseaseModelLabel otherDiseaseLabel = (StandardDiseaseModelLabel) nl;
if (otherDiseaseLabel.getDecorator()
.equals(this)
&& otherDiseaseLabel
.getIdentifier().equals(
otherAgeGroup)) {
String pop = otherDiseaseLabel.getIdentifier();
StandardDiseaseModelLabelValue value = (StandardDiseaseModelLabelValue)labelValueObjectPools.get(pop).get();
value.set(((StandardDiseaseModelLabel) nl).getTempValue());
if (value.getPopulationCount() > 0.0) {
value.scale(entry.getCount()
/ value.getPopulationCount());
}
double diseaseDeaths = myDelta
.getDiseaseDeaths();
myDelta.add(value);
myDelta.setDiseaseDeaths(diseaseDeaths);
// Remember for stochastic modeling
entry.getOtherLabels().add(nl); // Other label
entry.getOtherLabels().add(diseaseLabel); // This label
labelValueObjectPools.get(pop).release(value);
}
}
}
}
}
// Departures
for(int k=0;k<departures.size();++k) {
Exchange entry = departures.get(k);
String pop = diseaseLabel.getIdentifier();
// Departures are either deaths or population moving to
// other nodes, hence we substract from the local node.
StandardDiseaseModelLabelValue currentState = null;
if (entry.getType() == ExchangeType.BIRTHS_AND_DEATHS
|| entry.getType() == ExchangeType.AGING) {
currentState = (StandardDiseaseModelLabelValue)labelValueObjectPools.get(pop).get();
currentState.set(diseaseLabel.getProbeValue());
}
else if (entry.getType() == ExchangeType.MIGRATION) {
currentState = (StandardDiseaseModelLabelValue)labelValueObjectPools.get(pop).get();
currentState.set((diseaseLabel).getTempValue());
// Need to use
// temp value
// for migration
// or an
// inbalance
// will occyr
} else if(entry.getType() == ExchangeType.COMPARTMENT_TRANSITION)
continue; // Safe. But we should never have an ExchangeType COMPARTMENT_TRANSITIONS on deltas from population models
if(entry.getType() == ExchangeType.BIRTHS_AND_DEATHS) {
doDeaths(currentState, myDelta, entry.getCount());
entry.getOtherLabels().add(diseaseLabel);
labelValueObjectPools.get(pop).release(currentState);
continue;
}
double populationCount = currentState
.getPopulationCount();
double outflow = entry.getCount();
double factor = outflow / populationCount;
if (Double.isNaN(factor) || Double.isInfinite(factor))
factor = 0.0; // safe
currentState.scale(factor);
// Remember disease deaths since they will be
// overwritten by sub
double diseaseDeaths = myDelta.getDiseaseDeaths();
myDelta.sub(currentState);
myDelta.setDiseaseDeaths(diseaseDeaths);
labelValueObjectPools.get(pop).release(currentState);
}
}
}
}
}
/**
* Add stochastic noise if the flag is set. Noise is added to incidence. The method is overridden by any subclass that needs
* to do more sophisticated things. The next value has already been set to its deterministic solution when this method is called,
* and this method changes it to add the noise.
* This method calls BinomialDistributionUtil.fastPickFromBinomialDist which uses org.apache.commons.math.distribution.BinomialDistributionImpl;
* @generated NOT
*/
@Override
public void doStochasticProcess(IntegrationLabel iLabel, long timeDelta) {
//Turn next value into integers
if(this.getBinomialDistribution() == null)
this.setBinomialDistribution(new BinomialDistributionUtil(this.getRandomSeed()));
// Add stochastic noise to incidence and adjust the next value
if(iLabel.getNextValue() instanceof SEIRLabelValue) {
SEIRLabelValue nextSEIR = (SEIRLabelValue)iLabel.getNextValue();
SEIRLabelValue previousSEIR = (SEIRLabelValue)iLabel.getCurrentValue(); // before the last integration step
addNoise(previousSEIR, nextSEIR, StandardPackage.eINSTANCE.getStandardDiseaseModelLabelValue_S(), StandardPackage.eINSTANCE.getStandardDiseaseModelLabelValue_Incidence(), StandardPackage.eINSTANCE.getSEIRLabelValue_E(), 1.0);
} else if(iLabel.getNextValue() instanceof SILabelValue) {
SILabelValue nextSI = (SILabelValue)iLabel.getNextValue(); // NOW
SILabelValue previousSI = (SILabelValue)iLabel.getCurrentValue(); // before the last integration step
addNoise(previousSI, nextSI, StandardPackage.eINSTANCE.getStandardDiseaseModelLabelValue_S(), StandardPackage.eINSTANCE.getStandardDiseaseModelLabelValue_Incidence(), StandardPackage.eINSTANCE.getSILabelValue_I(),1.0);
}
IntegrationLabelValue nextVal = (IntegrationLabelValue)iLabel.getNextValue();
EList<EAttribute> attrs = nextVal.eClass().getEAllAttributes();
// for(int i=0;i<attrs.size();++i) {
// EAttribute attr = attrs.get(i);
// if(attr.getEType().getClassifierID() == EcorePackage.EDOUBLE && attr.isChangeable()) {
// nextVal.eSetDouble(attr.getFeatureID(), Math.round(nextVal.eGetDouble(attr.getFeatureID())));
// }
// }
}
/**
* Adds noise from the delta value subtracted from the source and added to the target, ensuring that neither the source or the target goes below zero.
*
* @param previous The previous label value
* @param next The next label value (with the deterministic incidence added)
* @param source The source attribute (e.g. S)
* @param delta The delta attribute (typically incidence)
* @param target The target attribute (e.g. I)
* @param fraction Set to < 1.0 if only a fraction of the incidence is drawn from the source compartment
*/
protected void addNoise(IntegrationLabelValue previous, IntegrationLabelValue next, EAttribute source, EAttribute delta, EAttribute target, double fraction) {
if(Double.isInfinite(fraction) || Double.isNaN(fraction)) return; // Nothing to do
double sourcePrevious = previous.eGetDouble(source.getFeatureID());
double sourceNow = next.eGetDouble(source.getFeatureID()); // already has deterministic incidence added back in
double Savg = (sourcePrevious+sourceNow)/2.0; // average over the last step. This is approximate average
if(Savg <= BinomialDistributionUtil.MAX_N) {
int S = (int)Math.ceil(Savg); // round up the x.5
double prob = next.eGetDouble(delta.getFeatureID())*fraction/S; // Random pick from the source but only by the fraction that came from there
// if(next.eContainer().toString().contains("E85723"))
// System.out.println("");
if((!Double.isNaN(prob))&&(prob < 1)) {
//dummy
int newDelta = this.getBinomialDistribution().fastPickFromBinomialDist(prob, S);
double oldDelta = (next.eGetDouble(delta.getFeatureID())*fraction); // Estimate of the part of the delta that was drawn from the source
double diff = newDelta - oldDelta;
// If we're drawing too much, skip stochastic step and set transition to 0
if(next.eGetDouble(source.getFeatureID())-diff < 0
|| next.eGetDouble(target.getFeatureID())+diff < 0) {
next.eSetDouble(source.getFeatureID(), next.eGetDouble(source.getFeatureID())+oldDelta);
next.eSetDouble(target.getFeatureID(), next.eGetDouble(target.getFeatureID())-oldDelta);
// safe
if(next.eGetDouble(source.getFeatureID()) < 0)
next.eSetDouble(source.getFeatureID(), 0);
if(next.eGetDouble(target.getFeatureID()) < 0)
next.eSetDouble(target.getFeatureID(), 0);
next.eSetDouble(delta.getFeatureID(), 0.0);
return;
}
next.eSetDouble(source.getFeatureID(), next.eGetDouble(source.getFeatureID())-diff);
next.eSetDouble(target.getFeatureID(), next.eGetDouble(target.getFeatureID())+diff);
next.eSetDouble(delta.getFeatureID(), next.eGetDouble(delta.getFeatureID())+diff); // Adjust delta by the difference. Will be an integer
}
}
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@SuppressWarnings("boxing")
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case StandardPackage.STANDARD_DISEASE_MODEL__REFERENCE_POPULATION_DENSITY:
return getReferencePopulationDensity();
case StandardPackage.STANDARD_DISEASE_MODEL__ROAD_NETWORK_INFECTIOUS_PROPORTION:
return getRoadNetworkInfectiousProportion();
case StandardPackage.STANDARD_DISEASE_MODEL__CHARACTERISTIC_MIXING_DISTANCE:
return getCharacteristicMixingDistance();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@SuppressWarnings("boxing")
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case StandardPackage.STANDARD_DISEASE_MODEL__REFERENCE_POPULATION_DENSITY:
setReferencePopulationDensity((Double)newValue);
return;
case StandardPackage.STANDARD_DISEASE_MODEL__ROAD_NETWORK_INFECTIOUS_PROPORTION:
setRoadNetworkInfectiousProportion((Double)newValue);
return;
case StandardPackage.STANDARD_DISEASE_MODEL__CHARACTERISTIC_MIXING_DISTANCE:
setCharacteristicMixingDistance((Double)newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case StandardPackage.STANDARD_DISEASE_MODEL__REFERENCE_POPULATION_DENSITY:
setReferencePopulationDensity(REFERENCE_POPULATION_DENSITY_EDEFAULT);
return;
case StandardPackage.STANDARD_DISEASE_MODEL__ROAD_NETWORK_INFECTIOUS_PROPORTION:
setRoadNetworkInfectiousProportion(ROAD_NETWORK_INFECTIOUS_PROPORTION_EDEFAULT);
return;
case StandardPackage.STANDARD_DISEASE_MODEL__CHARACTERISTIC_MIXING_DISTANCE:
setCharacteristicMixingDistance(CHARACTERISTIC_MIXING_DISTANCE_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case StandardPackage.STANDARD_DISEASE_MODEL__REFERENCE_POPULATION_DENSITY:
return referencePopulationDensity != REFERENCE_POPULATION_DENSITY_EDEFAULT;
case StandardPackage.STANDARD_DISEASE_MODEL__ROAD_NETWORK_INFECTIOUS_PROPORTION:
return roadNetworkInfectiousProportion != ROAD_NETWORK_INFECTIOUS_PROPORTION_EDEFAULT;
case StandardPackage.STANDARD_DISEASE_MODEL__CHARACTERISTIC_MIXING_DISTANCE:
return characteristicMixingDistance != CHARACTERISTIC_MIXING_DISTANCE_EDEFAULT;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
* @generated
*/
@Override
public String toString() {
if (eIsProxy()) return super.toString();
StringBuffer result = new StringBuffer(super.toString());
result.append(" (referencePopulationDensity: "); //$NON-NLS-1$
result.append(referencePopulationDensity);
result.append(", roadNetworkInfectiousProportion: "); //$NON-NLS-1$
result.append(roadNetworkInfectiousProportion);
result.append(", characteristicMixingDistance: "); //$NON-NLS-1$
result.append(characteristicMixingDistance);
result.append(')');
return result.toString();
}
/**
* This method replaces the onsiteInfectious value with an
* effectiveInfectious population size based on mixing with neighboring
* sites. In this implementation the edges are bidirectional and have a
* fixed weight based on getPhysicallyAdjacentInfectiousProportion() (called
* in the helper method getInfectiousChangeFromMixing(). But the mixing is
* also weighted by the areas and populations of a site relative to the area
* and population of it's neighbors. Note that the value returned must be
* NORMALIZED by the total population because the product beta * S *
* Ieffective must have units of persons. Since S has units of persons the
* Ieffective must be dimensionless (normalized to the population). beta is
* the infection rate and has units of inverse time.
*
* @see org.eclipse.stem.diseasemodels.standard.SI.getNormalizedEffectiveInfectious
* @param node
* the node we are currently looking at to compute the next
* disease state
* @param diseaseLabel
* the label for the current disease computation
* @param onsiteInfectious
* the number of infectious people on site (at the node above)
* @param infectiousAttribute
* Attribute representing the infectious compartment
* @generated NOT
*/
public double getNormalizedEffectiveInfectious(final Node node,
final StandardDiseaseModelLabel diseaseLabel,
final double onsiteInfectious, EAttribute infectiousAttribute, EAttribute mixingAttribute, EAttribute roadAttribute) {
// Determine whether to use mixing edges or common border edges
determineMixingStrategy();
// For performance, check if mixing parameters are zero. If so, just
// return the unmixed result
if( doCommonBorderMixing && (Double)this.eGet(mixingAttribute) == 0.0 && (Double)this.eGet(roadAttribute) == 0.0) {
double totalPopulation = ((StandardDiseaseModelLabelValue) diseaseLabel
.getTempValue()).getPopulationCount();
double retVal = 0.0;
if (totalPopulation > 0.0)
retVal = onsiteInfectious / totalPopulation;
return retVal;
}
List<Edge>cEdges = (List<Edge>)edgeListObjectPool.get();
cEdges.clear();
if(!doCommonBorderMixing)
return doMixingViaMixingEdges(node, diseaseLabel, onsiteInfectious, infectiousAttribute, mixingAttribute, roadAttribute);
else
return doMixingViaCommonBorderEdges(node, diseaseLabel, onsiteInfectious, infectiousAttribute, mixingAttribute, roadAttribute);
} // getNormalizedEffectiveInfectious
/**
* By default births goes into the S compartment. Override if a different compartment is used.
* @generated NOT
*/
public EAttribute getBirthsCompartment() {
return StandardPackage.eINSTANCE.getStandardDiseaseModelLabelValue_S();
}
/**
* Subclasses overrides this methods if they want to handle deaths differently in their model. By default deaths
* are substracted from all compartments
*
* @generated NOT
*/
public void doDeaths(StandardDiseaseModelLabelValue currentState, StandardDiseaseModelLabelValue delta, double deaths) {
double populationCount = currentState
.getPopulationCount();
double outflow = deaths;
double factor = outflow / populationCount;
if (Double.isNaN(factor) || Double.isInfinite(factor))
factor = 0.0; // safe
currentState.scale(factor);
// Remember disease deaths since they will be
// overwritten by sub
double diseaseDeaths = delta.getDiseaseDeaths();
delta.sub(currentState);
delta.setDiseaseDeaths(diseaseDeaths);
}
/**
* determineMixingStrategy. Determine which mixing strategy to use. If even a single mixing edge exists in the graph, mixing edges are being used instead of common
* border edges. When this method returns, the mixingStrategyDetermined flag has been set so any future calls are a no-op
*/
protected void determineMixingStrategy() {
if(mixingStrategyDetermined)
return; // already determined
Graph g = this.getGraph();
EMap<URI, Edge>edges = g.getEdges();
for(Map.Entry<URI, Edge>entry:edges.entrySet()) {
if(entry.getValue() instanceof MixingEdge) {
mixingStrategyDetermined = true;
doCommonBorderMixing = false;
return;
}
}
mixingStrategyDetermined = true;
}
/**
* Compute the mixing using common border edges. This code is only used when mixing edges are not available
*
* @param node
* @param diseaseLabel
* @param onsiteInfectious
* @param infectiousAttribute
* @param cEdges
* @return double Normalized effective infectious
*/
private double doMixingViaCommonBorderEdges(Node node,
StandardDiseaseModelLabel diseaseLabel, double onsiteInfectious,
EAttribute infectiousAttribute, EAttribute mixingAttribute, EAttribute roadAttribute) {
double infectiousChangeFromMixing = 0.0;
double populationChangeFromMixing = 0.0;
List<Edge>cEdges = (List<Edge>)edgeListObjectPool.get();
cEdges.clear();
CommonBorderRelationshipLabelImpl.getCommonBorderEdgesFromNode(node, cEdges);
for(int i=0;i<cEdges.size();++i) {
Edge borderEdge = cEdges.get(i);
// If it exists, we're looking for the label this disease model
// updates on the node at the other end of the border edge.
// sum up the changes from each connected node.
// NOTE: some of these changes could be negative
final Node otherNode = borderEdge.getOtherNode(node);
double borderLength = ((CommonBorderRelationshipLabelValue) borderEdge
.getLabel().getCurrentValue()).getBorderLength();
if (otherNode instanceof Region) {
double otherArea = 0.0;
double otherPopulation = 0.0;
double otherInfective = 0.0;
EList<NodeLabel>labs = otherNode.getLabels();
for(int j=0;j<labs.size();++j) {
NodeLabel otherLabel = labs.get(j);
if (otherLabel instanceof AreaLabel) {
otherArea = ((AreaLabel) otherLabel)
.getCurrentAreaValue().getArea();
} else if (otherLabel instanceof StandardDiseaseModelLabel) {
StandardDiseaseModelLabel otherDiseaseLabel = (StandardDiseaseModelLabel) otherLabel;
if (otherDiseaseLabel.getDecorator() == this
&& otherDiseaseLabel.getIdentifier().equals(
diseaseLabel.getIdentifier())) {
otherPopulation = ((StandardDiseaseModelLabelValue) otherDiseaseLabel
.getTempValue()).getPopulationCount();
otherInfective = otherDiseaseLabel
.getTempValue().eGetDouble(
infectiousAttribute.getFeatureID());
}
}
}
double mixingFactor = Math.min((Double)this.eGet(mixingAttribute)
* borderLength / otherArea, 1.0);
if (otherArea == 0.0) {
mixingFactor = 0.0;
}
infectiousChangeFromMixing += mixingFactor * otherInfective;
populationChangeFromMixing += mixingFactor * otherPopulation;
}
} // for each border edge
edgeListObjectPool.release(cEdges);
return addMixingRoadTransportationEdges(node,
diseaseLabel, onsiteInfectious,
infectiousAttribute, infectiousChangeFromMixing, populationChangeFromMixing, roadAttribute);
}
/**
* Compute the mixing using common border edges. This code is only used when mixing edges are not available
*
* @param node
* @param diseaseLabel
* @param onsiteInfectious
* @param infectiousAttribute
* @param cEdges
* @return double Normalized effective infectious
*/
private double doMixingViaMixingEdges(Node node,
StandardDiseaseModelLabel diseaseLabel, double onsiteInfectious,
EAttribute infectiousAttribute, EAttribute mixingAttribute, EAttribute roadAttribute) {
double infectiousChangeFromMixing = 0.0;
double populationChangeFromMixing = 0.0;
List<Edge>mEdges = (List<Edge>)edgeListObjectPool.get();
mEdges.clear();
MixingEdgeLabelImpl.getMixingEdgesFromNode(node, this.getPopulationIdentifier(), mEdges);
for(int i=0;i<mEdges.size();++i) {
MixingEdge mixingEdge =(MixingEdge)mEdges.get(i);
MixingEdgeLabelValue mixingLabelValue = mixingEdge.getLabel().getCurrentValue();
double mixingRateOrAbs = mixingLabelValue.getMixingRate();
boolean useAbsoluteValue = mixingEdge.isUseAbsoluteValues();
final Node otherNode = mixingEdge.getOtherNode(node);
if (otherNode instanceof Region) {
double otherPopulation = 0.0;
double otherInfective = 0.0;
EList<NodeLabel>labs = otherNode.getLabels();
for(int j=0;j<labs.size();++j) {
NodeLabel otherLabel = labs.get(j);
if (otherLabel instanceof StandardDiseaseModelLabel) {
StandardDiseaseModelLabel otherDiseaseLabel = (StandardDiseaseModelLabel) otherLabel;
if (otherDiseaseLabel.getDecorator() == this
&& otherDiseaseLabel.getIdentifier().equals(
diseaseLabel.getIdentifier())) {
otherPopulation = ((StandardDiseaseModelLabelValue) otherDiseaseLabel
.getTempValue()).getPopulationCount();
otherInfective = otherDiseaseLabel
.getTempValue().eGetDouble(
infectiousAttribute.getFeatureID());
}
}
}
if(useAbsoluteValue && otherPopulation > 0.0) {
if(mixingRateOrAbs > otherPopulation) // Cannot mix with more people than currently available
mixingRateOrAbs = otherPopulation;
double fraction = mixingRateOrAbs / otherPopulation; // Do get the right scaling for the population change we need this
infectiousChangeFromMixing += fraction * otherInfective;
populationChangeFromMixing += fraction * otherPopulation;
} else {
infectiousChangeFromMixing += mixingRateOrAbs * otherInfective;
populationChangeFromMixing += mixingRateOrAbs * otherPopulation;
}
}
} // for each border edge
edgeListObjectPool.release(mEdges);
return addMixingRoadTransportationEdges(node,
diseaseLabel, onsiteInfectious,
infectiousAttribute, infectiousChangeFromMixing, populationChangeFromMixing, roadAttribute);
}
private double addMixingRoadTransportationEdges(Node node,
StandardDiseaseModelLabel diseaseLabel, double onsiteInfectious,
EAttribute infectiousAttribute, double currentInfectiousChangeFromMixing,
double currentPopulationChangeFromMixing, EAttribute roadAttribute) {
double infectiousChangeFromMixing = currentInfectiousChangeFromMixing;
double populationChangeFromMixing = currentPopulationChangeFromMixing;
List<Edge>rEdges = (List<Edge>)edgeListObjectPool.get();
rEdges.clear();
RoadTransportRelationshipLabelImpl.getRoadEdgesFromNode(node, rEdges);
for(int i=0;i<rEdges.size();++i) {
Edge roadEdge = rEdges.get(i);
// find the number of edges from the road edge - could be more than
// one
// also, roads have differenct capacities
EdgeLabel edgeLabel = roadEdge.getLabel();
double numCrossings = ((RoadTransportRelationshipLabelValue) edgeLabel
.getCurrentValue()).getNumberCrossings();
double infectiousProportion = Math.min(
(Double)this.eGet(roadAttribute) * numCrossings, 1.0);
// If it exists, we're looking for the label this disease model
// updates on the node at the other end of the border edge.
Node otherNode = roadEdge.getOtherNode(node);
double otherPopulation = 0.0;
double otherInfective = 0.0;
EList<NodeLabel>labs = otherNode.getLabels();
for(int j=0;j<labs.size();++j) {
NodeLabel otherLabel = labs.get(j);
if (otherLabel instanceof StandardDiseaseModelLabel) {
StandardDiseaseModelLabel otherDiseaseLabel = (StandardDiseaseModelLabel) otherLabel;
if (otherDiseaseLabel.getDecorator() == this
&& otherDiseaseLabel.getIdentifier().equals(
diseaseLabel.getIdentifier())) {
otherPopulation = ((StandardDiseaseModelLabelValue) otherDiseaseLabel
.getTempValue()).getPopulationCount();
otherInfective = otherDiseaseLabel
.getTempValue().eGetDouble(infectiousAttribute.getFeatureID());
}
}
}
infectiousChangeFromMixing += infectiousProportion * otherInfective;
populationChangeFromMixing += infectiousProportion
* otherPopulation;
} // for each road edge
edgeListObjectPool.release(rEdges);
// return the sum normalized to the total population
double totalPopulation = ((StandardDiseaseModelLabelValue) diseaseLabel
.getTempValue()).getPopulationCount()
+ populationChangeFromMixing;
double retVal = 0.0;
if (totalPopulation > 0.0) {
retVal = (onsiteInfectious + infectiousChangeFromMixing)
/ totalPopulation;
}
return retVal;
}
/**
* @param populationLabel
* the population label that labels the node
* @return the area of the node associated with the label
*/
public double getArea(final PopulationLabel populationLabel) {
double retValue = 0.0;
// The population label could have an area specified for the population
// that we should use instead of the area of the region labeled by the
// population label. This value would be specified if the population was
// densely packed into a small area of the larger region, for instance
// like a city in an otherwise large desert.
retValue = populationLabel.getPopulatedArea();
// Is there an area specified for the population?
if (retValue == 0.0) {
// No
// Ok, go find the area label and return the area of the region
for (final Iterator<NodeLabel> labelIter = populationLabel
.getNode().getLabels().iterator(); labelIter.hasNext();) {
final NodeLabel nodeLabel = labelIter.next();
// Is this an area label?
if (nodeLabel instanceof AreaLabel) {
// Yes
final AreaLabel areaLabel = (AreaLabel) nodeLabel;
retValue = areaLabel.getCurrentAreaValue().getArea();
break;
}
} // for
} // If no population area specified
return retValue;
} // getArea
/**
* Need to override to reset the binomial distribution seed
* @throws ScenarioInitializationException
*
* @generated NOT
*/
@Override
public void resetLabels() throws ScenarioInitializationException {
super.resetLabels();
if(this.getBinomialDistribution() != null)
this.getBinomialDistribution().setSeed(this.getRandomSeed());
} // resetLabels
} // StandardDiseaseModelImpl