Skip to content

Commit 9dda198

Browse files
committed
networkDesign: extend to schedule to full day when saving scenarios
For simplicity and given the number of schedules generated, the simulations only create schedules for a few hours during a day. But when saving a scenario, in order to simulate it with real data, the schedules need to span the whole day. This adds the collection manager to the network design job, as it is required for the schedules generation. Also, it saves the scenarios, services and modified lines to the main cache after saving, so they are right away available to the user for calculations.
1 parent 6f82af4 commit 9dda198

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

packages/transition-backend/src/services/evolutionaryAlgorithm/preparation/ServicePreparation.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export const prepareServices = async (
231231
export const saveSimulationScenario = async (
232232
scenario: Scenario,
233233
jobWrapper: TransitNetworkDesignJobWrapper<EvolutionaryTransitNetworkDesignJobType>
234-
): Promise<string | undefined> => {
234+
): Promise<{ scenarioId: string; lineIds: string[] } | undefined> => {
235235
try {
236236
console.log('saving simulation scenario');
237237
// Find all simulated services to merge as one
@@ -247,7 +247,7 @@ export const saveSimulationScenario = async (
247247
// Create a new service for this scenario and save to DB
248248
const service = new Service(
249249
{
250-
name: `GALND_${scenario.attributes.name}`,
250+
name: `ND_${scenario.attributes.name}`,
251251
monday: true,
252252
tuesday: true,
253253
wednesday: true,
@@ -259,27 +259,47 @@ export const saveSimulationScenario = async (
259259
end_date: moment().format('YYYY-MM-DD'),
260260
data: { forJob: jobWrapper.job.id }
261261
},
262-
true
262+
true,
263+
jobWrapper.collectionManager
263264
);
264265
await service.save(serviceLocator.socketEventManager);
265266

266267
// For each service to merge and save, find the lines that has them
268+
const modifiedLines = {};
267269
for (let i = 0; i < simulatedServiceIds.length; i++) {
268270
const servicedLines = jobWrapper.simulatedLineCollection
269271
.getFeatures()
270272
.filter((line) => line.attributes.scheduleByServiceId[simulatedServiceIds[i]] !== undefined);
271-
// Copy the schedules for those services, add the service to the line, then save to the database
273+
// Copy the schedules for those services, extend the schedule for
274+
// the entire day, add the service to the line, then save to the
275+
// database
272276
for (let lineIdx = 0; lineIdx < servicedLines.length; lineIdx++) {
277+
const servicedLine = servicedLines[lineIdx];
273278
const currentSchedule = new Schedule(
274-
servicedLines[lineIdx].attributes.scheduleByServiceId[simulatedServiceIds[i]],
279+
servicedLine.attributes.scheduleByServiceId[simulatedServiceIds[i]],
275280
true
276281
);
282+
// Copy the schedule and extend to full day
277283
const scheduleAttributes = currentSchedule.getClonedAttributes(true);
284+
// Remove the constraints on custom start/end times to allow to extend to full day
285+
(scheduleAttributes.periods || []).forEach((period) => {
286+
delete period.custom_start_at_str;
287+
delete period.custom_end_at_str;
288+
return period;
289+
});
278290
scheduleAttributes.service_id = service.getId();
279-
const schedule = new Schedule(scheduleAttributes, true);
280-
servicedLines[lineIdx].addSchedule(schedule);
291+
const schedule = new Schedule(scheduleAttributes, true, jobWrapper.collectionManager);
292+
// Recreate schedule for full day period, without time constraints
293+
schedule.attributes.periods.forEach((period) => {
294+
if (period.period_shortname) {
295+
schedule.generateForPeriod(period.period_shortname);
296+
}
297+
});
298+
// Add the schedule to the line with the new service
299+
servicedLine.addSchedule(schedule);
281300
// Save the schedules to DB
282301
await schedule.save(serviceLocator.socketEventManager);
302+
modifiedLines[servicedLine.getId()] = servicedLine;
283303
}
284304
}
285305

@@ -297,7 +317,10 @@ export const saveSimulationScenario = async (
297317
);
298318
await newScenario.save(serviceLocator.socketEventManager);
299319

300-
return newScenario.getId();
320+
return {
321+
scenarioId: newScenario.getId(),
322+
lineIds: Object.keys(modifiedLines)
323+
};
301324
} catch (error) {
302325
console.error('Error saving simulation scenario:', error);
303326
return undefined;

packages/transition-backend/src/services/networkDesign/transitNetworkDesign/TransitNetworkDesignJobWrapper.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class TransitNetworkDesignJobWrapper<
4343
private _agencyCollection: AgencyCollection | undefined = undefined;
4444
private _serviceCollection: ServiceCollection | undefined = undefined;
4545
private _lineServices: LineServices | undefined = undefined;
46+
private _collectionManager: CollectionManager | undefined = undefined;
4647

4748
constructor(
4849
private wrappedJob: ExecutableJob<TJobType>,
@@ -107,6 +108,13 @@ export class TransitNetworkDesignJobWrapper<
107108
return this._lineServices;
108109
}
109110

111+
get collectionManager(): CollectionManager {
112+
if (this._collectionManager === undefined) {
113+
throw new Error('Collection manager not set yet');
114+
}
115+
return this._collectionManager;
116+
}
117+
110118
setLineServices(lineServices: LineServices) {
111119
this._lineServices = lineServices;
112120
}
@@ -135,6 +143,7 @@ export class TransitNetworkDesignJobWrapper<
135143
this._lineCollection = lines;
136144
this._agencyCollection = agencies;
137145
this._serviceCollection = services;
146+
this._collectionManager = collectionManager;
138147
};
139148

140149
/**

packages/transition-backend/src/services/networkDesign/transitNetworkDesign/evolutionary/EvolutionaryTransitNetworkDesignJob.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ import {
4343
import { prepareServices, saveSimulationScenario } from '../../../evolutionaryAlgorithm/preparation/ServicePreparation';
4444
import Line from 'transition-common/lib/services/line/Line';
4545
import resultsDbQueries from '../../../../models/db/networkDesignResults.db.queries';
46+
import {
47+
loadAndSaveLinesByIdsToCache,
48+
loadAndSaveScenariosToCache,
49+
loadAndSaveServicesToCache
50+
} from '../../../capnpCache/dbToCache';
4651

4752
const CANDIDATE_LINES_RESULTS_CSV_FILE_PREFIX = 'ndCandidateLinesResults';
4853
const CANDIDATE_SIMULATION_RESULTS_CSV_FILE_PREFIX = 'ndCandidateSimulationResults';
@@ -251,7 +256,7 @@ class EvolutionaryTransitNetworkDesignJobExecutor extends TransitNetworkDesignJo
251256
// Load the necessary data from the server
252257
const jobId = this.job.id;
253258
console.time(`Preparing data for evolutionary transit network design job from cache ${jobId}`);
254-
// FIXME Do we even need this? Or can we just start recovery at serviceCollectionFromCache call and get only required data?
259+
// FIXME We need to have the paths and lines loaded in a CollectionManager to save the scenarios and re-generate schedules at the end. But not the whole data
255260
await this.loadServerData(serviceLocator.socketEventManager);
256261

257262
// Get the simulated lines from cache, to make sure the order is the same as before
@@ -368,17 +373,24 @@ class EvolutionaryTransitNetworkDesignJobExecutor extends TransitNetworkDesignJo
368373

369374
// Save best scenarios if necessary
370375
// FIXME Refactor this so we can map a candidate with the scenario it generated and save it in the result's data for output in files
376+
// FIXME2 Extract this block to a function when we clean up this class
371377
if (this.options.numberOfGenerations - this.currentIteration < this.options.keepGenerations) {
372378
const bestScenarios = previousGeneration.getBestScenarios(this.options.keepCandidates);
373379
const scenarioSavePromises = bestScenarios?.map((scenario) =>
374380
saveSimulationScenario(scenario, this)
375381
);
376-
const scenarioIds = (await Promise.all(scenarioSavePromises)).filter(
377-
(scenarioId) => scenarioId !== undefined
378-
) as string[];
379-
algorithmResults.scenarioIds.push(...scenarioIds);
382+
const scenarioSaveResults = (await Promise.all(scenarioSavePromises)).filter(
383+
(scenarioSaveResult) => scenarioSaveResult !== undefined
384+
);
385+
algorithmResults.scenarioIds.push(...scenarioSaveResults.map((result) => result!.scenarioId));
380386
this.job.attributes.data.results = algorithmResults;
381387
await this.job.save(this.executorOptions.progressEmitter);
388+
389+
// Update the main cache with the saved scenarios to allow analyzing them from Transition UI
390+
await loadAndSaveScenariosToCache();
391+
await loadAndSaveServicesToCache();
392+
await loadAndSaveLinesByIdsToCache({ lineIds: scenarioSaveResults.flatMap((result) => result!.lineIds) });
393+
// FIXME Should signal the main thread to restart trRouting. Or maybe it works directly?
382394
}
383395

384396
// Save the results of this generation to the database

0 commit comments

Comments
 (0)