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

add load_pickle mode and create_pickle_in_report option #205

Merged
merged 8 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions data/examples/minimal.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Input and output files and paths ###
# Input file containing trip information
schedule_path = data/examples/trips_example.csv
# Electrified stations
electrified_stations_path = data/examples/electrified_stations.json
# Vehicle types
# Not strictly needed (defaults to: ./data/examples/vehicle_types.json),
# but if vehicle types have constant mileage, no other mileage files are needed
vehicle_types_path = data/examples/vehicle_types_constant_mileage.json
3 changes: 3 additions & 0 deletions data/examples/minimal_pickle.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mode = ["load_pickle", "report"]
# Load this pickle file, expects load_pickle as first mode
load_pickle = scenario.pkl
7 changes: 6 additions & 1 deletion data/examples/simba.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ rotation_filter_path = data/examples/rotation_filter.csv
# recombination splits and merges rotations to use the optimal number of busses
# report generates simulation output files, including costs.
mode = ["sim", "report"]
#mode = ["sim", "neg_depb_to_oppb", "service_optimization", "station_optimization", "remove_negative", "split_negative_depb", "report"]
#mode = ["sim", "load_pickle", "neg_depb_to_oppb", "service_optimization", "station_optimization", "remove_negative", "split_negative_depb", "report"]

##### Flags #####
### Activate optional functions ###
Expand All @@ -63,6 +63,11 @@ extended_output_plots = false
rotation_filter_variable = null
# Write a new trips.csv during report mode to output directory? (default: false)
create_trips_in_report = false
# Pickle current schedule and scenario during report mode
# create_pickle_in_report = false
# Load this pickle file, expects load_pickle as first mode
# load_pickle_path = "example.pkl"


##### Charging strategy #####
# Preferred charging type. Options: depb, oppb (default: depb)
Expand Down
43 changes: 43 additions & 0 deletions data/examples/vehicle_types_constant_mileage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"AB": { // vehicle_type
"depb": { // charging_type
"name": "articulated bus - depot charging", // long name
"capacity": 250, // battery capacity in kWh
"charging_curve": [[0, 150], [0.8, 150], [1, 15]], // charging curve [SoC, kW]
"min_charging_power": 0, // min charging power in KW
"v2g": false, // Is vehicle capable of vehicle to grid?
"mileage": 1.0, // mileage in kWh/km or link to consumption.csv
"battery_efficiency": 0.95, // optional. default: 0.95
"idle_consumption" : 0.0 // [kWh/h] consumption while standing during a rotation
},
"oppb": {
"name": "articulated bus - opportunity charging",
"capacity": 150,
"charging_curve": [[0, 250], [0.8, 250], [1, 25]],
"min_charging_power": 0,
"v2g": false,
"mileage": 1.0,
"idle_consumption" : 0.0
}
},
"SB": {
"depb": {
"name": "solo bus - depot charging",
"capacity": 250,
"charging_curve": [[0, 150], [0.8, 150], [1, 15]],
"min_charging_power": 0,
"v2g": false,
"mileage": 1.2,
"idle_consumption" : 0.0
},
"oppb": {
"name": "solo bus - opportunity charging",
"capacity": 150,
"charging_curve": [[0, 250], [0.8, 250], [1, 25]],
"min_charging_power": 0,
"v2g": false,
"mileage": 1.1,
"idle_consumption" : 0.0
}
}
}
11 changes: 11 additions & 0 deletions docs/source/modes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ Simple Simulation

The simple simulation case is the default mode. Its usage is explained in :ref:`Getting Started`. Every chain of modes starts with a simple simulation, even if it is not explicitly listed in the modes. The simulation takes the scenario as is. No parameters will be adjusted, optimized or changed in any way. The charging type for each vehicle is read from the rotation information from the trips.csv if this data is included. If the data is not included *preferred_charging_type* from the config file is used, as long as the provided vehicles data provides the preferred_charging_type for the specified vehicle type.

Load Pickle
-----------

::

mode = ["load_pickle"]

While normally scenarios are generated from a trips file and ancillary data, with this mode a previously simulated scenario can be loaded from a pickle file instead. This scenario already contains results. Modes can be chained after load_pickle in the usual fashion. Multiple "load_pickle" modes per chain are allowed, reloading the scenario again.
This mode needs the `load_pickle_path` option to point to a pickle file. If the `load_pickle_path` option is set, `load_pickle` must be the first mode.
All options in the config file (except for cost parameters) that are stored in schedule are ignored.

.. _neg_depb_to_oppb:

Negative Depot to Opportunity Charger
Expand Down
16 changes: 16 additions & 0 deletions docs/source/simulation_parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ The example (data/simba.cfg) contains parameter descriptions which are explained
- false
- Boolean
- If activated, plots are displayed with every run of :ref:`report` mode
* - rotation_filter_variable
- null
- string
- How to filter rotations according to file 'rotation_filter': options are "include" (whitelist), "exclude" (blacklist), null (ignore)
* - create_trips_in_report
- false
- Boolean
- Write a new trips.csv during report mode to output directory?
* - create_pickle_in_report
- false
- Boolean
- Pickle current schedule and scenario during report mode
* - load_pickle_path
- Optional, no default given
- Path to pickle file
- Load schedule and scenario from this pickle file, expects load_pickle as first mode
* - extended_output_plots
- false
- Boolean
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ numpy >= 1.12.0
matplotlib
-e git+https://github.com/rl-institut/spice_ev.git@dev#egg=spice_ev
pandas
dill
9 changes: 9 additions & 0 deletions simba/report.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" Module to generate meaningful output files and/or figures to describe simulation process. """
import csv
import datetime
import dill as pickle
import logging
from math import ceil
import re
Expand Down Expand Up @@ -347,6 +348,14 @@ def generate(schedule, scenario, args):
file_path = args.results_directory / "trips.csv"
write_csv(generate_trips_timeseries_data(schedule), file_path)

if vars(args).get('create_pickle_in_report', False):
file_path = args.results_directory / "scenario.pkl"
with file_path.open('wb') as f:
pickle.dump({
"schedule": schedule,
"scenario": scenario,
}, f)

# summary of used vehicle types and all costs
if args.cost_calculation:
file_path = args.results_directory / "summary_vehicles_costs.csv"
Expand Down
39 changes: 27 additions & 12 deletions simba/simulate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import dill as pickle
import traceback
from copy import deepcopy
from pathlib import Path
Expand All @@ -23,13 +24,19 @@ def simulate(args):
:return: final schedule and scenario
:rtype: tuple
"""
# The DataContainer stores various input data.
data_container = DataContainer().fill_with_args(args)

schedule, args = pre_simulation(args, data_container)
scenario = schedule.run(args)
schedule, scenario = modes_simulation(schedule, scenario, args)
return schedule, scenario
if vars(args).get("load_pickle_path"):
# load pickle file: skip pre_simulation
assert len(args.mode) > 1, "Load pickle: follow-up modes required, for example report"
assert args.mode[0] == "load_pickle", "Load pickle: first mode must be load_pickle"
# read out schedule and scenario as first mode
schedule, scenario = Mode.load_pickle(None, None, args=args)
args.mode = args.mode[1:]
else:
# DataContainer stores various input data
data_container = DataContainer().fill_with_args(args)
schedule, args = pre_simulation(args, data_container)
scenario = schedule.run(args)
return modes_simulation(schedule, scenario, args)


def pre_simulation(args, data_container: DataContainer):
Expand Down Expand Up @@ -83,11 +90,6 @@ def modes_simulation(schedule, scenario, args):
:rtype: tuple
:raises Exception: if args.propagate_mode_errors is set, re-raises error instead of continuing
"""

if not isinstance(args.mode, list):
# backwards compatibility: run single mode
args.mode = [args.mode]

for i, mode in enumerate(args.mode):
# scenario must be set from initial run / prior modes
assert scenario is not None, f"Scenario became None after mode {args.mode[i-1]} (index {i})"
Expand Down Expand Up @@ -261,6 +263,19 @@ def split_negative_depb(schedule, scenario, args, _i):
scenario = recombined_schedule.run(args)
return recombined_schedule, scenario

@staticmethod
def load_pickle(_schedule=None, _scenario=None, args=None, _i=None):
with open(args.load_pickle_path, 'rb') as f:
unpickle = pickle.load(f)
schedule = unpickle["schedule"]
scenario = unpickle["scenario"]
# DataContainer is part of schedule
# However, cost parameters are supposed to be mutable after loading from pickle
if args.cost_parameters_path:
schedule.data_container.add_cost_parameters_from_json(args.cost_parameters_path)

return schedule, scenario

@staticmethod
def report(schedule, scenario, args, i):
if args.output_path is None:
Expand Down
24 changes: 19 additions & 5 deletions simba/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,18 @@ def get_args():
# rename special options
args.timing = args.eta

mandatory_arguments = ["schedule_path", "electrified_stations_path"]
missing = [a for a in mandatory_arguments if vars(args).get(a) is None]
if missing:
raise Exception("The following arguments are required: {}".format(", ".join(missing)))
# enforce modes to be a list
if not isinstance(args.mode, list):
args.mode = [args.mode]

# check mandatory arguments
if not vars(args).get("load_pickle_path"):
mandatory_arguments = ["schedule_path", "electrified_stations_path"]
if "load_pickle" in args.mode:
mandatory_arguments.append("load_pickle_path")
missing = [a for a in mandatory_arguments if vars(args).get(a) is None]
if missing:
raise Exception("The following arguments are required: {}".format(", ".join(missing)))

return args

Expand Down Expand Up @@ -484,12 +492,13 @@ def get_parser():

# #### Modes #####
mode_choices = [
'sim', 'neg_depb_to_oppb', 'neg_oppb_to_depb', 'service_optimization',
'sim', 'load_pickle', 'neg_depb_to_oppb', 'neg_oppb_to_depb', 'service_optimization',
'station_optimization', 'remove_negative', 'split_negative_depb', 'report']
parser.add_argument('--mode', default=['sim', 'report'], nargs='*', choices=mode_choices,
help=f"Specify what you want to do. Choose one or more from \
{', '.join(mode_choices)}. \
sim runs a single simulation with the given inputs. \
load_pickle loads files specified in load_pickle_path argument. \
neg_depb_to_oppb changes charging type of negative depb rotations. \
neg_oppb_to_depb changes charging type of negative oppb rotations. \
service optimization finds the largest set of electrified rotations. \
Expand All @@ -514,6 +523,11 @@ def get_parser():
parser.add_argument('--create-scenario-file', help='Write scenario.json to file')
parser.add_argument('--create-trips-in-report', action='store_true',
help='Write a trips.csv during report mode')
parser.add_argument('--create-pickle-in-report', action='store_true',
help='Pickle current schedule and scenario during report mode')
parser.add_argument('--load-pickle-path',
help='Load given pickle file, expects load_pickle as first mode')

parser.add_argument('--optimizer-config-path', default=None,
help="For station_optimization an optimizer_config is needed. \
Input a path to an .cfg file or use the default_optimizer.cfg")
Expand Down
Loading
Loading