blob: e9df32001ff88e204319f69d2e121fbe8f9f1e2b [file] [log] [blame]
package org.eclipse.stem.populationmodels.standard.impl;
/*******************************************************************************
* Copyright (c) 2011 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 org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.stem.core.graph.DynamicLabel;
import org.eclipse.stem.core.graph.Exchange;
import org.eclipse.stem.core.graph.ExchangeType;
import org.eclipse.stem.core.graph.GraphFactory;
import org.eclipse.stem.core.graph.Node;
import org.eclipse.stem.core.model.STEMTime;
import org.eclipse.stem.data.geography.centers.GeographicCenters;
import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProvider;
import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProviderAdapter;
import org.eclipse.stem.definitions.adapters.spatial.geo.LatLongProviderAdapterFactory;
import org.eclipse.stem.definitions.nodes.impl.RegionImpl;
import org.eclipse.stem.populationmodels.Activator;
import org.eclipse.stem.populationmodels.standard.SeasonalPopulationModel;
import org.eclipse.stem.populationmodels.standard.StandardPackage;
import org.eclipse.stem.populationmodels.standard.StandardPopulationModelLabelValue;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Seasonal Population Model</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link org.eclipse.stem.populationmodels.standard.impl.SeasonalPopulationModelImpl#getPhase <em>Phase</em>}</li>
* <li>{@link org.eclipse.stem.populationmodels.standard.impl.SeasonalPopulationModelImpl#getModulationAmplitude <em>Modulation Amplitude</em>}</li>
* <li>{@link org.eclipse.stem.populationmodels.standard.impl.SeasonalPopulationModelImpl#getPeriod <em>Period</em>}</li>
* <li>{@link org.eclipse.stem.populationmodels.standard.impl.SeasonalPopulationModelImpl#isUseLatitude <em>Use Latitude</em>}</li>
* </ul>
* </p>
*
* @generated
*/
public class SeasonalPopulationModelImpl extends StandardPopulationModelImpl implements SeasonalPopulationModel {
/**
* constant
*/
private static final double MILLIS_PER_DAY = 1000.0*60.0*60.0*24.0;
/**
* todo model this in emf
*/
protected double seasonalModulationExponent = 2.0;
/**
* the equator
*/
private static final double EQUATOR_LATITUDE = 0.0;
/**
* TODO model this in EMF
*/
private static final boolean TROPICAL_ONLY = true;
/**
* Latitude of the Tropic of Cancer in Degrees (Tropic of Capricorn is -ve of this).
*/
public static final double TROPIC_OF_CANCER_LATITUDE = 23.439444;
/**
* TODO model this in EMF
*/
private static final double LATITUDE_SIGMOID_WIDTH = 4.5;
/**
* The default value of the '{@link #getPhase() <em>Phase</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getPhase()
* @generated
* @ordered
*/
protected static final double PHASE_EDEFAULT = 0.0; // TODO The default value literal "" is not valid.
/**
* The cached value of the '{@link #getPhase() <em>Phase</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getPhase()
* @generated
* @ordered
*/
protected double phase = PHASE_EDEFAULT;
/**
* The default value of the '{@link #getModulationAmplitude() <em>Modulation Amplitude</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getModulationAmplitude()
* @generated
* @ordered
*/
protected static final double MODULATION_AMPLITUDE_EDEFAULT = 1.0;
/**
* The cached value of the '{@link #getModulationAmplitude() <em>Modulation Amplitude</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getModulationAmplitude()
* @generated
* @ordered
*/
protected double modulationAmplitude = MODULATION_AMPLITUDE_EDEFAULT;
/**
* The default value of the '{@link #getPeriod() <em>Period</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getPeriod()
* @generated
* @ordered
*/
protected static final double PERIOD_EDEFAULT = 365.25;
/**
* The cached value of the '{@link #getPeriod() <em>Period</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getPeriod()
* @generated
* @ordered
*/
protected double period = PERIOD_EDEFAULT;
/**
* The default value of the '{@link #isUseLatitude() <em>Use Latitude</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #isUseLatitude()
* @generated
* @ordered
*/
protected static final boolean USE_LATITUDE_EDEFAULT = true;
/**
* The cached value of the '{@link #isUseLatitude() <em>Use Latitude</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #isUseLatitude()
* @generated
* @ordered
*/
protected boolean useLatitude = USE_LATITUDE_EDEFAULT;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public SeasonalPopulationModelImpl() {
super();
}
/**
* Compute the changes in the population
* The birth rate and death rates are modified in each time step by a seasonal modulation function
* The birth/death rates are NOT constant
* @param time
* @param timeDelta
* @param labels
* @generated NOT
*/
@Override
public void calculateDelta(STEMTime time, long timeDelta,
EList<DynamicLabel> labels) {
// We simply calculate the change from the birth/death rate
// adjusted for the time period used in the simulation
final double adjustedBirthRate = adjustRate(this.getBirthRate(), this.getTimePeriod(), timeDelta);
final double adjustedDeathRate = adjustRate(this.getDeathRate(), this.getTimePeriod(), timeDelta);
double localPhase = getPhase();
for(DynamicLabel label:labels) {
StandardPopulationModelLabelImpl slabel = (StandardPopulationModelLabelImpl)label;
StandardPopulationModelLabelValueImpl delta = (StandardPopulationModelLabelValueImpl)slabel.getDeltaValue();
StandardPopulationModelLabelValue current = (StandardPopulationModelLabelValue)slabel.getProbeValue();
double seasonalBirthFactor = 0.0;
double currentPopulation = current.getCount();
// need to find the latitude of this node
// default
double latitude = EQUATOR_LATITUDE;
double currentMillis = time.getTime().getTime();
double latFactor = 1.0; // default = no dependence
localPhase = getPhase();
if(isUseLatitude()) {
// center coords
double[] lat_long = null;
// Get THIS node
Node node = slabel.getNode();
if(node instanceof RegionImpl) {
String nodeURI = node.getURI().lastSegment();
lat_long = GeographicCenters.getCenter(nodeURI);
// still null? Compute it
if(lat_long==null) {
// Get the lat/long of the center of the node
final LatLongProviderAdapter latLongProviderB = (LatLongProviderAdapter) LatLongProviderAdapterFactory.INSTANCE
.adapt(node, LatLongProvider.class);
latLongProviderB.setTarget(node);
lat_long = latLongProviderB.getCenter();
}
if (lat_long == null) {
Activator.logError("Cannot find latitude for "+ nodeURI, null);
}
} else {
lat_long = new double[2];
lat_long[0] = EQUATOR_LATITUDE;
lat_long[1] = EQUATOR_LATITUDE; // not used. sets it to 0,0
}
// get the latitude
if(lat_long != null) {
latitude = lat_long[0] ;
}
// in a latitude dependent model, there is no modulation in the topics.
// As we move away from the equator, the modulation amplitude increases to it's maximal value
// and the constant floor decreases to it's minimum value
// based on a logistic (sigmoidal) function
latFactor = 1.0/(1.0 + Math.exp((TROPIC_OF_CANCER_LATITUDE - Math.abs(latitude))/LATITUDE_SIGMOID_WIDTH) );
// seasons reverse in the southern hemisphere
if(latitude <= 0.0) localPhase = getPhase()+Math.PI;
} //if useLatitude
double modulation = getModulationAmplitude()*latFactor;
double floor = (1.0-modulation);
double modulationPeriod = getPeriod();
double oscCos = Math.cos(localPhase + 2.0*Math.PI*currentMillis/(modulationPeriod*MILLIS_PER_DAY) ); // 1==>-1 cosine
// init default (phase 0) seasonalBirthFactor is 0.0 in winter. range is 0->1.0
seasonalBirthFactor = (1.0+(-1.0*oscCos))/2.0;
// now scale it
seasonalBirthFactor *= modulation ;
double oscBirthRate = adjustedBirthRate * Math.abs(seasonalBirthFactor);
// add the base rates
oscBirthRate += (floor*adjustedBirthRate);
if(TROPICAL_ONLY) {
oscBirthRate *= getTropicalEnvelope(latitude, (2*TROPIC_OF_CANCER_LATITUDE));
}
// only modulate the birth rate.
double births = currentPopulation * oscBirthRate;
// we don't modulate the death rate (insect life expectancy
double deaths = currentPopulation * adjustedDeathRate;
currentPopulation += births;
currentPopulation -= deaths;
delta.setCount(births-deaths);
delta.setBirths(births);
delta.setDeaths(deaths);
Exchange birthExchange = GraphFactory.eINSTANCE.createExchange();
Exchange deathExchange = GraphFactory.eINSTANCE.createExchange();
birthExchange.setType(ExchangeType.BIRTHS_AND_DEATHS);
birthExchange.setCount(births);
deathExchange.setType(ExchangeType.BIRTHS_AND_DEATHS);
deathExchange.setCount(deaths);
delta.getArrivals().clear();
delta.getDepartures().clear();
delta.getArrivals().add(birthExchange);
delta.getDepartures().add(deathExchange);
handleMigration(slabel, delta.getArrivals(),delta.getDepartures(), this.getTimePeriod(), timeDelta, delta);
handlePipeTransport(slabel, delta.getArrivals(),delta.getDepartures(), timeDelta, delta);
ECollections.sort(delta.getArrivals());
ECollections.sort(delta.getDepartures());
}
}// calculateDelta
/**
* Envelope function that bounds mosquito population to tropical band
* Function is ==1.0 at equator (centered about equator)
* @param lat
* @param sgima is the width
* @return
*/
private double getTropicalEnvelope(double lat, double sigma) {
double exp = -((lat*lat)/(2.0*sigma*sigma));
return Math.exp(exp);
}
/**
* Utility function
* @param x
* @param mu mean
* @param sigma width
* @return
*/
private double getGausianUnnormalize(double x, double mu, double sigma) {
double exp = -(((x-mu)*(x-mu))/(2.0*sigma*sigma));
return Math.exp(exp);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass() {
return StandardPackage.Literals.SEASONAL_POPULATION_MODEL;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public double getPhase() {
return phase;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setPhase(double newPhase) {
double oldPhase = phase;
phase = newPhase;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, StandardPackage.SEASONAL_POPULATION_MODEL__PHASE, oldPhase, phase));
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public double getModulationAmplitude() {
return modulationAmplitude;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setModulationAmplitude(double newModulationAmplitude) {
double oldModulationAmplitude = modulationAmplitude;
modulationAmplitude = newModulationAmplitude;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, StandardPackage.SEASONAL_POPULATION_MODEL__MODULATION_AMPLITUDE, oldModulationAmplitude, modulationAmplitude));
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public double getPeriod() {
return period;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public boolean isUseLatitude() {
return useLatitude;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setUseLatitude(boolean newUseLatitude) {
boolean oldUseLatitude = useLatitude;
useLatitude = newUseLatitude;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, StandardPackage.SEASONAL_POPULATION_MODEL__USE_LATITUDE, oldUseLatitude, useLatitude));
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case StandardPackage.SEASONAL_POPULATION_MODEL__PHASE:
return getPhase();
case StandardPackage.SEASONAL_POPULATION_MODEL__MODULATION_AMPLITUDE:
return getModulationAmplitude();
case StandardPackage.SEASONAL_POPULATION_MODEL__PERIOD:
return getPeriod();
case StandardPackage.SEASONAL_POPULATION_MODEL__USE_LATITUDE:
return isUseLatitude();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case StandardPackage.SEASONAL_POPULATION_MODEL__PHASE:
setPhase((Double)newValue);
return;
case StandardPackage.SEASONAL_POPULATION_MODEL__MODULATION_AMPLITUDE:
setModulationAmplitude((Double)newValue);
return;
case StandardPackage.SEASONAL_POPULATION_MODEL__USE_LATITUDE:
setUseLatitude((Boolean)newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case StandardPackage.SEASONAL_POPULATION_MODEL__PHASE:
setPhase(PHASE_EDEFAULT);
return;
case StandardPackage.SEASONAL_POPULATION_MODEL__MODULATION_AMPLITUDE:
setModulationAmplitude(MODULATION_AMPLITUDE_EDEFAULT);
return;
case StandardPackage.SEASONAL_POPULATION_MODEL__USE_LATITUDE:
setUseLatitude(USE_LATITUDE_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case StandardPackage.SEASONAL_POPULATION_MODEL__PHASE:
return phase != PHASE_EDEFAULT;
case StandardPackage.SEASONAL_POPULATION_MODEL__MODULATION_AMPLITUDE:
return modulationAmplitude != MODULATION_AMPLITUDE_EDEFAULT;
case StandardPackage.SEASONAL_POPULATION_MODEL__PERIOD:
return period != PERIOD_EDEFAULT;
case StandardPackage.SEASONAL_POPULATION_MODEL__USE_LATITUDE:
return useLatitude != USE_LATITUDE_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(" (phase: ");
result.append(phase);
result.append(", modulationAmplitude: ");
result.append(modulationAmplitude);
result.append(", period: ");
result.append(period);
result.append(", useLatitude: ");
result.append(useLatitude);
result.append(')');
return result.toString();
}
} //SeasonalPopulationModelImpl