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

Upgrade timeseries #177

Draft
wants to merge 7 commits into
base: main
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions disco/cli/disco.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from disco.cli.make_summary_tables import make_summary_tables
from disco.cli.compute_hosting_capacity import compute_hosting_capacity
from disco.cli.plots import plot
from disco.cli.select_time_points import select_time_points
from disco.cli.summarize_hosting_capacity import summarize_hosting_capacity
from disco.cli.config_generic_models import config_generic_models
from disco.cli.upgrade_cost_analysis import upgrade_cost_analysis
Expand Down Expand Up @@ -54,6 +55,7 @@ def cli():
cli.add_command(ingest_tables)
cli.add_command(install_extensions)
cli.add_command(make_summary_tables)
cli.add_command(select_time_points)
cli.add_command(summarize_hosting_capacity)
cli.add_command(upgrade_cost_analysis)
cli.add_command(hosting_capacity_by_timestep)
Expand Down
148 changes: 148 additions & 0 deletions disco/cli/select_time_points.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/usr/bin/env python

"""
Down-selects load shape time points in the circuit based on
user-specified critical conditions.
"""

import logging
import shutil
import sys
from pathlib import Path

import click

from jade.loggers import setup_logging
from jade.utils.utils import get_cli_string

from disco.preprocess.select_timepoints2 import (
CriticalCondition,
DemandCategory,
GenerationCategory,
main,
)


logger = logging.getLogger(__name__)


@click.command()
@click.argument("master_file", type=click.Path(exists=True), callback=lambda *x: Path(x[2]))
@click.option(
"-c",
"--critical-conditions",
type=click.Choice([x.value for x in CriticalCondition]),
default=tuple(x.value for x in CriticalCondition),
show_default=True,
multiple=True,
callback=lambda *x: tuple(CriticalCondition(y) for y in x[2]),
help="critical conditions to use for time-point selection",
)
@click.option(
"-d",
"--demand-categories",
type=click.Choice([x.value for x in DemandCategory]),
default=tuple(x.value for x in DemandCategory),
show_default=True,
multiple=True,
callback=lambda *x: tuple(DemandCategory(y) for y in x[2]),
help="Demand-based devices to use in time-point selection algorithm",
)
@click.option(
"-g",
"--generation-categories",
type=click.Choice([x.value for x in GenerationCategory]),
default=tuple(x.value for x in GenerationCategory),
show_default=True,
multiple=True,
callback=lambda *x: tuple(GenerationCategory(y) for y in x[2]),
help="Generation-based devices to use in time-point selection algorithm",
)
@click.option(
"--feederhead-only",
default=False,
# is_flag=True,
show_default=True,
help="Select critical timepoints for feederhead bus only.",
)
@click.option(
"-o",
"--output",
default="output_time_points",
callback=lambda *x: Path(x[2]),
help="Output directory",
)
@click.option(
"--create-new-circuit/--no-create-new-circuit",
default=True,
is_flag=True,
show_default=True,
help="Create new circuit with down-selected time points.",
)
@click.option(
"--fix-master-file/--no-fix-master-file",
is_flag=True,
show_default=True,
default=False,
help="Remove commands in the Master.dss file that interfere with time-point selection.",
)
@click.option(
"-f",
"--force",
is_flag=True,
show_default=True,
default=False,
help="Delete output directory if it exists.",
)
@click.option(
"-v",
"--verbose",
is_flag=True,
show_default=True,
default=False,
help="Enabled debug logging.",
)
def select_time_points(
master_file,
demand_categories,
generation_categories,
critical_conditions,
feederhead_only,
output,
create_new_circuit,
fix_master_file,
force,
verbose,
):
"""Select load shape time points in the circuit based on the specified critical conditions.

By default, the Master.dss file is not allowed to enable time-series mode. Specify
--fix-master-file to disable time-series mode and other disallowed parameters.

"""
if output.exists():
if force:
shutil.rmtree(output)
else:
print(
f"Output directory {output} exists. Choose a different path or set --force.",
file=sys.stderr,
)
sys.exit(1)

output.mkdir()
level = logging.DEBUG if verbose else logging.INFO
log_file = output / "disco.log"
setup_logging("disco", log_file, console_level=level, packages=["disco"])
logger.info(get_cli_string())
categories = {"demand": demand_categories, "generation": generation_categories}
main(
master_file,
categories=categories,
critical_conditions=critical_conditions,
feederhead_only=feederhead_only,
destination_dir=output,
create_new_circuit=create_new_circuit,
fix_master_file=fix_master_file,
recreate_profiles=False,
)
26 changes: 9 additions & 17 deletions disco/cli/upgrade_cost_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from disco.exceptions import DiscoBaseException, get_error_code_from_exception
from disco.models.base import OpenDssDeploymentModel
from disco.models.upgrade_cost_analysis_generic_input_model import (
UpgradeCostAnalysisSimulationModel
UpgradeCostAnalysisSimulationModel, UpgradeSimulationParamsModel
)
from disco.models.upgrade_cost_analysis_generic_output_model import (
JobUpgradeSummaryOutputModel,
Expand Down Expand Up @@ -218,7 +218,7 @@ def run(
start = time.time()
ret = EXIT_CODE_GOOD
try:
run_job(job, config, jobs_output_dir, file_log_level)
run_job(job, config, jobs_output_dir)
all_failed = False
except DiscoBaseException as exc:
logger.exception("Unexpected DISCO error in upgrade cost analysis job=%s", job.name)
Expand Down Expand Up @@ -273,7 +273,7 @@ def _get_return_code_filename(output_dir, job_name):
return output_dir / job_name / "return_code"


def run_job(job, config, jobs_output_dir, file_log_level):
def run_job(job, config, jobs_output_dir):
job_output_dir = jobs_output_dir / job.name
job_output_dir.mkdir(exist_ok=True)
job = UpgradeParameters(
Expand All @@ -284,35 +284,27 @@ def run_job(job, config, jobs_output_dir, file_log_level):
feeder="NA",
),
)

upgrade_simulation_params_names = list(UpgradeSimulationParamsModel.schema()["properties"].keys())
global_config = {
"thermal_upgrade_params": config.thermal_upgrade_params.dict(),
"voltage_upgrade_params": config.voltage_upgrade_params.dict(),
"upgrade_simulation_params": {
"enable_pydss_controller": config.enable_pydss_controllers,
},
"upgrade_simulation_params": dict((k, config.dict()[k]) for k in upgrade_simulation_params_names),
"upgrade_cost_database": config.upgrade_cost_database,
"dc_ac_ratio": config.dc_ac_ratio,
}
global_config["upgrade_simulation_params"]["pydss_controller"] = None
if (config.pydss_controllers.pv_controller is not None) and config.enable_pydss_controllers:
global_config["upgrade_simulation_params"]["pydss_controller"] = (
config.pydss_controllers.pv_controller.dict(),
)
# replace PyDSS PV Controller dictionary with Model
if (global_config["upgrade_simulation_params"]["pydss_controllers"]["pv_controller"] is not None) and global_config["upgrade_simulation_params"]["enable_pydss_controllers"]:
global_config["upgrade_simulation_params"]["pydss_controllers"] = config.pydss_controllers.pv_controller

simulation = UpgradeSimulation(
job=job,
job_global_config=global_config,
output=jobs_output_dir,
)
simulation.run(
dc_ac_ratio=global_config["dc_ac_ratio"],
enable_pydss_solve=global_config["upgrade_simulation_params"]["enable_pydss_controller"],
pydss_controller_model=config.pydss_controllers.pv_controller,
thermal_config=global_config["thermal_upgrade_params"],
voltage_config=global_config["voltage_upgrade_params"],
upgrade_simulation_params_config=global_config["upgrade_simulation_params"],
cost_database_filepath=global_config["upgrade_cost_database"],
verbose=file_log_level == logging.DEBUG,
)


Expand Down
4 changes: 4 additions & 0 deletions disco/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class OpenDssConvergenceError(DiscoBaseException):
"""Raise when OpenDSS fails to converge"""


class OpenDssModelDisconnectedError(DiscoBaseException):
"""Raise when OpenDSS model has isolated elements"""


class PyDssConvergenceError(DiscoBaseException):
"""Raise when PyDSS fails to converge"""

Expand Down
33 changes: 18 additions & 15 deletions disco/extensions/upgrade_simulation/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from disco.extensions.upgrade_simulation.upgrade_simulation import UpgradeSimulation
from disco.pydss.pydss_configuration_base import DEFAULT_CONTROLLER_CONFIG_FILE
from disco.version import __version__ as disco_version
from disco.models.upgrade_cost_analysis_generic_input_model import (
UpgradeCostAnalysisSimulationModel, UpgradeSimulationParamsModel
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -52,30 +55,30 @@ def run(config_file, name, output, output_format, verbose):
job_global_config=config.job_global_config,
output=output
)
upgrade_simulation_params_config = config.job_global_config["upgrade_simulation_params"]
try:
upgrade_simulation_params = config.job_global_config["upgrade_simulation_params"]
dc_ac_ratio = upgrade_simulation_params["dc_ac_ratio"]
enable_pydss_controller = upgrade_simulation_params["enable_pydss_controller"]
if enable_pydss_controller:
thermal_config = config.job_global_config["thermal_upgrade_params"]
voltage_config = config.job_global_config["voltage_upgrade_params"]
cost_database_filepath = config.job_global_config["upgrade_cost_database"]
upgrade_simulation_params_names = list(UpgradeSimulationParamsModel.schema()["properties"].keys())
fields = set(upgrade_simulation_params_names) & set(upgrade_simulation_params_config.keys())
temp = {key: upgrade_simulation_params_config[key] for key in fields}
upgrade_simulation_params_config = UpgradeSimulationParamsModel(**temp).dict()

if config.job_global_config["upgrade_simulation_params"]["enable_pydss_controllers"]:
pv_controllers = load_data(DEFAULT_CONTROLLER_CONFIG_FILE)
pydss_controller_model = PvControllerModel(
**pv_controllers[upgrade_simulation_params["pydss_controller_name"]]
controller_model = PvControllerModel(
**pv_controllers[config.job_global_config["upgrade_simulation_params"]["pydss_controller_name"]]
)
upgrade_simulation_params_config["pydss_controllers"] = controller_model
else:
pv_controllers = None
pydss_controller_model= None
upgrade_simulation_params_config["pydss_controllers"] = None

thermal_config = config.job_global_config["thermal_upgrade_params"]
voltage_config = config.job_global_config["voltage_upgrade_params"]
cost_database_filepath = config.job_global_config["upgrade_cost_database"]
ret = simulation.run(
dc_ac_ratio = dc_ac_ratio,
enable_pydss_solve=enable_pydss_controller,
pydss_controller_model=pydss_controller_model,
thermal_config=thermal_config,
voltage_config=voltage_config,
upgrade_simulation_params_config=upgrade_simulation_params_config,
cost_database_filepath=cost_database_filepath,
verbose=verbose
)
return ret
except Exception:
Expand Down
8 changes: 4 additions & 4 deletions disco/extensions/upgrade_simulation/upgrade_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def __init__(self, **kwargs):
self._pydss_inputs = self.get_default_pydss_config()

# Customize pydss config
if "enable_pydss_solve" in kwargs:
self.enable_pydss_solve(kwargs["enable_pydss_solve"])
if "enable_pydss_controllers" in kwargs:
self.enable_pydss_controllers(kwargs["enable_pydss_controllers"])

@classmethod
def auto_config(cls, inputs, **kwargs):
Expand Down Expand Up @@ -59,8 +59,8 @@ def get_default_pydss_config():
# this method not in use, simply return an empty dict.
return {}

def enable_pydss_solve(self, value: bool):
self._pydss_inputs[ConfigType.SIMULATION_CONFIG]["default"]["enable_pydss_solve"] = value
def enable_pydss_controllers(self, value: bool):
self._pydss_inputs[ConfigType.SIMULATION_CONFIG]["default"]["enable_pydss_controllers"] = value
if value is True:
message = "Enable PyDSS solve."
else:
Expand Down
15 changes: 3 additions & 12 deletions disco/extensions/upgrade_simulation/upgrade_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,46 +114,37 @@ def generate_command(job, output, config_file, verbose=False):

def run(
self,
enable_pydss_solve,
pydss_controller_model,
dc_ac_ratio,
thermal_config,
voltage_config,
upgrade_simulation_params_config,
cost_database_filepath,
verbose=False
):
determine_thermal_upgrades(
job_name = self.job.name,
master_path=self.model.deployment.deployment_file,
enable_pydss_solve=enable_pydss_solve,
thermal_config=thermal_config,
pydss_volt_var_model=pydss_controller_model,
upgrade_simulation_params_config=upgrade_simulation_params_config,
internal_upgrades_technical_catalog_filepath=self.internal_upgrades_technical_catalog_filepath(),
thermal_upgrades_dss_filepath=self.get_thermal_upgrades_dss_file(),
upgraded_master_dss_filepath=self.get_upgraded_master_dss_file(),
output_json_thermal_upgrades_filepath=self.get_thermal_upgrades_json_file(),
feeder_stats_json_file = self.get_feeder_stats_json_file(),
thermal_upgrades_directory=self.get_thermal_upgrades_directory(),
overall_output_summary_filepath=self.get_overall_output_summary_file(),
dc_ac_ratio=dc_ac_ratio,
verbose=verbose
)
determine_voltage_upgrades(
job_name = self.job.name,
master_path=self.model.deployment.deployment_file,
enable_pydss_solve=enable_pydss_solve,
pydss_volt_var_model=pydss_controller_model,
thermal_config=thermal_config,
voltage_config=voltage_config,
upgrade_simulation_params_config=upgrade_simulation_params_config,
thermal_upgrades_dss_filepath=self.get_thermal_upgrades_dss_file(),
voltage_upgrades_dss_filepath=self.get_voltage_upgrades_dss_file(),
upgraded_master_dss_filepath=self.get_upgraded_master_dss_file(),
output_json_voltage_upgrades_filepath = self.get_voltage_upgrades_json_file(),
feeder_stats_json_file = self.get_feeder_stats_json_file(),
voltage_upgrades_directory=self.get_voltage_upgrades_directory(),
overall_output_summary_filepath=self.get_overall_output_summary_file(),
dc_ac_ratio=dc_ac_ratio,
verbose=verbose
)
compute_all_costs(
job_name = self.job.name,
Expand Down
Loading
Loading