diff --git a/aws/src/main/python/beam_lambda/lambda_function.py b/aws/src/main/python/beam_lambda/lambda_function.py index d9582cf506d..3776a47bbf5 100755 --- a/aws/src/main/python/beam_lambda/lambda_function.py +++ b/aws/src/main/python/beam_lambda/lambda_function.py @@ -74,36 +74,14 @@ - crontab -l - echo "notification scheduled..." - git fetch - - echo "git checkout ..." + - 'echo "git checkout: $(date)"' - GIT_LFS_SKIP_SMUDGE=1 sudo git checkout $BRANCH - sudo git pull - sudo git lfs pull - echo "git checkout -qf ..." - GIT_LFS_SKIP_SMUDGE=1 sudo git checkout -qf $COMMIT - - echo "preparing for python analysis" - - sudo dpkg --configure -a - - sudo dpkg --remove --force-remove-reinstreq unattended-upgrades - - sudo apt-get install unattended-upgrades - - sudo dpkg --configure -a - - sudo apt update - - sudo apt install npm -y - - sudo apt install nodejs-legacy -y - - sudo apt install python-pip -y - - pip install --upgrade pip - - sudo pip install pandas - - sudo pip install plotly - - sudo pip install psutil requests - - sudo npm cache clean -f - - sudo npm install -g n - - sudo n stable - - sudo npm install -g npm - - sudo apt-get install curl - - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - - - sudo apt-get install nodejs -y - - sudo apt-get install libgtkextra-dev libgconf2-dev libnss3 libasound2 libxtst-dev -y - - sudo npm install -g electron@1.8.4 orca --unsafe-perm=true --alow-root -y - - sudo apt-get install xvfb -y - - echo "gradlew assemble ..." + + - 'echo "gradlew assemble: $(date)"' - ./gradlew assemble - echo "looping config ..." - export MAXRAM=$MAX_RAM @@ -192,30 +170,30 @@ def validate(name): def deploy(script, instance_type, region_prefix, shutdown_behaviour, instance_name, volume_size): res = ec2.run_instances(BlockDeviceMappings=[ - { - 'DeviceName': '/dev/sda1', - 'Ebs': { - 'VolumeSize': volume_size, - 'VolumeType': 'gp2' - } - } - ], - ImageId=os.environ[region_prefix + 'IMAGE_ID'], - InstanceType=instance_type, - UserData=script, - KeyName=os.environ[region_prefix + 'KEY_NAME'], - MinCount=1, - MaxCount=1, - SecurityGroupIds=[os.environ[region_prefix + 'SECURITY_GROUP']], - IamInstanceProfile={'Name': os.environ['IAM_ROLE'] }, - InstanceInitiatedShutdownBehavior=shutdown_behaviour, - TagSpecifications=[ { - 'ResourceType': 'instance', - 'Tags': [ { - 'Key': 'Name', - 'Value': instance_name - } ] - } ]) + { + 'DeviceName': '/dev/sda1', + 'Ebs': { + 'VolumeSize': volume_size, + 'VolumeType': 'gp2' + } + } + ], + ImageId=os.environ[region_prefix + 'IMAGE_ID'], + InstanceType=instance_type, + UserData=script, + KeyName=os.environ[region_prefix + 'KEY_NAME'], + MinCount=1, + MaxCount=1, + SecurityGroupIds=[os.environ[region_prefix + 'SECURITY_GROUP']], + IamInstanceProfile={'Name': os.environ['IAM_ROLE'] }, + InstanceInitiatedShutdownBehavior=shutdown_behaviour, + TagSpecifications=[ { + 'ResourceType': 'instance', + 'Tags': [ { + 'Key': 'Name', + 'Value': instance_name + } ] + } ]) return res['Instances'][0]['InstanceId'] def get_dns(instance_id): diff --git a/aws/src/main/python/cloneAMI/lambda_function.py b/aws/src/main/python/cloneAMI/lambda_function.py new file mode 100644 index 00000000000..497a9df60ee --- /dev/null +++ b/aws/src/main/python/cloneAMI/lambda_function.py @@ -0,0 +1,24 @@ +# coding=utf-8 +import time +import json +import boto3 +import logging +from botocore.errorfactory import ClientError + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + ami_id = event.get('ami_id') + image_name = event.get('ami_name') + region = event.get('region') + + return copy_ami(image_name, ami_id, region) + +def copy_ami(image_name, image_id, region): + ec2 = boto3.client('ec2',region_name=region) + res = ec2.copy_image(Name=image_name, + SourceImageId=image_id, + SourceRegion='us-east-2') + logger.info('ami [' + image_id + '] coppied to region ' + region + ' with id ' + res['ImageId']) + return res['ImageId'] diff --git a/aws/src/main/python/createAMI/lambda_function.py b/aws/src/main/python/createAMI/lambda_function.py new file mode 100644 index 00000000000..80cd9748be4 --- /dev/null +++ b/aws/src/main/python/createAMI/lambda_function.py @@ -0,0 +1,31 @@ +# coding=utf-8 +import time +import json +import boto3 +import logging +from botocore.errorfactory import ClientError + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + instance_id = event.get('instance_id') + terminate_instance = event.get('terminate_instance', "false") + + image_name = 'beam-automation-'+time.strftime("%Y-%m-%d-%H%M%S", time.gmtime()) + ami_id = create_ami(image_name, instance_id, terminate_instance) + + return ami_id + "|" + image_name + + +def create_ami(image_name, instance_id, terminate_instance): + ec2 = boto3.client('ec2',region_name='us-east-2') + logger.info('creating ami of instance' + instance_id) + res = ec2.create_image(InstanceId=instance_id, + Name=image_name, NoReboot=True) + logger.info('ami creation started for ' + res['ImageId']) + if terminate_instance == "true": + logger.info('shutting down instance' + instance_id) + ec2.terminate_instances(InstanceIds=[instance_id]) + logger.info('shutting down complete for instance' + instance_id) + return res['ImageId'] \ No newline at end of file diff --git a/aws/src/main/python/updateBeamAMI/lambda_function.py b/aws/src/main/python/updateBeamAMI/lambda_function.py index c92f9dfdc5f..fa786449cb8 100644 --- a/aws/src/main/python/updateBeamAMI/lambda_function.py +++ b/aws/src/main/python/updateBeamAMI/lambda_function.py @@ -2,60 +2,72 @@ import time import json import boto3 +import logging from botocore.errorfactory import ClientError +logger = logging.getLogger() +logger.setLevel(logging.INFO) + def lambda_handler(event, context): instance_id = event.get('instance_id') region_id = event.get('region_id', 'us-east-2') - + image_name = 'beam-automation-'+time.strftime("%Y-%m-%d-%H%M%S", time.gmtime()) image_ids = {} - + + image_ids['us-east-2'] = create_ami(image_name, instance_id) image_ids['us-east-1'] = copy_ami(image_name, image_ids['us-east-2'], 'us-east-1') image_ids['us-west-2'] = copy_ami(image_name, image_ids['us-east-2'], 'us-west-2') update_lambda(image_ids) - + return json.dumps(image_ids) def create_ami(image_name, instance_id): ec2 = boto3.client('ec2',region_name='us-east-2') + logger.info('creating ami of instance' + instance_id) res = ec2.create_image(InstanceId=instance_id, - Name=image_name) + Name=image_name) wait4image(ec2, res['ImageId']) - ec2.terminate_instances(InstanceIds=[instance_id]) + logger.info('ami created ' + res['ImageId']) + logger.info('shutting down instance' + instance_id) + ec2.terminate_instances(InstanceIds=[instance_id]) + logger.info('shutting down complete for instance' + instance_id) return res['ImageId'] - + def copy_ami(image_name, image_id, region): ec2 = boto3.client('ec2',region_name=region) res = ec2.copy_image(Name=image_name, - SourceImageId=image_id, - SourceRegion='us-east-2') + SourceImageId=image_id, + SourceRegion='us-east-2') + logger.info('ami [' + image_id + '] coppied to region ' + region + ' with id ' + res['ImageId']) # wait4image(ec2, res['ImageId']) return res['ImageId'] - + def wait4image(ec2, image_id): waiter = ec2.get_waiter('image_available') waiter.wait(Filters=[{'Name': 'state', 'Values': ['available']}], ImageIds=[image_id]) - + def update_lambda(image_ids): lm = boto3.client('lambda') + logger.info('updateing image ids ' + str(image_ids)) en_var = lm.get_function_configuration(FunctionName='simulateBeam')['Environment']['Variables'] en_var.update({ - 'us_east_2_IMAGE_ID': image_ids['us-east-2'], - 'us_east_1_IMAGE_ID': image_ids['us-east-1'], - 'us_west_2_IMAGE_ID': image_ids['us-west-2'], - }) + 'us_east_2_IMAGE_ID': image_ids['us-east-2'], + 'us_east_1_IMAGE_ID': image_ids['us-east-1'], + 'us_west_2_IMAGE_ID': image_ids['us-west-2'], + }) lm.update_function_configuration( - FunctionName='simulateBeam', - Environment={ - 'Variables': en_var - } - ) + FunctionName='simulateBeam', + Environment={ + 'Variables': en_var + } + ) + logger.info('simulateBeam image ids updated') + - def check_instance_id(instance_ids): for reservation in ec2.describe_instances()['Reservations']: for instance in reservation['Instances']: diff --git a/aws/src/main/python/updateDependencies/lambda_function.py b/aws/src/main/python/updateDependencies/lambda_function.py index df56b643963..aa4a588f2f6 100644 --- a/aws/src/main/python/updateDependencies/lambda_function.py +++ b/aws/src/main/python/updateDependencies/lambda_function.py @@ -1,6 +1,7 @@ # coding=utf-8 import os import boto3 +from botocore.errorfactory import ClientError initscript = (('''#cloud-config runcmd: @@ -12,20 +13,73 @@ - sudo git reset origin/HEAD - sudo git checkout -- . - sudo git clean -df - - git fetch + - sudo git checkout develop + - sudo git pull + - sudo git fetch + - sudo git fetch --prune + - sudo git remote prune origin + - echo "-git remote prune origin is done-" + - sudo chown -R ubuntu:ubuntu /home/ubuntu/git/beam - for bn in $BRANCH - do - echo "-------------------checkout $bn----------------------" - - GIT_LFS_SKIP_SMUDGE=1 sudo git checkout $bn + - sudo GIT_LFS_SKIP_SMUDGE=1 git checkout $bn + - sudo git reset --hard origin/$bn - sudo git pull - sudo git lfs pull - done + - sudo chown -R ubuntu:ubuntu /home/ubuntu/git/beam - echo "gradlew assemble ..." - ./gradlew assemble - ./gradlew clean - - echo "invoke lambda ..." - - sudo aws lambda invoke --invocation-type RequestResponse --function-name updateBeamAMI --region 'us-east-2' --payload '{"instance_id":"'"$(ec2metadata --instance-id)"'","region_id":"us-east-2"}' outputfile.txt + - echo "preparing for python analysis" + - sudo dpkg --configure -a + - sudo dpkg --remove --force-remove-reinstreq unattended-upgrades + - sudo apt-get install unattended-upgrades + - sudo dpkg --configure -a + - sudo apt update + - sudo apt install npm -y + - sudo apt install nodejs-legacy -y + - sudo apt install python-pip -y + - pip install --upgrade pip + - sudo pip install pandas + - sudo pip install plotly + - sudo pip install psutil requests + - sudo npm cache clean -f + - sudo npm install -g n + - sudo n stable + - sudo npm install -g npm + - sudo apt-get install curl + - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - + - sudo apt-get install nodejs -y + - sudo apt-get install libgtkextra-dev libgconf2-dev libnss3 libasound2 libxtst-dev -y + - sudo npm install -g electron@1.8.4 orca --unsafe-perm=true --alow-root -y + - sudo apt-get install xvfb -y + - 'echo resetting git to base: "$(date)"' + - sudo git reset --hard + - 'echo fetching the latest: "$(date)"' + - sudo git fetch + - 'echo current git status: "$(date)"' + - sudo git status + - 'echo invoke create ami lambda after a 5 minute sleep to let the file system settle..."$(date)"' + - sudo sleep 5m + - sudo aws lambda invoke --invocation-type RequestResponse --function-name createAMI --region 'us-east-2' --payload '{"instance_id":"'"$(ec2metadata --instance-id)"'","region_id":"us-east-2"}' outputfile.txt + - export created_ami_id=$(sed -r 's/"(.*)\|.*/\\1/' outputfile.txt) + - export ami_filename=$(sed -r 's/.*\|(.*)"/\\1/' outputfile.txt) + - while ! aws ec2 describe-images --image-ids $created_ami_id --region us-east-2 | grep "available"; do echo "Waiting 30 seconds for AMI in us-east-2 $created_ami_id ..."; sleep 30s; done + - echo "invoke copy ami lambda to us-east-1 ..." + - sudo aws lambda invoke --invocation-type RequestResponse --function-name cloneAMI --region 'us-east-2' --payload '{"ami_id":"'"$created_ami_id"'","ami_name":"'"$ami_filename"'","region":"us-east-1"}' outputfile.txt + - export created_ami_id_us_east_1=$(sed -r 's/"(.*)"/\\1/' outputfile.txt) + - echo "invoke copy ami lambda to us-west-2 ..." + - sudo aws lambda invoke --invocation-type RequestResponse --function-name cloneAMI --region 'us-east-2' --payload '{"ami_id":"'"$created_ami_id"'","ami_name":"'"$ami_filename"'","region":"us-west-2"}' outputfile.txt + - export created_ami_id_us_west_2=$(sed -r 's/"(.*)"/\\1/' outputfile.txt) + - while ! aws ec2 describe-images --image-ids $created_ami_id_us_east_1 --region us-east-1 | grep "available"; do echo "Waiting 30 seconds for AMI in us-east-1 $created_ami_id_us_east_1 ..."; sleep 30s; done + - while ! aws ec2 describe-images --image-ids $created_ami_id_us_west_2 --region us-west-2 | grep "available"; do echo "Waiting 30 seconds for AMI in us-west-2 $created_ami_id_us_west_2 ..."; sleep 30s; done + - echo "invoke update simulateBeam lambda ..." + - sudo aws lambda invoke --invocation-type RequestResponse --function-name updateSimulateBeamEnvVars --region 'us-east-2' --payload '{"ami_id":"'"$created_ami_id"'","ami_id_us_east_1":"'"$created_ami_id_us_east_1"'","ami_id_us_west_2":"'"$created_ami_id_us_west_2"'"}' outputfile.txt + - echo "setting up auto shutdown ..." - sudo shutdown -h +$SHUTDOWN_WAIT + - echo "shutdown in $SHUTDOWN_WAIT ..." ''')) @@ -61,8 +115,8 @@ def lambda_handler(event, context): region = 'us-east-2' instance_type = os.environ['INSTANCE_TYPE'] - shutdown_behaviour = 'stop' - shutdown_wait = "1" + shutdown_behaviour = 'terminate' + shutdown_wait = "10" runName = 'update-beam-dependencies' branches = os.environ['BRANCHES'] diff --git a/aws/src/main/python/updateSimulateBeamEnvVars/lambda_function.py b/aws/src/main/python/updateSimulateBeamEnvVars/lambda_function.py new file mode 100644 index 00000000000..f9c13a00215 --- /dev/null +++ b/aws/src/main/python/updateSimulateBeamEnvVars/lambda_function.py @@ -0,0 +1,36 @@ +# coding=utf-8 +import time +import json +import boto3 +import logging +from botocore.errorfactory import ClientError + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + image_ids = {} + image_ids['us-east-2'] = event.get('ami_id') + image_ids['us-east-1'] = event.get('ami_id_us_east_1') + image_ids['us-west-2'] = event.get('ami_id_us_west_2') + + update_lambda(image_ids) + + return "Update complete" + +def update_lambda(image_ids): + lm = boto3.client('lambda') + logger.info('updating image ids ' + str(image_ids)) + en_var = lm.get_function_configuration(FunctionName='simulateBeam')['Environment']['Variables'] + en_var.update({ + 'us_east_2_IMAGE_ID': image_ids['us-east-2'], + 'us_east_1_IMAGE_ID': image_ids['us-east-1'], + 'us_west_2_IMAGE_ID': image_ids['us-west-2'] + }) + lm.update_function_configuration( + FunctionName='simulateBeam', + Environment={ + 'Variables': en_var + } + ) + logger.info('simulateBeam image ids updated') \ No newline at end of file diff --git a/build.gradle b/build.gradle index a3cfc8983b4..9a22e283c51 100755 --- a/build.gradle +++ b/build.gradle @@ -189,11 +189,11 @@ dependencies { ///////////////////////////////// // CORE Scala // - compile "org.scala-lang:scala-library:2.12.7" + compile "org.scala-lang:scala-library:2.12.10" compile group: 'org.scala-lang.modules', name: "scala-xml_${scalaBinaryVersion}", version: '1.0.6' // NEEDED FOR USING REPL // - compile "org.scala-lang:scala-compiler:2.12.7" + compile "org.scala-lang:scala-compiler:2.12.10" // TEST Scala // testCompile group: 'org.scalatest', name: "scalatest_${scalaBinaryVersion}", version: '3.0.1' diff --git a/src/main/java/beam/analysis/physsim/PhyssimCalcLinkStats.java b/src/main/java/beam/analysis/physsim/PhyssimCalcLinkStats.java index f16727c66b1..89e92ceeffe 100755 --- a/src/main/java/beam/analysis/physsim/PhyssimCalcLinkStats.java +++ b/src/main/java/beam/analysis/physsim/PhyssimCalcLinkStats.java @@ -130,7 +130,7 @@ private void processData(int iteration, TravelTimeCalculator travelTimeCalculato double averageSpeedToFreeSpeedRatio = averageSpeed / freeSpeed; - double relativeSpeed = Math.round(averageSpeedToFreeSpeedRatio * 10) / 10; + double relativeSpeed = Math.round(averageSpeedToFreeSpeedRatio * 50) / 10; Map hoursDataMap = relativeSpeedFrequenciesPerBin.get(relativeSpeed); @@ -179,26 +179,32 @@ private double[][] buildModesFrequencyDataset() { List relativeSpeedsCategoriesList = getSortedListRelativeSpeedCategoryList(); - double[][] dataset = new double[relativeSpeedsCategoriesList.size()][noOfBins]; + double[][] dataset = new double[0][]; - for (int i = 0; i < relativeSpeedsCategoriesList.size(); i++) { + Optional optionalMaxRelativeSpeedsCategories = relativeSpeedsCategoriesList.stream().max(Comparator.naturalOrder()); - Double relativeSpeedCategory = relativeSpeedsCategoriesList.get(i); - Map relativeSpeedBins = relativeSpeedFrequenciesPerBin.get(relativeSpeedCategory); + if(optionalMaxRelativeSpeedsCategories.isPresent()) { + int maxRelativeSpeedsCategories = optionalMaxRelativeSpeedsCategories.get().intValue(); + dataset = new double[maxRelativeSpeedsCategories+1][noOfBins]; - double[] relativeSpeedFrequencyPerHour = new double[noOfBins]; - int index = 0; + for (int i = 0; i <= maxRelativeSpeedsCategories; i++) { - for (int binIndex = 0; binIndex < noOfBins; binIndex++) { - Integer hourFrequency = relativeSpeedBins.get(binIndex); - if (hourFrequency != null) { - relativeSpeedFrequencyPerHour[index] = hourFrequency; - } else { - relativeSpeedFrequencyPerHour[index] = 0; + Map relativeSpeedBins = relativeSpeedFrequenciesPerBin.getOrDefault((double)i, new HashMap<>()); + + double[] relativeSpeedFrequencyPerHour = new double[noOfBins]; + int index = 0; + + for (int binIndex = 0; binIndex < noOfBins; binIndex++) { + Integer hourFrequency = relativeSpeedBins.get(binIndex); + if (hourFrequency != null) { + relativeSpeedFrequencyPerHour[index] = hourFrequency; + } else { + relativeSpeedFrequencyPerHour[index] = 0; + } + index = index + 1; } - index = index + 1; + dataset[i] = relativeSpeedFrequencyPerHour; } - dataset[i] = relativeSpeedFrequencyPerHour; } return dataset; @@ -228,13 +234,12 @@ private void createModesFrequencyGraph(CategoryDataset dataset, int iterationNum List relativeSpeedsCategoriesList = new ArrayList<>(relativeSpeedFrequenciesPerBin.keySet()); - Collections.sort(relativeSpeedsCategoriesList); - - for (int i = 0; i < dataset.getRowCount(); i++) { + int max = Collections.max(relativeSpeedsCategoriesList).intValue(); + for (int i = 0; i <= max ; i++) { - legendItems.add(new LegendItem(relativeSpeedsCategoriesList.get(i).toString(), getColor(i))); + legendItems.add(new LegendItem(String.valueOf(i), getColor(i))); plot.getRenderer().setSeriesPaint(i, getColor(i)); diff --git a/src/main/java/beam/analysis/plots/GraphUtils.java b/src/main/java/beam/analysis/plots/GraphUtils.java index 151e8524a40..1bcc40ef5a9 100755 --- a/src/main/java/beam/analysis/plots/GraphUtils.java +++ b/src/main/java/beam/analysis/plots/GraphUtils.java @@ -30,7 +30,7 @@ public class GraphUtils { private static final Color OLIVE = new Color(107,142,35); private static final Color THISTLE = new Color(216,191,216); private static final Color CADETBLUE = new Color(95,158,160); - + private static final Color DARK_PINK = new Color(255, 0, 227); /** * Map < iteration number, ride hailing revenue> @@ -61,6 +61,7 @@ public class GraphUtils { colorsForModes.put("rail", VERY_DARK_BLUE); colorsForModes.put("bus", LIGHT_YELLOW); colorsForModes.put("ride_hail_pooled", CADETBLUE); + colorsForModes.put("bike_transit", DARK_PINK); } diff --git a/src/main/java/beam/analysis/plots/ModeChosenAnalysisObject.scala b/src/main/java/beam/analysis/plots/ModeChosenAnalysisObject.scala index b72a50d8693..2e222d34501 100644 --- a/src/main/java/beam/analysis/plots/ModeChosenAnalysisObject.scala +++ b/src/main/java/beam/analysis/plots/ModeChosenAnalysisObject.scala @@ -91,6 +91,15 @@ object ModeChosenAnalysisObject extends OutputDataDescriptor { "iteration number" ) ) + list + .add( + OutputDataDescription( + getClass.getSimpleName, + referenceModeChoiceRelativePath, + "bike_transit", + "Walk to transit chosen as travel mode" + ) + ) list .add( OutputDataDescription( diff --git a/src/main/java/beam/analysis/plots/RideHailWaitingAnalysis.java b/src/main/java/beam/analysis/plots/RideHailWaitingAnalysis.java index 3b50cc17ffb..b35b75cf0ed 100644 --- a/src/main/java/beam/analysis/plots/RideHailWaitingAnalysis.java +++ b/src/main/java/beam/analysis/plots/RideHailWaitingAnalysis.java @@ -124,7 +124,7 @@ private double[][] buildModesFrequencyDataset(Map> private static final String graphTitle = "Ride Hail Waiting Histogram"; private static final String xAxisTitle = "Hour"; - private static final String yAxisTitle = "Waiting Time (frequencies)"; + private static final String yAxisTitle = "Frequency binned by Waiting Time"; static final String fileName = "rideHailWaitingHistogram"; static final String rideHailIndividualWaitingTimesFileBaseName = "rideHailIndividualWaitingTimes"; private static final String rideHailWaitingSingleStatsFileBaseName = "rideHailWaitingSingleStats"; diff --git a/src/main/java/beam/physsim/jdeqsim/AgentSimToPhysSimPlanConverter.java b/src/main/java/beam/physsim/jdeqsim/AgentSimToPhysSimPlanConverter.java index 8a0f97848f3..6d94d72e289 100755 --- a/src/main/java/beam/physsim/jdeqsim/AgentSimToPhysSimPlanConverter.java +++ b/src/main/java/beam/physsim/jdeqsim/AgentSimToPhysSimPlanConverter.java @@ -167,7 +167,7 @@ private void setupActorsAndRunPhysSim(int iterationNumber) { log.info("JDEQSim Start"); startSegment("jdeqsim-execution", "jdeqsim"); if (beamConfig.beam().debug().debugEnabled()) { - log.info(DebugLib.gcAndGetMemoryLogMessage("Memory Use Before JDEQSim (after GC): ")); + log.info(DebugLib.getMemoryLogMessage("Memory Use Before JDEQSim: ")); } jdeqSimulation.run(); @@ -177,7 +177,7 @@ private void setupActorsAndRunPhysSim(int iterationNumber) { } if (beamConfig.beam().debug().debugEnabled()) { - log.info(DebugLib.gcAndGetMemoryLogMessage("Memory Use After JDEQSim (after GC): ")); + log.info(DebugLib.getMemoryLogMessage("Memory Use After JDEQSim: ")); } endSegment("jdeqsim-execution", "jdeqsim"); diff --git a/src/main/java/beam/physsim/jdeqsim/JDEQSimMemoryFootprint.java b/src/main/java/beam/physsim/jdeqsim/JDEQSimMemoryFootprint.java index 5f5317a7095..fa385e339b0 100755 --- a/src/main/java/beam/physsim/jdeqsim/JDEQSimMemoryFootprint.java +++ b/src/main/java/beam/physsim/jdeqsim/JDEQSimMemoryFootprint.java @@ -21,7 +21,7 @@ public void handleEvent(Event event) { int currentHour = (int) Math.floor(event.getTime() / 3600.0); if (Math.abs(prevHour - currentHour) >= 1) { if (debugMode) { - log.info(DebugLib.gcAndGetMemoryLogMessage("Hour " + currentHour + " completed. ")); + log.info(DebugLib.getMemoryLogMessage("Hour " + currentHour + " completed. ")); } else { log.info("Hour " + currentHour + " completed. "); } diff --git a/src/main/java/beam/utils/DebugLib.java b/src/main/java/beam/utils/DebugLib.java index 3e50d1ba0a4..c51b5f6ab2b 100755 --- a/src/main/java/beam/utils/DebugLib.java +++ b/src/main/java/beam/utils/DebugLib.java @@ -18,8 +18,7 @@ public static void emptyFunctionForSettingBreakPoint() { } - public static String gcAndGetMemoryLogMessage(String message) { - System.gc(); + public static String getMemoryLogMessage(String message) { long jvmTotalMemoryInBytes = Runtime.getRuntime().totalMemory(); long jvmFreeMemoryInBytes = Runtime.getRuntime().freeMemory(); long jvmMemoryInUseInBytes = jvmTotalMemoryInBytes - jvmFreeMemoryInBytes; diff --git a/src/main/python/smart/mep/build_heatmap.py b/src/main/python/smart/mep/build_heatmap.py new file mode 100644 index 00000000000..b88fc5ea7dd --- /dev/null +++ b/src/main/python/smart/mep/build_heatmap.py @@ -0,0 +1,317 @@ +import io +import random +import math +import time +import datetime +import mercantile + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.colors as colors +import pandas as pd + +from cairo import ImageSurface, FORMAT_ARGB32, Context +from urllib.request import urlopen, Request +from mpl_toolkits.axes_grid1 import make_axes_locatable + + +def readData(sourceFile, dataColumnName, cellSize, minX, minY, maxX, maxY): + """ + Read heat map data from csv file. + + Function read three columns of data from provided csv file. + Two columns for geografical coordinates (lon and lat) and one for data. + Readed data are grouping by coordinates into cells of array. + All data that does not fit into given min\max are ignored. + Every cell of array will contain sum of data which was grouped into that cell + or None if no data fall into cell. + + Parameters + ---------- + sourceFile : string + path to csv file + + dataColumnName : string + column name which contains data + + cellSize : float + size of cells for data grouping + + minX, maxX, minY, maxY : float + coordinates of area of interest + + Returns + ------- + numpy.array + data which was read from csv file + """ + + coordX = "lon" # longitude; x + coordY = "lat" # Latitude; y + + data = pd.read_csv(sourceFile, sep=",") + + tableSizeX = int(abs(maxX - minX) / cellSize) + tableSizeY = int(abs(maxY - minY) / cellSize) + + def normX(x): + nx = abs(x - minX) + return int(nx / cellSize) + + def normY(y): + ny = abs(y - minY) + # because array indexes grows downwards but map coordinates grows upwards + return int(tableSizeY - ny / cellSize) + + def writeToTable(table, value, x, y): + if x < minX or x > maxX or y < minY or y > maxY: + return + + nx = normX(x) + ny = normY(y) + if math.isnan(table[ny][nx]): + table[ny][nx] = value + else: + table[ny][nx] += value + + table = np.zeros((tableSizeY, tableSizeX)) + table[:] = None + + for index, row in data.iterrows(): + writeToTable(table, row[dataColumnName], row[coordX], row[coordY]) + + return table + + +def downloadOSMMap(west, south, east, north, zoom, mapType=None): + """ + Download OSM map of selected area. + + Original was found on https://smyt.ru/blog/statc-osm-map-with-python/ + Map may be download from few different OSM tile servers with different zoom. + Download speed depends on size of selected area. + + Parameters + ---------- + weast,south,east,north : float + coordinates of borders of map + + zoom : int + map zoom, changes map detail + + mapType : {'white', 'dark', 'toner', None}, optional + type of OSM tile server, default is None + None - regular OSM map + white, dark - white or dark cartodb OSM map + toner - stamen toner OSM map + + Returns + ------- + numpy.array + background map of selected area + """ + + tiles = list(mercantile.tiles(west, south, east, north, zoom)) + + min_x = min([t.x for t in tiles]) + min_y = min([t.y for t in tiles]) + max_x = max([t.x for t in tiles]) + max_y = max([t.y for t in tiles]) + + tile_size = (256, 256) + map_image = ImageSurface( + FORMAT_ARGB32, + tile_size[0] * (max_x - min_x + 1), + tile_size[1] * (max_y - min_y + 1), + ) + + ctx = Context(map_image) + + def getTileUrl(mapType, zoom, x, y): + if mapType == None: + server = random.choice(["a", "b", "c"]) + return "http://{server}.tile.openstreetmap.org/{zoom}/{x}/{y}.png".format( + server=server, zoom=zoom, x=x, y=y + ) + + if mapType == "white": + return "http://cartodb-basemaps-1.global.ssl.fastly.net/light_all/{zoom}/{x}/{y}.png".format( + zoom=zoom, x=x, y=y + ) + + if mapType == "dark": + return "http://cartodb-basemaps-2.global.ssl.fastly.net/dark_all/{zoom}/{x}/{y}.png".format( + zoom=zoom, x=x, y=y + ) + + if mapType == "toner": + return "http://a.tile.stamen.com/toner/{zoom}/{x}/{y}.png".format( + zoom=zoom, x=x, y=y + ) + + for t in tiles: + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.3" + } + + url = getTileUrl(mapType, t.z, t.x, t.y) + req = Request(url, headers=headers) + response = urlopen(req) + + img = ImageSurface.create_from_png(io.BytesIO(response.read())) + + ctx.set_source_surface( + img, (t.x - min_x) * tile_size[0], (t.y - min_y) * tile_size[0] + ) + ctx.paint() + + bounds = { + "left": min([mercantile.xy_bounds(t).left for t in tiles]), + "right": max([mercantile.xy_bounds(t).right for t in tiles]), + "bottom": min([mercantile.xy_bounds(t).bottom for t in tiles]), + "top": max([mercantile.xy_bounds(t).top for t in tiles]), + } + + kx = map_image.get_width() / (bounds["right"] - bounds["left"]) + ky = map_image.get_height() / (bounds["top"] - bounds["bottom"]) + + left_top = mercantile.xy(west, north) + right_bottom = mercantile.xy(east, south) + offset_left = (left_top[0] - bounds["left"]) * kx + offset_top = (bounds["top"] - left_top[1]) * ky + offset_right = (bounds["right"] - right_bottom[0]) * kx + offset_bottom = (right_bottom[1] - bounds["bottom"]) * ky + + result_width = map_image.get_width() - int(offset_left + offset_right) + result_height = map_image.get_height() - int(offset_top + offset_bottom) + + map_image_clipped = ImageSurface(FORMAT_ARGB32, result_width, result_height) + + ctx = Context(map_image_clipped) + ctx.set_source_surface(map_image, -offset_left, -offset_top) + ctx.paint() + + def surface_to_npim(surface): + """ Transforms a Cairo surface into a numpy array. """ + im = +np.frombuffer(surface.get_data(), np.uint8) + H, W = surface.get_height(), surface.get_width() + im.shape = (H, W, 4) # for RGBA + return im[:, :, :3] + + return surface_to_npim(map_image_clipped) + + +def drawHeatMap(background, cellSize, colormap, sourceFile, dataColumnName, minX, maxX, minY, maxY): + """ + Save to a file a heat map with given background and parameters for selected area. + + Parameters + ---------- + background : numpy.array + heat map background + + cellSize : float + cell size to group data in cells + + colormap + name of existing colormap. + handmade colormap. + + sourceFile : string + CSV data source file + + dataColumnName : string + column name which contains data + + minX, maxX, minY, maxY : float + coordinates of area of interest + """ + + pngImageDPI = 800 + + data = readData( + sourceFile, dataColumnName, cellSize, minX=minX, maxX=maxX, minY=minY, maxY=maxY + ) + + fig, ax = plt.subplots(dpi=pngImageDPI) # figsize=(12, 8), + + # drawing heat map + im = ax.imshow( + data, + cmap=colormap, + zorder=1, + alpha=0.85, + #interpolation="hermite", + norm=colors.DivergingNorm(vmin=-120, vmax=120, vcenter=0.0), # + ) + + # specifying colorbar on the left + divider = make_axes_locatable(ax) + cax1 = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(im, cax=cax1) + + # drawing background + ax.imshow(background, aspect=ax.get_aspect(), extent=ax.get_xlim() + ax.get_ylim(), zorder=0) + + # removing x and y scale text + ax.set_xticks([]) + ax.set_yticks([]) + + ax.set_title(sourceFile + "\n\n") + fig.tight_layout() + plt.savefig(sourceFile + ".png", dpi=pngImageDPI) + + +print("was started at", datetime.datetime.now()) +print() + +start = time.time() + +# +# selected bay area +# + +topLeftPoint = (37.980285, -122.668632) # (y,x) +bottomRightPoint = (37.279124,-121.748156) # (y,x) + +minX = topLeftPoint[1] +maxY = topLeftPoint[0] +maxX = bottomRightPoint[1] +minY = bottomRightPoint[0] + +# +# downloading background map +# + +backgroundMapZoom = 10 +backgroundMapType="white" # None | white | black | toner + +sfbaymap = downloadOSMMap(minX, minY, maxX, maxY, backgroundMapZoom, backgroundMapType) + +# +# drawing heat map(s) +# + +import os + +dataFiles = [] +for root, dirs, files in os.walk('.'): + for file in files: + if file.endswith('.csv') and file.startswith('diff'): + dataFiles.append(file) + +# it is possible to generate colormap by hands +# available color maps https://matplotlib.org/3.1.1/tutorials/colors/colormaps.html +colorMap = "RdBu" + +# size of square heat map cell +# for last used data the number of 93 fits best because of coordinate values distribution +cellSize = abs(minX - maxX) / 93 + +for sourceFile in dataFiles: + drawHeatMap(sfbaymap, cellSize, colorMap, sourceFile, "mep", minX, maxX, minY, maxY) + print(sourceFile, "finished") + +end = time.time() +print() +print("was running for", math.floor(end - start), "sec") \ No newline at end of file diff --git a/src/main/resources/beam-template.conf b/src/main/resources/beam-template.conf index a09f28a7b71..906a03f7eda 100755 --- a/src/main/resources/beam-template.conf +++ b/src/main/resources/beam-template.conf @@ -92,6 +92,8 @@ beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_intercept beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_pooled_intercept = "double | 0.0" beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.walk_intercept = "double | 0.0" beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.bike_intercept = "double | 0.0" +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.bike_transit_intercept = "double | 0.0" +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.utility_scale_factor = "double | 1.0" beam.agentsim.agents.modalBehaviors.lccm.filePath = ${beam.inputDirectory}"/lccm-long.csv" beam.agentsim.agents.modeIncentive.filePath = "" beam.agentsim.agents.ptFare.filePath = "" diff --git a/src/main/scala/beam/agentsim/agents/PersonAgent.scala b/src/main/scala/beam/agentsim/agents/PersonAgent.scala index 6d4f0b80ced..3675b36d31c 100755 --- a/src/main/scala/beam/agentsim/agents/PersonAgent.scala +++ b/src/main/scala/beam/agentsim/agents/PersonAgent.scala @@ -1,7 +1,6 @@ package beam.agentsim.agents import scala.annotation.tailrec - import akka.actor.FSM.Failure import akka.actor.{ActorRef, FSM, Props, Stash, Status} import beam.agentsim.Resource._ @@ -42,9 +41,10 @@ import org.matsim.api.core.v01.population._ import org.matsim.core.api.experimental.events.{EventsManager, TeleportationArrivalEvent} import org.matsim.core.utils.misc.Time import org.matsim.vehicles.Vehicle -import scala.concurrent.duration._ +import scala.concurrent.duration._ import beam.agentsim.infrastructure.parking.ParkingMNL +import beam.utils.logging.ExponentialLazyLogging /** */ @@ -252,7 +252,8 @@ class PersonAgent( ) extends DrivesVehicle[PersonData] with ChoosesMode with ChoosesParking - with Stash { + with Stash + with ExponentialLazyLogging { val networkHelper = beamServices.networkHelper val geo = beamServices.geo @@ -435,7 +436,7 @@ class PersonAgent( case Event(TriggerWithId(ActivityEndTrigger(tick), triggerId), data: BasePersonData) => nextActivity(data) match { case None => - logDebug(s"didn't get nextActivity") + logger.warn(s"didn't get nextActivity, PersonAgent:438") // if we still have a BEV/PHEV that is connected to a charging point, // we assume that they will charge until the end of the simulation and throwing events accordingly @@ -448,7 +449,7 @@ class PersonAgent( } }) }) - stop replying CompletionNotice(triggerId) + stay replying CompletionNotice(triggerId) case Some(nextAct) => logDebug(s"wants to go to ${nextAct.getType} @ $tick") holdTickAndTriggerId(tick, triggerId) @@ -668,6 +669,9 @@ class PersonAgent( new PersonLeavesVehicleEvent(_currentTick.get, Id.createPersonId(id), data.currentVehicle.head) ) if (currentBeamVehicle != body) { + if (currentBeamVehicle.beamVehicleType.vehicleCategory != Bike) { + if (currentBeamVehicle.stall.isEmpty) logWarn("Expected currentBeamVehicle.stall to be defined.") + } if (!currentBeamVehicle.mustBeDrivenHome) { // Is a shared vehicle. Give it up. currentBeamVehicle.manager.get ! ReleaseVehicle(currentBeamVehicle) @@ -911,7 +915,7 @@ class PersonAgent( } else if (activity.getEndTime >= 0.0 && activity.getEndTime < tick) { tick } else { - // logWarn(s"Activity endTime is negative or infinite ${activity}, assuming duration of 10 + // logWarn(s"Activity endTime is negative or infinite ${activity}, assuming duration of 10 // minutes.") //TODO consider ending the day here to match MATSim convention for start/end activity tick + 60 * 10 @@ -1062,6 +1066,10 @@ class PersonAgent( if (stateName == Moving) { log.warning("Still travelling at end of simulation.") log.warning(s"Events leading up to this point:\n\t${getLog.mkString("\n\t")}") + } else if (stateName == PerformingActivity) { + logger.warn(s"Performing Activity at end of simulation") + } else { + logger.warn(s"Received Finish while in state: ${stateName}") } stop case Event( diff --git a/src/main/scala/beam/agentsim/agents/choice/logit/MultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/logit/MultinomialLogit.scala index 807fca248ee..5957ba490e3 100644 --- a/src/main/scala/beam/agentsim/agents/choice/logit/MultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/logit/MultinomialLogit.scala @@ -15,7 +15,8 @@ import org.matsim.api.core.v01.population.Person */ class MultinomialLogit[A, T]( val utilityFunctions: Map[A, Map[T, UtilityFunctionOperation]], - common: Map[T, UtilityFunctionOperation] + common: Map[T, UtilityFunctionOperation], + scale_factor: Double = 1.0 ) extends LazyLogging { import MultinomialLogit._ @@ -47,9 +48,13 @@ class MultinomialLogit[A, T]( case Some(thisUtility) => if (thisUtility == Double.PositiveInfinity) { // place on tail of list, allowing us to short-circuit the sampling in next step - accumulator :+ AlternativeWithUtility(alt, thisUtility, math.exp(thisUtility)) + accumulator :+ AlternativeWithUtility( + alt, + thisUtility * scale_factor, + math.exp(thisUtility * scale_factor) + ) } else { - AlternativeWithUtility(alt, thisUtility, math.exp(thisUtility)) +: accumulator + AlternativeWithUtility(alt, thisUtility * scale_factor, math.exp(thisUtility * scale_factor)) +: accumulator } } } @@ -179,7 +184,14 @@ object MultinomialLogit { utilityFunctions: Map[A, Map[T, UtilityFunctionOperation]], commonUtilityFunction: Map[T, UtilityFunctionOperation] ): MultinomialLogit[A, T] = { - new MultinomialLogit(utilityFunctions, commonUtilityFunction) + new MultinomialLogit(utilityFunctions, commonUtilityFunction, 1.0) } + def apply[A, T]( + utilityFunctions: Map[A, Map[T, UtilityFunctionOperation]], + commonUtilityFunction: Map[T, UtilityFunctionOperation], + scale_factor: Double + ): MultinomialLogit[A, T] = { + new MultinomialLogit(utilityFunctions, commonUtilityFunction, scale_factor) + } } diff --git a/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala b/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala index 61259b551a5..2ca49360ba1 100755 --- a/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala +++ b/src/main/scala/beam/agentsim/agents/choice/mode/ModeChoiceMultinomialLogit.scala @@ -64,7 +64,6 @@ class ModeChoiceMultinomialLogit( } (mct.mode.value, theParams ++ transferParam) }.toMap - val chosenModeOpt = { model.sampleAlternative(inputData, random) } @@ -167,7 +166,7 @@ class ModeChoiceMultinomialLogit( ) val numTransfers = mode match { - case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT => + case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT | BIKE_TRANSIT => var nVeh = -1 var vehId = Id.create("dummy", classOf[Vehicle]) altAndIdx._1.legs.foreach { leg => @@ -291,7 +290,8 @@ object ModeChoiceMultinomialLogit { val commonUtility: Map[String, UtilityFunctionOperation] = Map( "cost" -> UtilityFunctionOperation("multiplier", -1) ) - + val scale_factor: Double = + configHolder.beamConfig.beam.agentsim.agents.modalBehaviors.mulitnomialLogit.utility_scale_factor val mnlUtilityFunctions: Map[String, Map[String, UtilityFunctionOperation]] = Map( "car" -> Map( "intercept" -> @@ -308,6 +308,10 @@ object ModeChoiceMultinomialLogit { "transfer" -> UtilityFunctionOperation("multiplier", params.transfer) ), "bike" -> Map("intercept" -> UtilityFunctionOperation("intercept", params.bike_intercept)), + "bike_transit" -> Map( + "intercept" -> UtilityFunctionOperation("intercept", params.bike_transit_intercept), + "transfer" -> UtilityFunctionOperation("multiplier", params.transfer) + ), "walk_transit" -> Map( "intercept" -> UtilityFunctionOperation("intercept", params.walk_transit_intercept), "transfer" -> UtilityFunctionOperation("multiplier", params.transfer) @@ -320,7 +324,8 @@ object ModeChoiceMultinomialLogit { logit.MultinomialLogit( mnlUtilityFunctions, - commonUtility + commonUtility, + scale_factor ) } diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala index 146141d29d2..7fd5228f95d 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ChoosesMode.scala @@ -57,30 +57,32 @@ trait ChoosesMode { def boundingBox: Envelope - def currentTourBeamVehicle: Option[BeamVehicle] = - if (stateData.isInstanceOf[ChoosesModeData]) { - stateData.asInstanceOf[ChoosesModeData].personData.currentTourPersonalVehicle match { - case Some(personalVehicle) => - Option( - beamVehicles(personalVehicle) - .asInstanceOf[ActualVehicle] - .vehicle - ) - case _ => None - } - } else if (stateData.isInstanceOf[BasePersonData]) { - stateData.asInstanceOf[BasePersonData].currentTourPersonalVehicle match { - case Some(personalVehicle) => - Option( - beamVehicles(personalVehicle) - .asInstanceOf[ActualVehicle] - .vehicle - ) - case _ => None - } - } else { - None + def currentTourBeamVehicle: Option[BeamVehicle] = { + stateData match { + case data: ChoosesModeData => + data.personData.currentTourPersonalVehicle match { + case Some(personalVehicle) => + Option( + beamVehicles(personalVehicle) + .asInstanceOf[ActualVehicle] + .vehicle + ) + case _ => None + } + case data: BasePersonData => + data.currentTourPersonalVehicle match { + case Some(personalVehicle) => + Option( + beamVehicles(personalVehicle) + .asInstanceOf[ActualVehicle] + .vehicle + ) + case _ => None + } + case _ => + None } + } onTransition { case _ -> ChoosingMode => @@ -117,7 +119,7 @@ trait ChoosesMode { _, _, _, - None | Some(CAR | BIKE | DRIVE_TRANSIT), + None | Some(CAR | BIKE | DRIVE_TRANSIT | BIKE_TRANSIT), _, _, _, @@ -214,7 +216,7 @@ trait ChoosesMode { case None | Some(CAR | BIKE) => // In these cases, a personal vehicle will be involved newlyAvailableBeamVehicles - case Some(DRIVE_TRANSIT) => + case Some(DRIVE_TRANSIT | BIKE_TRANSIT) => val tour = _experiencedBeamPlan.getTourContaining(nextAct) val tripIndex = tour.tripIndexOfElement(nextAct) if (tripIndex == 0 || tripIndex == tour.trips.size - 1) { @@ -380,8 +382,8 @@ trait ChoosesMode { responsePlaceholders = makeResponsePlaceholders(boundingBox, withRouting = true, withParking = mode == CAR) } - case Some(DRIVE_TRANSIT) => - val LastTripIndex = currentTour(choosesModeData.personData).trips.size - 1 + case mode @ Some(DRIVE_TRANSIT | BIKE_TRANSIT) => + val lastTripIndex = currentTour(choosesModeData.personData).trips.size - 1 ( currentTour(choosesModeData.personData).tripIndexOfElement(nextAct), choosesModeData.personData.currentTourPersonalVehicle @@ -390,13 +392,16 @@ trait ChoosesMode { // We use our car if we are not replanning, otherwise we end up doing a walk transit (catch-all below) // we do not send parking inquiry here, instead we wait for drive_transit route to come back and we use // actual location of transit station + + val r5Mode = mode.get + makeRequestWith( withTransit = true, - filterStreetVehiclesForQuery(newlyAvailableBeamVehicles.map(_.streetVehicle), CAR) :+ bodyStreetVehicle, + filterStreetVehiclesForQuery(newlyAvailableBeamVehicles.map(_.streetVehicle), r5Mode) :+ bodyStreetVehicle, withParking = false ) responsePlaceholders = makeResponsePlaceholders(boundingBox, withRouting = true, withParking = false) - case (LastTripIndex, Some(currentTourPersonalVehicle)) => + case (lastTripIndex, Some(currentTourPersonalVehicle)) => // At the end of the tour, only drive home a vehicle that we have also taken away from there. parkingRequestId = makeRequestWith( withTransit = true, @@ -731,21 +736,21 @@ trait ChoosesMode { Some( EmbodiedBeamTrip( EmbodiedBeamLeg.dummyLegAt( - fullTrip.head.beamLeg.startTime, - body.id, - false, - fullTrip.head.beamLeg.travelPath.startPoint.loc, - WALK, - body.beamVehicleType.id + start = fullTrip.head.beamLeg.startTime, + vehicleId = body.id, + isLastLeg = false, + location = fullTrip.head.beamLeg.travelPath.startPoint.loc, + mode = WALK, + vehicleTypeId = body.beamVehicleType.id ) +: fullTrip :+ EmbodiedBeamLeg.dummyLegAt( - fullTrip.last.beamLeg.endTime, - body.id, - true, - fullTrip.last.beamLeg.travelPath.endPoint.loc, - WALK, - body.beamVehicleType.id + start = fullTrip.last.beamLeg.endTime, + vehicleId = body.id, + isLastLeg = true, + location = fullTrip.last.beamLeg.travelPath.endPoint.loc, + mode = WALK, + vehicleTypeId = body.beamVehicleType.id ) ) ) @@ -891,23 +896,23 @@ trait ChoosesMode { Vector(origLegs) }).map { partialItin => EmbodiedBeamTrip( - (EmbodiedBeamLeg.dummyLegAt( - _currentTick.get, - body.id, - false, - partialItin.head.beamLeg.travelPath.startPoint.loc, - WALK, - body.beamVehicleType.id + EmbodiedBeamLeg.dummyLegAt( + start = _currentTick.get, + vehicleId = body.id, + isLastLeg = false, + location = partialItin.head.beamLeg.travelPath.startPoint.loc, + mode = WALK, + vehicleTypeId = body.beamVehicleType.id ) +: partialItin :+ EmbodiedBeamLeg.dummyLegAt( - partialItin.last.beamLeg.endTime, - body.id, - true, - partialItin.last.beamLeg.travelPath.endPoint.loc, - WALK, - body.beamVehicleType.id - )) + start = partialItin.last.beamLeg.endTime, + vehicleId = body.id, + isLastLeg = true, + location = partialItin.last.beamLeg.travelPath.endPoint.loc, + mode = WALK, + vehicleTypeId = body.beamVehicleType.id + ) ) } case None => @@ -925,14 +930,14 @@ trait ChoosesMode { ).filterNot(mode => choosesModeData.excludeModes.contains(mode)) val filteredItinerariesForChoice = (choosesModeData.personData.currentTourMode match { - case Some(DRIVE_TRANSIT) => + case mode @ Some(DRIVE_TRANSIT | BIKE_TRANSIT) => val LastTripIndex = currentTour(choosesModeData.personData).trips.size - 1 ( currentTour(choosesModeData.personData).tripIndexOfElement(nextAct), personData.hasDeparted ) match { case (0 | LastTripIndex, false) => - combinedItinerariesForChoice.filter(_.tripClassifier == DRIVE_TRANSIT) + combinedItinerariesForChoice.filter(_.tripClassifier == mode.get) case _ => combinedItinerariesForChoice.filter( trip => trip.tripClassifier == WALK_TRANSIT || trip.tripClassifier == RIDE_HAIL_TRANSIT diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala index f5f71ec895f..fbdf6e8d824 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/DrivesVehicle.scala @@ -233,6 +233,7 @@ trait DrivesVehicle[T <: DrivingData] extends BeamAgent[T] with Stash { .getOrElse(throw new RuntimeException("Current Vehicle is not available.")) val isLastLeg = data.currentLegPassengerScheduleIndex + 1 == data.passengerSchedule.schedule.size val fuelConsumed = currentBeamVehicle.useFuel(currentLeg, beamScenario, networkHelper) + currentBeamVehicle.spaceTime = geo.wgs2Utm(currentLeg.travelPath.endPoint) var nbPassengers = data.passengerSchedule.schedule(currentLeg).riders.size if (nbPassengers > 0) { diff --git a/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala b/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala index 518846ba844..faac0e09640 100755 --- a/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala +++ b/src/main/scala/beam/agentsim/agents/modalbehaviors/ModeChoiceCalculator.scala @@ -66,7 +66,7 @@ trait ModeChoiceCalculator { def getNonTimeCost(embodiedBeamTrip: EmbodiedBeamTrip, includeReplanningPenalty: Boolean = false): Double = { val totalCost = embodiedBeamTrip.tripClassifier match { - case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT => + case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | BIKE_TRANSIT => val transitFareDefault = TransitFareDefaults.estimateTransitFares(IndexedSeq(embodiedBeamTrip)).head (embodiedBeamTrip.costEstimate + transitFareDefault) * beamConfig.beam.agentsim.tuning.transitPrice diff --git a/src/main/scala/beam/agentsim/agents/ridehail/RideHailAgent.scala b/src/main/scala/beam/agentsim/agents/ridehail/RideHailAgent.scala index af4650c984f..476300415b2 100755 --- a/src/main/scala/beam/agentsim/agents/ridehail/RideHailAgent.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/RideHailAgent.scala @@ -10,15 +10,17 @@ import beam.agentsim.agents.modalbehaviors.DrivesVehicle import beam.agentsim.agents.modalbehaviors.DrivesVehicle._ import beam.agentsim.agents.ridehail.RideHailAgent._ import beam.agentsim.agents.ridehail.RideHailVehicleManager.RideHailAgentLocation +import beam.agentsim.agents.vehicles.VehicleProtocol.StreetVehicle import beam.agentsim.agents.vehicles.{BeamVehicle, PassengerSchedule} import beam.agentsim.agents.{BeamAgent, InitializeTrigger} import beam.agentsim.events._ import beam.agentsim.infrastructure.parking.ParkingZoneSearch -import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse} +import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse, ParkingStall} import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, IllegalTriggerGoToError, ScheduleTrigger} import beam.agentsim.scheduler.Trigger import beam.agentsim.scheduler.Trigger.TriggerWithId -import beam.router.BeamRouter.Location +import beam.router.BeamRouter.{Location, RoutingRequest, RoutingResponse} +import beam.router.Modes.BeamMode.CAR import beam.router.model.{EmbodiedBeamLeg, EmbodiedBeamTrip} import beam.router.osm.TollCalculator import beam.sim.common.Range @@ -115,30 +117,28 @@ object RideHailAgent { case class ModifyPassengerScheduleAcks(acks: List[ModifyPassengerScheduleAck]) - case class Interrupt(interruptId: Id[Interrupt], tick: Int) + case class Interrupt(interruptId: Int, tick: Int) case object Resume sealed trait InterruptReply { - val interruptId: Id[Interrupt] + val interruptId: Int val vehicleId: Id[Vehicle] val tick: Int } case class InterruptedWhileDriving( - interruptId: Id[Interrupt], + interruptId: Int, vehicleId: Id[Vehicle], tick: Int, passengerSchedule: PassengerSchedule, currentPassengerScheduleIndex: Int, ) extends InterruptReply - case class InterruptedWhileIdle(interruptId: Id[Interrupt], vehicleId: Id[Vehicle], tick: Int) extends InterruptReply + case class InterruptedWhileIdle(interruptId: Int, vehicleId: Id[Vehicle], tick: Int) extends InterruptReply - case class InterruptedWhileOffline(interruptId: Id[Interrupt], vehicleId: Id[Vehicle], tick: Int) - extends InterruptReply - case class InterruptedWhileWaitingToDrive(interruptId: Id[Interrupt], vehicleId: Id[Vehicle], tick: Int) - extends InterruptReply + case class InterruptedWhileOffline(interruptId: Int, vehicleId: Id[Vehicle], tick: Int) extends InterruptReply + case class InterruptedWhileWaitingToDrive(interruptId: Int, vehicleId: Id[Vehicle], tick: Int) extends InterruptReply case object Idle extends BeamAgentState @@ -177,6 +177,7 @@ class RideHailAgent( val networkHelper = beamServices.networkHelper val geo = beamServices.geo + var isOnWayToParkAtStall: Option[ParkingStall] = None val myUnhandled: StateFunction = { case Event(TriggerWithId(StartShiftTrigger(tick), triggerId), _) => @@ -290,14 +291,51 @@ class RideHailAgent( case ev @ Event(ParkingInquiryResponse(stall, _), _) => log.debug("state(RideHailAgent.Offline.ParkingInquiryResponse): {}", ev) + val currentLocationUTM = beamServices.geo.wgs2Utm(currentBeamVehicle.spaceTime.loc) vehicle.useParkingStall(stall) - val (tick, triggerId) = releaseTickAndTriggerId() - eventsManager.processEvent( - ParkEvent(tick, stall, geo.utm2Wgs(stall.locationUTM), currentBeamVehicle.id, id.toString) + //TODO make sure vehicle.spacetime is up to date + val distance = beamServices.geo.distUTMInMeters(stall.locationUTM, currentLocationUTM) + // If the stall is co-located with our destination... then continue on but add the stall to PersonData +// if (distance <= beamServices.beamConfig.beam.agentsim.thresholdForWalkingInMeters) { +// // PARK AND CHARGE HERE +// parkAndStartRefueling(stall) +// } else { + // Else the stall requires a trip + val carStreetVeh = + StreetVehicle( + currentBeamVehicle.id, + currentBeamVehicle.beamVehicleType.id, + SpaceTime(currentLocationUTM, _currentTick.get), + CAR, + asDriver = true + ) + val veh2StallRequest = RoutingRequest( + currentLocationUTM, + stall.locationUTM, + _currentTick.get, + withTransit = false, + Vector(carStreetVeh), + None ) - log.debug("Refuel started at {}, triggerId: {}", tick, triggerId) - startRefueling(tick, triggerId) + isOnWayToParkAtStall = Some(stall) + beamServices.beamRouter ! veh2StallRequest +// } stay + case Event(RoutingResponse(itineraries, _), data) => + log.debug("Received routing response, initiating trip to parking stall") + val theLeg = itineraries.head.beamLegs.head + val updatedPassengerSchedule = PassengerSchedule().addLegs(Seq(theLeg)) + val (tick, triggerId) = releaseTickAndTriggerId() + scheduler ! CompletionNotice( + triggerId, + Vector( + ScheduleTrigger(StartLegTrigger(tick, theLeg), self) + ) + ) + goto(WaitingToDrive) using data + .copy(geofence = geofence) + .withPassengerSchedule(updatedPassengerSchedule) + .asInstanceOf[RideHailAgentData] case Event(TriggerWithId(StartShiftTrigger(tick), triggerId), _) => updateLatestObservedTick(tick) log.debug("state(RideHailingAgent.Offline): starting shift {}", id) @@ -311,7 +349,7 @@ class RideHailAgent( Some(triggerId) ) goto(Idle) - case ev @ Event(Interrupt(interruptId: Id[Interrupt], tick), _) => + case ev @ Event(Interrupt(interruptId, tick), _) => log.debug("state(RideHailingAgent.Offline): {}", ev) goto(OfflineInterrupted) replying InterruptedWhileOffline(interruptId, vehicle.id, latestObservedTick) case ev @ Event(Resume, _) => @@ -371,6 +409,9 @@ class RideHailAgent( case ev @ Event(ParkingInquiryResponse(_, _), _) => stash() stay() + case ev @ Event(RoutingResponse(_, _), _) => + stash() + stay() } when(Idle) { @@ -386,7 +427,7 @@ class RideHailAgent( } rideHailManager ! NotifyVehicleOutOfService(vehicle.id) goto(Offline) replying CompletionNotice(triggerId, newShiftToSchedule) - case ev @ Event(Interrupt(interruptId: Id[Interrupt], tick), _) => + case ev @ Event(Interrupt(interruptId, tick), _) => log.debug("state(RideHailingAgent.Idle): {}", ev) goto(IdleInterrupted) replying InterruptedWhileIdle(interruptId, vehicle.id, latestObservedTick) case ev @ Event( @@ -480,7 +521,7 @@ class RideHailAgent( case ev @ Event(Resume, _) => log.debug("state(RideHailingAgent.IdleInterrupted): {}", ev) goto(Idle) - case ev @ Event(Interrupt(interruptId: Id[Interrupt], tick), _) => + case ev @ Event(Interrupt(interruptId, tick), _) => log.debug("state(RideHailingAgent.IdleInterrupted): {}", ev) stay() replying InterruptedWhileIdle(interruptId, vehicle.id, latestObservedTick) case ev @ Event( @@ -515,31 +556,36 @@ class RideHailAgent( when(PassengerScheduleEmpty) { case ev @ Event(PassengerScheduleEmptyMessage(lastTime, _, _), data) => log.debug("state(RideHailingAgent.PassengerScheduleEmpty): {} Remaining Shifts: {}", ev, data.remainingShifts) - if (!vehicle.isCAV && vehicle.isRefuelNeeded( - beamScenario.beamConfig.beam.agentsim.agents.rideHail.human.refuelRequiredThresholdInMeters, - beamScenario.beamConfig.beam.agentsim.agents.rideHail.human.noRefuelThresholdInMeters - )) { - log.debug("Empty human ridehail vehicle requesting parking stall: event = " + ev) - rideHailManager ! NotifyVehicleOutOfService(vehicle.id) - - //Should I use the tick or the last time? - val (_, triggerId) = releaseTickAndTriggerId() - val startFuelTrigger = ScheduleTrigger( - StartRefuelSessionTrigger(lastTime.time), - self - ) - scheduler ! CompletionNotice(triggerId, Vector(startFuelTrigger)) - - goto(Offline) using data - .withPassengerSchedule(PassengerSchedule()) - .withCurrentLegPassengerScheduleIndex(0) - .asInstanceOf[RideHailAgentData] - } else { - if (!vehicle.isCAV) log.debug("No refueling selected for {}", vehicle) - goto(Idle) using data - .withPassengerSchedule(PassengerSchedule()) - .withCurrentLegPassengerScheduleIndex(0) - .asInstanceOf[RideHailAgentData] + isOnWayToParkAtStall match { + case Some(stall) => + currentBeamVehicle.useParkingStall(stall) + parkAndStartRefueling(stall) + isOnWayToParkAtStall = None + goto(Offline) using data + .withPassengerSchedule(PassengerSchedule()) + .withCurrentLegPassengerScheduleIndex(0) + .asInstanceOf[RideHailAgentData] + case None => + if (!vehicle.isCAV && vehicle.isRefuelNeeded( + beamScenario.beamConfig.beam.agentsim.agents.rideHail.human.refuelRequiredThresholdInMeters, + beamScenario.beamConfig.beam.agentsim.agents.rideHail.human.noRefuelThresholdInMeters + )) { + log.debug("Empty human ridehail vehicle requesting parking stall: event = " + ev) + rideHailManager ! NotifyVehicleOutOfService(vehicle.id) + + requestParkingStall() + + goto(Offline) using data + .withPassengerSchedule(PassengerSchedule()) + .withCurrentLegPassengerScheduleIndex(0) + .asInstanceOf[RideHailAgentData] + } else { + if (!vehicle.isCAV) log.debug("No refueling selected for {}", vehicle) + goto(Idle) using data + .withPassengerSchedule(PassengerSchedule()) + .withCurrentLegPassengerScheduleIndex(0) + .asInstanceOf[RideHailAgentData] + } } case ev @ Event(Interrupt(_, _), _) => log.debug("state(RideHailingAgent.PassengerScheduleEmpty): {}", ev) @@ -579,16 +625,6 @@ class RideHailAgent( override def logPrefix(): String = s"RideHailAgent $id: " def handleEndRefuel(energyInJoules: Double, tick: Int, sessionStart: Int): Unit = { - nextNotifyVehicleResourceIdle = Some( - NotifyVehicleIdle( - currentBeamVehicle.id, - geo.wgs2Utm(currentBeamVehicle.spaceTime.copy(time = tick)), - PassengerSchedule(), - currentBeamVehicle.getState, - None, - _currentTriggerId - ) - ) vehicle.addFuel(energyInJoules) eventsManager.processEvent( new RefuelSessionEvent( @@ -626,6 +662,25 @@ class RideHailAgent( currentLocation } vehicle.spaceTime = SpaceTime(newLocation, tick) + nextNotifyVehicleResourceIdle = Some( + NotifyVehicleIdle( + vehicle.id, + geo.wgs2Utm(vehicle.spaceTime), + PassengerSchedule(), + vehicle.getState, + None, + _currentTriggerId + ) + ) + } + + def parkAndStartRefueling(stall: ParkingStall) = { + val (tick, triggerId) = releaseTickAndTriggerId() + eventsManager.processEvent( + ParkEvent(tick, stall, geo.utm2Wgs(stall.locationUTM), currentBeamVehicle.id, id.toString) + ) + log.debug("Refuel started at {}, triggerId: {}", tick, triggerId) + startRefueling(tick, triggerId) } def startRefueling(tick: Int, triggerId: Long) = { @@ -639,15 +694,6 @@ class RideHailAgent( val rideHailAgentLocation = RideHailAgentLocation(vehicle.driver.get, vehicle.id, vehicle.beamVehicleType, vehicle.spaceTime, geofence) val destinationUtm = rideHailAgentLocation.currentLocationUTM.loc -// val beta1 = 1 -// val beta2 = 1 -// val beta3 = 0.001 -// val commonUtilityParams: Map[String, UtilityFunctionOperation] = Map( -// "energyPriceFactor" -> UtilityFunctionOperation("multiplier", -beta1), -// "distanceFactor" -> UtilityFunctionOperation("multiplier", -beta2), -// "installedCapacity" -> UtilityFunctionOperation("multiplier", -beta3) -// ) -// val mnl = new MultinomialLogit[ParkingZoneSearch.ParkingAlternative, String](Map.empty, commonUtilityParams) val inquiry = ParkingInquiry(destinationUtm, "charge", beamVehicle = Some(vehicle)) parkingManager ! inquiry } diff --git a/src/main/scala/beam/agentsim/agents/ridehail/RideHailManager.scala b/src/main/scala/beam/agentsim/agents/ridehail/RideHailManager.scala index 07af8df472a..ed06dd166c2 100755 --- a/src/main/scala/beam/agentsim/agents/ridehail/RideHailManager.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/RideHailManager.scala @@ -2,19 +2,18 @@ package beam.agentsim.agents.ridehail import java.awt.Color import java.io.File -import java.lang.reflect.Method import java.util import java.util.concurrent.TimeUnit import akka.actor.SupervisorStrategy.Stop -import akka.actor.{Actor, ActorLogging, ActorRef, Cancellable, OneForOneStrategy, Props, Stash, Terminated} +import akka.actor.{Actor, ActorLogging, ActorRef, OneForOneStrategy, Props, Stash, Terminated} import akka.event.LoggingReceive import akka.pattern._ import akka.util.Timeout import beam.agentsim import beam.agentsim.Resource._ import beam.agentsim.agents.BeamAgent.Finish -import beam.agentsim.agents.{Dropoff, InitializeTrigger, MobilityRequest, Pickup} +import beam.agentsim.agents.choice.logit.UtilityFunctionOperation import beam.agentsim.agents.household.CAVSchedule.RouteOrEmbodyRequest import beam.agentsim.agents.modalbehaviors.DrivesVehicle._ import beam.agentsim.agents.ridehail.RideHailAgent._ @@ -29,16 +28,9 @@ import beam.agentsim.agents.vehicles.AccessErrorCodes.{ import beam.agentsim.agents.vehicles.EnergyEconomyAttributes.Powertrain import beam.agentsim.agents.vehicles.VehicleProtocol.StreetVehicle import beam.agentsim.agents.vehicles.{PassengerSchedule, _} +import beam.agentsim.agents.{Dropoff, InitializeTrigger, MobilityRequest, Pickup} import beam.agentsim.events.SpaceTime -import beam.agentsim.infrastructure.ZonalParkingManager.logger -import beam.agentsim.infrastructure.parking.{ - ParkingMNL, - ParkingType, - ParkingZone, - ParkingZoneFileUtils, - ParkingZoneSearch -} -import beam.agentsim.infrastructure.taz.TAZ +import beam.agentsim.infrastructure.parking.ParkingMNL import beam.agentsim.infrastructure.{ParkingInquiry, ParkingInquiryResponse, ParkingStall} import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, ScheduleTrigger} import beam.agentsim.scheduler.Trigger @@ -57,7 +49,6 @@ import beam.utils.logging.LogActorState import beam.utils.matsim_conversion.ShapeUtils.QuadTreeBounds import beam.utils.reflection.ReflectionUtils import com.conveyal.r5.transit.TransportNetwork -import com.eaio.uuid.UUIDGen import com.google.common.cache.{Cache, CacheBuilder} import com.vividsolutions.jts.geom.Envelope import org.apache.commons.math3.distribution.UniformRealDistribution @@ -65,6 +56,7 @@ import org.matsim.api.core.v01.population.{Activity, Person} import org.matsim.api.core.v01.{Coord, Id, Scenario} import org.matsim.core.api.experimental.events.EventsManager import org.matsim.vehicles.Vehicle + import scala.collection.JavaConverters._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -72,8 +64,8 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.math.{max, min} import scala.util.{Failure, Random, Success, Try} - import beam.agentsim.agents.choice.logit.{MultinomialLogit, UtilityFunctionOperation} +import beam.agentsim.agents.choice.mode.DrivingCost import beam.agentsim.infrastructure.parking.ParkingMNL.RemainingTripData import beam.agentsim.infrastructure.parking.ParkingZoneSearch.ParkingAlternative @@ -84,10 +76,6 @@ object RideHailManager { val INITIAL_RIDE_HAIL_LOCATION_ALL_AT_CENTER = "ALL_AT_CENTER" val INITIAL_RIDE_HAIL_LOCATION_ALL_IN_CORNER = "ALL_IN_CORNER" - def nextRideHailInquiryId: Id[RideHailRequest] = { - Id.create(UUIDGen.createTime(UUIDGen.newTime()).toString, classOf[RideHailRequest]) - } - sealed trait RideHailServiceStatus case object NotifyIterationEnds @@ -660,7 +648,16 @@ class RideHailManager( ) val driverPassengerSchedule = singleOccupantItinsToPassengerSchedule(request, embodiedBeamTrip) - val baseFare = embodiedBeamTrip.legs.map(_.cost).sum + val baseFare = embodiedBeamTrip.legs + .map( + leg => + leg.cost - DrivingCost.estimateDrivingCost( + leg.beamLeg, + beamScenario.vehicleTypes(leg.beamVehicleTypeId), + beamScenario.fuelTypePrices + ) + ) + .sum val travelProposal = TravelProposal( singleOccupantQuoteAndPoolingInfo.rideHailAgentLocation, @@ -1548,7 +1545,19 @@ class RideHailManager( //TODO this doesn't distinguish fare by customer, lumps them all together def createTravelProposal(alloc: VehicleMatchedToCustomers): TravelProposal = { val passSched = mobilityRequestToPassengerSchedule(alloc.schedule) - val baseFare = alloc.schedule.flatMap(_.beamLegAfterTag.map(_.cost)).sum + val baseFare = alloc.schedule + .flatMap( + _.beamLegAfterTag.map( + leg => + leg.cost - DrivingCost.estimateDrivingCost( + leg.beamLeg, + beamScenario.vehicleTypes(leg.beamVehicleTypeId), + beamScenario.fuelTypePrices + ) + ) + ) + .sum + TravelProposal( alloc.rideHailAgentLocation, passSched, diff --git a/src/main/scala/beam/agentsim/agents/ridehail/RideHailModifyPassengerScheduleManager.scala b/src/main/scala/beam/agentsim/agents/ridehail/RideHailModifyPassengerScheduleManager.scala index 00765e50b6a..69c525beb5f 100755 --- a/src/main/scala/beam/agentsim/agents/ridehail/RideHailModifyPassengerScheduleManager.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/RideHailModifyPassengerScheduleManager.scala @@ -6,14 +6,11 @@ import beam.agentsim.agents.HasTickAndTrigger import beam.agentsim.agents.modalbehaviors.DrivesVehicle.StopDriving import beam.agentsim.agents.ridehail.RideHailAgent._ import beam.agentsim.agents.ridehail.RideHailManager.{BufferedRideHailRequestsTrigger, RideHailRepositioningTrigger} -import beam.agentsim.agents.ridehail.RideHailVehicleManager.RideHailAgentLocation import beam.agentsim.agents.vehicles.PassengerSchedule -import beam.agentsim.events.SpaceTime import beam.agentsim.scheduler.BeamAgentScheduler import beam.agentsim.scheduler.BeamAgentScheduler.{CompletionNotice, ScheduleTrigger} import beam.sim.config.BeamConfig -import beam.utils.DebugLib -import com.eaio.uuid.UUIDGen +import beam.utils.InterruptIdIdGenerator import org.matsim.api.core.v01.Id import org.matsim.vehicles.Vehicle @@ -28,7 +25,7 @@ class RideHailModifyPassengerScheduleManager( ) extends HasTickAndTrigger { private val interruptIdToModifyPassengerScheduleStatus = - mutable.Map[Id[Interrupt], RideHailModifyPassengerScheduleStatus]() + mutable.Map[Int, RideHailModifyPassengerScheduleStatus]() private val vehicleIdToModifyPassengerScheduleStatus = mutable.Map[Id[Vehicle], RideHailModifyPassengerScheduleStatus]() private val interruptedVehicleIds = mutable.Set[Id[Vehicle]]() // For debug only @@ -367,7 +364,7 @@ class RideHailModifyPassengerScheduleManager( } } private def clearModifyStatusFromCacheWithInterruptId( - interruptId: Id[Interrupt] + interruptId: Int ): Unit = { log.debug("remove interrupt from clearModifyStatusFromCacheWithInterruptId {}", interruptId) interruptIdToModifyPassengerScheduleStatus.remove(interruptId).foreach { rideHailModifyPassengerScheduleStatus => @@ -435,7 +432,7 @@ case object Reposition extends InterruptOrigin case object HoldForPlanning extends InterruptOrigin case class RideHailModifyPassengerScheduleStatus( - interruptId: Id[Interrupt], + interruptId: Int, vehicleId: Id[Vehicle], modifyPassengerSchedule: ModifyPassengerSchedule, interruptOrigin: InterruptOrigin, @@ -448,8 +445,5 @@ case class RideHailModifyPassengerScheduleStatus( case class ReduceAwaitingRepositioningAckMessagesByOne(vehicleId: Id[Vehicle]) object RideHailModifyPassengerScheduleManager { - - def nextRideHailAgentInterruptId: Id[Interrupt] = { - Id.create(UUIDGen.createTime(UUIDGen.newTime()).toString, classOf[Interrupt]) - } + def nextRideHailAgentInterruptId: Int = InterruptIdIdGenerator.nextId } diff --git a/src/main/scala/beam/agentsim/agents/ridehail/RideHailVehicleManager.scala b/src/main/scala/beam/agentsim/agents/ridehail/RideHailVehicleManager.scala index 7d33e9b935f..12f302be5f2 100644 --- a/src/main/scala/beam/agentsim/agents/ridehail/RideHailVehicleManager.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/RideHailVehicleManager.scala @@ -340,6 +340,9 @@ class RideHailVehicleManager(val rideHailManager: RideHailManager, boundingBox: } object RideHailVehicleManager { + + /** Please be careful when use it as a Key in Map/Set. It has overridden `equals` and `hashCode` which only respects `vehicleId` + */ case class RideHailAgentLocation( rideHailAgent: ActorRef, vehicleId: Id[Vehicle], @@ -355,6 +358,19 @@ object RideHailVehicleManager { def toStreetVehicle: StreetVehicle = { StreetVehicle(vehicleId, vehicleType.id, currentLocationUTM, CAR, asDriver = true) } + + override def equals(obj: Any): Boolean = { + obj match { + case that: RideHailAgentLocation => + that.canEqual(this) && vehicleId == that.vehicleId + } + } + + override def hashCode(): Int = { + vehicleId.hashCode() + } + + def canEqual(other: Any): Boolean = other.isInstanceOf[RideHailAgentLocation] } case class RideHailAgentETA( diff --git a/src/main/scala/beam/agentsim/agents/ridehail/allocation/PoolingAlonsoMora.scala b/src/main/scala/beam/agentsim/agents/ridehail/allocation/PoolingAlonsoMora.scala index 3196bcb0acf..ff3dc887e93 100755 --- a/src/main/scala/beam/agentsim/agents/ridehail/allocation/PoolingAlonsoMora.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/allocation/PoolingAlonsoMora.scala @@ -268,7 +268,10 @@ class PoolingAlonsoMora(val rideHailManager: RideHailManager) val wereAllocated = allocResponses .flatMap(resp => resp.request.groupedWithOtherRequests.map(_.requestId).toSet + resp.request.requestId) .toSet - pooledAllocationReqs.filterNot(req => wereAllocated.contains(req.requestId)).foreach { unsatisfiedReq => + + val nonAllocated = pooledAllocationReqs.filterNot(req => wereAllocated.contains(req.requestId)) + var s = System.currentTimeMillis() + nonAllocated.foreach { unsatisfiedReq => Pooling.serveOneRequest(unsatisfiedReq, tick, alreadyAllocated, rideHailManager) match { case res @ RoutingRequiredToAllocateVehicle(_, routes) => allocResponses = allocResponses :+ res @@ -277,7 +280,12 @@ class PoolingAlonsoMora(val rideHailManager: RideHailManager) allocResponses = allocResponses :+ res } } + var e = System.currentTimeMillis() + logger.debug(s"Served nonAllocated ${nonAllocated.size} in ${e - s} ms") + + s = System.currentTimeMillis() // Now satisfy the solo customers + val soloCustomer = toAllocate.filterNot(_.asPooled) toAllocate.filterNot(_.asPooled).foreach { req => Pooling.serveOneRequest(req, tick, alreadyAllocated, rideHailManager) match { case res @ RoutingRequiredToAllocateVehicle(_, routes) => @@ -299,6 +307,8 @@ class PoolingAlonsoMora(val rideHailManager: RideHailManager) ) } } + e = System.currentTimeMillis() + logger.debug(s"Served soloCustomer ${soloCustomer.size} in ${e - s} ms") } rideHailManager.log.debug( "AllocResponses: {}", diff --git a/src/main/scala/beam/agentsim/agents/ridehail/repositioningmanager/DemandFollowingRepositioningManager.scala b/src/main/scala/beam/agentsim/agents/ridehail/repositioningmanager/DemandFollowingRepositioningManager.scala index 5e4b1ebf6ca..243169101ae 100644 --- a/src/main/scala/beam/agentsim/agents/ridehail/repositioningmanager/DemandFollowingRepositioningManager.scala +++ b/src/main/scala/beam/agentsim/agents/ridehail/repositioningmanager/DemandFollowingRepositioningManager.scala @@ -55,12 +55,15 @@ class DemandFollowingRepositioningManager(val beamServices: BeamServices, val ri val hourToAct: Array[(Int, Activity)] = activitySegment.activities.map(act => ((act.getEndTime / 3600).toInt, act)) - val hourToActivities: Array[Array[Activity]] = - hourToAct.groupBy { case (h, _) => h }.map { case (_, xs) => xs.map(_._2) }.toArray + val groupedByHour: Map[Int, Array[Activity]] = + hourToAct.groupBy { case (h, _) => h }.map { case (h, xs) => h -> xs.map(_._2) } + + val hourToActivities: Array[Array[Activity]] = groupedByHour.toArray + .sortBy { case (h, _) => h } + .map(_._2) // Index is hour, value is number of activities - val activitiesPerHour: Array[Int] = hourToAct - .groupBy { case (h, _) => h } + val activitiesPerHour: Array[Int] = groupedByHour .map { case (h, xs) => (h, xs.length) } .toArray .sortBy { case (h, _) => h } diff --git a/src/main/scala/beam/agentsim/agents/vehicles/VehicleAccessRequest.scala b/src/main/scala/beam/agentsim/agents/vehicles/VehicleAccessRequest.scala index 0de06797776..164eee4d8f3 100755 --- a/src/main/scala/beam/agentsim/agents/vehicles/VehicleAccessRequest.scala +++ b/src/main/scala/beam/agentsim/agents/vehicles/VehicleAccessRequest.scala @@ -3,19 +3,11 @@ package beam.agentsim.agents.vehicles import beam.agentsim.events.resources.ReservationErrorCode._ import beam.agentsim.events.resources._ import beam.agentsim.scheduler.BeamAgentScheduler.ScheduleTrigger -import beam.router.Modes.BeamMode import beam.router.model.BeamLeg -import com.eaio.uuid.UUIDGen -import org.matsim.api.core.v01.Id - -object Reservation { - - def nextReservationId: Id[ReservationRequest] = - Id.create(UUIDGen.createTime(UUIDGen.newTime()).toString, classOf[ReservationRequest]) -} +import beam.utils.ReservationRequestIdGenerator case class ReservationRequest( - requestId: Id[ReservationRequest], + requestId: Int, departFrom: BeamLeg, arriveAt: BeamLeg, passengerVehiclePersonId: PersonIdWithActorRef @@ -29,7 +21,7 @@ object ReservationRequest { passengerVehiclePersonId: PersonIdWithActorRef ): ReservationRequest = ReservationRequest( - Reservation.nextReservationId, + ReservationRequestIdGenerator.nextId, departFrom, arriveAt, passengerVehiclePersonId diff --git a/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala b/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala index 81e294d2d2e..fb930e36797 100644 --- a/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala +++ b/src/main/scala/beam/agentsim/events/PathTraversalEvent.scala @@ -219,7 +219,8 @@ object PathTraversalEvent { val linkIds: IndexedSeq[Int] = if (linkIdsAsStr == "") IndexedSeq.empty else linkIdsAsStr.split(",").map(_.toInt) val linkTravelTimeStr = attr.getOrElse(ATTRIBUTE_LINK_TRAVEL_TIME, "") val linkTravelTime: IndexedSeq[Double] = - if (linkTravelTimeStr == "") IndexedSeq.empty else linkTravelTimeStr.split(",").map(_.toDouble) + if (linkTravelTimeStr == null || linkTravelTimeStr == "") IndexedSeq.empty + else linkTravelTimeStr.split(",").map(_.toDouble) val startX: Double = attr(ATTRIBUTE_START_COORDINATE_X).toDouble val startY: Double = attr(ATTRIBUTE_START_COORDINATE_Y).toDouble val endX: Double = attr(ATTRIBUTE_END_COORDINATE_X).toDouble diff --git a/src/main/scala/beam/agentsim/events/RefuelSessionEvent.scala b/src/main/scala/beam/agentsim/events/RefuelSessionEvent.scala index 54c2f5dd853..f36ef9b6a52 100644 --- a/src/main/scala/beam/agentsim/events/RefuelSessionEvent.scala +++ b/src/main/scala/beam/agentsim/events/RefuelSessionEvent.scala @@ -43,7 +43,7 @@ class RefuelSessionEvent( attributes.put(ATTRIBUTE_PRICING_MODEL, pricingModelString) attributes.put(ATTRIBUTE_CHARGING_TYPE, chargingPointString) attributes.put(ATTRIBUTE_PARKING_TAZ, stall.tazId.toString) - attributes.put(ATTRIBUTE_VEHICLE_TYPE, vehicleType.toString) + attributes.put(ATTRIBUTE_VEHICLE_TYPE, vehicleType.id.toString) attributes } } diff --git a/src/main/scala/beam/agentsim/infrastructure/parking/ParkingStallSampling.scala b/src/main/scala/beam/agentsim/infrastructure/parking/ParkingStallSampling.scala index e8bee35f8a3..bd4a0bd934a 100644 --- a/src/main/scala/beam/agentsim/infrastructure/parking/ParkingStallSampling.scala +++ b/src/main/scala/beam/agentsim/infrastructure/parking/ParkingStallSampling.scala @@ -23,12 +23,18 @@ object ParkingStallSampling { val xDistance: Double = taz.coord.getX - agent.getX val yDistance: Double = taz.coord.getY - agent.getY + val euclideanDistance = Math.sqrt(Math.pow(xDistance, 2.0) + Math.pow(yDistance, 2.0)) val tazCharacteristicDiameter: Double = math.sqrt(taz.areaInSquareMeters) val sampleStandardDeviation: Double = tazCharacteristicDiameter * 0.33 + val adjustedAvailabilityRatio = + if (euclideanDistance < tazCharacteristicDiameter) availabilityRatio + else availabilityRatio / Math.pow(euclideanDistance / tazCharacteristicDiameter, 2.0) + // this coefficient models the effect of parking supply constraint on the distance a parking stall // might be placed from the agent's desired destination - val availabilityFactor: Double = if (availabilityRatio < 0.01) 1.0 else -0.25 * math.log(availabilityRatio) + val availabilityFactor: Double = + if (adjustedAvailabilityRatio < 0.01) 1.0 else -0.25 * math.log(adjustedAvailabilityRatio) // finding a location between the agent and the TAZ centroid to sample from, scaled back by increased availability val (scaledXDistance, scaledYDistance) = ( diff --git a/src/main/scala/beam/router/BeamSkimmer.scala b/src/main/scala/beam/router/BeamSkimmer.scala index 0d6780419dc..d1ba6a903b0 100644 --- a/src/main/scala/beam/router/BeamSkimmer.scala +++ b/src/main/scala/beam/router/BeamSkimmer.scala @@ -10,6 +10,7 @@ import beam.router.BeamRouter.Location import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode.{ BIKE, + BIKE_TRANSIT, CAR, CAV, DRIVE_TRANSIT, @@ -101,8 +102,8 @@ class BeamSkimmer @Inject()( beamConfig.beam.agentsim.agents.rideHail.defaultBaseCost + beamConfig.beam.agentsim.agents.rideHail.defaultCostPerMile * travelDistance / 1609.0 + beamConfig.beam.agentsim.agents.rideHail.defaultCostPerMinute * travelTime / 60.0 case RIDE_HAIL_POOLED => beamConfig.beam.agentsim.agents.rideHail.pooledBaseCost + beamConfig.beam.agentsim.agents.rideHail.pooledCostPerMile * travelDistance / 1609.0 + beamConfig.beam.agentsim.agents.rideHail.pooledCostPerMinute * travelTime / 60.0 - case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT => 0.25 * travelDistance / 1609 - case _ => 0.0 + case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT | BIKE_TRANSIT => 0.25 * travelDistance / 1609 + case _ => 0.0 } Skim( travelTime, @@ -187,16 +188,18 @@ class BeamSkimmer @Inject()( energy = 0.0 ) } - (pooled.time / solo.time, pooled.cost / solo.cost) + val timeFactor = if (solo.time > 0.0) { pooled.time / solo.time } else { 1.0 } + val costFactor = if (solo.cost > 0.0) { pooled.cost / solo.cost } else { 1.0 } + (timeFactor, costFactor) } private def distanceAndTime(mode: BeamMode, originUTM: Location, destinationUTM: Location) = { val speed = mode match { - case CAR | CAV | RIDE_HAIL => carSpeedMeterPerSec - case RIDE_HAIL_POOLED => carSpeedMeterPerSec / 1.1 - case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT => transitSpeedMeterPerSec - case BIKE => bicycleSpeedMeterPerSec - case _ => walkSpeedMeterPerSec + case CAR | CAV | RIDE_HAIL => carSpeedMeterPerSec + case RIDE_HAIL_POOLED => carSpeedMeterPerSec / 1.1 + case TRANSIT | WALK_TRANSIT | DRIVE_TRANSIT | RIDE_HAIL_TRANSIT | BIKE_TRANSIT => transitSpeedMeterPerSec + case BIKE => bicycleSpeedMeterPerSec + case _ => walkSpeedMeterPerSec } val travelDistance: Int = Math.ceil(GeoUtils.minkowskiDistFormula(originUTM, destinationUTM)).toInt val travelTime: Int = Math @@ -659,6 +662,7 @@ object BeamSkimmer extends LazyLogging { RIDE_HAIL -> carSpeedMeterPerSec, RIDE_HAIL_POOLED -> carSpeedMeterPerSec, RIDE_HAIL_TRANSIT -> transitSpeedMeterPerSec, + BIKE_TRANSIT -> transitSpeedMeterPerSec, TRANSIT -> transitSpeedMeterPerSec ) diff --git a/src/main/scala/beam/router/Modes.scala b/src/main/scala/beam/router/Modes.scala index b6f64627883..a547e0240e8 100755 --- a/src/main/scala/beam/router/Modes.scala +++ b/src/main/scala/beam/router/Modes.scala @@ -107,6 +107,13 @@ object Modes { TransportMode.pt ) + case object BIKE_TRANSIT + extends BeamMode( + value = "bike_transit", + Some(Right(TransitModes.TRANSIT)), + TransportMode.other + ) + val chainBasedModes = Seq(CAR, BIKE) val transitModes = @@ -125,7 +132,8 @@ object Modes { RIDE_HAIL_POOLED, RIDE_HAIL_TRANSIT, DRIVE_TRANSIT, - WALK_TRANSIT + WALK_TRANSIT, + BIKE_TRANSIT ) def fromString(stringMode: String): Option[BeamMode] = { diff --git a/src/main/scala/beam/router/model/EmbodiedBeamTrip.scala b/src/main/scala/beam/router/model/EmbodiedBeamTrip.scala index 086772c85d6..f924ad09098 100644 --- a/src/main/scala/beam/router/model/EmbodiedBeamTrip.scala +++ b/src/main/scala/beam/router/model/EmbodiedBeamTrip.scala @@ -3,6 +3,7 @@ package beam.router.model import beam.router.Modes.BeamMode import beam.router.Modes.BeamMode.{ BIKE, + BIKE_TRANSIT, CAR, CAV, DRIVE_TRANSIT, @@ -44,6 +45,7 @@ case class EmbodiedBeamTrip(legs: IndexedSeq[EmbodiedBeamLeg]) { def determineTripMode(legs: IndexedSeq[EmbodiedBeamLeg]): BeamMode = { var theMode: BeamMode = WALK var hasUsedCar: Boolean = false + var hasUsedBike: Boolean = false var hasUsedRideHail: Boolean = false legs.foreach { leg => // Any presence of transit makes it transit @@ -62,11 +64,16 @@ case class EmbodiedBeamTrip(legs: IndexedSeq[EmbodiedBeamLeg]) { } else if (theMode == WALK && leg.beamLeg.mode == BIKE) { theMode = BIKE } + if (leg.beamLeg.mode == CAR) hasUsedCar = true + if (leg.beamLeg.mode == BIKE) hasUsedBike = true if (leg.isRideHail) hasUsedRideHail = true + } if (theMode == TRANSIT && hasUsedRideHail) { RIDE_HAIL_TRANSIT + } else if (theMode == TRANSIT && hasUsedBike) { + BIKE_TRANSIT } else if (theMode == TRANSIT && hasUsedCar) { DRIVE_TRANSIT } else if (theMode == TRANSIT && !hasUsedCar) { diff --git a/src/main/scala/beam/router/r5/R5RoutingWorker.scala b/src/main/scala/beam/router/r5/R5RoutingWorker.scala index 9f64258943b..bad5bb94982 100755 --- a/src/main/scala/beam/router/r5/R5RoutingWorker.scala +++ b/src/main/scala/beam/router/r5/R5RoutingWorker.scala @@ -286,7 +286,7 @@ class R5Wrapper(workerParams: WorkerParameters, travelTime: TravelTime) extends ) val toll = tollCalculator.calcTollByLinkIds(updatedTravelPath) val updatedLeg = leg.copy(travelPath = updatedTravelPath, duration = updatedTravelPath.duration) - val drivingCost = DrivingCost.estimateDrivingCost(leg, vehicleTypes(vehicleTypeId), fuelTypePrices) + val drivingCost = DrivingCost.estimateDrivingCost(updatedLeg, vehicleTypes(vehicleTypeId), fuelTypePrices) val response = RoutingResponse( Vector( EmbodiedBeamTrip( @@ -1158,8 +1158,9 @@ class R5Wrapper(workerParams: WorkerParameters, travelTime: TravelTime) extends } object R5RoutingWorker { - val BUSHWHACKING_SPEED_IN_METERS_PER_SECOND = 0.447 // 1 mile per hour + val BUSHWHACKING_SPEED_IN_METERS_PER_SECOND = 1.38 + // 3.1 mph -> 1.38 meter per second, changed from 1 mph def props( beamScenario: BeamScenario, transportNetwork: TransportNetwork, @@ -1204,15 +1205,15 @@ object R5RoutingWorker { endUTM: Location, geo: GeoUtils ): BeamLeg = { - val beelineDistanceInMeters = geo.distUTMInMeters(startUTM, endUTM) - val bushwhackingTime = Math.round(beelineDistanceInMeters / BUSHWHACKING_SPEED_IN_METERS_PER_SECOND) + val distanceInMeters = GeoUtils.minkowskiDistFormula(startUTM, endUTM) //changed from geo.distUTMInMeters(startUTM, endUTM) + val bushwhackingTime = Math.round(distanceInMeters / BUSHWHACKING_SPEED_IN_METERS_PER_SECOND) val path = BeamPath( Vector(), Vector(), None, SpaceTime(geo.utm2Wgs(startUTM), atTime), SpaceTime(geo.utm2Wgs(endUTM), atTime + bushwhackingTime.toInt), - beelineDistanceInMeters + distanceInMeters ) BeamLeg(atTime, WALK, bushwhackingTime.toInt, path) } diff --git a/src/main/scala/beam/sim/BeamMobsim.scala b/src/main/scala/beam/sim/BeamMobsim.scala index 6584bb58bc5..61eca8ca44c 100755 --- a/src/main/scala/beam/sim/BeamMobsim.scala +++ b/src/main/scala/beam/sim/BeamMobsim.scala @@ -65,7 +65,7 @@ class BeamMobsim @Inject()( validateVehicleTypes() if (beamServices.beamConfig.beam.debug.debugEnabled) - logger.info(DebugLib.gcAndGetMemoryLogMessage("run.start (after GC): ")) + logger.info(DebugLib.getMemoryLogMessage("run.start (after GC): ")) Metrics.iterationNumber = beamServices.matsimServices.getIterationNumber eventsManager.initProcessing() @@ -314,8 +314,13 @@ class BeamMobsimIteration( } private def scheduleRideHailManagerTimerMessages(): Unit = { - if (config.agents.rideHail.repositioningManager.timeout > 0) - scheduler ! ScheduleTrigger(RideHailRepositioningTrigger(0), rideHailManager) + if (config.agents.rideHail.repositioningManager.timeout > 0) { + // We need to stagger init tick for repositioning manager and allocation manager + // This is important because during the `requestBufferTimeoutInSeconds` repositioned vehicle is not available, so to make them work together + // we have to make sure that there is no overlap + val initTick = config.agents.rideHail.repositioningManager.timeout / 2 + scheduler ! ScheduleTrigger(RideHailRepositioningTrigger(initTick), rideHailManager) + } if (config.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds > 0) scheduler ! ScheduleTrigger(BufferedRideHailRequestsTrigger(0), rideHailManager) } diff --git a/src/main/scala/beam/sim/BeamSim.scala b/src/main/scala/beam/sim/BeamSim.scala index 1d0441eac82..2460bb3cdf7 100755 --- a/src/main/scala/beam/sim/BeamSim.scala +++ b/src/main/scala/beam/sim/BeamSim.scala @@ -219,7 +219,7 @@ class BeamSim @Inject()( } if (beamConfig.beam.debug.debugEnabled) - logger.info(DebugLib.gcAndGetMemoryLogMessage("notifyIterationEnds.start (after GC): ")) + logger.info(DebugLib.getMemoryLogMessage("notifyIterationEnds.start (after GC): ")) rideHailUtilizationCollector.notifyIterationEnds(event) @@ -273,7 +273,7 @@ class BeamSim @Inject()( } if (beamConfig.beam.debug.debugEnabled) - logger.info(DebugLib.gcAndGetMemoryLogMessage("notifyIterationEnds.end (after GC): ")) + logger.info(DebugLib.getMemoryLogMessage("notifyIterationEnds.end (after GC): ")) stopMeasuringIteration() val persons = scenario.getPopulation.getPersons.values().asScala diff --git a/src/main/scala/beam/sim/config/BeamConfig.scala b/src/main/scala/beam/sim/config/BeamConfig.scala index c2c3856d7b5..8e4e29b3dc6 100755 --- a/src/main/scala/beam/sim/config/BeamConfig.scala +++ b/src/main/scala/beam/sim/config/BeamConfig.scala @@ -1,4 +1,4 @@ -// generated by tscfg 0.9.4 on Fri Sep 06 01:02:36 EDT 2019 +// generated by tscfg 0.9.4 on Tue Oct 08 01:34:10 EDT 2019 // source: src/main/resources/beam-template.conf package beam.sim.config @@ -454,12 +454,14 @@ object BeamConfig { } case class MulitnomialLogit( - params: BeamConfig.Beam.Agentsim.Agents.ModalBehaviors.MulitnomialLogit.Params + params: BeamConfig.Beam.Agentsim.Agents.ModalBehaviors.MulitnomialLogit.Params, + utility_scale_factor: scala.Double ) object MulitnomialLogit { case class Params( bike_intercept: scala.Double, + bike_transit_intercept: scala.Double, car_intercept: scala.Double, cav_intercept: scala.Double, drive_transit_intercept: scala.Double, @@ -478,6 +480,8 @@ object BeamConfig { ): BeamConfig.Beam.Agentsim.Agents.ModalBehaviors.MulitnomialLogit.Params = { BeamConfig.Beam.Agentsim.Agents.ModalBehaviors.MulitnomialLogit.Params( bike_intercept = if (c.hasPathOrNull("bike_intercept")) c.getDouble("bike_intercept") else 0.0, + bike_transit_intercept = + if (c.hasPathOrNull("bike_transit_intercept")) c.getDouble("bike_transit_intercept") else 0.0, car_intercept = if (c.hasPathOrNull("car_intercept")) c.getDouble("car_intercept") else 0.0, cav_intercept = if (c.hasPathOrNull("cav_intercept")) c.getDouble("cav_intercept") else 0.0, drive_transit_intercept = @@ -505,7 +509,9 @@ object BeamConfig { params = BeamConfig.Beam.Agentsim.Agents.ModalBehaviors.MulitnomialLogit.Params( if (c.hasPathOrNull("params")) c.getConfig("params") else com.typesafe.config.ConfigFactory.parseString("params{}") - ) + ), + utility_scale_factor = + if (c.hasPathOrNull("utility_scale_factor")) c.getDouble("utility_scale_factor") else 1.0 ) } } diff --git a/src/main/scala/beam/sim/population/PercentagePopulationAdjustment.scala b/src/main/scala/beam/sim/population/PercentagePopulationAdjustment.scala index e7ef006555c..46cea0a8bdc 100644 --- a/src/main/scala/beam/sim/population/PercentagePopulationAdjustment.scala +++ b/src/main/scala/beam/sim/population/PercentagePopulationAdjustment.scala @@ -1,10 +1,8 @@ package beam.sim.population -import java.util.Random - import beam.sim.{BeamScenario, BeamServices} -import org.matsim.api.core.v01.population.{Person, Population} -import org.matsim.api.core.v01.{Id, Scenario} +import org.matsim.api.core.v01.population.Population +import org.matsim.api.core.v01.Scenario case class PercentagePopulationAdjustment(beamServices: BeamServices) extends PopulationAdjustment { diff --git a/src/main/scala/beam/sim/population/PopulationAdjustment.scala b/src/main/scala/beam/sim/population/PopulationAdjustment.scala index aaf676fb63e..40960a9314d 100644 --- a/src/main/scala/beam/sim/population/PopulationAdjustment.scala +++ b/src/main/scala/beam/sim/population/PopulationAdjustment.scala @@ -258,13 +258,13 @@ object PopulationAdjustment extends LazyLogging { ) // Generate the AttributesOfIndividual object as save it as custom attribute - "beam-attributes" for the person AttributesOfIndividual( - householdAttributes, - modalityStyle, - Option(PersonUtils.getSex(person)).getOrElse("M").equalsIgnoreCase("M"), - availableModes, - valueOfTime, - Option(PersonUtils.getAge(person)), - Some(income) + householdAttributes = householdAttributes, + modalityStyle = modalityStyle, + isMale = Option(PersonUtils.getSex(person)).getOrElse("M").equalsIgnoreCase("M"), + availableModes = availableModes, + valueOfTime = valueOfTime, + age = Option(PersonUtils.getAge(person)), + income = Some(income) ) } private def IncomeToValueOfTime(income: Double): Option[Double] = { diff --git a/src/main/scala/beam/utils/DebugActorWithTimer.scala b/src/main/scala/beam/utils/DebugActorWithTimer.scala index 87d1890cddb..ff4a9563690 100755 --- a/src/main/scala/beam/utils/DebugActorWithTimer.scala +++ b/src/main/scala/beam/utils/DebugActorWithTimer.scala @@ -8,7 +8,7 @@ class DebugActorWithTimer(val rideHailManager: ActorRef, val scheduler: ActorRef def receive: PartialFunction[Any, Unit] = { case Tick => - log.info(DebugLib.gcAndGetMemoryLogMessage("Memory use after GC: ")) + log.info(DebugLib.getMemoryLogMessage("Memory use after GC: ")) rideHailManager ! DebugRideHailManagerDuringExecution scheduler ! Monitor } diff --git a/src/main/scala/beam/utils/IdGenerator.scala b/src/main/scala/beam/utils/IdGenerator.scala index 800e48d658c..33abe2d9690 100644 --- a/src/main/scala/beam/utils/IdGenerator.scala +++ b/src/main/scala/beam/utils/IdGenerator.scala @@ -28,3 +28,19 @@ object ParkingManagerIdGenerator extends IdGenerator { id.getAndIncrement() } } + +object InterruptIdIdGenerator extends IdGenerator { + private val id: AtomicInteger = new AtomicInteger(0) + + def nextId: Int = { + id.getAndIncrement() + } +} + +object ReservationRequestIdGenerator extends IdGenerator { + private val id: AtomicInteger = new AtomicInteger(0) + + def nextId: Int = { + id.getAndIncrement() + } +} diff --git a/src/main/scala/beam/utils/RespositioingValidation.scala b/src/main/scala/beam/utils/RespositioingValidation.scala new file mode 100644 index 00000000000..9b6450d627d --- /dev/null +++ b/src/main/scala/beam/utils/RespositioingValidation.scala @@ -0,0 +1,97 @@ +package beam.utils + +import beam.agentsim.events.PathTraversalEvent +import beam.router.Modes.BeamMode +import org.matsim.api.core.v01.events.Event +import org.matsim.core.events.handler.BasicEventHandler +import org.matsim.core.events.{EventsUtils, MatsimEventsReader} + +import scala.collection.mutable +import scala.collection.mutable.ListBuffer + +case class RepositioningDuration(repositioningDurationStart: Double, repositioningDurationEnd: Double) + +object RepositioningValidation { + + val vehicleRepositioning: mutable.Map[String, ListBuffer[RepositioningDuration]] = + mutable.Map[String, ListBuffer[RepositioningDuration]]() + + def main(args: Array[String]): Unit = { + var eventsFileFormat = "xml" + if (args.length == 2) { + eventsFileFormat = args(1) + } + if (eventsFileFormat == "csv") { + val (eventCount, foundEvent) = readCSVEvents(args(0)) + println(s"Total event count $eventCount") + println(s"Event found $foundEvent") + + } else { + val (eventCount, foundEvent) = readXMLEvents(args(0)) + println(s"Total event count $eventCount") + println(s"Event found $foundEvent") + } + //readCSVEvents("/home/rajnikant/IdeaProjects/beam/output/sf-light/sf-light-1k-xml__2019-10-04_03-19-45/ITERS/it.0/0.events.csv.gz") + + } + + private def readCSVEvents(path: String): (Int, Int) = { + val (eventSeq, _) = EventReader.fromCsvFile(path, _ => true) + var eventCount = 0 + var foundEvent = 0 + eventSeq.foreach(event => { + if (event.getEventType == "PathTraversal") { + eventCount += 1 + if (processForRepositioningDebug(PathTraversalEvent.apply(event))) + foundEvent += 1 + } + }) + (eventCount, foundEvent) + } + + private def readXMLEvents(path: String): (Int, Int) = { + val eventsManager = EventsUtils.createEventsManager() + var eventCount = 0 + var foundEvent = 0 + eventsManager.addHandler(new BasicEventHandler { + def handleEvent(event: Event): Unit = { + if (event.getEventType == "PathTraversal") { + eventCount += 1 + if (processForRepositioningDebug(PathTraversalEvent.apply(event))) + foundEvent += 1 + } + } + }) + new MatsimEventsReader(eventsManager).readFile(path) + (eventCount, foundEvent) + } + + private def processForRepositioningDebug(event: PathTraversalEvent): Boolean = { + var option = false + val vehicleId = event.vehicleId.toString + + if (event.numberOfPassengers > 0) { + if (event.mode == BeamMode.CAR && vehicleId.contains("rideHail")) { + vehicleRepositioning + .get(vehicleId) + .foreach(repositioningDurationBuffer => { + if (repositioningDurationBuffer.size > 1) { + val size = repositioningDurationBuffer.size + val deadHeading = repositioningDurationBuffer.last + val repositioning = repositioningDurationBuffer(size - 2) + if (deadHeading.repositioningDurationStart == repositioning.repositioningDurationEnd) { + println("Found PathTraversal event with start deadheading and end repositioning") + option = true + } + } + }) + } + vehicleRepositioning.remove(vehicleId) + } else { + val listBuffer = vehicleRepositioning.getOrElse(vehicleId, ListBuffer[RepositioningDuration]()) + listBuffer += RepositioningDuration(event.departureTime, event.arrivalTime) + vehicleRepositioning.put(vehicleId, listBuffer) + } + option + } +} diff --git a/src/main/scala/beam/utils/Stats.scala b/src/main/scala/beam/utils/Stats.scala index e1ad7277e34..e1c4a20656f 100755 --- a/src/main/scala/beam/utils/Stats.scala +++ b/src/main/scala/beam/utils/Stats.scala @@ -13,7 +13,7 @@ object Stats { class Stats[T](values: Vector[T])(implicit ev$1: T => Double) { class _Stats(var minValue: Double, var maxValue: Double, var sum: Double, var sumSqr: Double) require(values.nonEmpty, "Stats: Cannot initialize stats with undefined values") - private[this] val sums = values./:((0.0, 0.0))((acc, s) => (acc._1 + s, acc._2 + s * s)) + private[this] val sums = values.foldLeft((0.0, 0.0))((acc, s) => (acc._1 + s, acc._2 + s * s)) @inline lazy val mean: Double = sums._1 / values.size diff --git a/src/main/scala/beam/utils/analysis/GenerateTripTable.scala b/src/main/scala/beam/utils/analysis/GenerateTripTable.scala new file mode 100644 index 00000000000..602299bc1c8 --- /dev/null +++ b/src/main/scala/beam/utils/analysis/GenerateTripTable.scala @@ -0,0 +1,169 @@ +package beam.utils.analysis + +import java.io.Closeable + +import beam.utils.EventReader +import beam.utils.csv.CsvWriter +import org.matsim.api.core.v01.{Coord, Id} +import org.matsim.api.core.v01.events.Event +import org.matsim.api.core.v01.network.Link +import org.matsim.core.network.NetworkUtils +import org.matsim.core.network.io.MatsimNetworkReader + +import scala.collection.mutable + +object GenerateTripTable { + + def main(args: Array[String]): Unit = { + + val eventsFile = args(0) //"/home/rajnikant/Downloads/0.events.csv" + val networkFile = args(1) //"/home/rajnikant/Downloads/outputNetwork.xml.gz" + val outputPath = args(2) //"/home/rajnikant/IdeaProjects/beam/output/beamville/" + val outputFile = "personMilesTravelled.csv" + + val network = NetworkUtils.createNetwork() + new MatsimNetworkReader(network).readFile(networkFile) + val links = network.getLinks + + val (events: Iterator[Event], closable: Closeable) = EventReader.fromCsvFile(eventsFile, _ => true) + + val filterEvent = events.map { + case event if event.getEventType == "actstart" | event.getEventType == "actend" => + val link = event.getAttributes.get("link") + val linkId = Id.create[Link](link, classOf[Link]) + val nodeLink = links.get(linkId) + val fromCoord = nodeLink.getFromNode.getCoord + val toCoord = nodeLink.getToNode.getCoord + val (centerX, centerY) = linkCenter(fromCoord, toCoord) + event.getAttributes.put("center_x", String.valueOf(centerX)) + event.getAttributes.put("center_y", String.valueOf(centerY)) + event + case event => event + } //.filterNot(event => event.getAttributes.get("person") == null || event.getAttributes.get("person").contains("rideHail") || event.getAttributes.get("person").contains("Driver")) + + val headers = IndexedSeq( + "", + "personId", + "legId", + "time", + "mode", + "numberOfReplannings", + "legDurationBasedOnActEndActStart", + "distanceTravelledPathTraversal", + "euclideanDistanceToPreviousAct", + "previousActType" + ) + val csvWriter = new CsvWriter(s"$outputPath$outputFile", headers) + writeCSVRows(filterEvent, csvWriter) + } + + def writeCSVRows(filterRows: Iterator[Event], csvWriter: CsvWriter): Unit = { + val personStates = new mutable.HashMap[String, Person] + val vehicles = new mutable.HashMap[String, mutable.Set[String]] + var count = 0 + filterRows.foreach { event => + val attributes = event.getAttributes + val eventType = attributes.get("type") + val personId = attributes.get("person") + val vehicleId = attributes.get("vehicle") + val time = attributes.get("time").toDouble + + if (count % 10000 == 0) + println(s"processing row $count") + + val person = personStates.getOrElse(personId, Person()) + eventType match { + case "Replanning" => + personStates(personId) = person.copy(numberOfReplannings = person.numberOfReplannings + 1) + + case "ModeChoice" => + personStates(personId) = person.copy(currentMode = attributes.get("mode")) + + case "actstart" => + val linkCenterX = attributes.get("center_x").toDouble + val linkCenterY = attributes.get("center_y").toDouble + csvWriter.writeRow( + IndexedSeq( + count, + personId, + person.legId, + time, + person.currentMode, + person.numberOfReplannings, + time - person.lastActEndTime, + person.distanceTravelledPathTraversal, + distance(person.last_act_x, person.last_act_y, linkCenterX, linkCenterY), + person.previousActType + ) + ) + count = count + 1 + val newPerson = Person(legId = person.legId + 1) + personStates(personId) = newPerson + + case "actend" => + val linkCenterX = attributes.get("center_x").toDouble + val linkCenterY = attributes.get("center_y").toDouble + val actType = attributes.get("actType") + personStates(personId) = person.copy( + lastActEndTime = time, + last_act_x = linkCenterX, + last_act_y = linkCenterY, + previousActType = actType + ) + + case "PersonEntersVehicle" if !(personId.contains("rideHail") || personId.contains("Driver")) => + personStates(personId) = person.copy(lastTimePersonEnteredVehicle = time, currentVehicle = vehicleId) + val persons = vehicles.getOrElse(vehicleId, mutable.Set()) + vehicles(vehicleId) = persons + personId + + case "PersonLeavesVehicle" => + personStates(personId) = person.copy(lastTimePersonEnteredVehicle = time.toInt, currentVehicle = "") + val persons = vehicles.getOrElse(vehicleId, mutable.Set()) + if (persons.contains(personId)) { + vehicles(vehicleId) = persons - personId + } + + case "PathTraversal" => + val length = attributes.get("length").toDouble + if (vehicleId.contains("body")) { + val personId = vehicleId.split('-')(1) + val person = personStates.getOrElse(personId, Person()) + val distanceTravelledPathTraversal = person.distanceTravelledPathTraversal + length + personStates(personId) = person.copy(distanceTravelledPathTraversal = distanceTravelledPathTraversal) + } else { + val persons = vehicles.getOrElse(vehicleId, mutable.Set()) + persons.foreach(personId => { + val person = personStates.getOrElse(personId, Person()) + val distanceTravelledPathTraversal = person.distanceTravelledPathTraversal + length + personStates(personId) = person.copy(distanceTravelledPathTraversal = distanceTravelledPathTraversal) + }) + } + + case _ => + } + } + csvWriter.close() + } + + def distance(x1: Double, y1: Double, x2: Double, y2: Double): Double = + math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2)) + def linkCenter(from: Coord, to: Coord): (Double, Double) = ((from.getX + to.getX) / 2, (from.getY + to.getY) / 2) +} + +case class Person( + lastTimePersonEnteredVehicle: Double = -1, + lastTimePersonLeftVehicle: Double = -1, + lastTimePersonEnteredBodyVehicle: Double = -1, + lastActEndTime: Double = -1, + numberOfReplannings: Int = 0, + distanceTravelledPathTraversal: Double = 0, + durationTripBasedOnPathTraversal: Int = 0, + last_act_x: Double = 0, + last_act_y: Double = 0, + legId: Int = 0, + currentMode: String = "", + previousActType: String = "", + firstPathTraversalDepartureTime: Int = -1, + lastPathTraversalArrivalTime: Int = -1, + currentVehicle: String = "" +) diff --git a/src/main/scala/beam/utils/scenario/UrbanSimScenarioLoader.scala b/src/main/scala/beam/utils/scenario/UrbanSimScenarioLoader.scala index 82351a636a6..1b8e8884d6b 100644 --- a/src/main/scala/beam/utils/scenario/UrbanSimScenarioLoader.scala +++ b/src/main/scala/beam/utils/scenario/UrbanSimScenarioLoader.scala @@ -16,12 +16,11 @@ import org.matsim.core.scenario.MutableScenario import org.matsim.households._ import org.matsim.vehicles.{Vehicle, VehicleType, VehicleUtils} +import scala.collection.Iterable import scala.collection.JavaConverters._ import scala.collection.mutable.ArrayBuffer - import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration._ - import scala.util.Random class UrbanSimScenarioLoader( @@ -70,7 +69,7 @@ class UrbanSimScenarioLoader( logger.info(s"There are ${householdsWithMembers.size} non-empty households") logger.info("Applying households...") - applyHousehold(householdsWithMembers, householdIdToPersons) + applyHousehold(householdsWithMembers, householdIdToPersons, plans) // beamServices.privateVehicles is properly populated here, after `applyHousehold` call // beamServices.personHouseholds is used later on in PopulationAdjustment.createAttributesOfIndividual when we @@ -105,9 +104,72 @@ class UrbanSimScenarioLoader( Seq.fill(nTrials)(randomSeed.nextDouble).count(_ < p) } + private def getPersonScore(personInfo: PersonInfo, personTravelStats: PersonTravelStats): Double = { + val distanceExcludingLastTrip = + personTravelStats.tripStats.dropRight(1).map(x => geo.distUTMInMeters(x.origin, x.destination)).sum + val tripTimePenalty = personTravelStats.tripStats + .map( + x => + if (x.departureTime < 6.0) { + 5000.0 + } else if (x.departureTime > 23.5) { + 5000.0 + } else { + 0.0 + } + ) + .sum + distanceExcludingLastTrip + tripTimePenalty + } + + case class PlanTripStats( + departureTime: Double, + origin: Coord, + destination: Coord + ) + + case class PersonTravelStats( + homeLocation: Option[Coord], + tripStats: Seq[PlanTripStats] + ) + + private def plansToTravelStats(planElements: Iterable[PlanElement]): PersonTravelStats = { + val homeCoord = planElements.find(_.activityType.getOrElse("") == "Home") match { + case Some(homeElement) => + Some(geo.wgs2Utm(new Coord(homeElement.activityLocationX.get, homeElement.activityLocationY.get))) + case None => + None + } + val planTripStats = planElements.toSeq + .filter(_.planElementType == "activity") + .sliding(2) + .flatMap { + case Seq(firstElement, secondElement, _*) => + Some( + PlanTripStats( + firstElement.activityEndTime.getOrElse(0.0), + geo.wgs2Utm( + new Coord(firstElement.activityLocationX.getOrElse(0.0), firstElement.activityLocationY.getOrElse(0.0)) + ), + geo.wgs2Utm( + new Coord( + secondElement.activityLocationX.getOrElse(0.0), + secondElement.activityLocationY.getOrElse(0.0) + ) + ) + ) + ) + case _ => + None + } + .toSeq + PersonTravelStats(homeCoord, planTripStats) + } + private[utils] def applyHousehold( households: Iterable[HouseholdInfo], - householdIdToPersons: Map[HouseholdId, Iterable[PersonInfo]] + householdIdToPersons: Map[HouseholdId, Iterable[PersonInfo]], + plans: Iterable[PlanElement] ): Unit = { val scenarioHouseholdAttributes = scenario.getHouseholds.getHouseholdAttributes val scenarioHouseholds = scenario.getHouseholds.getHouseholds @@ -115,6 +177,16 @@ class UrbanSimScenarioLoader( var vehicleCounter: Int = 0 var initialVehicleCounter: Int = 0 var totalCarCount: Int = 0 + val personIdToTravelStats: Map[PersonId, PersonTravelStats] = + plans + .groupBy(_.personId) + .map(x => (x._1, plansToTravelStats(x._2))) + + val householdIdToPersonScore: Map[HouseholdId, Iterable[(PersonId, Double)]] = + householdIdToPersons.map { + case (hhId, persons) => + (hhId, persons.map(x => (x.personId, getPersonScore(x, personIdToTravelStats(x.personId))))) + } val scaleFactor = beamScenario.beamConfig.beam.agentsim.agents.vehicles.fractionOfInitialVehicleFleet @@ -122,7 +194,7 @@ class UrbanSimScenarioLoader( val realDistribution: UniformRealDistribution = new UniformRealDistribution() realDistribution.reseedRandomGenerator(beamScenario.beamConfig.matsim.modules.global.randomSeed) - assignVehicles(households).foreach { + assignVehicles(households, householdIdToPersons, householdIdToPersonScore).foreach { case (householdInfo, nVehicles) => val id = Id.create(householdInfo.householdId.id, classOf[org.matsim.households.Household]) val household = new HouseholdsFactoryImpl().createHousehold(id) @@ -186,40 +258,111 @@ class UrbanSimScenarioLoader( ) } - // Iterable[(HouseholdInfo, List[BeamVehicleType])] - - private def assignVehicles(households: Iterable[HouseholdInfo]): Iterable[(HouseholdInfo, Int)] = { + private def assignVehicles( + households: Iterable[HouseholdInfo], + householdIdToPersons: Map[HouseholdId, Iterable[PersonInfo]], + householdIdToPersonScore: Map[HouseholdId, Iterable[(PersonId, Double)]] + ): Iterable[(HouseholdInfo, Int)] = { beamScenario.beamConfig.beam.agentsim.agents.vehicles.downsamplingMethod match { case "SECONDARY_VEHICLES_FIRST" => + val numberOfWorkersWithVehicles = + households.map(x => math.min(x.cars, householdIdToPersons(x.householdId).size)).sum val rand = new Random(beamScenario.beamConfig.matsim.modules.global.randomSeed) - val hh_car_count = collection.mutable.Map(households.groupBy(_.cars).toSeq: _*) + val hh_car_count = + collection.mutable.Map(collection.mutable.ArrayBuffer(households.toSeq: _*).groupBy(_.cars).toSeq: _*) val totalCars = households.foldLeft(0)(_ + _.cars) + val goalCarTotal = math .round(beamScenario.beamConfig.beam.agentsim.agents.vehicles.fractionOfInitialVehicleFleet * totalCars) .toInt + val numberOfWorkVehiclesToBeRemoved = math.max(numberOfWorkersWithVehicles - goalCarTotal, 0) + val numberOfExcessVehiclesToBeRemoved = totalCars - goalCarTotal - numberOfWorkVehiclesToBeRemoved + val personsToGetCarsRemoved = households + .flatMap( + x => + householdIdToPersonScore(x.householdId).toSeq + .sortBy(_._2) + .takeRight(x.cars) // for each household, assign vehicles to the people with the highest commute distances + ) + .toSeq + .sortBy(_._2) // sort all people with assigned cars by commute distance + .map(_._1) + .take(numberOfWorkVehiclesToBeRemoved) // Take the people with shortest commutes and remove their cars + .toSet + logger.info( + s"Identified $numberOfWorkVehiclesToBeRemoved household vehicles with short commutes and $numberOfExcessVehiclesToBeRemoved excess vehicles to be removed" + ) + val householdIdToPersonToHaveVehicleRemoved = householdIdToPersons + .map(x => x._2.map(y => (x._1, y))) + .flatten + .filter(x => personsToGetCarsRemoved.contains(x._2.personId)) + .groupBy(_._1) + var currentTotalCars = totalCars - hh_car_count.keys.toSeq.sorted.reverse.foreach { key => - if (currentTotalCars > goalCarTotal) { - if (currentTotalCars - hh_car_count(key).size > goalCarTotal) { - currentTotalCars -= hh_car_count(key).size - hh_car_count(key - 1) ++= hh_car_count(key) - hh_car_count -= key + hh_car_count.keys.toSeq.sorted.reverse.foreach { key => // start with households with the most vehicles + if ((currentTotalCars > (goalCarTotal + numberOfWorkVehiclesToBeRemoved)) & key > 0) { + val numberOfHouseholdsWithThisManyVehicles = hh_car_count(key).size + + val (householdsWithExcessVehicles, householdsWithCorrectNumberOfVehicles) = + hh_car_count(key).partition(x => key > householdIdToPersons(x.householdId).size) + val numberOfExcessVehicles = householdsWithExcessVehicles.size + logger.info( + s"Identified $numberOfExcessVehicles excess vehicles from the $numberOfHouseholdsWithThisManyVehicles households with $key vehicles" + ) + if (currentTotalCars - numberOfExcessVehicles > goalCarTotal) { + logger.info( + s"Removing all $numberOfExcessVehicles excess vehicles" + ) + currentTotalCars -= numberOfExcessVehicles + hh_car_count(key - 1) ++= householdsWithExcessVehicles + hh_car_count(key) = householdsWithCorrectNumberOfVehicles } else { - val householdsInGroup = hh_car_count(key).size + val householdsInGroup = householdsWithExcessVehicles.size val numberToRemain = householdsInGroup - (currentTotalCars - goalCarTotal) - val shuffled = rand.shuffle(hh_car_count(key)) - hh_car_count(key) = shuffled.take(numberToRemain) + logger.info( + s"Removing all but $numberToRemain of the $numberOfExcessVehicles excess vehicles" + ) + val shuffled = rand.shuffle(householdsWithExcessVehicles) + hh_car_count(key) = shuffled.take(numberToRemain) ++ householdsWithCorrectNumberOfVehicles hh_car_count(key - 1) ++= shuffled.takeRight(householdsInGroup - numberToRemain) - currentTotalCars -= (currentTotalCars - goalCarTotal) + currentTotalCars -= (householdsInGroup - numberToRemain) } } } + logger.info( + s"Currently $currentTotalCars are left, $numberOfWorkVehiclesToBeRemoved work vehicles are yet to be removed" + ) + hh_car_count.keys.toSeq.sorted.foreach { key => + if (key > 0) { + val initialNumberOfHouseholds = hh_car_count(key).size + hh_car_count(key) = hh_car_count(key).filter( + hh => + householdIdToPersonToHaveVehicleRemoved.get(hh.householdId) match { + case Some(personIdsToRemove) => + hh_car_count(key - personIdsToRemove.size) ++= Iterable(hh) + currentTotalCars -= personIdsToRemove.size + false + case None => + true + } + ) + val nRemoved = initialNumberOfHouseholds - hh_car_count(key).size + logger.info( + s"Originally had $initialNumberOfHouseholds work vehicles from households with $key workers, removed vehicles from $nRemoved of them" + ) + } + } val householdsOut = ArrayBuffer[HouseholdInfo]() val nVehiclesOut = ArrayBuffer[Int]() - hh_car_count.toSeq.foreach { hhGroup => - householdsOut ++= hhGroup._2 - nVehiclesOut ++= ArrayBuffer.fill(hhGroup._2.size)(hhGroup._1) + hh_car_count.toSeq.foreach { + case (nVehicles, householdIds) => + householdsOut ++= householdIds + nVehiclesOut ++= ArrayBuffer.fill(householdIds.size)(nVehicles) } + val totalVehiclesOut = nVehiclesOut.sum + logger.info( + s"Ended up with $totalVehiclesOut vehicles" + ) householdsOut.zip(nVehiclesOut) case "RANDOM" => val rand = new Random(beamScenario.beamConfig.matsim.modules.global.randomSeed) diff --git a/src/test/java/beam/analysis/physsim/PhyssimCalcLinkStatsTest.java b/src/test/java/beam/analysis/physsim/PhyssimCalcLinkStatsTest.java index 3c814d1e890..697ca8da067 100755 --- a/src/test/java/beam/analysis/physsim/PhyssimCalcLinkStatsTest.java +++ b/src/test/java/beam/analysis/physsim/PhyssimCalcLinkStatsTest.java @@ -59,7 +59,7 @@ public static void createDummySimWithXML() { @Test public void testShouldPassShouldReturnCountRelativeSpeedOfSpecificHour() { - Double expectedResult = 10.0; + Double expectedResult = 7.0; Double actualResult = physsimCalcLinkStats.getRelativeSpeedOfSpecificHour(0, 7); assertEquals(expectedResult, actualResult); } @@ -77,7 +77,7 @@ public void testShouldPassShouldReturnCountOfAllRelativeSpeedCategoryForSpecific @Test public void testShouldPassShouldReturnSumOfRelativeSpeedForSpecificHour() { - Double expectedResult = 148.0; + Double expectedResult = 111.0; Double actualResult = physsimCalcLinkStats.getRelativeSpeedCountOfSpecificCategory(0); assertEquals(expectedResult, actualResult); } diff --git a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala index ddfc3e3c3f0..173f67166e3 100644 --- a/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala +++ b/src/test/scala/beam/agentsim/agents/PersonAgentSpec.scala @@ -125,7 +125,6 @@ class PersonAgentSpec watch(personAgentRef) scheduler ! ScheduleTrigger(InitializeTrigger(0), personAgentRef) scheduler ! StartSchedule(0) - expectTerminated(personAgentRef) expectMsg(CompletionNotice(0, Vector())) } diff --git a/src/test/scala/beam/agentsim/agents/PersonAndTransitDriverSpec.scala b/src/test/scala/beam/agentsim/agents/PersonAndTransitDriverSpec.scala index a820acea80b..b06840ddae7 100644 --- a/src/test/scala/beam/agentsim/agents/PersonAndTransitDriverSpec.scala +++ b/src/test/scala/beam/agentsim/agents/PersonAndTransitDriverSpec.scala @@ -65,7 +65,7 @@ class PersonAndTransitDriverSpec .withFallback(testConfig("test/input/beamville/beam.conf")) .resolve() - lazy implicit val system: ActorSystem = ActorSystem("PersonWithVehicleSharingSpec", config) + lazy implicit val system: ActorSystem = ActorSystem("PersonAndTransitDriverSpec", config) override def outputDirPath: String = TestConfigUtils.testOutputDir diff --git a/src/test/scala/beam/agentsim/agents/RideHailAgentSpec.scala b/src/test/scala/beam/agentsim/agents/RideHailAgentSpec.scala index 68c7c97fdf4..baa0a830078 100755 --- a/src/test/scala/beam/agentsim/agents/RideHailAgentSpec.scala +++ b/src/test/scala/beam/agentsim/agents/RideHailAgentSpec.scala @@ -53,7 +53,7 @@ class RideHailAgentSpec .withFallback(testConfig("test/input/beamville/beam.conf")) .resolve() - lazy implicit val system: ActorSystem = ActorSystem("PersonWithPersonalVehiclePlanSpec", config) + lazy implicit val system: ActorSystem = ActorSystem("RideHailAgentSpec", config) override def outputDirPath: String = TestConfigUtils.testOutputDir @@ -128,7 +128,7 @@ class RideHailAgentSpec ) ) ) - rideHailAgent ! Interrupt(Id.create("1", classOf[Interrupt]), 30000) + rideHailAgent ! Interrupt(1, 30000) expectMsgType[InterruptedWhileIdle] rideHailAgent ! ModifyPassengerSchedule(passengerSchedule, 30000) rideHailAgent ! Resume @@ -182,7 +182,7 @@ class RideHailAgentSpec // Now I want to interrupt the agent, and it will say that for any point in time after 28800, // I can tell it whatever I want. Even though it is already 30000 for me. - rideHailAgent ! Interrupt(Id.create("1", classOf[Interrupt]), 30000) + rideHailAgent ! Interrupt(1, 30000) val interruptedAt = expectMsgType[InterruptedWhileDriving] assert(interruptedAt.currentPassengerScheduleIndex == 0) // I know this agent hasn't picked up the passenger yet assert(rideHailAgent.stateName == DrivingInterrupted) @@ -258,7 +258,7 @@ class RideHailAgentSpec // Now I want to interrupt the agent, and it will say that for any point in time after 28800, // I can tell it whatever I want. Even though it is already 30000 for me. - rideHailAgent ! Interrupt(Id.create("1", classOf[Interrupt]), 30000) + rideHailAgent ! Interrupt(1, 30000) val interruptedAt = expectMsgType[InterruptedWhileDriving] assert(interruptedAt.currentPassengerScheduleIndex == 0) // I know this agent hasn't picked up the passenger yet assert(rideHailAgent.stateName == DrivingInterrupted) @@ -341,7 +341,7 @@ class RideHailAgentSpec t } - rideHailAgent ! Interrupt(Id.create("1", classOf[Interrupt]), 30000) + rideHailAgent ! Interrupt(1, 30000) val interruptedAt = expectMsgType[InterruptedWhileDriving] assert(interruptedAt.currentPassengerScheduleIndex == 1) // I know this agent has now picked up the passenger assert(rideHailAgent.stateName == DrivingInterrupted) diff --git a/src/test/scala/beam/integration/AgentsimWithMaximallyBadRouterSpec.scala b/src/test/scala/beam/integration/AgentsimWithMaximallyBadRouterSpec.scala index d811547bfe6..2a84387493e 100755 --- a/src/test/scala/beam/integration/AgentsimWithMaximallyBadRouterSpec.scala +++ b/src/test/scala/beam/integration/AgentsimWithMaximallyBadRouterSpec.scala @@ -38,7 +38,7 @@ class AgentsimWithMaximallyBadRouterSpec def outputDirPath: String = basePath + "/" + testOutputDir + "bad-router-test" - lazy implicit val system: ActorSystem = ActorSystem("AgentSimWithBadRouterSpec", config) + lazy implicit val system: ActorSystem = ActorSystem("AgentsimWithMaximallyBadRouterSpec", config) "The agentsim" must { "not get stuck even if the router only throws exceptions" in { diff --git a/src/test/scala/beam/integration/BikeTransitModeSpec.scala b/src/test/scala/beam/integration/BikeTransitModeSpec.scala new file mode 100644 index 00000000000..0ceaa245b4c --- /dev/null +++ b/src/test/scala/beam/integration/BikeTransitModeSpec.scala @@ -0,0 +1,95 @@ +package beam.integration + +import akka.actor._ +import akka.testkit.TestKitBase +import beam.agentsim.agents.PersonTestUtil +import beam.agentsim.agents.ridehail.{RideHailIterationHistory, RideHailSurgePricingManager} +import beam.router.Modes.BeamMode +import beam.router.{BeamSkimmer, RouteHistory, TravelTimeObserved} +import beam.sflight.RouterForTest +import beam.sim.common.GeoUtilsImpl +import beam.sim.{BeamHelper, BeamMobsim} +import beam.utils.SimRunnerForTest +import beam.utils.TestConfigUtils.testConfig +import com.typesafe.config.ConfigFactory +import org.matsim.api.core.v01.events.{ActivityEndEvent, Event, PersonDepartureEvent, PersonEntersVehicleEvent} +import org.matsim.api.core.v01.population.{Activity, Leg} +import org.matsim.core.events.handler.BasicEventHandler +import org.scalatest._ + +import scala.collection.JavaConverters._ +import scala.collection.mutable +import scala.language.postfixOps + +class BikeTransitModeSpec + extends WordSpecLike + with TestKitBase + with SimRunnerForTest + with RouterForTest + with BeamHelper + with Matchers { + + def config: com.typesafe.config.Config = + ConfigFactory + .parseString("""akka.test.timefactor = 10""") + .withFallback(testConfig("test/input/sf-light/sf-light.conf").resolve()) + + def outputDirPath: String = basePath + "/" + testOutputDir + "transit-mode-test" + + lazy implicit val system: ActorSystem = ActorSystem("BikeTransitModeSpec", config) + + "The agentsim" must { + "let everybody take bike_transit when their plan says so" in { + scenario.getPopulation.getPersons.values.asScala + .foreach(p => PersonTestUtil.putDefaultBeamAttributes(p, BeamMode.allModes)) + scenario.getPopulation.getPersons + .values() + .forEach { person => + person.getSelectedPlan.getPlanElements.asScala.collect { + case leg: Leg => + leg.setMode("bike_transit") + } + } + val events = mutable.ListBuffer[Event]() + services.matsimServices.getEvents.addHandler( + new BasicEventHandler { + override def handleEvent(event: Event): Unit = { + event match { + case event: PersonDepartureEvent => + events += event + case _ => + } + } + } + ) + val mobsim = new BeamMobsim( + services, + beamScenario, + beamScenario.transportNetwork, + services.tollCalculator, + scenario, + services.matsimServices.getEvents, + system, + new RideHailSurgePricingManager(services), + new RideHailIterationHistory(), + new RouteHistory(services.beamConfig), + new BeamSkimmer(beamScenario, services.geo), + new TravelTimeObserved(beamScenario, services.geo), + new GeoUtilsImpl(services.beamConfig), + services.networkHelper + ) + mobsim.run() + + assert(events.nonEmpty) + var seenEvent = false + events.foreach { + case event: PersonDepartureEvent => + assert( + event.getLegMode == "walk" || event.getLegMode == "walk_transit" || event.getLegMode == "be_a_tnc_driver" || event.getLegMode == "be_a_household_cav_driver" || event.getLegMode == "be_a_transit_driver" || event.getLegMode == "cav" + ) + seenEvent = true + } + assert(seenEvent, "Have not seen `PersonDepartureEvent`") + } + } +} diff --git a/src/test/scala/beam/sim/BeamWarmStartRunSpec.scala b/src/test/scala/beam/sim/BeamWarmStartRunSpec.scala new file mode 100644 index 00000000000..cb7ee18905d --- /dev/null +++ b/src/test/scala/beam/sim/BeamWarmStartRunSpec.scala @@ -0,0 +1,60 @@ +package beam.sim + +import beam.analysis.plots.PersonTravelTimeAnalysis +import beam.utils.FileUtils +import beam.utils.TestConfigUtils.testConfig +import com.typesafe.config.ConfigFactory +import org.matsim.core.controler.OutputDirectoryHierarchy +import org.scalatest.{BeforeAndAfterAllConfigMap, Matchers, WordSpecLike} + +import scala.io.Source + +class BeamWarmStartRunSpec extends WordSpecLike with Matchers with BeamHelper with BeforeAndAfterAllConfigMap { + + "Beam WarmStart" must { + "run sf-light scenario for two iteration with warmstart" in { + + val baseConf = ConfigFactory + .parseString(s""" + |beam.agentsim.lastIteration = 1 + |beam.warmStart.enabled = true + |beam.agentsim.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds = 0 + |beam.warmStart.path = test/input/sf-light/warmstart + """.stripMargin) + .withFallback(testConfig("test/input/sf-light/sf-light.conf")) + .resolve() + + val (_, output) = runBeamWithConfig(baseConf) + val averageCarSpeedIt0 = BeamWarmStartRunSpec.avgCarModeFromCsv(extractFileName(output, 0)) + val averageCarSpeedIt1 = BeamWarmStartRunSpec.avgCarModeFromCsv(extractFileName(output, 1)) + averageCarSpeedIt0 should equal(5.9 +- 1.6) + averageCarSpeedIt1 should equal(5.9 +- 1.6) + + } + } + + private def extractFileName(outputDir: String, iterationNumber: Int): String = { + val outputDirectoryHierarchy = + new OutputDirectoryHierarchy(outputDir, OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles) + + outputDirectoryHierarchy.getIterationFilename(iterationNumber, PersonTravelTimeAnalysis.fileBaseName + ".csv") + } + +} + +object BeamWarmStartRunSpec { + + def avgCarModeFromCsv(filePath: String): Double = { + val carLine = FileUtils.using(Source.fromFile(filePath)) { source => + source.getLines().find(_.startsWith("car")) + } + val allHourAvg = carLine + .getOrElse(throw new IllegalStateException("The line does not contain 'car' as TravelTimeMode")) + .split(",") + .tail + .map(_.toDouble) + + val relevantTimes = allHourAvg.filterNot(_ == 0D) + relevantTimes.sum / relevantTimes.length + } +} diff --git a/src/test/scala/beam/utils/SpatialIndexForRideHailAgentLocationTest.scala b/src/test/scala/beam/utils/SpatialIndexForRideHailAgentLocationTest.scala new file mode 100644 index 00000000000..115acd96f80 --- /dev/null +++ b/src/test/scala/beam/utils/SpatialIndexForRideHailAgentLocationTest.scala @@ -0,0 +1,73 @@ +package beam.utils + +import beam.agentsim.agents.ridehail.RideHailVehicleManager.RideHailAgentLocation +import beam.agentsim.agents.vehicles.{BeamVehicleType, FuelType, VehicleCategory} +import beam.agentsim.events.SpaceTime +import beam.sim.Geofence +import org.matsim.api.core.v01.Id +import org.matsim.core.utils.collections.QuadTree +import org.scalatest.{FunSuite, Matchers} + +import scala.collection.JavaConverters._ + +class SpatialIndexForRideHailAgentLocationTest extends FunSuite with Matchers { + // ##################################################################################### + // If one day this test breaks then most probably it is because of the changes in `RideHailAgentLocation.equals` + // ##################################################################################### + test("Should not allow to put multiple RideHailAgentLocation with the same vehicleId") { + val spatialIndex = new QuadTree[RideHailAgentLocation](0, 0, 1000, 100) + val rideHailAgentLocation = RideHailAgentLocation( + null, + Id.createVehicleId("1"), + BeamVehicleType( + Id.create("car", classOf[BeamVehicleType]), + 1, + 1, + 3, + FuelType.Gasoline, + 0.1, + 0.1, + vehicleCategory = VehicleCategory.Car + ), + SpaceTime(1, 1, 1) + ) + + spatialIndex.put(10, 10, rideHailAgentLocation) + spatialIndex.size() shouldBe (1) + + val updatedRideHailAgentLocation = rideHailAgentLocation.copy(currentLocationUTM = SpaceTime(2, 2, 4)) + spatialIndex.put(10, 10, updatedRideHailAgentLocation) + spatialIndex.size() shouldBe (1) + + spatialIndex.getDisk(0, 0, 20).asScala.head shouldBe (rideHailAgentLocation) + } + + test("Remove should respect only `RideHailAgentLocation.vehicleId`") { + // If one day this test breaks it that `RideHailAgentLocation.equals` has been changed + val spatialIndex = new QuadTree[RideHailAgentLocation](0, 0, 1000, 100) + val rideHailAgentLocation = RideHailAgentLocation( + null, + Id.createVehicleId("1"), + BeamVehicleType( + Id.create("car", classOf[BeamVehicleType]), + 1, + 1, + 3, + FuelType.Gasoline, + 0.1, + 0.1, + vehicleCategory = VehicleCategory.Car + ), + SpaceTime(1, 1, 1) + ) + spatialIndex.put(10, 10, rideHailAgentLocation) + + val updatedRideHailAgentLocation = rideHailAgentLocation.copy( + currentLocationUTM = SpaceTime(2, 2, 4), + vehicleType = rideHailAgentLocation.vehicleType.copy(seatingCapacity = 123), + geofence = Some(Geofence(1, 2, 3)) + ) + spatialIndex.remove(10, 10, updatedRideHailAgentLocation) + spatialIndex.size() shouldBe (0) + } +} diff --git a/test/input/beamville/beam.conf b/test/input/beamville/beam.conf index 73199612b25..73d7998777a 100755 --- a/test/input/beamville/beam.conf +++ b/test/input/beamville/beam.conf @@ -49,7 +49,7 @@ beam.agentsim.agents.vehicles.fuelTypesFilePath = ${beam.inputDirectory}"/beamFu beam.agentsim.agents.vehicles.vehicleTypesFilePath = ${beam.inputDirectory}"/vehicleTypes.csv" beam.agentsim.agents.vehicles.vehiclesFilePath = ${beam.inputDirectory}"/vehicles.csv" beam.agentsim.agents.vehicles.sharedFleets = [] -# Population Adjustmnet (DEFAULT_ADJUSTMENT | PERCENTAGE_ADJUSTMENT) +# Population Adjustment (DEFAULT_ADJUSTMENT | PERCENTAGE_ADJUSTMENT) beam.agentsim.populationAdjustment="DEFAULT_ADJUSTMENT" #Toll params beam.agentsim.toll.filePath=${beam.inputDirectory}"/toll-prices.csv" diff --git a/test/input/beamville/calibration/benchmark.csv b/test/input/beamville/calibration/benchmark.csv index b4174743619..bc633e8568f 100755 --- a/test/input/beamville/calibration/benchmark.csv +++ b/test/input/beamville/calibration/benchmark.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bea5b71ff90bc63760f76b0545a12cc6b60b1de43d4b717b2b7ea631319dccb -size 153 +oid sha256:cc07bb3c41c74448db6facf23606caf94e20d8d1ead0ab0a200353449b67947f +size 170 diff --git a/test/input/sf-light/parking-with-chargers/rh-parking.csv b/test/input/sf-light/parking-with-chargers/rh-parking.csv new file mode 100644 index 00000000000..95f59bec259 --- /dev/null +++ b/test/input/sf-light/parking-with-chargers/rh-parking.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37695ed268c292a22f7b94435ae5664a0b85435e5290353e219d31a612d986ee +size 50030 diff --git a/test/input/sf-light/parking-with-chargers/taz-parking-1.0FracCharger.csv b/test/input/sf-light/parking-with-chargers/taz-parking-1.0FracCharger.csv index 94da4caaf26..995475b7fef 100644 --- a/test/input/sf-light/parking-with-chargers/taz-parking-1.0FracCharger.csv +++ b/test/input/sf-light/parking-with-chargers/taz-parking-1.0FracCharger.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08a062e96f036ec8ed0049ea6b7d81ab6a83ec82f06e674b872bd99f350975f6 +oid sha256:d0f267d45c1a1605cc94dd6e17d7cedfeeea93e6daa97ce94a1b02aad395a15e size 1675938 diff --git a/test/input/sf-light/sf-light-25k.conf b/test/input/sf-light/sf-light-25k.conf index 2affe6ff9d4..68eb598a2e9 100755 --- a/test/input/sf-light/sf-light-25k.conf +++ b/test/input/sf-light/sf-light-25k.conf @@ -11,7 +11,7 @@ include "../common/matsim.conf" beam.agentsim.simulationName = "sf-light-25k-xml" beam.agentsim.agentSampleSizeAsFractionOfPopulation = 1.0 beam.agentsim.firstIteration = 0 -beam.agentsim.lastIteration = 40 +beam.agentsim.lastIteration = 1 beam.agentsim.thresholdForWalkingInMeters = 100 beam.agentsim.timeBinSize = 3600 beam.agentsim.startTime = "00:00:00" @@ -130,8 +130,8 @@ beam.agentsim.agents.rideHail.surgePricing.minimumSurgeLevel = 0.1 # priceAdjustmentStrategy(KEEP_PRICE_LEVEL_FIXED_AT_ONE | CONTINUES_DEMAND_SUPPLY_MATCHING) beam.agentsim.agents.rideHail.surgePricing.priceAdjustmentStrategy="KEEP_PRICE_LEVEL_FIXED_AT_ONE" # allocationManager(DEFAULT_MANAGER | STANFORD_V1 | BUFFERED_IMPL_TEMPLATE) -beam.agentsim.agents.rideHail.allocationManager.name="DEFAULT_MANAGER" -beam.agentsim.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds = 200 +beam.agentsim.agents.rideHail.allocationManager.name="POOLING_ALONSO_MORA" +beam.agentsim.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds = 300 # repositioningManager can be DEFAULT_REPOSITIONING_MANAGER | DEMAND_FOLLOWING_REPOSITIONING_MANAGER | REPOSITIONING_LOW_WAITING_TIMES beam.agentsim.agents.rideHail.repositioningManager.name="DEFAULT_REPOSITIONING_MANAGER" beam.agentsim.agents.rideHail.repositioningManager.timeout=300 diff --git a/test/input/sf-light/sf-light-calibration/benchmark.csv b/test/input/sf-light/sf-light-calibration/benchmark.csv index b1986bd7917..064a470460a 100755 --- a/test/input/sf-light/sf-light-calibration/benchmark.csv +++ b/test/input/sf-light/sf-light-calibration/benchmark.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0298000b2b943cb0ed5a14de6b8ec4410319a25f6fc9eb87b15455891820242 -size 149 +oid sha256:0ccb18ebe9749432f4ca9fd68f249929b8bf5a31f5c9449fc7a0b2d751780f01 +size 166 diff --git a/test/input/sf-light/urbansim-1k-rh-ev-cavs.conf b/test/input/sf-light/urbansim-1k-rh-ev-cavs.conf new file mode 100755 index 00000000000..b5ec0a2a141 --- /dev/null +++ b/test/input/sf-light/urbansim-1k-rh-ev-cavs.conf @@ -0,0 +1,318 @@ +include "../common/akka.conf" +include "../common/akka-router.conf" +include "../common/metrics.conf" +include "../common/matsim.conf" + +# This version, base-sf-light.conf, is configured to use a subsample of the population located in: +# ${beam.inputDirectory}"/sample" +################################################################## +# Agentsim +################################################################## +beam.agentsim.simulationName = "urbansim-1k" +beam.agentsim.agentSampleSizeAsFractionOfPopulation = 1.0 +beam.agentsim.firstIteration = 0 +beam.agentsim.lastIteration = 0 +beam.agentsim.thresholdForWalkingInMeters = 100 +beam.agentsim.timeBinSize = 3600 +beam.agentsim.startTime = "00:00:00" +beam.agentsim.endTime = "30:00:00" +beam.agentsim.schedulerParallelismWindow = 30 +# MODE CHOICE OPTIONS: +# ModeChoiceMultinomialLogit ModeChoiceTransitIfAvailable ModeChoiceDriveIfAvailable ModeChoiceRideHailIfAvailable +# ModeChoiceUniformRandom +beam.agentsim.agents.modalBehaviors.modeChoiceClass = "ModeChoiceMultinomialLogit" +beam.agentsim.agents.modalBehaviors.defaultValueOfTime = 8.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.transfer = -1.4 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.car_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.walk_transit_intercept = 2.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.drive_transit_intercept = 2.5 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_transit_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_pooled_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.walk_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.bike_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.lccm.filePath = ${beam.inputDirectory}"/lccm-long.csv" +beam.agentsim.agents.modeIncentive.filePath = ${beam.inputDirectory}"/incentives.csv" +beam.agentsim.agents.ptFare.filePath = ${beam.inputDirectory}"/ptFares.csv" +beam.agentsim.agents.plans { + inputPlansFilePath = ${beam.inputDirectory}"/sample/1k/population.xml.gz" + inputPersonAttributesFilePath = ${beam.inputDirectory}"/sample/1k/populationAttributes.xml.gz" +} +beam.agentsim.agents.households { + inputFilePath = ${beam.inputDirectory}"/sample/1k/households.xml.gz" + inputHouseholdAttributesFilePath = ${beam.inputDirectory}"/sample/1k/householdAttributes.xml.gz" +} +beam.exchange.scenario { + # source for now can be "MATSim" or "UrbanSim" + source = "UrbanSim" + # Input file format for scenario loader can be "csv" or "parquet" + fileFormat = "csv" + folder = ${beam.inputDirectory}"/urbansim/1k/" + convertWgs2Utm = true +} +#BeamVehicles Params +beam.agentsim.agents.population.useVehicleSampling = true +beam.agentsim.agents.vehicles.vehicleAdjustmentMethod = "INCOME_BASED" +beam.agentsim.agents.vehicles.linkToGradePercentFilePath = ${beam.inputDirectory}"/r5/linkToGradePercent.csv" +beam.agentsim.agents.vehicles.fuelTypesFilePath = ${beam.inputDirectory}"/beamFuelTypes.csv" +beam.agentsim.agents.vehicles.vehicleTypesFilePath = ${beam.inputDirectory}"/vehicleTypes-rh-cavs.csv" +beam.agentsim.agents.vehicles.vehiclesFilePath = ${beam.inputDirectory}"/sample/1k/vehicles.csv.gz" +beam.agentsim.agents.vehicles.sharedFleets = [] +#TAZ params +beam.agentsim.taz.filePath=${beam.inputDirectory}"/taz-centers.csv.gz" +beam.agentsim.taz.parkingFilePath = ${beam.inputDirectory}"/taz-parking.csv.gz" +# SurgePricing parameters +beam.agentsim.agents.rideHail.surgePricing.surgeLevelAdaptionStep=0.1 +beam.agentsim.agents.rideHail.surgePricing.minimumSurgeLevel=0.1 +beam.agentsim.agents.rideHail.surgePricing.priceAdjustmentStrategy="KEEP_PRICE_LEVEL_FIXED_AT_ONE" +#Toll params +beam.agentsim.toll.filePath=${beam.inputDirectory}"/toll-prices.csv" +# Scaling and Tuning Params +beam.agentsim.tuning.transitCapacity = 0.5 +beam.agentsim.tuning.transitPrice = 1.0 +beam.agentsim.tuning.tollPrice = 1.0 +beam.agentsim.tuning.rideHailPrice = 1.0 +########################### +# Physsim +########################### +beam.physsim.inputNetworkFilePath = ${beam.inputDirectory}"/r5/physsim-network.xml" +beam.physsim.flowCapacityFactor = 0.01 +beam.physsim.storageCapacityFactor = 1.0 +beam.physsim.writeEventsInterval = 0 +beam.physsim.writePlansInterval = 0 +beam.physsim.writeMATSimNetwork = false +beam.physsim.linkStatsWriteInterval = 1 +beam.physsim.linkStatsBinSize = 3600 +beam.physsim.ptSampleSize = 0.03 +beam.physsim.jdeqsim.agentSimPhysSimInterfaceDebugger.enabled = false +beam.physsim.skipPhysSim = false + +########################### +# Replanning +########################### +beam.replanning{ + maxAgentPlanMemorySize = 6 + Module_1 = "SelectExpBeta" + ModuleProbability_1 = 0.7 + Module_2 = "ClearRoutes" + ModuleProbability_2 = 0.1 + Module_3 = "ClearModes" + ModuleProbability_3 = 0.1 + Module_4 = "TimeMutator" + ModuleProbability_4 = 0.0 +} + +################################################################## +# Warm Mode +################################################################## +beam.warmStart.enabled = false +#PATH TYPE OPTIONS: PARENT_RUN, ABSOLUTE_PATH +#PARENT_RUN: can be a director or zip archive of the output directory (e.g. like what get's stored on S3). We should also be able to specify a URL to an S3 output. +#ABSOLUTE_PATH: a directory that contains required warm stats files (e.g. linkstats and eventually a plans). +beam.warmStart.path = "https://s3.us-east-2.amazonaws.com/beam-outputs/run149-base__2018-06-27_20-28-26_2a2e2bd3.zip" + +################################################################## +# RideHail +################################################################## +# Initialization Type(PROCEDURAL | FILE) +beam.agentsim.agents.rideHail.initialization.initType = "PROCEDURAL" +# If PROCEDURAL, use these params +# initialization.procedural.initialLocation.name(INITIAL_RIDE_HAIL_LOCATION_HOME | INITIAL_RIDE_HAIL_LOCATION_UNIFORM_RANDOM | INITIAL_RIDE_HAIL_LOCATION_ALL_AT_CENTER | INITIAL_RIDE_HAIL_LOCATION_ALL_IN_CORNER) +beam.agentsim.agents.rideHail.initialization.procedural.initialLocation.name = "HOME" +beam.agentsim.agents.rideHail.initialization.procedural.initialLocation.home.radiusInMeters = 500 +beam.agentsim.agents.rideHail.initialization.procedural.vehicleTypeId="Car" +beam.agentsim.agents.rideHail.initialization.procedural.fractionOfInitialVehicleFleet=0.5 +# If FILE, use this param +beam.agentsim.agents.rideHail.initialization.filePath=${beam.inputDirectory}"/rideHailFleet.csv" +# Ride Hailing General Params +beam.agentsim.agents.rideHail.initialization.parking.filePath = ${beam.inputDirectory}"/parking-with-chargers/rh-parking.csv" +beam.agentsim.agents.rideHail.refuelThresholdInMeters=20000.0 +beam.agentsim.agents.rideHail.rideHailManager.radiusInMeters=10000 +beam.agentsim.agents.rideHail.iterationStats.timeBinSizeInSec=3600 +# SurgePricing parameters +beam.agentsim.agents.rideHail.surgePricing.surgeLevelAdaptionStep=0.1 +beam.agentsim.agents.rideHail.surgePricing.minimumSurgeLevel=0.1 +# priceAdjustmentStrategy(KEEP_PRICE_LEVEL_FIXED_AT_ONE | CONTINUES_DEMAND_SUPPLY_MATCHING) +beam.agentsim.agents.rideHail.surgePricing.priceAdjustmentStrategy="KEEP_PRICE_LEVEL_FIXED_AT_ONE" +# allocationManager(DEFAULT_MANAGER | POOLING_ALONSO_MORA) +beam.agentsim.agents.rideHail.allocationManager.name="POOLING_ALONSO_MORA" +beam.agentsim.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds = 100 +# repositioningManager can be DEFAULT_REPOSITIONING_MANAGER | DEMAND_FOLLOWING_REPOSITIONING_MANAGER | REPOSITIONING_LOW_WAITING_TIMES +beam.agentsim.agents.rideHail.repositioningManager.name="DEFAULT_REPOSITIONING_MANAGER" +beam.agentsim.agents.rideHail.repositioningManager.timeout=600 +# DEMAND_FOLLOWING_REPOSITIONING_MANAGER +beam.agentsim.agents.rideHail.repositioningManager.demandFollowingRepositioningManager.sensitivityOfRepositioningToDemand=1 +beam.agentsim.agents.rideHail.repositioningManager.demandFollowingRepositioningManager.numberOfClustersForDemand=30 +# REPOSITIONING_LOW_WAITING_TIMES +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.percentageOfVehiclesToReposition=0.01 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.timeWindowSizeInSecForDecidingAboutRepositioning=1200 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.allowIncreasingRadiusIfDemandInRadiusLow=true +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.minDemandPercentageInRadius=0.1 +# repositioningMethod(TOP_SCORES | KMEANS) +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.repositioningMethod="TOP_SCORES" +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.keepMaxTopNScores=5 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.minScoreThresholdForRepositioning=0.00001 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.distanceWeight=0.01 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.waitingTimeWeight=4.0 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.demandWeight=4.0 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.produceDebugImages=true +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.pickupTimeWindowInSec=600 +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.dropoffTimeWindowInSec=0.2 +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.maxRequestsPerVehicle=5 +# when range below refuelRequiredThresholdInMeters, EV Ride Hail CAVs will charge +# when range above noRefuelThresholdInMeters, EV Ride Hail CAVs will not charge +# (between these params probability of charging is linear interpolation from 0% to 100%) +beam.agentsim.agents.rideHail.cav.refuelRequiredThresholdInMeters = 16000.0 # 10 miles +beam.agentsim.agents.rideHail.cav.noRefuelThresholdInMeters = 160000.0 # 60 miles + +################################################################## +# OUTPUTS +################################################################## +# The baseOutputDirectory is the base directory where outputs will be written. The beam.agentsim.simulationName param will +# be used as the name of a sub-directory beneath the baseOutputDirectory for simulation results. +# If addTimestampToOutputDirectory == true, a timestamp will be added, e.g. "beamville_2017-12-18_16-48-57" +beam.outputs.baseOutputDirectory = ${PWD}"/output/sf-light" +beam.outputs.baseOutputDirectory = ${?BEAM_OUTPUT} +beam.outputs.addTimestampToOutputDirectory = true + +# To keep all logging params in one place, BEAM overrides MATSim params normally in the controller config module +beam.outputs.writePlansInterval = 1 +beam.outputs.writeEventsInterval = 1 +beam.outputs.writeLinkTraversalInterval = 0 +beam.outputs.writeSkimsInterval = 1 +beam.outputs.writeAnalysis = false + +# The remaining params customize how events are written to output files +beam.outputs.events.fileOutputFormats = "csv.gz" # valid options: xml(.gz) , csv(.gz), none - DEFAULT: csv.gz + +# Events Writing Logging Levels: +# Any event types not explicitly listed in overrideWritingLevels take on defaultWritingLevel +beam.outputs.events.eventsToWrite = "ActivityEndEvent,ActivityStartEvent,PersonEntersVehicleEvent,PersonLeavesVehicleEvent,ModeChoiceEvent,PathTraversalEvent,ReserveRideHailEvent,ReplanningEvent,RefuelSessionEvent,ChargingPlugInEvent,ChargingPlugOutEvent,ParkEvent,LeavingParkingEvent" +beam.outputs.stats.binSize = 3600 + +################################################################## +# Debugging +################################################################## +beam.debug.debugEnabled = true +beam.debug.actor.logDepth = 20 +beam.debug.debugActorTimerIntervalInSec = 30 + +beam.debug.stuckAgentDetection { + checkIntervalMs = 200 + checkMaxNumberOfMessagesEnabled = false + defaultTimeoutMs = 60000 + enabled = false + overallSimulationTimeoutMs = 100000 + thresholds = [ + { + actorTypeToMaxNumberOfMessages { + population = 1 + rideHailAgent = 1 + transitDriverAgent = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.InitializeTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 11 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$ActivityEndTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$ActivityStartTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$PersonDepartureTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$AlightVehicleTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$BoardVehicleTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 80, + rideHailAgent = 104, + transitDriverAgent = 114 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$EndLegTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 80, + rideHailAgent = 104, + transitDriverAgent = 114 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$StartLegTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + transitDriverAgent = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.scheduler.BeamAgentScheduler$KillTrigger" + } + ] +} + + + +################################################################## +# SPATIAL +################################################################## +beam.spatial = { + localCRS = "epsg:26910" # what crs to use for distance calculations, must be in units of meters + boundingBoxBuffer = 10000 # meters of buffer around network for defining extend of spatial indices +} + +################################################################## +# BEAM ROUTING SERVICE +################################################################## +beam.routing { + #Base local date in ISO 8061 YYYY-MM-DDTHH:MM:SS+HH:MM + transitOnStreetNetwork = true + baseDate = "2017-09-22T00:00:00-07:00" + r5 { + directory = ${beam.inputDirectory}"/r5" + # Departure window in min + departureWindow = 1.0167 + osmFile = ${beam.inputDirectory}"/r5/sf-light.osm.pbf" + osmMapdbFile = ${beam.inputDirectory}"/r5/osm.mapdb" + mNetBuilder.fromCRS = "epsg:4326" # WGS84 + mNetBuilder.toCRS = ${beam.spatial.localCRS} + } +} + +################################################################## +# Calibration +################################################################## +beam.calibration.objectiveFunction = "CountsObjectiveFunction" +beam.calibration.mode.benchmarkFilePath=${beam.inputDirectory}"/sf-light-calibration/benchmark.csv" +beam.calibration.counts { + countsScaleFactor = 10.355 + writeCountsInterval = 1 + averageCountsOverIterations = 1 + inputCountsFile = ${beam.inputDirectory}"/counts/fall_2015_wed_filtered.xml" +} + diff --git a/test/input/sf-light/urbansim-1k-rh-ev-humans.conf b/test/input/sf-light/urbansim-1k-rh-ev-humans.conf new file mode 100755 index 00000000000..430f579eded --- /dev/null +++ b/test/input/sf-light/urbansim-1k-rh-ev-humans.conf @@ -0,0 +1,320 @@ +include "../common/akka.conf" +include "../common/akka-router.conf" +include "../common/metrics.conf" +include "../common/matsim.conf" + +# This version, base-sf-light.conf, is configured to use a subsample of the population located in: +# ${beam.inputDirectory}"/sample" +################################################################## +# Agentsim +################################################################## +beam.agentsim.simulationName = "urbansim-1k" +beam.agentsim.agentSampleSizeAsFractionOfPopulation = 1.0 +beam.agentsim.firstIteration = 0 +beam.agentsim.lastIteration = 0 +beam.agentsim.thresholdForWalkingInMeters = 100 +beam.agentsim.timeBinSize = 3600 +beam.agentsim.startTime = "00:00:00" +beam.agentsim.endTime = "30:00:00" +beam.agentsim.schedulerParallelismWindow = 30 +# MODE CHOICE OPTIONS: +# ModeChoiceMultinomialLogit ModeChoiceTransitIfAvailable ModeChoiceDriveIfAvailable ModeChoiceRideHailIfAvailable +# ModeChoiceUniformRandom +beam.agentsim.agents.modalBehaviors.modeChoiceClass = "ModeChoiceMultinomialLogit" +beam.agentsim.agents.modalBehaviors.defaultValueOfTime = 8.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.transfer = -1.4 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.car_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.walk_transit_intercept = 2.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.drive_transit_intercept = 2.5 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_transit_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_intercept = 20.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.ride_hail_pooled_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.walk_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.mulitnomialLogit.params.bike_intercept = 0.0 +beam.agentsim.agents.modalBehaviors.lccm.filePath = ${beam.inputDirectory}"/lccm-long.csv" +beam.agentsim.agents.modeIncentive.filePath = ${beam.inputDirectory}"/incentives.csv" +beam.agentsim.agents.ptFare.filePath = ${beam.inputDirectory}"/ptFares.csv" +beam.agentsim.agents.plans { + inputPlansFilePath = ${beam.inputDirectory}"/sample/1k/population.xml.gz" + inputPersonAttributesFilePath = ${beam.inputDirectory}"/sample/1k/populationAttributes.xml.gz" +} +beam.agentsim.agents.households { + inputFilePath = ${beam.inputDirectory}"/sample/1k/households.xml.gz" + inputHouseholdAttributesFilePath = ${beam.inputDirectory}"/sample/1k/householdAttributes.xml.gz" +} +beam.exchange.scenario { + # source for now can be "MATSim" or "UrbanSim" + source = "UrbanSim" + # Input file format for scenario loader can be "csv" or "parquet" + fileFormat = "csv" + folder = ${beam.inputDirectory}"/urbansim/1k/" + convertWgs2Utm = true +} +#BeamVehicles Params +beam.agentsim.agents.population.useVehicleSampling = true +beam.agentsim.agents.vehicles.vehicleAdjustmentMethod = "INCOME_BASED" +beam.agentsim.agents.vehicles.linkToGradePercentFilePath = ${beam.inputDirectory}"/r5/linkToGradePercent.csv" +beam.agentsim.agents.vehicles.fuelTypesFilePath = ${beam.inputDirectory}"/beamFuelTypes.csv" +beam.agentsim.agents.vehicles.vehicleTypesFilePath = ${beam.inputDirectory}"/vehicleTypes-rh-ev-humans.csv" +beam.agentsim.agents.vehicles.vehiclesFilePath = ${beam.inputDirectory}"/sample/1k/vehicles.csv.gz" +beam.agentsim.agents.vehicles.sharedFleets = [] +#TAZ params +beam.agentsim.taz.filePath=${beam.inputDirectory}"/taz-centers.csv.gz" +beam.agentsim.taz.parkingFilePath = ${beam.inputDirectory}"/parking-with-chargers/taz-parking-1.0FracCharger.csv" +# SurgePricing parameters +beam.agentsim.agents.rideHail.surgePricing.surgeLevelAdaptionStep=0.1 +beam.agentsim.agents.rideHail.surgePricing.minimumSurgeLevel=0.1 +beam.agentsim.agents.rideHail.surgePricing.priceAdjustmentStrategy="KEEP_PRICE_LEVEL_FIXED_AT_ONE" +#Toll params +beam.agentsim.toll.filePath=${beam.inputDirectory}"/toll-prices.csv" +# Scaling and Tuning Params +beam.agentsim.tuning.transitCapacity = 0.5 +beam.agentsim.tuning.transitPrice = 1.0 +beam.agentsim.tuning.tollPrice = 1.0 +beam.agentsim.tuning.rideHailPrice = 1.0 +########################### +# Physsim +########################### +beam.physsim.inputNetworkFilePath = ${beam.inputDirectory}"/r5/physsim-network.xml" +beam.physsim.flowCapacityFactor = 0.01 +beam.physsim.storageCapacityFactor = 1.0 +beam.physsim.writeEventsInterval = 0 +beam.physsim.writePlansInterval = 0 +beam.physsim.writeMATSimNetwork = false +beam.physsim.linkStatsWriteInterval = 1 +beam.physsim.linkStatsBinSize = 3600 +beam.physsim.ptSampleSize = 0.03 +beam.physsim.jdeqsim.agentSimPhysSimInterfaceDebugger.enabled = false +beam.physsim.skipPhysSim = false + +########################### +# Replanning +########################### +beam.replanning{ + maxAgentPlanMemorySize = 6 + Module_1 = "SelectExpBeta" + ModuleProbability_1 = 0.7 + Module_2 = "ClearRoutes" + ModuleProbability_2 = 0.1 + Module_3 = "ClearModes" + ModuleProbability_3 = 0.1 + Module_4 = "TimeMutator" + ModuleProbability_4 = 0.0 +} + +################################################################## +# Warm Mode +################################################################## +beam.warmStart.enabled = false +#PATH TYPE OPTIONS: PARENT_RUN, ABSOLUTE_PATH +#PARENT_RUN: can be a director or zip archive of the output directory (e.g. like what get's stored on S3). We should also be able to specify a URL to an S3 output. +#ABSOLUTE_PATH: a directory that contains required warm stats files (e.g. linkstats and eventually a plans). +beam.warmStart.path = "https://s3.us-east-2.amazonaws.com/beam-outputs/run149-base__2018-06-27_20-28-26_2a2e2bd3.zip" + +################################################################## +# RideHail +################################################################## +# Initialization Type(PROCEDURAL | FILE) +beam.agentsim.agents.rideHail.initialization.initType = "PROCEDURAL" +# If PROCEDURAL, use these params +# initialization.procedural.initialLocation.name(INITIAL_RIDE_HAIL_LOCATION_HOME | INITIAL_RIDE_HAIL_LOCATION_UNIFORM_RANDOM | INITIAL_RIDE_HAIL_LOCATION_ALL_AT_CENTER | INITIAL_RIDE_HAIL_LOCATION_ALL_IN_CORNER) +beam.agentsim.agents.rideHail.initialization.procedural.initialLocation.name = "HOME" +beam.agentsim.agents.rideHail.initialization.procedural.initialLocation.home.radiusInMeters = 500 +beam.agentsim.agents.rideHail.initialization.procedural.vehicleTypeId="Car" +beam.agentsim.agents.rideHail.initialization.procedural.fractionOfInitialVehicleFleet=0.5 +# If FILE, use this param +beam.agentsim.agents.rideHail.initialization.filePath=${beam.inputDirectory}"/rideHailFleet.csv" +# Ride Hailing General Params +beam.agentsim.agents.rideHail.initialization.parking.filePath = ${beam.inputDirectory}"/parking-with-chargers/rh-parking.csv" +beam.agentsim.agents.rideHail.refuelThresholdInMeters=20000.0 +beam.agentsim.agents.rideHail.rideHailManager.radiusInMeters=10000 +beam.agentsim.agents.rideHail.iterationStats.timeBinSizeInSec=3600 +# SurgePricing parameters +beam.agentsim.agents.rideHail.surgePricing.surgeLevelAdaptionStep=0.1 +beam.agentsim.agents.rideHail.surgePricing.minimumSurgeLevel=0.1 +# priceAdjustmentStrategy(KEEP_PRICE_LEVEL_FIXED_AT_ONE | CONTINUES_DEMAND_SUPPLY_MATCHING) +beam.agentsim.agents.rideHail.surgePricing.priceAdjustmentStrategy="KEEP_PRICE_LEVEL_FIXED_AT_ONE" +# allocationManager(DEFAULT_MANAGER | POOLING_ALONSO_MORA) +beam.agentsim.agents.rideHail.allocationManager.name="POOLING_ALONSO_MORA" +beam.agentsim.agents.rideHail.allocationManager.requestBufferTimeoutInSeconds = 100 +# repositioningManager can be DEFAULT_REPOSITIONING_MANAGER | DEMAND_FOLLOWING_REPOSITIONING_MANAGER | REPOSITIONING_LOW_WAITING_TIMES +beam.agentsim.agents.rideHail.repositioningManager.name="DEFAULT_REPOSITIONING_MANAGER" +beam.agentsim.agents.rideHail.repositioningManager.timeout=600 +# DEMAND_FOLLOWING_REPOSITIONING_MANAGER +beam.agentsim.agents.rideHail.repositioningManager.demandFollowingRepositioningManager.sensitivityOfRepositioningToDemand=1 +beam.agentsim.agents.rideHail.repositioningManager.demandFollowingRepositioningManager.numberOfClustersForDemand=30 +# REPOSITIONING_LOW_WAITING_TIMES +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.percentageOfVehiclesToReposition=0.01 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.timeWindowSizeInSecForDecidingAboutRepositioning=1200 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.allowIncreasingRadiusIfDemandInRadiusLow=true +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.minDemandPercentageInRadius=0.1 +# repositioningMethod(TOP_SCORES | KMEANS) +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.repositioningMethod="TOP_SCORES" +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.keepMaxTopNScores=5 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.minScoreThresholdForRepositioning=0.00001 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.distanceWeight=0.01 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.waitingTimeWeight=4.0 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.demandWeight=4.0 +beam.agentsim.agents.rideHail.allocationManager.repositionLowWaitingTimes.produceDebugImages=true +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.pickupTimeWindowInSec=600 +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.dropoffTimeWindowInSec=0.2 +beam.agentsim.agents.rideHail.allocationManager.alonsoMora.maxRequestsPerVehicle=5 +# when range below refuelRequiredThresholdInMeters, EV Ride Hail CAVs will charge +# when range above noRefuelThresholdInMeters, EV Ride Hail CAVs will not charge +# (between these params probability of charging is linear interpolation from 0% to 100%) +beam.agentsim.agents.rideHail.human.refuelRequiredThresholdInMeters = 60000.0 +beam.agentsim.agents.rideHail.human.noRefuelThresholdInMeters = 160000.0 +beam.agentsim.agents.rideHail.cav.refuelRequiredThresholdInMeters = 16000.0 # 10 miles +beam.agentsim.agents.rideHail.cav.noRefuelThresholdInMeters = 160000.0 # 60 miles + +################################################################## +# OUTPUTS +################################################################## +# The baseOutputDirectory is the base directory where outputs will be written. The beam.agentsim.simulationName param will +# be used as the name of a sub-directory beneath the baseOutputDirectory for simulation results. +# If addTimestampToOutputDirectory == true, a timestamp will be added, e.g. "beamville_2017-12-18_16-48-57" +beam.outputs.baseOutputDirectory = ${PWD}"/output/sf-light" +beam.outputs.baseOutputDirectory = ${?BEAM_OUTPUT} +beam.outputs.addTimestampToOutputDirectory = true + +# To keep all logging params in one place, BEAM overrides MATSim params normally in the controller config module +beam.outputs.writePlansInterval = 1 +beam.outputs.writeEventsInterval = 1 +beam.outputs.writeLinkTraversalInterval = 0 +beam.outputs.writeSkimsInterval = 1 +beam.outputs.writeAnalysis = false + +# The remaining params customize how events are written to output files +beam.outputs.events.fileOutputFormats = "csv.gz" # valid options: xml(.gz) , csv(.gz), none - DEFAULT: csv.gz + +# Events Writing Logging Levels: +# Any event types not explicitly listed in overrideWritingLevels take on defaultWritingLevel +beam.outputs.events.eventsToWrite = "ActivityEndEvent,ActivityStartEvent,PersonEntersVehicleEvent,PersonLeavesVehicleEvent,ModeChoiceEvent,PathTraversalEvent,ReserveRideHailEvent,ReplanningEvent,RefuelSessionEvent,ChargingPlugInEvent,ChargingPlugOutEvent,ParkEvent,LeavingParkingEvent" +beam.outputs.stats.binSize = 3600 + +################################################################## +# Debugging +################################################################## +beam.debug.debugEnabled = true +beam.debug.actor.logDepth = 20 +beam.debug.debugActorTimerIntervalInSec = 30 + +beam.debug.stuckAgentDetection { + checkIntervalMs = 200 + checkMaxNumberOfMessagesEnabled = false + defaultTimeoutMs = 60000 + enabled = false + overallSimulationTimeoutMs = 100000 + thresholds = [ + { + actorTypeToMaxNumberOfMessages { + population = 1 + rideHailAgent = 1 + transitDriverAgent = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.InitializeTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 11 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$ActivityEndTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$ActivityStartTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.PersonAgent$PersonDepartureTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$AlightVehicleTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 20 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$BoardVehicleTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 80, + rideHailAgent = 104, + transitDriverAgent = 114 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$EndLegTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + population = 80, + rideHailAgent = 104, + transitDriverAgent = 114 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.agents.modalbehaviors.DrivesVehicle$StartLegTrigger" + }, + { + actorTypeToMaxNumberOfMessages { + transitDriverAgent = 1 + } + markAsStuckAfterMs = 20000 + triggerType = "beam.agentsim.scheduler.BeamAgentScheduler$KillTrigger" + } + ] +} + + + +################################################################## +# SPATIAL +################################################################## +beam.spatial = { + localCRS = "epsg:26910" # what crs to use for distance calculations, must be in units of meters + boundingBoxBuffer = 10000 # meters of buffer around network for defining extend of spatial indices +} + +################################################################## +# BEAM ROUTING SERVICE +################################################################## +beam.routing { + #Base local date in ISO 8061 YYYY-MM-DDTHH:MM:SS+HH:MM + transitOnStreetNetwork = true + baseDate = "2017-09-22T00:00:00-07:00" + r5 { + directory = ${beam.inputDirectory}"/r5" + # Departure window in min + departureWindow = 1.0167 + osmFile = ${beam.inputDirectory}"/r5/sf-light.osm.pbf" + osmMapdbFile = ${beam.inputDirectory}"/r5/osm.mapdb" + mNetBuilder.fromCRS = "epsg:4326" # WGS84 + mNetBuilder.toCRS = ${beam.spatial.localCRS} + } +} + +################################################################## +# Calibration +################################################################## +beam.calibration.objectiveFunction = "CountsObjectiveFunction" +beam.calibration.mode.benchmarkFilePath=${beam.inputDirectory}"/sf-light-calibration/benchmark.csv" +beam.calibration.counts { + countsScaleFactor = 10.355 + writeCountsInterval = 1 + averageCountsOverIterations = 1 + inputCountsFile = ${beam.inputDirectory}"/counts/fall_2015_wed_filtered.xml" +} + diff --git a/test/input/sf-light/vehicleTypes-rh-ev-humans.csv b/test/input/sf-light/vehicleTypes-rh-ev-humans.csv new file mode 100644 index 00000000000..0f87d0483ad --- /dev/null +++ b/test/input/sf-light/vehicleTypes-rh-ev-humans.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:709b7e4ef9185ab672cf4402a543e101e72a27b8270bdc9cd7b964ce97179c47 +size 4266 diff --git a/test/input/sf-light/warmstart/households.csv.gz b/test/input/sf-light/warmstart/households.csv.gz new file mode 100644 index 00000000000..aea88e06bfb --- /dev/null +++ b/test/input/sf-light/warmstart/households.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d79ddd8d163447ffd7d02eddb05470315a20ee3c636a55a8d63900f59b4a01cd +size 23781 diff --git a/test/input/sf-light/warmstart/it.30/30.linkstats.csv.gz b/test/input/sf-light/warmstart/it.30/30.linkstats.csv.gz new file mode 100644 index 00000000000..0af6a5ed5a6 --- /dev/null +++ b/test/input/sf-light/warmstart/it.30/30.linkstats.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cd4227934879557353c270754072c0a802c861d27653fc01c7e3f44b9bf70b5 +size 10489683 diff --git a/test/input/sf-light/warmstart/it.30/30.plans.xml.gz b/test/input/sf-light/warmstart/it.30/30.plans.xml.gz new file mode 100644 index 00000000000..91cd0739413 --- /dev/null +++ b/test/input/sf-light/warmstart/it.30/30.plans.xml.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e8ec3b0e4dce30e1ba10c5fb0656e8a18f8e16c989b3315141b37d698b4fd3b +size 2271847 diff --git a/test/input/sf-light/warmstart/it.30/30.rideHailFleet.csv.gz b/test/input/sf-light/warmstart/it.30/30.rideHailFleet.csv.gz new file mode 100644 index 00000000000..0305cc8ac08 --- /dev/null +++ b/test/input/sf-light/warmstart/it.30/30.rideHailFleet.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4830a7f05c0878707feefd0111a6c5acd84dc534f4beb128a49737e549e2064f +size 12555 diff --git a/test/input/sf-light/warmstart/it.30/30.routeHistory.csv.gz b/test/input/sf-light/warmstart/it.30/30.routeHistory.csv.gz new file mode 100644 index 00000000000..1699afd13ff --- /dev/null +++ b/test/input/sf-light/warmstart/it.30/30.routeHistory.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b9a8d00634e3ef39542f9f3aa0e5ea7834415cd28fe3356c44d236c11fe32a8 +size 701594 diff --git a/test/input/sf-light/warmstart/it.30/30.skims.csv.gz b/test/input/sf-light/warmstart/it.30/30.skims.csv.gz new file mode 100644 index 00000000000..7a595e24b26 --- /dev/null +++ b/test/input/sf-light/warmstart/it.30/30.skims.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11148b6167b4426f6934484876a8946c8c7f4147fe1403bea27c3df813f9e056 +size 157393 diff --git a/test/input/sf-light/warmstart/outputPersonAttributes.xml.gz b/test/input/sf-light/warmstart/outputPersonAttributes.xml.gz new file mode 100644 index 00000000000..d16208722f0 --- /dev/null +++ b/test/input/sf-light/warmstart/outputPersonAttributes.xml.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8222d7a23ea82edaeabeb59065c08ce90276af3a64f96ab36f1cb78c7fefd50b +size 45107 diff --git a/test/input/sf-light/warmstart/plans.csv.gz b/test/input/sf-light/warmstart/plans.csv.gz new file mode 100644 index 00000000000..0262e8ae362 --- /dev/null +++ b/test/input/sf-light/warmstart/plans.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:158064e55420d63d86d6af1b932cbf2d259eb2df502b41b3902a900dbffbb3b0 +size 130914 diff --git a/test/input/sf-light/warmstart/population.csv.gz b/test/input/sf-light/warmstart/population.csv.gz new file mode 100644 index 00000000000..393c3f284e2 --- /dev/null +++ b/test/input/sf-light/warmstart/population.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37cd18e24f8c0f62c5ea542b0984ffc279d6b5fe55cde8cb405e950e842f95bc +size 44473 diff --git a/test/input/sf-light/warmstart/vehicles.csv.gz b/test/input/sf-light/warmstart/vehicles.csv.gz new file mode 100644 index 00000000000..d9e86bc77b8 --- /dev/null +++ b/test/input/sf-light/warmstart/vehicles.csv.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a5f50f2b6c60a38d6e3576da78858292c6bb7c62f411f675af6ef9bdb2748eb +size 16360 diff --git a/test/input/siouxfalls/calibration/benchmark.csv b/test/input/siouxfalls/calibration/benchmark.csv index 14f361a8905..5db29746d33 100755 --- a/test/input/siouxfalls/calibration/benchmark.csv +++ b/test/input/siouxfalls/calibration/benchmark.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ab94c42f14854a99e837c876a5c4685ae359cef80e534c6890c6a85e4d33c62 -size 123 +oid sha256:7bf1fc992420741222bc65890a81ac22e8762b737200bfc990ba2b0b18057465 +size 140