Problem
Melodie appends to existing CSVs and writes the header only when the file is new (MelodieInfra/table/table_objects.py, Table.to_file):
is_new_file = True if not os.path.exists(file_name) else False
writer = TableWriter(file_name, append=not is_new_file).write()
if is_new_file:
writer.send(headers)
The output CSVs already carry id_scenario, id_run, period. But id_run is Melodie's run_num within a single invocation — always 0 here. Running the simulation twice writes two complete sets of rows into the same files under identical keys, with nothing distinguishing them.
export_bundle.py filters on id_scenario only:
edf = edf[edf["id_scenario"] == scenario].sort_values("period")
Result: a bundle with 730 rows for 365 periods, two simulations interleaved and sorted together. No error is raised. The only thing keeping the current output correct is deleting data/output/ by hand between runs.
Related: because the header is written only for a new file, a run whose columns differ appends rows under the old header, silently misaligned.
Decision — one directory per run
Config.output_tables_path() returns output_folder, which main.py sets, so this needs no Melodie internals.
data/output/
runs.json
a3f9c1e2-.../
Result_Simulator_Environment.csv
Result_Simulator_BrazilFarms.csv
7b21d0f4-.../
- UUID generated once at startup, used as the directory name
- The same UUID becomes the Postgres
run_id in the follow-up ticket, so both paths share one identity
- Fresh files per run make schema drift and PDL drift impossible
- Deleting a bad run is deleting a folder
runs.json provides the ordering the UUID does not: run_id, started_at, PDL filename and content hash, scenario ids, period_num, git sha, optional label.
Scope
main.py — generate the run UUID, point the Melodie Config output folder at it
- manifest writer — append the run entry to
runs.json
export_bundle.py — --run <id> defaulting to the newest entry in runs.json; it resolves to the existing --input directory, --scenario unchanged
- README — document the layout
Out of scope: frontend run selection, Postgres.
Acceptance
- Two consecutive runs produce two directories and two
runs.json entries
- Neither run's CSVs contain rows from the other
export_bundle.py with no --run exports the newest run
export_bundle.py --run <older id> exports that run
- No manual deletion of
data/output/ is needed for correct output
Problem
Melodie appends to existing CSVs and writes the header only when the file is new (
MelodieInfra/table/table_objects.py,Table.to_file):The output CSVs already carry
id_scenario,id_run,period. Butid_runis Melodie'srun_numwithin a single invocation — always0here. Running the simulation twice writes two complete sets of rows into the same files under identical keys, with nothing distinguishing them.export_bundle.pyfilters onid_scenarioonly:Result: a bundle with 730 rows for 365 periods, two simulations interleaved and sorted together. No error is raised. The only thing keeping the current output correct is deleting
data/output/by hand between runs.Related: because the header is written only for a new file, a run whose columns differ appends rows under the old header, silently misaligned.
Decision — one directory per run
Config.output_tables_path()returnsoutput_folder, whichmain.pysets, so this needs no Melodie internals.run_idin the follow-up ticket, so both paths share one identityruns.jsonprovides the ordering the UUID does not:run_id,started_at, PDL filename and content hash, scenario ids,period_num, git sha, optional label.Scope
main.py— generate the run UUID, point the MelodieConfigoutput folder at itruns.jsonexport_bundle.py—--run <id>defaulting to the newest entry inruns.json; it resolves to the existing--inputdirectory,--scenariounchangedOut of scope: frontend run selection, Postgres.
Acceptance
runs.jsonentriesexport_bundle.pywith no--runexports the newest runexport_bundle.py --run <older id>exports that rundata/output/is needed for correct output