Sample9_FCVDiffusionSimulation.java
Created with JBuilder |
import mads.core.*;
import mads.networks.*;
import mads.servicefunctions.*;
import mads.consumers.*;
import mads.suppliers.*;
import java.util.*;
/**
* <p><H1>FCV Diffusion Simulation</H1></p>
*
* <p><B>Sample project no. 9 </B><BR> </p>
* <BR>
* This is the largest simulation from the tutorials sections. It is a real-world
* simulation of the diffusion of fuel cell vehicles (FCVs). These have two competitors,
* the standard gasoline vehicles and the gasoline-hybrid vehicles (e.g. Toyota Prius).
* A set of N_CONSUMERS consumers must decide once every REPLACE_PERIOD years which
* vehicle to buy. There are two aspects that govern this choice. The first is money and
* the second is generated utility.
* Regarding money, consumers have a household income generated acording to the
* US Census. We use the sfIncomeGenerator service function to get realization of the
* probability distribution of the income and the GenCategAndIncome auxiliary class
* to actually generate the money.
* The utility generated by each product is computed on a hedonistic basis (meaning that
* it is consider that a vehicle's value to a consumer is the sum of the value of its
* attributes (price, acceleration, speed and so on). As the attributes are expressed in
* different units, one needs to first convert all of them to the same thing (here USD)
* so that they can be added. To do this, we multiply each product's characteristics to
* coefficients (where a coefficient represents how much importance a consumer places
* on that particular vehicle attribute). This kind of utility evaluation is done by the
* AttributeValuingConsumer and the NeighburhoodAwareAttributeValuatingConsumer classes,
* where the latter considers * the value of interactions between consumers as well.
* Before speaking of utility and characteristics, we first have to define the product
* whoose utility we are calculating. Here the product represents a vehicle and has three
* variants representing the three competitors: gasoline, gasoline-hybrid and FCV. Each
* product has characteristics, in this case acceleration, fuel consumption, fuel
* availability, range and top speed.
* Actual technical data for characteristics is requested from service agents and
* received in the form of service functions. The same is true for the coefficients used
* to compute utility.
*/
public class Sample9_FCVDiffusionSimulation extends ASimulationAgent
{
/**
* New car acquisition policy
*/
// Average vehicle replacement period. Once every REPLACE_PERIOD years, a consumer
// will be consider "active" and will make his choice. For the following period,
// until becoming active again, he will be considered as owning the last chosen car
final int REPLACE_PERIOD = 4;
// Income fraction spent on a new car. A majority consumer can spend no more than
// INCOME_FRACTION_ON_CAR on the new vehicle, gathered for a period of REPLACE_PERIOD
// years. If these money are not enough, the consumer will buy from a default supplier.
// Here, this means he will buy a standard gasoline vehicle. For early adopters and
// early buyers, being more privileged, we do not consider monetary restrictions.
final double INCOME_FRACTION_ON_CAR = 0.08;
/**
* Consumer characteristics
*/
// Consumers are arranged in a network, so that they can interact. We consider the
// network being a square with the CONSUMER_NETWORK_SIZE size
final int CONSUMER_NETWORK_SIZE = 50;
// From the network size, we compute the number of consumers N_CONSUMERS
final int N_CONSUMERS = CONSUMER_NETWORK_SIZE * CONSUMER_NETWORK_SIZE;
// From these consumers, the ones that can spen on the car more than RICH_TRSH are
// consider rich. Early buyers will be selected from these ones as well as from
// very rich
final double RICH_TRSH = 75000 * REPLACE_PERIOD * INCOME_FRACTION_ON_CAR;
// The ones who can spend more than VERYRICH_TRSH are considered very rich. We will
// select the early adopters from these one.
final double VERYRICH_TRSH = 150000 * REPLACE_PERIOD * INCOME_FRACTION_ON_CAR;
// Different consumers assign different weights on vehicle attributes. We divide the
// consumer in three categories: early adopters, early buyers and majority.
// Early adopters are selected from the very rich.
final double EARLY_ADOPTERS_PERCENT = 3;
// Early buyers are selected from rich and very rich
final double EARLY_BUYERS_PERCENT = 12;
// Majority represents the rest to 100%;
// Defining the consumer groups
final int GROUP_EARLY_ADOPTERS = 0;
final int GROUP_EARLY_BUYERS = 1;
final int GROUP_MAJORITY = 2;
// Early buyers are computed as being between early adopters and majority, at a distance f from the former
ServiceFunction f = new ConstantServiceFunction(0.25);
// After a number of years denoted "delay", the early adopters`s interest decays to the one of the early buyers
// We denote by "d" the rate of this decay
ServiceFunction delay = new ConstantServiceFunction(5);
ServiceFunction d = new ConstantServiceFunction(0.25);
// Simulation time range (becose these is how much data we have ... )
final int START_YEAR = 2000;
final int END_YEAR = 2030;
// The annual sales of personal cars in the US is about SEGMENT_SIZE.
final long SEGMENT_SIZE = 9000000;
// As we have few agents, one agent will thus represent SCALE actual consumers
final double SCALE = (double)N_CONSUMERS / SEGMENT_SIZE;
/**
* Vehicle production
*/
// Hybrid introduction capacity (scaled down to the actual no. of agents)
final long HYBRID_INTRODUCTION_CAPACITY = Math.round(10000 * SCALE);
// The year the hybrid is introduced in the US
final int HYBRID_INTRODUCTION_YEAR = 2000;
// If the demand is larger than the suply, how much can production be increased from
// year to year
final double HYBRID_SUPPLY_INC_COEF = 0.5;
// FCV introduction capacity (scaled down to the actual no. of agents)
final long FCV_INTRODUCTION_CAPACITY = Math.round(100000 * SCALE);
// The most probable year the FCV will be introduced in the US
final int FCV_INTRODUCTION_YEAR = 2015;
// If the demand is larger than the supply, how much can production be increased from
// year to year
final double FCV_SUPPLY_INC_COEF = 0.5;
/**
* Refelling infrastructure
* As the low availability of hydrogen-equipped fuel stations is one of the big
* obstacles in adopting FCVs, the growth of the infrastructure has to be modeled
* as well. We consider there are two forces directing this growth. One is a
* exogenous one, coming as investments from outside the sector (e.g. from
* the government). The other is an endogenous one, meaning the number of stations
* will increase if there is demand, therefore if the percent of FCVs on the streets
* is larger than the percent of hydrogen stations.
*/
// The level of the infrastructure before any investment or growth
private static final double START_INFRASTRUCTURE = 0.0004;
// The year the external investments start coming
private static final double EXOG_START_YEAR = 2006;
// The level of the external investment (GH2_EXOG per year)
private static final double GH2_EXOG = 0.1 * 0.01;
// The max amount by which the infrastructure can increase in one year
private static final double GH2_MAX = 1.5 *0.01;
// If there are more FCVs on the road than stations, how fast the infrastructure
// will develop to account for this
private static final double V = 1.5;
// Aux: Random number generator
static Random rand = new Random();
/**
* The actual simulation
*/
public void doWork(){
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// DATA //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* We now request the data for all the characteristics and prices we consider in the simulation from
* service providing agents.
*/
/**
* First request the price for fuel, in this case gasoline and hydrogen
*/
// Gasoline price - high price scenario
ServiceFunction sfGasoline_Hi = findRemoteServiceFunction("sags.gasoline.AGasoline_Hi", new ServiceFunction[]{});
// Gasoline price - low price scenario
ServiceFunction sfGasoline_Lo = findRemoteServiceFunction("sags.gasoline.AGasoline_Lo", new ServiceFunction[]{});
// Hydrogen price - high price scenario
ServiceFunction sfHydrogen_Hi = findRemoteServiceFunction("sags.hydrogen.AHydrogen_Hi", new ServiceFunction[]{});
// Hydrogen price - low price scenario
ServiceFunction sfHydrogen_Lo = findRemoteServiceFunction("sags.hydrogen.AHydrogen_Lo", new ServiceFunction[]{});
/**
* Now request the technical attributes for all vehicles. These will be used to fill
* the characteristics of the products, so that utility can be computed
*/
// Request the technical attributes for the FCV
// Acceleration (seconds it takes to go from 0 to 100km/k)
ServiceFunction sfFcvAcceleration = findRemoteServiceFunction("sags.cars.fcv.FcvAcceleration", new ServiceFunction[]{});
// Fuel consumption (liters/km)
ServiceFunction sfFcvConsumption = findRemoteServiceFunction("sags.cars.fcv.FcvConsumption", new ServiceFunction[]{});
// Range (km)
ServiceFunction sfFcvRange = findRemoteServiceFunction("sags.cars.fcv.FcvRange", new ServiceFunction[]{});
// Top speed (km/h)
ServiceFunction sfFcvTopSpeed = findRemoteServiceFunction("sags.cars.fcv.FcvTopSpeed", new ServiceFunction[]{});
// Price, low price scenario (USD)
ServiceFunction sfFcvPrice = findRemoteServiceFunction("sags.cars.fcv.FcvPrice", new ServiceFunction[]{});
// Price, high price scenario (USD)
ServiceFunction sfFcvPrice_Hi = findRemoteServiceFunction("sags.cars.fcv.FcvPrice_Hi", new ServiceFunction[]{});
// Request the technical attributes for the standard gasoline vehicle
// Acceleration (seconds it takes to go from 0 to 100km/k)
ServiceFunction sfGasolineAcceleration = findRemoteServiceFunction("sags.cars.gasoline.GasolineAcceleration", new ServiceFunction[]{});
// Fuel consumption inside city (liters/km)
ServiceFunction sfGasolineConsumptionCity = findRemoteServiceFunction("sags.cars.gasoline.GasolineConsumptionCity", new ServiceFunction[]{});
// Fuel consumption on highway (liters/km)
ServiceFunction sfGasolineConsumptionHW = findRemoteServiceFunction("sags.cars.gasoline.GasolineConsumptionHW", new ServiceFunction[]{});
// Price (USD)
ServiceFunction sfGasolinePrice = findRemoteServiceFunction("sags.cars.gasoline.GasolinePrice", new ServiceFunction[]{});
// Range, if running only inside city (km)
ServiceFunction sfGasolineRangeCity = findRemoteServiceFunction("sags.cars.gasoline.GasolineRangeCity", new ServiceFunction[]{});
// Range, if running only on highway (km)
ServiceFunction sfGasolineRangeHW = findRemoteServiceFunction("sags.cars.gasoline.GasolineRangeHW", new ServiceFunction[]{});
// Top speed (km/h)
ServiceFunction sfGasolineTopSpeed = findRemoteServiceFunction("sags.cars.gasoline.GasolineTopSpeed", new ServiceFunction[]{});
// Request the technical attributes for the gasoline hybrid vehicle
// Acceleration (seconds it takes to go from 0 to 100km/k)
ServiceFunction sfHybridGasAcceleration = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasAcceleration", new ServiceFunction[]{});
// Fuel consumption inside city (liters/km)
ServiceFunction sfHybridGasConsumptionCity = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasConsumptionCity", new ServiceFunction[]{});
// Fuel consumption on highway (liters/km)
ServiceFunction sfHybridGasConsumptionHW = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasConsumptionHW", new ServiceFunction[]{});
// Price (USD)
ServiceFunction sfHybridGasPrice = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasPrice", new ServiceFunction[]{});
// Range, if running only inside city (km)
ServiceFunction sfHybridGasRangeCity = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasRangeCity", new ServiceFunction[]{});
// Range, if running only on highway (km)
ServiceFunction sfHybridGasRangeHW = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasRangeHW", new ServiceFunction[]{});
// Top speed (km/h)
ServiceFunction sfHybridGasTopSpeed = findRemoteServiceFunction("sags.cars.hybrid.gasoline.HybridGasTopSpeed", new ServiceFunction[]{});
// Combine the highway and city consumptions and ranges, according to the standard cycle
// For US, the car usage ratio is 45|55 for city|highway
ServiceFunction sfGasolineConsumption = sfGasolineConsumptionCity.multiply(0.45).plus(sfGasolineConsumptionHW.multiply(0.55));
ServiceFunction sfHybridGasConsumption = sfHybridGasConsumptionCity.multiply(0.45).plus(sfHybridGasConsumptionHW.multiply(0.55));
ServiceFunction sfGasolineRange = sfGasolineRangeCity.multiply(0.45).plus(sfGasolineRangeHW.multiply(0.55));
ServiceFunction sfHybridGasRange = sfHybridGasRangeCity.multiply(0.45).plus(sfHybridGasRangeHW.multiply(0.55));
// Define fuel availability for FCV; special case, will be computed afterwards
// It cannot be computed now, since it depends on the the adoption of the FCV.
// It will be computed by the stationsSupplier, which is a special kind of supplier
// called an ProductModifyingSupplier, defined at the end of the simulation. This
// supplier does not make any offers to consumer, it just computes the infrastructure.
ServiceFunction sfFcvFuelAvailability = new ConstantServiceFunction(START_INFRASTRUCTURE);
/**
* Consumer preferences
* We will request consumer preferences for all three consumer groups. These
* are based on the consumer model called AVID.
*/
// Request the preferences for the early adopter group
// As there is a decay process involved here, we need to specify the base year as well so the start
// of the decay be well defined
ServiceFunction sfAvidEarlyAdopters_Acceleration = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyAdopters_Acceleration", new ServiceFunction[]{f, d, delay});
ServiceFunction sfAvidEarlyAdopters_FuelCost = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyAdopters_FuelCost", new ServiceFunction[]{f, d, delay});
ServiceFunction sfAvidEarlyAdopters_FuelAvailability = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyAdopters_FuelAvailability", new ServiceFunction[]{f, d, delay});
ServiceFunction sfAvidEarlyAdopters_Range = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyAdopters_Range", new ServiceFunction[]{f, d, delay});
ServiceFunction sfAvidEarlyAdopters_TopSpeed = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyAdopters_TopSpeed", new ServiceFunction[]{f, d, delay});
sfAvidEarlyAdopters_Acceleration.setBase(START_YEAR);
sfAvidEarlyAdopters_FuelCost.setBase(START_YEAR);
sfAvidEarlyAdopters_FuelAvailability.setBase(START_YEAR);
sfAvidEarlyAdopters_Range.setBase(START_YEAR);
sfAvidEarlyAdopters_TopSpeed.setBase(START_YEAR);
// Request the preferences for the early buyers group
ServiceFunction sfAvidEarlyBuyers_Acceleration = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyBuyers_Acceleration", new ServiceFunction[]{f});
ServiceFunction sfAvidEarlyBuyers_FuelAvailability = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyBuyers_FuelAvailability", new ServiceFunction[]{f});
ServiceFunction sfAvidEarlyBuyers_FuelCost = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyBuyers_FuelCost", new ServiceFunction[]{f});
ServiceFunction sfAvidEarlyBuyers_Range = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyBuyers_Range", new ServiceFunction[]{f});
ServiceFunction sfAvidEarlyBuyers_TopSpeed = findRemoteServiceFunction("sags.coefs.cars.avid.EarlyBuyers_TopSpeed ", new ServiceFunction[]{f});
// Request the preferences for the majority group
ServiceFunction sfAvidMajority_Acceleration = findRemoteServiceFunction("sags.coefs.cars.avid.Majority_Acceleration", new ServiceFunction[]{f, d});
ServiceFunction sfAvidMajority_FuelCost = findRemoteServiceFunction("sags.coefs.cars.avid.Majority_FuelCost", new ServiceFunction[]{f, d});
ServiceFunction sfAvidMajority_FuelAvailability = findRemoteServiceFunction("sags.coefs.cars.avid.Majority_FuelAvailability", new ServiceFunction[]{f, d});
ServiceFunction sfAvidMajority_Range = findRemoteServiceFunction("sags.coefs.cars.avid.Majority_Range", new ServiceFunction[]{f, d});
ServiceFunction sfAvidMajority_TopSpeed = findRemoteServiceFunction("sags.coefs.cars.avid.Majority_TopSpeed", new ServiceFunction[]{f, d});
/**
* Other data
*/
// Request a C02 tax scenario. This tax will be added to the gasoline price
ServiceFunction sfC02Tax = findRemoteServiceFunction("sags.tax.co2.C02_Tax", new ServiceFunction[]{});
// Request the US income distribution. Used to generate the incomes of consumers
ServiceFunction sfIncomeGenerator = findRemoteServiceFunction("sags.incomes.USA_HouseholdIncome_Generator", new ServiceFunction[]{});
// Set the familiarity coefficient for early buyers and majority. This represents
// how much (in USD) does it mean for a consumer that the others near him (neighbors
// in the consumer network) have the same products.
ServiceFunction sfEarlyAdopters_Familiarity = new ConstantServiceFunction(0);
ServiceFunction sfEarlyBuyers_Familiarity = new ConstantServiceFunction(0);
ServiceFunction sfMajority_Familiarity = new ConstantServiceFunction(1500);
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// PRODUCT //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* Now that we have all the data, we can define the product whose diffusion
* we are simulating. The product is a vehicle, and has three variants:
* FCV, gasoline and gasoline hybrid.
* We will then add the attributes for each one. The values are among the
* service functions that we have previously received.
*/
// Define the three products: gasoline, gasoline-hybrid and FCV
Product prodGasoline = new Product("gasoline"),
prodHybrid = new Product("hybrid"),
prodFcv = new Product("fcv");
// Now set the characteristics (all attributes but the price)
// The price will be set by the suppliers
// Note that for range and fuel availability, we do not use the value but its inverse.
// This is because of the way the coefficients were defined in AVID
// For fcv
prodFcv.addCharact("acceleration" , sfFcvAcceleration);
prodFcv.addCharact("consumption" , sfFcvConsumption.multiply(sfHydrogen_Lo));
prodFcv.addCharact("range" , (new ConstantServiceFunction(1)).divide(sfFcvRange));
prodFcv.addCharact("top_speed" , sfFcvTopSpeed);
prodFcv.addCharact("fuelAvailability", (new ConstantServiceFunction(1)).divide(sfFcvFuelAvailability));
// For gasoline
prodGasoline.addCharact("acceleration" , sfGasolineAcceleration);
prodGasoline.addCharact("consumption" , sfGasolineConsumption.multiply(sfGasoline_Hi.plus(sfC02Tax)));
prodGasoline.addCharact("range" , (new ConstantServiceFunction(1)).divide(sfGasolineRange));
prodGasoline.addCharact("top_speed" , sfGasolineTopSpeed);
prodGasoline.addCharact("fuelAvailability", new ConstantServiceFunction(1));
// For hybrid
prodHybrid.addCharact("acceleration" , sfHybridGasAcceleration);
prodHybrid.addCharact("consumption" , sfHybridGasConsumption.multiply(sfGasoline_Hi.plus(sfC02Tax)));
prodHybrid.addCharact("range" , (new ConstantServiceFunction(1)).divide(sfHybridGasRange));
prodHybrid.addCharact("top_speed" , sfHybridGasTopSpeed);
prodHybrid.addCharact("fuelAvailability" , new ConstantServiceFunction(1));
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// SUPPLIERS //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* We have 3 + 1 = 4 suppliers. Just the first three are actual suppliers (in the
* sense that they send offers to consumers), the forth is a dummy supplier used
* to grow the FCV infrastructure.
* At the beginning of each year, each supplier will put on the market a specified
* quantify from a product, at a specified price, which in this simulation is not
* computed by the supplier but given externally.
*/
// First define the gasoline supplier, as a virtual supplier (no limit in supply)
// This supplier will put on the market an (almost) infinite number of gasoline
// cars, at the price given by sfGasolinePrice
Supplier gasolineSupplier = new VirtualSupplier("Gasoline Supplier", prodGasoline, sfGasolinePrice);
// Now define the hybrid and FCV suppliers as simple adaptive suppliers
// (start with a small supply, then increase with demand)
// These suppliers will start send offers in the HYBRID_INTRODUCTION_YEAR and
// FCV_INTRODUCTION_YEAR, respectively. In the beginning, they will put on the
// market HYBRID_INTRODUCTION_CAPACITY and FCV_INTRODUCTION_CAPACITY products.
// If the demand from consumers is greater than the supply, they will increase
// the production in the next year by HYBRID_SUPPLY_INC_COEF (FCV_SUPPLY_INC_COEF resp.)
Supplier hybridSupplier = new SimpleAdaptiveSupplier("Hybrid Gasoline Supplier", prodHybrid, sfHybridGasPrice, HYBRID_INTRODUCTION_YEAR, HYBRID_INTRODUCTION_CAPACITY, HYBRID_SUPPLY_INC_COEF);
Supplier fcvSupplier = new SimpleAdaptiveSupplier("Fcv Supplier", prodFcv, sfFcvPrice, FCV_INTRODUCTION_YEAR, FCV_INTRODUCTION_CAPACITY, FCV_SUPPLY_INC_COEF);
// Now add a special supplier, the gasoline station supplier
// This has and endogenous part (adaptive) and an exogenous part (investments)
// The most special part is that it has to modify the fuel availability for the FCV
H2StationsSupplier stationsSupplier = new H2StationsSupplier(sfFcvFuelAvailability ,fcvSupplier);
// Put all the supplers in a collection. This collecting will later be given as
// parameter to the main simulation class.
Collection<Supplier> suppliersCollection = new ArrayList();
suppliersCollection.add(gasolineSupplier);
suppliersCollection.add(hybridSupplier);
suppliersCollection.add(fcvSupplier);
suppliersCollection.add(stationsSupplier);
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// CONSUMERS //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* We will now declare the consumers. After the suppliers release their offers,
* each active consumer will compute the utility of each offer and order the best one.
* For the majority consumers, if they do not have enough money, they will buy one
* from the standard supplier (in this case the gasoline car supplier).
*/
// Declare the consumers vector
Consumer consumers[] = new Consumer[N_CONSUMERS];
/**
* We have three consumer categories. We will establish who is in which
* randomly, based on the income level.
*/
// Generate the income distribution
GenCategAndIncome getCategAndIncome = new GenCategAndIncome(sfIncomeGenerator);
// For each consumer, get his income and decide his category
for (int i = 0; i < N_CONSUMERS; i++){
// Get the income
double income = getCategAndIncome.getIncome(i);
// Get the group
double group = getCategAndIncome.getCateg(i);
// We make the assumption that early adopters and early buyers are not
// constrained by money, so we make their income very large
if (group != GROUP_MAJORITY)
income = 10000000.0;
/**
* We now create the actual consumer, depending on category
*/
// For early adopters
if (group == GROUP_EARLY_ADOPTERS) {
consumers[i] = new NeighborhoodAwareAttributeValuatingConsumer(
// The consumer has "income" to sped on the car, which he consider's buying
// once every REPLACE_PERIOD years.
income, REPLACE_PERIOD,
// The coefficients for this category of consumer, one for each attribute
new ServiceFunction[] {sfAvidEarlyAdopters_Acceleration, sfAvidEarlyAdopters_FuelCost, sfAvidEarlyAdopters_FuelAvailability, sfAvidEarlyAdopters_Range, sfAvidEarlyAdopters_TopSpeed},
// The attributes one considers while buying a car
new String[] {"acceleration", "consumption", "fuelAvailability", "range", "top_speed"},
// No default supplier, therefore buy something or nothing at all
null,
// As the coefficients for this category change with time, we must make sure
// the coefficients match the vehicle's introduction year.
new String[]{"fcv", "hybrid"},
new int[]{START_YEAR - FCV_INTRODUCTION_YEAR, START_YEAR - HYBRID_INTRODUCTION_YEAR},
// Familiarity term for this category
sfEarlyBuyers_Familiarity);
consumers[i].setName("Early adopter");
}
else if (group == GROUP_EARLY_BUYERS){
consumers[i] = new NeighborhoodAwareAttributeValuatingConsumer(
income, REPLACE_PERIOD,
new ServiceFunction[]{sfAvidEarlyBuyers_Acceleration, sfAvidEarlyBuyers_FuelCost, sfAvidEarlyBuyers_FuelAvailability, sfAvidEarlyBuyers_Range, sfAvidEarlyBuyers_TopSpeed},
new String[]{"acceleration", "consumption", "fuelAvailability", "range", "top_speed"},
null,
// For this category all coefs are constants, so no need to
// do time-shifting
new String[]{},
new int[]{},
sfEarlyBuyers_Familiarity);
consumers[i].setName("Early buyer");
}
else {
consumers[i] = new NeighborhoodAwareAttributeValuatingConsumer(
income, REPLACE_PERIOD,
new ServiceFunction[]{sfAvidMajority_Acceleration, sfAvidMajority_FuelCost, sfAvidMajority_FuelAvailability, sfAvidMajority_Range, sfAvidMajority_TopSpeed},
new String[]{"acceleration", "consumption", "fuelAvailability", "range", "top_speed"},
// Here the standard supplier is considersrd
gasolineSupplier,
new String[]{},
new int[]{},
sfMajority_Familiarity);
consumers[i].setName("Majority");
}
}
// Create the consumer network, as a small world network
ServiceNetworkFunction snfConsumers = (new WattsSmallWorldNetworkGenerator(N_CONSUMERS, 0.01, 4)).generateNetwork();
// Put all the consumers in a collection
Collection<Consumer> consumersCollection = new ArrayList();
for (int i = 0; i < N_CONSUMERS; i++)
consumersCollection.add(consumers[i]);
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// THE SIMULATION //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* This is the main simulation. It takes a collection of consumers, one of suppliers
* and their networks, if we care to consider them (here, only network between
* consumers is considered).
* The framework will automatically do all the trading. The results will then be
* returned in the "result" variable.
*/
// Define the simulation
ComplexInteractionSimulation sim = new ComplexInteractionSimulation(consumersCollection, snfConsumers, suppliersCollection, null);
// Run it
ComplexInteractionSimulationResult result = sim.run(START_YEAR, END_YEAR);
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
// RESULTS //
// -- // -- // -- // -- // -- // -- // -- // -- // -- // -- //
/**
* Now all it remains to do is to get the results. For each year, we can see how
* many consumers have adopted a specified product.
*/
// Plot the gasoline adoption path
result.getConsProductAdoptionPath("gasoline").plot("Adoption percent", "Gasoline vehicle adoption");;
// Plot the hybrid adoption path
result.getConsProductAdoptionPath("hybrid").plot("Adoption percent", "Gasoline-Hybrid vehicle adoption");;
// Plot the FCV adoption path
result.getConsProductAdoptionPath("fcv").plot("Adoption percent", "FCV vehicle adoption");;
// Plot the hybrid adoption path with actual numbers
result.getConsProductAdoptionPath("hybrid").multiply(SEGMENT_SIZE/100.0).plot("New sales", "Gasoline-Hybrid vehicle adoption");
}
/**
* <p>Title: H2 Stations Supplier</p>
* <p>Description: Class that implements the special infrastructure supplier.
* This supplier will modify the sfFcvFuelAvailability, to reflect the percent
* of hydrogen refelling stations. It will also take as parameter the fcvSupplier,
* from where the FCV diffusion information are taken
*/
class H2StationsSupplier extends ProductModifyingSupplier{
// The fcv supplier (the one from the main simulation)
private Supplier fcvSupplier;
// The infrastructure size for each year
private double infrastrHistory[] = new double[END_YEAR + 1];
// Define the constructor
public H2StationsSupplier(ServiceFunction sfH2Availability, Supplier fcvSupplier){
super("H2InfrastructureSupplier", sfH2Availability);
this.fcvSupplier = fcvSupplier;
}
// Define the modification function
// This is where sfFcvFuelAvailability gets modified
protected void modifyAttribute(ServiceFunction sfH2Availability){
// If before the exogenous start, put the value of START_INFRASTRUCTURE
if (time < EXOG_START_YEAR)
sfH2Availability.setValue(time, START_INFRASTRUCTURE);
// After the exogenous growth starts
else {
// Compute the current adoption percent
double fcvCurrentAdoption = fcvSupplier.getSellHistory(time - 1) / N_CONSUMERS;
// Compute the differente between the FCV percent and the available stations
double fcvInfrastructureGap = fcvCurrentAdoption - infrastrHistory[time-1];
// If there are more FCVs than stations, grow as fast as possible
if (fcvInfrastructureGap > 0)
sfH2Availability.setValue(time, infrastrHistory[time - 1] + GH2_MAX);
// If there is no gap, then grow exogenously and endo
else
{
// Must check that in order to get the adoption from 2 years ago
if (time >= START_YEAR + 2)
{
// The increase in adoption over the last two years
double fcvAdoptionIncrease = (fcvSupplier.getSellHistory(time - 1) - fcvSupplier.getSellHistory(time - 2)) / (double)N_CONSUMERS;
// Increas the infrastructure by the exogenous factor + some factor * the increase in adoption
sfH2Availability.setValue(time, infrastrHistory[time - 1] + Math.min(GH2_MAX, V*fcvAdoptionIncrease) + GH2_EXOG);
}
else
// If we are at the beginning and we cannot see the history, put just exogenous increase
sfH2Availability.setValue(time, infrastrHistory[time - 1] + GH2_EXOG);
}
}
// Record to history
infrastrHistory[time] = sfH2Availability.getValue(time);
}
}
/**
* <p>Title: genCategAndIncome</p>
* <p>Description: Auxiliary class used to generate the buyer groups and
* income, according to a given income distribution</p>
*/
class GenCategAndIncome{
// The category for each consumer (early adopter, early buyer or majority)
private int categs[] = new int[N_CONSUMERS];
// The income of each adopter
private double incomes[] = new double[N_CONSUMERS];
// Holds the income generator, we get it from the main simulation via the constructor
private ServiceFunction incomeGenerator;
// Count the number of rich and very rich
private int sum_veryRich = 0, sum_rich = 0;
/**
* Constructor
* Get the income generator from the main simulation and do the computations
* @param incomeGenerator ServiceFunction
*/
public GenCategAndIncome(ServiceFunction incomeGenerator){
this.incomeGenerator = incomeGenerator;
compute();
}
// Return the income for the index-th consumer
public double getIncome(int index){
return incomes[index];
}
// Return the category of the index-th consumer
public double getCateg(int index){
return categs[index];
}
// The actual computation
private void compute(){
// First generate the incomes and do some statistics
// Not this is not the actual income, just what can be spent on a new car
for (int i = 0; i < N_CONSUMERS; i++) {
// Generate the income according to the income distribution
incomes[i] = (long)incomeGenerator.getValue(rand.nextInt(101)) * INCOME_FRACTION_ON_CAR * REPLACE_PERIOD;
// Initialize all with majority
categs[i] = GROUP_MAJORITY;
// If very rich, increase the counter
if (incomes[i] >= VERYRICH_TRSH)
sum_veryRich++;
// If just rich, increase that counter
else if (incomes[i] < VERYRICH_TRSH && incomes[i] > RICH_TRSH)
sum_rich++;
}
// Compute the rich percent
double richPercent = 100 * (double)sum_rich / N_CONSUMERS;
// Compute the very rich percent
double veryRichPercent = 100 *(double)sum_veryRich / N_CONSUMERS;
// Compute how many actual early adopters correspond to the EARLY_ADOPTERS_PERCENT parameter
double adjEarlyAdopters = sum_veryRich * (100 / veryRichPercent) * EARLY_ADOPTERS_PERCENT / 100;
// Compute how many actual early buyers correspond to the EARLY_BUYERS_PERCENT parameter
double adjEarlyBuyers = (sum_veryRich + sum_rich)*(100 / (veryRichPercent + richPercent)) * EARLY_BUYERS_PERCENT / 100;
// Set the early adopters. Make no more than adjEarlyAdopters
int total = 0;
for (int i = 0; i < N_CONSUMERS; i++) {
if (incomes[i] >= VERYRICH_TRSH)
if (categs[i] == GROUP_MAJORITY) {
categs[i] = GROUP_EARLY_ADOPTERS;
total++;
}
if (total >= adjEarlyAdopters)
break;
}
// Set the early buyers. Make no more than adjEarlyBuyers
total = 0;
for (int i = 0; i < N_CONSUMERS; i++) {
if (incomes[i] >= RICH_TRSH)
if (categs[i] == GROUP_MAJORITY) {
categs[i] = GROUP_EARLY_BUYERS;
total++;
}
if (total >= adjEarlyBuyers)
break;
}
// The rest will remain majority
}
}
}
Sample9_FCVDiffusionSimulation.java
Created with JBuilder |