001/* *********************************************************************** * 002 * project: org.matsim.* 003 * * 004 * *********************************************************************** * 005 * * 006 * copyright : (C) 2012 by the members listed in the COPYING, * 007 * LICENSE and WARRANTY file. * 008 * email : info at matsim dot org * 009 * * 010 * *********************************************************************** * 011 * * 012 * This program is free software; you can redistribute it and/or modify * 013 * it under the terms of the GNU General Public License as published by * 014 * the Free Software Foundation; either version 2 of the License, or * 015 * (at your option) any later version. * 016 * See also COPYING, LICENSE and WARRANTY file * 017 * * 018 * *********************************************************************** */ 019 020package org.matsim.contrib.cadyts.general; 021 022import cadyts.calibrators.analytical.AnalyticalCalibrator; 023import cadyts.measurements.SingleLinkMeasurement; 024import cadyts.measurements.SingleLinkMeasurement.TYPE; 025import org.apache.log4j.Logger; 026import org.matsim.api.core.v01.Id; 027import org.matsim.core.config.Config; 028import org.matsim.core.config.ConfigUtils; 029import org.matsim.core.gbl.MatsimRandom; 030import org.matsim.core.utils.misc.Time; 031import org.matsim.counts.Count; 032import org.matsim.counts.Counts; 033import org.matsim.counts.Volume; 034 035import java.util.Map; 036 037/** 038 * @author nagel 039 * @author mrieser 040 */ 041public final class CadytsBuilderImpl { 042 private static Logger log = Logger.getLogger( CadytsBuilderImpl.class ) ; 043 044 private CadytsBuilderImpl(){} // do not instantiate 045 046 public static <T> AnalyticalCalibrator<T> buildCalibratorAndAddMeasurements(final Config config, final Counts<T> occupCounts, 047 LookUpItemFromId<T> lookUp, Class<T> idType) { 048 049 if (occupCounts.getCounts().size() == 0) { 050 log.warn("Counts container is empty."); 051 } 052 053 CadytsConfigGroup cadytsConfig = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class); 054 055 AnalyticalCalibrator<T> matsimCalibrator = buildCalibrator(config); 056 057 int multiple = cadytsConfig.getTimeBinSize() / 3600 ; // e.g. "3" when timeBinSize_s = 3*3600 = 10800 058 059 // If I remember correctly, the following is trying to get around the fact that the counts time bins are fixed at hourly, but we want to 060 // be able to be more flexible. As a first step, time bins which are multiples from 1 hour are allowed, say "3". In order to get a somewhat 061 // meaningful connection to the counts format, it will add up all entries from the corresponding 3 hours before giving this to cadyts, i.e. 062 // you may keep a higher resolution counts file but tell cadyts to work on longer time intervals. kai, dec'13 063 064 // yyyy However, it seems that some of this did not work: We are using "hourly" counts, and dividing the multi-hour values by 065 // the number of hours. ??????? 066 067 // yyyyyy I am currently of the opinion that the multi-hour version should be decoupled from the counts format. There is a 068 // cadyts file format which allows setting mult-hour measurements, and that seems a lot more direct than trying to use a file 069 // format/data structure which is really not meant for this. kai, dec'13 070 071 //add counts data into calibrator 072 int numberOfAddedMeasurements = 0 ; 073 for (Map.Entry<Id<T>, Count<T>> entry : occupCounts.getCounts().entrySet()) { 074 // (loop over all counting "items" (usually locations/stations) 075 076 T item = lookUp.getItem(Id.create(entry.getKey(), idType)) ; 077 if ( item==null ) { 078 throw new RuntimeException("item is null; entry=" + entry + " idType=" + idType ) ; 079 } 080 int timeBinIndex = 0 ; // starting with zero which is different from the counts file!!! 081 int startTimeOfBin_s = -1 ; 082 double count = -1 ; 083 for (Volume volume : entry.getValue().getVolumes().values()){ 084 // (loop over the different time slots) 085 086 if ( timeBinIndex%multiple == 0 ) { 087 // (i.e. first timeBinIndex belonging to given bin) 088 089 startTimeOfBin_s = (volume.getHourOfDayStartingWithOne()-1)*3600 ; 090 count = 0 ; 091 } 092 count += volume.getValue() ; 093 if ( ! ( (timeBinIndex%multiple) == (multiple-1) ) ) { 094 log.warn( " NOT adding measurement: timeBinIndex: " + timeBinIndex + "; multiple: " + multiple ) ; 095 } else { 096 // (i.e. last timeBinIndex belonging to given bin) 097 098 int endTimeOfBin_s = volume.getHourOfDayStartingWithOne()*3600 - 1 ; 099 if ( !( cadytsConfig.getStartTime() <= startTimeOfBin_s && endTimeOfBin_s <= cadytsConfig.getEndTime()) ) { 100 log.warn( " NOT adding measurement: cadytsConfigStartTime: " + cadytsConfig.getStartTime() + "; startTimeOfBin_s: " + startTimeOfBin_s + 101 "; endTimeOfBin_s: " + endTimeOfBin_s + "; cadytsConfigEndTime: " + cadytsConfig.getEndTime() ); 102 } else { //add volumes for each bin to calibrator 103 numberOfAddedMeasurements++ ; 104// matsimCalibrator.addMeasurement(item, startTimeOfBin_s, endTimeOfBin_s, count/multiple, SingleLinkMeasurement.TYPE.FLOW_VEH_H); 105 matsimCalibrator.addMeasurement(item, startTimeOfBin_s, endTimeOfBin_s, count, SingleLinkMeasurement.TYPE.COUNT_VEH ); 106 107 // changed this from FLOW_VEH_H to COUNT_VEH on 30/jul/2012 since this is no longer "hourly". 108 // kai/manuel, jul'12 109 // Despite the above comment, I am finding this with FLOW_VEH_H. Why? kai, feb'13 110 // yyyyyy For the test case, this seems to produce weird results. The expected counts are 1 and 5, the expected result is 0 and 4. 111 // Possibly, it divides 1 and 5 by 2 and then has 0.5 and 2.5 for the given hour, in which case 1 and 3 would be best????? 112 // kai, feb'13 113 } 114 } 115 timeBinIndex++ ; 116 } 117 } 118 119 if ( numberOfAddedMeasurements==0 ) { 120 log.warn("No measurements were added."); 121 } 122 123 if ( matsimCalibrator.getProportionalAssignment() ) { 124 throw new RuntimeException("Gunnar says that this may not work so do not set to true. kai, sep'14") ; 125 } 126 return matsimCalibrator; 127 } 128 129 public static <T> AnalyticalCalibrator<T> buildCalibrator(final Config config) { 130 CadytsConfigGroup cadytsConfig = ConfigUtils.addOrGetModule(config, CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class ) ; 131 132 //get timeBinSize_s and validate it 133 if ((Time.MIDNIGHT % cadytsConfig.getTimeBinSize())!= 0 ){ 134 throw new RuntimeException("Cadyts requires a divisor of 86400 as time bin size value ."); 135 } 136 if ( (cadytsConfig.getTimeBinSize() % 3600) != 0 ) { 137 throw new RuntimeException("At this point, time bin sizes need to be multiples of 3600. This is not a restriction " + 138 "of Cadyts, but of the counts file format, which only allows for hourly inputs") ; 139 } 140 141 142 AnalyticalCalibrator<T> matsimCalibrator = new AnalyticalCalibrator<>( 143 config.controler().getOutputDirectory() + "/cadyts.log", 144 MatsimRandom.getLocalInstance().nextLong(),cadytsConfig.getTimeBinSize() 145 ) ; 146 147 matsimCalibrator.setRegressionInertia(cadytsConfig.getRegressionInertia()) ; 148 matsimCalibrator.setMinStddev(cadytsConfig.getMinFlowStddev_vehPerHour(), TYPE.FLOW_VEH_H); 149 matsimCalibrator.setMinStddev(cadytsConfig.getMinFlowStddev_vehPerHour(), TYPE.COUNT_VEH); 150 matsimCalibrator.setFreezeIteration(cadytsConfig.getFreezeIteration()); 151 matsimCalibrator.setPreparatoryIterations(cadytsConfig.getPreparatoryIterations()); 152 matsimCalibrator.setVarianceScale(cadytsConfig.getVarianceScale()); 153 154 matsimCalibrator.setBruteForce(cadytsConfig.useBruteForce()); 155 // I don't think this has an influence on any of the variants we are using. (Has an influence only when plan choice is left 156 // completely to cadyts, rather than just taking the score offsets.) kai, dec'13 157 // More formally, one would need to use the selectPlan() method of AnalyticalCalibrator which we are, however, not using. kai, mar'14 158 if ( matsimCalibrator.getBruteForce() ) { 159 log.warn("setting bruteForce==true for calibrator, but this won't do anything in the way the cadyts matsim integration is set up. kai, mar'14") ; 160 } 161 162 matsimCalibrator.setStatisticsFile(config.controler().getOutputDirectory() + "/calibration-stats.txt"); 163 return matsimCalibrator; 164 } 165 166}