Skip to content
Open
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: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.7.0-slim
FROM python:3.11.0-slim

RUN apt-get update

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Welcome to the pydss Repository!


**PyDSS** is a high level python interface for **OpenDSS** and provides the following functionalities

Documentation on installation, setup and examples can be found here https://natlabrockies.github.io/PyDSS/index.html
Expand Down
3 changes: 2 additions & 1 deletion src/pydss/SolveMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@


def GetSolver(settings: SimulationSettingsModel, dssInstance):
logger.info('Setting solver to %s mode.', settings.project.simulation_type.value)
# logger.info('Setting solver to %s mode.', settings.project.simulation_type.value)
logger.info(f'Setting solver to {settings.project.simulation_type.value} mode.')
return get_solver_from_simulation_type(settings.project)


Expand Down
9 changes: 8 additions & 1 deletion src/pydss/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pathlib import Path
import ast
import sys
import os

from loguru import logger
import click
Expand Down Expand Up @@ -80,6 +81,11 @@ def run(project_path, options=None, tar_project=False, zip_project=False, verbos
logger.error("Logs path %s does not exist", logs_path)
sys.exit(1)
filename = logs_path / "pydss.log"
if settings.logging.clear_old_log_file:
logs_path = project_path / "Logs"
filename = logs_path / "pydss.log"
if os.path.exists(filename):
os.remove(filename)

logger.level(console_level)
if filename:
Expand All @@ -92,8 +98,9 @@ def run(project_path, options=None, tar_project=False, zip_project=False, verbos
if not isinstance(options, dict):
logger.error("options are invalid: %s", options)
sys.exit(1)

# logger.info(f"indicator 1")
project = PyDssProject.load_project(project_path, options=options, simulation_file=simulations_file)
# logger.info(f"indicator 2")
project.run(tar_project=tar_project, zip_project=zip_project, dry_run=dry_run)

if dry_run:
Expand Down
1 change: 0 additions & 1 deletion src/pydss/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class ControllerType(enum.Enum):
STORAGE_CONTROLLER = "StorageController"
THERMOSTATIC_LOAD_CONTROLLER = "ThermostaticLoad"
XMFR_CONTROLLER = "xmfrController"
DYNAMIC_VOLTAGE_SUPPORT = "DynamicVoltageSupport"

CONTROLLER_TYPES = tuple(x.value for x in ControllerType)
CONFIG_EXT = ".toml"
Expand Down
1 change: 1 addition & 0 deletions src/pydss/dataset_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(
max_chunk_bytes=None, attributes=None, names=None,
column_ranges_per_name=None, data=None
):

if max_chunk_bytes is None:
max_chunk_bytes = DEFAULT_MAX_CHUNK_BYTES
self._buf_index = 0
Expand Down
9 changes: 7 additions & 2 deletions src/pydss/dssElement.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ast

from loguru import logger
from opendssdirect import DSSException

from pydss.dssBus import dssBus
Expand Down Expand Up @@ -60,9 +60,10 @@ def __init__(self, dssInstance):
super(dssElement, self).__init__(dssInstance, name, fullName)
self._Enabled = dssInstance.CktElement.Enabled()
if not self._Enabled:
logger.debug(f"Element isn't defined: {fullName}")
return

self._Parameters = {}
logger.debug(fullName)
self._NumTerminals = dssInstance.CktElement.NumTerminals()
self._NumConductors = dssInstance.CktElement.NumConductors()

Expand Down Expand Up @@ -135,8 +136,12 @@ def DataLength(self, VarName):
return 0, None

def GetValue(self, VarName, convert=False):

if self._dssInstance.Element.Name() != self._FullName:
self.SetActiveObject()
fullName = self._dssInstance.Element.Name()
# logger.debug("123456789123456789123456789123456789123456789123456789")
# logger.debug(fullName)
if VarName in self._Variables:
VarValue = self.GetVariable(VarName, convert=convert)
elif VarName in self._Parameters:
Expand Down
51 changes: 43 additions & 8 deletions src/pydss/dssInstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ def _compile_model(self):
def _CreateControllers(self, ControllerDict):
self._pyControls = {}
self._pyControls_types = {}
# logger.info(f'self._dssObjects -> {self._dssObjects}')
# os.system("PAUSE")
for ControllerType, ElementsDict in ControllerDict.items():
# logger.info(f'ControllerType -> {ControllerType}, ElementsDict -> {ElementsDict}')
# os.system("PAUSE")
for ElmName, SettingsDict in ElementsDict.items():
Controller = pyControllers.pyController.Create(ElmName, ControllerType, SettingsDict, self._dssObjects,
self._dssInstance, self._dssSolver)
Expand Down Expand Up @@ -205,7 +209,8 @@ def CreateDssObjects(dssBuses):
InvalidSelection = ['Settings', 'ActiveClass', 'dss', 'utils', 'PDElements', 'XYCurves', 'Bus', 'Properties']
# TODO: this causes a segmentation fault. Aadil says it may not be needed.
#self._dssObjectsByClass={'LoadShape': self._get_relavent_object_dict('LoadShape')}

# logger.info(f"dss.Circuit.AllElementNames() -> {dss.Circuit.AllElementNames()}")
# os.system("PAUSE")
for ElmName in dss.Circuit.AllElementNames():
Class, Name = ElmName.split('.', 1)
ClassName = Class + 's'
Expand Down Expand Up @@ -246,13 +251,28 @@ def RunStep(self, step, updateObjects=None):
if self._settings.profiles.use_profile_manager:
self.profileStore.update()

if self._settings.helics.co_simulation_mode:
# self._heilcs_interface.updateHelicsPublications()
self._increment_flag, helics_time = self._heilcs_interface.request_time_increment()

if self._settings.helics.co_simulation_mode:
self._heilcs_interface.updateHelicsSubscriptions()
else:
if updateObjects:
for object, params in updateObjects.items():
cl, name = object.split('.')
self._Modifier.Edit_Element(cl, name, params)

# pydss_update_agian = "y"
# while pydss_update_agian == "y":
# if self._settings.helics.co_simulation_mode:
# self._heilcs_interface.updateHelicsSubscriptions()
# else:
# if updateObjects:
# for object, params in updateObjects.items():
# cl, name = object.split('.')
# self._Modifier.Edit_Element(cl, name, params)
# pydss_update_agian = input('enter your pydss update command: ')

# run simulation time step and get results
time_step_has_converged = True
Expand All @@ -263,6 +283,7 @@ def RunStep(self, step, updateObjects=None):
for i in range(self._settings.project.max_control_iterations):
has_converged, error = self._update_controllers(priority, step, i, UpdateResults=False)
logger.debug('Control Loop {} convergence error: {}'.format(priority, error))
logger.debug('Control Loop {} convergence @step {} '.format(priority, step))
if has_converged:
priority_has_converged = True
break
Expand Down Expand Up @@ -297,8 +318,9 @@ def RunStep(self, step, updateObjects=None):

if self._settings.helics.co_simulation_mode:
self._heilcs_interface.updateHelicsPublications()
self._increment_flag, helics_time = self._heilcs_interface.request_time_increment()

# if step < 1:
# self._increment_flag, helics_time = self._heilcs_interface.request_time_increment_2()
# os.system("PAUSE")
return time_step_has_converged

def _HandleConvergenceErrorChecks(self, step, error):
Expand All @@ -316,7 +338,7 @@ def _HandleOpenDSSConvergenceErrorChecks(self, step):
self._convergenceErrorsOpenDSS += 1

if self._maxConvergenceErrorCount is not None and self._convergenceErrorsOpenDSS > self._maxConvergenceErrorCount:
logger.error("Exceeded OpenDSS convergence error count threshold at step %s", step)
logger.error(f"Exceeded OpenDSS convergence error count threshold at step {step}")
raise OpenDssConvergenceErrorCountExceeded(f"{self._convergenceErrorsOpenDSS} errors occurred")

def DryRunSimulation(self, project, scenario):
Expand Down Expand Up @@ -357,7 +379,7 @@ def RunSimulation(self, project, scenario, MC_scenario_number=None):
dss.Solution.Convergence(self._settings.project.error_tolerance)
logger.info('Running simulation from {} till {}.'.format(sTime, eTime))
logger.info('Simulation time step {}.'.format(Steps))
logger.info("Set OpenDSS convergence to %s", dss.Solution.Convergence())
logger.info(f"Set OpenDSS convergence to {dss.Solution.Convergence()}")
logger.info('Max convergence error count {}.'.format(self._maxConvergenceErrorCount))
logger.info("initializing store")
self.ResultContainer.InitializeDataStore(project.hdf_store, Steps, MC_scenario_number)
Expand Down Expand Up @@ -388,19 +410,32 @@ def RunSimulation(self, project, scenario, MC_scenario_number=None):
within_range = self._simulation_range.is_within_range(self._dssSolver.GetDateTime())
if within_range:
pydss_has_converged = self.RunStep(step)
# logger.info(f'Finish simulation: step {step} 1')
# pydss_has_converged = self.RunStep(step)
# logger.info(f'Finish simulation: step {step} 2')
# pydss_has_converged = self.RunStep(step)
# logger.info(f'Finish simulation: step {step} 3')
opendss_has_converged = dss.Solution.Converged()
if not opendss_has_converged:
logger.error("OpenDSS did not converge at step=%s pydss_converged=%s",
step, pydss_has_converged)
logger.error(f"OpenDSS did not converge at step={step} pydss_converged={pydss_has_converged}")
self._HandleOpenDSSConvergenceErrorChecks(step)
has_converged = pydss_has_converged and opendss_has_converged

if pydss_has_converged:
has_converged = True
else:
has_converged = pydss_has_converged and opendss_has_converged

if step == 0 and self.ResultContainer is not None:
size = make_human_readable_size(self.ResultContainer.max_num_bytes())
logger.info('Storage requirement estimation: %s, estimated based on first time step run.', size)
if postprocessors and within_range:
step, has_converged = self._RunPostProcessors(step, Steps, postprocessors)
if self._increment_flag:
step += 1
# if step < 1:
# step += 1
# else:
# step += 2

# In the case of a frequency sweep, the code updates results at each frequency.
# Doing so again would cause a duplicate result.
Expand Down
42 changes: 38 additions & 4 deletions src/pydss/helics_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import helics
import os
import re

import pandas as pd
from loguru import logger

from pydss.simulation_input_models import SimulationSettingsModel
Expand Down Expand Up @@ -208,6 +208,7 @@ def _create_helics_federate(self):
IP = self._settings.helics.broker
Port = self._settings.helics.broker_port
logger.info("Connecting to broker @ {}".format(f"{IP}:{Port}" if Port else IP))
logger.info(f"Connecting to broker @ {self._settings.helics}")
if self._settings.helics.broker:
helics.helicsFederateInfoSetBroker(self.fedinfo, str(self._settings.helics.broker))
if self._settings.helics.broker_port:
Expand Down Expand Up @@ -261,8 +262,9 @@ def updateHelicsSubscriptions(self):
value = helics.helicsInputGetInteger(subscription.sub)

if value and value != 0:
if value > 1e6 or value < -1e6:
value = 1.0
logger.info(f"value is {value}")
if value > 1e6 or value < -1e6 or pd.isna(value):
value = 1.0

value = value * subscription.multiplier
subscription.object.SetParameter(subscription.property, value)
Expand Down Expand Up @@ -337,7 +339,39 @@ def updateHelicsPublications(self):

def request_time_increment(self):
error = sum([abs(sub.states[0] - sub.states[1]) for sub in self.subscriptions.subscriptions])
r_seconds = self._dss_solver.GetTotalSeconds() #- self._dss_solver.GetStepResolutionSeconds()
# r_seconds = self._dss_solver.GetTotalSeconds() # parallel
r_seconds = self._dss_solver.GetTotalSeconds() + self._dss_solver.GetStepResolutionSeconds() # transmission priority
# if r_seconds > 0:
# r_seconds = self._dss_solver.GetTotalSeconds() + self._dss_solver.GetStepResolutionSeconds()
# logger.info(f"self._dss_solver.GetTotalSeconds(): {self._dss_solver.GetTotalSeconds()}, self._dss_solver.GetStepResolutionSeconds(): {self._dss_solver.GetStepResolutionSeconds()}")
# os.system('PAUSE')
if not self._settings.helics.iterative_mode:
while self.c_seconds < r_seconds:
self.c_seconds = helics.helicsFederateRequestTime(self._federate, r_seconds)
logger.info('Time requested: {} - time granted: {} '.format(r_seconds, self.c_seconds))
return True, self.c_seconds
else:

self.c_seconds, iteration_state = helics.helicsFederateRequestTimeIterative(
self._federate,
r_seconds,
helics.helics_iteration_request_iterate_if_needed
)

logger.info('Time requested: {} - time granted: {} error: {} it: {}'.format(
r_seconds, self.c_seconds, error, self.itr))
if error > -1 and self.itr < self._co_convergance_max_iterations - 1:
self.itr += 1
return False, self.c_seconds
else:
self.itr = 0
return True, self.c_seconds

def request_time_increment_2(self):
error = sum([abs(sub.states[0] - sub.states[1]) for sub in self.subscriptions.subscriptions])
r_seconds = self._dss_solver.GetTotalSeconds() + self._dss_solver.GetStepResolutionSeconds()/2
# logger.info('Time requested: {} - self._dss_solver.GetStepResolutionSeconds(): {} '.format(r_seconds, self._dss_solver.GetStepResolutionSeconds()))
# os.system('PAUSE')
if not self._settings.helics.iterative_mode:
while self.c_seconds < r_seconds:
self.c_seconds = helics.helicsFederateRequestTime(self._federate, r_seconds)
Expand Down
2 changes: 1 addition & 1 deletion src/pydss/modes/solver_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, dssInstance, settings: ProjectModel):

#self._dssSolution.DblHour()
self.reSolve()
logger.info("%s solver setup complete", settings.simulation_type)
logger.info(f"{settings.simulation_type} solver setup complete")

def setFrequency(self, frequency):
self._dssSolution.Frequency(frequency)
Expand Down
Loading
Loading