Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual access for grid origins #935

Draft
wants to merge 6 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ public MultiOriginAssembler (RegionalAnalysis regionalAnalysis, Job job, FileSto
// We might want to record a grid of dual accessibility values, but this will require some serious
// refactoring of the GridResultWriter.
// if (job.templateTask.dualAccessibilityThreshold > 0) { ... }
throw new IllegalArgumentException("Temporal density of opportunities cannot be recorded for gridded origin points.");
// throw new IllegalArgumentException("Temporal density of opportunities cannot be recorded for " +
// "gridded origin points.");
} else {
// Freeform origins.
// Output includes temporal density of opportunities and optionally dual accessibility.
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/conveyal/r5/analyst/TemporalDensityResult.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.conveyal.r5.analyst;

import com.conveyal.r5.analyst.cluster.AnalysisWorkerTask;
import com.conveyal.r5.analyst.cluster.RegionalTask;
import com.google.common.base.Preconditions;

import static com.conveyal.r5.common.Util.notNullOrEmpty;
Expand Down Expand Up @@ -89,6 +90,50 @@ public int[][] minutesToReachOpportunities(int n) {
return result;
}

/**
* Writes dual access travel time values (in minutes) to our standard access grid format. The value returned (for
* an origin) is the number of minutes required to reach a threshold number of opportunities (specified by
* the cutoffs and task.dualAccessibilityThreshold) in the specified destination layer at a given percentile of
* travel time. If the threshold cannot be reached in less than 120 minutes, returns 0.
* This is a temporary experimental feature, (ab)using existing features in the UI and backend so that dual access
* results for grid origins can be obtained without any changes to those other components of our system. It uses
* the supplied task.cutoffsMinutes as dual access thresholds. If a nonzero task.dualAccessibility In place of the
* last
* cutoffsMinutes value, it uses task.dualAccessibilityThreshold (which is initialized to 0, so this is safe even
* if a user does not supply it).
*/
public int[][][] fakeDualAccess (RegionalTask task) {
int nPointSets = task.destinationPointSets.length;
int nCutoffs = task.cutoffsMinutes.length;
int[][][] dualAccess = new int[nPointSets][nPercentiles][nCutoffs];
for (int d = 0; d < nPointSets; d++) {
for (int p = 0; p < nPercentiles; p++) {
// Hack: use cutoffs as dual access thresholds
for (int c = 0; c < nCutoffs; c++) {
int m = 0;
double sum = 0;
while (sum < task.cutoffsMinutes[c] && m < 120) {
sum += opportunitiesPerMinute[d][p][m];
m += 1;
}
dualAccess[d][p][c] = m;
}
// But the hack above won't allow thresholds over 120 (see validateCutoffsMinutes()); so overwrite
// the value for the last cutoff if a nonzero task.dualAccessibilityThreshold value is supplied.
if (task.dualAccessibilityThreshold != 0) {
int m = 0;
double sum = 0;
while (sum < task.dualAccessibilityThreshold && m < 120) {
sum += opportunitiesPerMinute[d][p][m];
m += 1;
}
dualAccess[d][p][nCutoffs - 1] = m;
}
}
}
return dualAccess;
}

public int[][] minutesToReachOpportunities() {
return minutesToReachOpportunities(opportunityThreshold);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public TravelTimeReducer (AnalysisWorkerTask task, TransportNetwork network) {
if (task.includePathResults) {
pathResult = new PathResult(task, network.transitLayer);
}
if (task.includeTemporalDensity) {
if (task.includeTemporalDensity || task.hasFlag("gridDualAccess")) {
temporalDensityResult = new TemporalDensityResult(task);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
Expand Down Expand Up @@ -407,6 +408,10 @@ protected void handleOneRegionalTask (RegionalTask task) throws Throwable {

LOG.debug("Handling regional task {}", task.toString());

// Force dual access results for grid (remove once broker has been relaunched to support custom flags)
LOG.info("Forcing dual access results");
task.flags = Set.of("gridDualAccess");

if (task.injectFault != null) {
task.injectFault.considerShutdownOrException(task.taskId);
if (task.injectFault.shouldDropTaskBeforeCompute(task.taskId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,8 @@ public void validateCutoffsMinutes () {
}
}

public boolean hasFlag (String flag) {
return this.flags != null && this.flags.contains(flag);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,13 @@ public RegionalWorkResult(OneOriginResult result, RegionalTask task) {
this.jobId = task.jobId;
this.taskId = task.taskId;
this.travelTimeValues = result.travelTimes == null ? null : result.travelTimes.values;
this.accessibilityValues = result.accessibility == null ? null : result.accessibility.getIntValues();
if (result.accessibility == null) {
this.accessibilityValues = null;
} else if (task.hasFlag("gridDualAccess")) {
this.accessibilityValues = result.density.fakeDualAccess(task);
} else {
this.accessibilityValues = result.accessibility.getIntValues();
}
this.pathResult = result.paths == null ? null : result.paths.summarizeIterations(PathResult.Stat.MINIMUM);
this.opportunitiesPerMinute = result.density == null ? null : result.density.opportunitiesPerMinute;
// TODO checkTravelTimeInvariants, checkAccessibilityInvariants to verify that values are monotonically increasing
Expand Down