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
54 changes: 39 additions & 15 deletions src/pydss/dssInstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,47 @@ def _CreateControllers(self, ControllerDict):
if controller_name not in self._pyControls_types:
self._pyControls_types[controller_name] = class_name
logger.info('Created pyController -> Controller.' + ElmName)

# --- Batch MotorStall controllers ---
from pydss.pyControllers.Controllers.MotorStall import MotorStall
from pydss.pyControllers.Controllers.MotorStallBatch import MotorStallBatch
motor_stall_keys = [k for k, v in self._pyControls.items()
if isinstance(v, MotorStall)]
if len(motor_stall_keys) > 0:
motor_stall_ctrls = [self._pyControls[k] for k in motor_stall_keys]
batch = MotorStallBatch(motor_stall_ctrls)
# Remove individual controllers, add the batch
for k in motor_stall_keys:
del self._pyControls[k]
self._pyControls['Controller.MotorStallBatch'] = batch
logger.info(f"Batched {len(motor_stall_keys)} MotorStall controllers into MotorStallBatch")

# --- Batch PvVoltageRideThru controllers ---
from pydss.pyControllers.Controllers.PvVoltageRideThru import PvVoltageRideThru
from pydss.pyControllers.Controllers.PvVoltageRideThruBatch import PvVoltageRideThruBatch
pv_rt_keys = [k for k, v in self._pyControls.items()
if isinstance(v, PvVoltageRideThru)]
if len(pv_rt_keys) > 0:
pv_rt_ctrls = [self._pyControls[k] for k in pv_rt_keys]
pv_batch = PvVoltageRideThruBatch(pv_rt_ctrls)
for k in pv_rt_keys:
del self._pyControls[k]
self._pyControls['Controller.PvVoltageRideThruBatch'] = pv_batch
logger.info(f"Batched {len(pv_rt_keys)} PvVoltageRideThru controllers into PvVoltageRideThruBatch")

self._controller_list = list(self._pyControls.values())
self._controllers_by_priority = {p: [] for p in range(CONTROLLER_PRIORITIES)}
for controller in self._controller_list:
for p in getattr(controller, 'ACTIVE_PRIORITIES', range(CONTROLLER_PRIORITIES)):
self._controllers_by_priority[p].append(controller)
return

def _update_controllers(self, Priority, Time, Iteration, UpdateResults):
errors = []
maxError = 0
_pyControls_types = set(self._pyControls_types.values())

for class_name in _pyControls_types:
self._dssInstance.Basic.SetActiveClass(class_name)
elm = self._dssInstance.ActiveClass.First()
while elm:
element_name = self._dssInstance.CktElement.Name()
controller_name = 'Controller.' + element_name
if controller_name in self._pyControls:
controller = self._pyControls[controller_name]
error = controller.Update(Priority, Time, UpdateResults)
maxError = error if error > maxError else maxError
elm = self._dssInstance.ActiveClass.Next()
for controller in self._controllers_by_priority[Priority]:
error = controller.Update(Priority, Time, UpdateResults)
if error > maxError:
maxError = error
return maxError < self._settings.project.error_tolerance, maxError

@staticmethod
Expand Down Expand Up @@ -239,6 +262,7 @@ def _get_relavent_object_dict(self, key):

@track_timing(timer_stats_collector)
def RunStep(self, step, updateObjects=None):

# updating parameters before simulation run
if self._settings.logging.log_time_step_updates:
logger.info(f'Pydss datetime - {self._dssSolver.GetDateTime()}')
Expand Down Expand Up @@ -279,7 +303,6 @@ def RunStep(self, step, updateObjects=None):
logger.warning('Control Loop {} no convergence @ {} '.format(priority, step))
self._HandleConvergenceErrorChecks(step, error)


if self._settings.frequency.enable_frequency_sweep and \
self._settings.project.simulation_type != SimulationType.DYNAMIC:
self._dssSolver.setMode('Harmonic')
Expand Down Expand Up @@ -426,6 +449,7 @@ def RunSimulation(self, project, scenario, MC_scenario_number=None):

if self._settings.exports.export_results:
current_results = self.ResultContainer.CurrentResults

yield False, step, has_converged, current_results

finally:
Expand Down
32 changes: 32 additions & 0 deletions src/pydss/helics_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,18 @@ def _registerFederatePublications(self, publications:Publications = None):
logger.info(str(self.publications.publications))
for publication in self.publications.publications:
logger.info(f"pubscription created: {publication}")

# Register an aggregate generator total-power publication so that
# the co-simulation launcher can subscribe PSSE machines to it.
# Published as [P_kW, Q_kvar] with POSITIVE values for generation.
self._gen_total_pub = None
gen_class = self._objects_by_class.get("Generators", {})
if gen_class:
gen_pub_name = f"{self._settings.helics.federate_name}.Generators.Total.TotalPower"
self._gen_total_pub = helics.helicsFederateRegisterGlobalTypePublication(
self._federate, gen_pub_name, "vector", ""
)
logger.info(f"Registered generator aggregate publication: {gen_pub_name}")
return

def updateHelicsPublications(self):
Expand All @@ -333,6 +345,26 @@ def updateHelicsPublications(self):
else:
raise ValueError("Unsupported data type forr teh HELICS interface")
logger.info(f"{publication} - {value}")

# Publish aggregate generator total power [P_kW, Q_kvar].
# Sign convention: POSITIVE = generation (negated from OpenDSS
# CktElement.Powers which uses load convention, i.e. negative for
# power injected by generators).
if self._gen_total_pub is not None:
total_gen_p = 0.0
total_gen_q = 0.0
for gen_name, gen_obj in self._objects_by_class.get("Generators", {}).items():
powers = gen_obj.GetValue("Powers")
if powers is not None and isinstance(powers, list):
for i in range(0, len(powers), 2):
total_gen_p += powers[i]
if i + 1 < len(powers):
total_gen_q += powers[i + 1]
# Negate: OpenDSS Powers are negative for gen injection
total_gen_p = -total_gen_p
total_gen_q = -total_gen_q
helics.helicsPublicationPublishVector(self._gen_total_pub, [total_gen_p, total_gen_q])
logger.debug(f"Published generator total power: [{total_gen_p:.4f}, {total_gen_q:.4f}]")
return

def request_time_increment(self):
Expand Down
Loading
Loading