Skip to content

Commit

Permalink
Merge branch 'main' into al/config
Browse files Browse the repository at this point in the history
  • Loading branch information
AadilLatif authored Apr 16, 2024
2 parents 790ea5a + 1369855 commit 1173caa
Show file tree
Hide file tree
Showing 20 changed files with 875 additions and 286 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
shell: bash -l {0}
run: |
pip install matplotlib pyarrow numpy matplotlib pandas
pip install oedisi==1.0.0
pip install oedisi==1.2.1
python post_analysis.py outputs_build
- name: Archive logs
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/test-omoo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: RunOMOO

on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ['3.10']
#include:
#- os: ubuntu-latest
#python-version: 3.10

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
- name: Install python dependencies
shell: bash -l {0}
run: |
pip install -r requirements.txt
pip install plotille
pip install click
- name: Run example
shell: bash -l {0}
run: |
git clone https://github.com/openEDI/oedisi-ieee123
mv oedisi-ieee123/profiles LocalFeeder/profiles
mv oedisi-ieee123/qsts LocalFeeder/opendss
# Change every kVA=50 and Pmpp=50 to kVA=200 and Pmpp=200 in LocalFeeder/opendss/IEEE123Pv.dss
sed -i 's/kVA=50/kVA=200/g; s/Pmpp=50/Pmpp=200/g' LocalFeeder/opendss/IEEE123Pv.dss
oedisi build --system scenarios/omoo_system.json
oedisi run
python opf_analysis.py
- name: Archive logs
uses: actions/upload-artifact@v2
if: always()
with:
name: test_logs
path: |
build/*.log
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ COPY README.md .
COPY measuring_federate measuring_federate
COPY wls_federate wls_federate
COPY recorder recorder
COPY omoo_federate omoo_federate

RUN mkdir -p outputs build

Expand Down
143 changes: 105 additions & 38 deletions LocalFeeder/FeederSimulator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Core class to abstract OpenDSS into Feeder class."""

import json
import logging
import math
Expand All @@ -15,10 +16,20 @@
import xarray as xr
from botocore import UNSIGNED
from botocore.config import Config
from dss_functions import (get_capacitors, get_generators, get_loads,
get_pvsystems, get_voltages)
from oedisi.types.data_types import (Command, InverterControl,
InverterControlMode)
from dss_functions import (
get_capacitors,
get_generators,
get_loads,
get_pvsystems,
get_voltages,
)

from oedisi.types.data_types import (
Command,
InverterControl,
InverterControlMode,
IncidenceList,
)
from pydantic import BaseModel
from scipy.sparse import coo_matrix, csc_matrix

Expand Down Expand Up @@ -53,11 +64,12 @@ class FeederConfig(BaseModel):
existing_feeder_file: Optional[str] = None
sensor_location: Optional[str] = None
start_date: str
number_of_timesteps: float
number_of_timesteps: int
run_freq_sec: float = 15 * 60
start_time_index: int = 0
topology_output: str = "topology.json"
use_sparse_admittance: bool = False
tap_setting: Optional[int] = None


class FeederMapping(BaseModel):
Expand Down Expand Up @@ -115,6 +127,8 @@ def __init__(self, config: FeederConfig):
self._number_of_timesteps = config.number_of_timesteps
self._vmult = 0.001

self.tap_setting = config.tap_setting

self._simulation_time_step = "15m"
if config.existing_feeder_file is None:
if self._use_smartds:
Expand Down Expand Up @@ -286,12 +300,15 @@ def load_feeder(self):
self._pvsystems = set()
for PV in get_pvsystems(dss):
self._pvsystems.add("PVSystem." + PV["name"])

if self.tap_setting is not None:
# Doesn't work with AutoTrans or 3-winding transformers.
dss.Text.Command(f"batchedit transformer..* wdg=2 tap={self.tap_setting}")
self._state = OpenDSSState.LOADED

def disable_elements(self):
"""Disable most elements. Used in disabled_run."""
assert self._state != OpenDSSState.UNLOADED, f"{self._state}"
# dss.Text.Command("batchedit transformer..* wdg=2 tap=1")
dss.Text.Command("batchedit regcontrol..* enabled=false")
dss.Text.Command("batchedit vsource..* enabled=false")
dss.Text.Command("batchedit isource..* enabled=false")
Expand Down Expand Up @@ -580,9 +597,9 @@ def _get_voltages(self):
name_voltage_dict = get_voltages(self._circuit)
res_feeder_voltages = np.zeros((len(self._AllNodeNames)), dtype=np.complex_)
for voltage_name in name_voltage_dict.keys():
res_feeder_voltages[
self._name_index_dict[voltage_name]
] = name_voltage_dict[voltage_name]
res_feeder_voltages[self._name_index_dict[voltage_name]] = (
name_voltage_dict[voltage_name]
)

return xr.DataArray(
res_feeder_voltages, {"ids": list(name_voltage_dict.keys())}
Expand Down Expand Up @@ -701,7 +718,8 @@ def set_properties_to_inverter(self, inverter: str, inv_control: InverterControl
)
if inv_control.vwcontrol is not None:
vw_curve = self.create_xy_curve(
inv_control.vwcontrol.voltage, inv_control.vwcontrol.power_response,
inv_control.vwcontrol.voltage,
inv_control.vwcontrol.power_response,
)
dss.Text.Command(f"{inverter}.voltwatt_curve={vw_curve.split('.')[1]}")
dss.Text.Command(
Expand All @@ -713,49 +731,51 @@ def set_properties_to_inverter(self, inverter: str, inv_control: InverterControl
dss.Text.Command(f"{inverter}.Mode = {inv_control.mode.value}")

def set_pv_output(self, pv_system, p, q):
"""Sets the P and Q values for a PV system in OpenDSS
"""

"""Sets the P and Q values for a PV system in OpenDSS"""
max_pv = self.get_max_pv_available(pv_system)
#pf = q / ((p**2 + q **2)**0.5)
# pf = q / ((p**2 + q **2)**0.5)

obj_name = f"PVSystem.{pv_system}"
if max_pv <=0 or p == 0:
if max_pv <= 0 or p == 0:
Warning("Maximum PV Value is 0")
obj_val = 100
q=0
q = 0
elif p < max_pv:
obj_val = p/float(max_pv) *100
obj_val = p / float(max_pv) * 100
else:
obj_val = 100
ratio = float(max_pv)/p
q = q*ratio #adjust q value to that it matches the kw output
command = [Command(obj_name=obj_name,obj_property="%Pmpp",val=str(obj_val)), Command(obj_name=obj_name,obj_property="kvar",val=str(q)), Command(obj_name=obj_name,obj_property="%Cutout", val="0"), Command(obj_name=obj_name,obj_property="%Cutin", val="0")]
ratio = float(max_pv) / p
q = q * ratio # adjust q value to that it matches the kw output
command = [
Command(obj_name=obj_name, obj_property="%Pmpp", val=str(obj_val)),
Command(obj_name=obj_name, obj_property="kvar", val=str(q)),
Command(obj_name=obj_name, obj_property="%Cutout", val="0"),
Command(obj_name=obj_name, obj_property="%Cutin", val="0"),
]
self.change_obj(command)

def get_pv_output(self,pv_system):
dss.PVsystems.First()
while True:
if dss.PVsystems.Name() == pv_system:
kw = dss.PVsystems.kW()
kvar = dss.PVsystems.kvar()
if not dss.PVsystems.Next() > 0:
break
return kw,kvar

def get_max_pv_available(self,pv_system):
dss.PVsystems.First()

def get_max_pv_available(self, pv_system):
irradiance = None
pmpp = None
while True:
flag = dss.PVsystems.First()
while flag:
if dss.PVsystems.Name() == pv_system:
irradiance = dss.PVsystems.Irradiance()
irradiance = dss.PVsystems.IrradianceNow()
pmpp = dss.PVsystems.Pmpp()
if not dss.PVsystems.Next() > 0:
break
flag = dss.PVsystems.Next()
if irradiance is None or pmpp is None:
raise ValueError(f"Irradiance or PMPP not found for {pv_system}")
return irradiance*pmpp
return irradiance * pmpp

def get_available_pv(self):
pv_names = []
powers = []
flag = dss.PVsystems.First()
while flag:
pv_names.append(f"PVSystem.{dss.PVsystems.Name()}")
powers.append(dss.PVsystems.Pmpp() * dss.PVsystems.IrradianceNow())
flag = dss.PVsystems.Next()
return xr.DataArray(powers, coords={"ids": pv_names})

def apply_inverter_control(self, inv_control: InverterControl):
"""Apply inverter control to OpenDSS.
Expand Down Expand Up @@ -801,3 +821,50 @@ def apply_inverter_control(self, inv_control: InverterControl):

self.set_properties_to_inverter(inverter, inv_control)
return inverter

def get_incidences(self) -> IncidenceList:
"""Get Incidence from line names to buses."""
assert self._state != OpenDSSState.UNLOADED, f"{self._state}"
from_list = []
to_list = []
equipment_ids = []
equipment_types = []
for line in dss.Lines.AllNames():
dss.Circuit.SetActiveElement("Line." + line)
names = dss.CktElement.BusNames()
if len(names) != 2:
bus_names = map(lambda x: x.split(".")[0], names)
# dicts are insert-ordered in >=3.7
names = list(dict.fromkeys(bus_names))
if len(names) != 2:
logging.info(
f"Line {line} has {len(names)} terminals, skipping in incidence matrix"
)
continue
from_bus, to_bus = names
from_list.append(from_bus.upper())
to_list.append(to_bus.upper())
equipment_ids.append(line)
equipment_types.append("Line")
for transformer in dss.Transformers.AllNames():
dss.Circuit.SetActiveElement("Transformer." + transformer)
names = dss.CktElement.BusNames()
if len(names) != 2:
bus_names = map(lambda x: x.split(".")[0], names)
names = list(dict.fromkeys(bus_names))
if len(names) != 2:
logging.info(
f"Transformer {transformer} has {len(names)} terminals, skipping in incidence matrix"
)
continue
from_bus, to_bus = names
from_list.append(from_bus.upper())
to_list.append(to_bus.upper())
equipment_ids.append(transformer)
equipment_types.append("Transformer")
return IncidenceList(
from_equipment=from_list,
to_equipment=to_list,
ids=equipment_ids,
equipment_types=equipment_types,
)
92 changes: 76 additions & 16 deletions LocalFeeder/component_definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,84 @@
"directory": "LocalFeeder",
"execute_function": "python sender_cosim.py",
"static_inputs": [
{"type": "", "port_id": "feeder_file"},
{"type": "", "port_id": "start_date"},
{"type": "", "port_id": "run_freq_sec"},
{"type": "", "port_id": "number_of_timesteps"},
{"type": "", "port_id": "start_time_index"}
{
"type": "",
"port_id": "feeder_file"
},
{
"type": "",
"port_id": "start_date"
},
{
"type": "",
"port_id": "run_freq_sec"
},
{
"type": "",
"port_id": "number_of_timesteps"
},
{
"type": "",
"port_id": "start_time_index"
},
{
"type": "",
"port_id": "tap_setting"
}
],
"dynamic_inputs": [
{"type": "CommandList", "port_id": "change_commands", "optional": true},
{"type": "InverterControlList", "port_id": "inv_control", "optional": true}
{
"type": "",
"port_id": "pv_set",
"optional": true
},
{
"type": "CommandList",
"port_id": "change_commands",
"optional": true
},
{
"type": "InverterControlList",
"port_id": "inv_control",
"optional": true
}
],
"dynamic_outputs": [
{"type": "VoltagesMagnitude", "port_id": "voltages_magnitude"},
{"type": "VoltagesReal", "port_id": "voltages_real"},
{"type": "VoltagesImaginary", "port_id": "voltages_imag"},
{"type": "PowersReal", "port_id": "powers_real"},
{"type": "PowersImaginary", "port_id": "powers_imag"},
{"type": "Topology", "port_id": "topology"},
{"type": "Injection", "port_id": "injections"},
{"type": "", "port_id": "load_y_matrix"}
{
"type": "VoltagesMagnitude",
"port_id": "voltages_magnitude"
},
{
"type": "VoltagesReal",
"port_id": "voltages_real"
},
{
"type": "VoltagesImaginary",
"port_id": "voltages_imag"
},
{
"type": "PowersReal",
"port_id": "powers_real"
},
{
"type": "PowersImaginary",
"port_id": "powers_imag"
},
{
"type": "Topology",
"port_id": "topology"
},
{
"type": "Injection",
"port_id": "injections"
},
{
"type": "",
"port_id": "load_y_matrix"
},
{
"type": "PowersReal",
"port_id": "available_power"
}
]
}
}
Loading

0 comments on commit 1173caa

Please sign in to comment.