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