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
7 changes: 7 additions & 0 deletions src/ditto/writers/opendss/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@
)
from ditto.writers.opendss.profile import ProfileMapper
from ditto.writers.opendss.components.distribution_solar import DistributionSolarMapper

from ditto.writers.opendss.components.matrix_impedance_recloser import (
MatrixImpedanceRecloserMapper,
)
from ditto.writers.opendss.equipment.matrix_impedance_recloser_equipment import (
MatrixImpedanceRecloserEquipmentMapper,
)
6 changes: 3 additions & 3 deletions src/ditto/writers/opendss/components/distribution_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.LINES_FILE.value

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_buses(self):
self.opendss_dict["Bus1"] = self.model.buses[0].name
self.opendss_dict["Bus2"] = self.model.buses[1].name
self.opendss_dict["Bus1"] = self.get_opendss_safe_name(self.model.buses[0].name)
self.opendss_dict["Bus2"] = self.get_opendss_safe_name(self.model.buses[1].name)
for phase in self.model.phases:
if phase != Phase.N:
self.opendss_dict["Bus1"] += self.phase_map[phase]
Expand Down
2 changes: 1 addition & 1 deletion src/ditto/writers/opendss/components/distribution_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.COORDINATE_FILE.value

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_coordinate(self):
if hasattr(self.model.coordinate, "x"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_bus(self):
self.opendss_dict["Bus1"] = self.model.bus.name
self.opendss_dict["Bus1"] = self.get_opendss_safe_name(self.model.bus.name)
num_phases = len(self.model.phases)
for phase in self.model.phases:
self.opendss_dict["Bus1"] += self.phase_map[phase]
Expand Down
4 changes: 2 additions & 2 deletions src/ditto/writers/opendss/components/distribution_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

profile_name = self.get_profile_name(self.model)
if profile_name:
self.opendss_dict["Yearly"] = profile_name

def map_bus(self):
num_phases = len(self.model.phases)
self.opendss_dict["Bus1"] = self.model.bus.name
self.opendss_dict["Bus1"] = self.get_opendss_safe_name(self.model.bus.name)
for phase in self.model.phases:
self.opendss_dict["Bus1"] += self.phase_map[phase]
# TODO: Should we include the phases its connected to here?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_buses(self):
buses = []
Expand All @@ -27,7 +27,7 @@ def map_buses(self):
if is_center_tapped:
for i in range(len(self.model.buses)):
bus = self.model.buses[i]
buses.append(bus.name)
buses.append(self.get_opendss_safe_name(bus.name))
dss_phases = ""
for phase in self.model.winding_phases[0]:
dss_phases += self.phase_map[phase]
Expand All @@ -37,7 +37,7 @@ def map_buses(self):

else:
for bus in self.model.buses:
buses.append(bus.name)
buses.append(self.get_opendss_safe_name(bus.name))
for winding_phases in self.model.winding_phases:
dss_phases = ""
for phase in winding_phases:
Expand All @@ -54,7 +54,7 @@ def map_winding_phases(self):

def map_equipment(self):
equipment = self.model.equipment
self.opendss_dict["XfmrCode"] = equipment.name
self.opendss_dict["XfmrCode"] = self.get_opendss_safe_name(equipment.name)

def map_controllers(self):
...
4 changes: 2 additions & 2 deletions src/ditto/writers/opendss/components/distribution_solar.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

profile_name = self.get_profile_name(self.model)
if profile_name:
self.opendss_dict["Yearly"] = profile_name

def map_bus(self):
num_phases = len(self.model.phases)
self.opendss_dict["Bus1"] = self.model.bus.name
self.opendss_dict["Bus1"] = self.get_opendss_safe_name(self.model.bus.name)
for phase in self.model.phases:
self.opendss_dict["Bus1"] += self.phase_map[phase]
# TODO: Should we include the phases its connected to here?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_buses(self):
buses = []
Expand All @@ -28,7 +28,7 @@ def map_buses(self):
if is_center_tapped:
for i in range(len(self.model.buses)):
bus = self.model.buses[i]
buses.append(bus.name)
buses.append(self.get_opendss_safe_name(bus.name))
dss_phases = ""
for phase in self.model.winding_phases[0]:
dss_phases += self.phase_map[phase]
Expand All @@ -38,7 +38,7 @@ def map_buses(self):

else:
for bus in self.model.buses:
buses.append(bus.name)
buses.append(self.get_opendss_safe_name(bus.name))
for winding_phases in self.model.winding_phases:
dss_phases = ""
for phase in winding_phases:
Expand All @@ -55,4 +55,4 @@ def map_winding_phases(self):

def map_equipment(self):
equipment = self.model.equipment
self.opendss_dict["XfmrCode"] = equipment.name
self.opendss_dict["XfmrCode"] = self.get_opendss_safe_name(equipment.name)
16 changes: 13 additions & 3 deletions src/ditto/writers/opendss/components/distribution_vsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

profile_name = self.get_profile_name(self.model)
if profile_name:
self.opendss_dict["Yearly"] = profile_name

def map_bus(self):
self.opendss_dict["Bus1"] = self.model.bus.name
self.opendss_dict["Bus1"] = self.get_opendss_safe_name(self.model.bus.name)
for phase in self.model.phases:
self.opendss_dict["Bus1"] += self.phase_map[phase]

Expand Down Expand Up @@ -71,7 +71,17 @@ def map_equipment(self):
else:
v_mag = voltage.magnitude

v_nom = rated_voltage.magnitude if num_phases == 1 else rated_voltage.magnitude * 1.732
if self.model.bus.voltage_type == VoltageTypes.LINE_TO_GROUND:
if num_phases == 1:
v_nom = rated_voltage.magnitude
else:
v_nom = rated_voltage.magnitude * 1.732
else:
if num_phases == 1:
v_nom = rated_voltage.magnitude / 1.732
else:
v_nom = rated_voltage.magnitude

self.opendss_dict["Angle"] = angle.magnitude
self.opendss_dict["pu"] = v_mag / v_nom
self.opendss_dict["BasekV"] = v_nom
Expand Down
2 changes: 1 addition & 1 deletion src/ditto/writers/opendss/components/geometry_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.LINES_FILE.value

def map_equipment(self):
self.opendss_dict["Geometry"] = self.model.equipment.name
self.opendss_dict["Geometry"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.LINES_FILE.value

def map_equipment(self):
self.opendss_dict["LineCode"] = self.model.equipment.name
self.opendss_dict["LineCode"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.FUSE_FILE.value

def map_equipment(self):
self.opendss_dict["LineCode"] = self.model.equipment.name
self.opendss_dict["LineCode"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_is_closed(self):
# Require every phase to be enabled for the OpenDSS line to be enabled.
self.opendss_dict["Switch"] = "true"

def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service
28 changes: 28 additions & 0 deletions src/ditto/writers/opendss/components/matrix_impedance_recloser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from gdm.distribution import DistributionSystem
from infrasys import Component


from ditto.writers.opendss.components.distribution_branch import DistributionBranchMapper
from ditto.enumerations import OpenDSSFileTypes


class MatrixImpedanceRecloserMapper(DistributionBranchMapper):
def __init__(self, model: Component, system: DistributionSystem):
super().__init__(model, system)

altdss_name = "Line_LineCode"
altdss_composition_name = "Line"
opendss_file = OpenDSSFileTypes.SWITCH_FILE.value

def map_equipment(self):
self.opendss_dict["LineCode"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_is_closed(self):
# Require every phase to be enabled for the OpenDSS line to be enabled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a switch by default, and the is_closed be covered by in_service?
How is in_service different from is_closed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to question this. I did not and just copied what's here from the matrix_impedance_switch mapper.

self.opendss_dict["Switch"] = "true"

def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service

def map_controller(self):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.SWITCH_FILE.value

def map_equipment(self):
self.opendss_dict["LineCode"] = self.model.equipment.name
self.opendss_dict["LineCode"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_is_closed(self):
# Require every phase to be enabled for the OpenDSS line to be enabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.LINES_FILE.value

def map_equipment(self):
self.opendss_dict["LineCode"] = self.model.equipment.name
self.opendss_dict["LineCode"] = self.get_opendss_safe_name(self.model.equipment.name)

def map_in_service(self):
self.opendss_dict["enabled"] = self.model.in_service
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def __init__(self, model: RegulatorController, xfmr_name: str, system: Distribut
self.xfmr_name = xfmr_name

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Transformer"] = self.xfmr_name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)
self.opendss_dict["Transformer"] = self.get_opendss_safe_name(self.xfmr_name)

def map_delay(self):
self.opendss_dict["TapDelay"] = self.model.delay.to("s").magnitude
Expand Down Expand Up @@ -59,7 +59,7 @@ def map_bandwidth(self):
def map_controlled_bus(self):
self.opendss_dict[
"Bus"
] = f"{self.model.controlled_bus.name}{self.phase_map[self.model.controlled_phase]}"
] = f"{self.get_opendss_safe_name(self.model.controlled_bus.name)}{self.phase_map[self.model.controlled_phase]}"

def map_controlled_phase(self):
...
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.WIRES_FILE.value

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_conductor_diameter(self):
self.opendss_dict["Radius"] = self.model.conductor_diameter.magnitude / 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.TRANSFORMERS_FILE.value

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_pct_no_load_loss(self):
self.opendss_dict["pctNoLoadLoss"] = self.model.pct_no_load_loss
Expand Down Expand Up @@ -44,13 +44,20 @@ def map_windings(self):
num_phases = winding.num_phases
# rated_voltage
nom_voltage = winding.rated_voltage.to("kV").magnitude
kvs.append(nom_voltage if num_phases == 1 else nom_voltage * 1.732)
voltage_type = winding.voltage_type
connection_type = winding.connection_type
nom_voltage = nom_voltage if voltage_type == "line-to-ground" else nom_voltage / 1.732
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will work for now, but we should create an issue to deal with the line-to-ground vs line-line voltage issue, so that we're really clear about how we standardize the voltage measurements in ditto or GDM. This seems like something that we should have a better long-term solution for than dividing by a magic number.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, happy to change this to at least a constant or have a function that clearly states the transformation. I think there will always need to be some wrangling at this step give how particular opendss is on where it expects L-L vs L-N

kvs.append(
nom_voltage
if num_phases == 1 and connection_type != "DELTA"
else nom_voltage * 1.732
)
# resistance
pctRs.append(winding.resistance)
# rated_power
kVAs.append(winding.rated_power.to("kva").magnitude)
# connection_type
conns.append(self.connection_map[winding.connection_type])
conns.append(self.connection_map[connection_type])
# TODO: num_phases and is_grounded aren't included
if self.model.is_center_tapped and i == len(self.model.windings):
kvs.append(nom_voltage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, model: Component, system: DistributionSystem):
opendss_file = OpenDSSFileTypes.LINECODES_FILE.value

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_common(self):
units = []
Expand Down Expand Up @@ -58,7 +58,7 @@ def map_conductors(self):
# conductor_type = 'tsdata'
else:
raise ValueError(f"Unknown conductor type {conductor}")
all_conductors.append(f"{conductor_type}.{conductor.name}")
all_conductors.append(f"{conductor_type}.{self.get_opendss_safe_name(conductor.name)}")
self.opendss_dict["Conductors"] = all_conductors

def map_insulation(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,31 @@ def map_common(self):
self.opendss_dict["Units"] = "km"

def map_name(self):
self.opendss_dict["Name"] = self.model.name
self.opendss_dict["Name"] = self.get_opendss_safe_name(self.model.name)

def map_r_matrix(self):
r_matrix_ohms = self.model.r_matrix.to("ohm/km")
self.opendss_dict["RMatrix"] = r_matrix_ohms.magnitude
assert (
abs(r_matrix_ohms.magnitude - r_matrix_ohms.T.magnitude).max() < 1e-6
), "RMatrix must be symmetric"
r_matrix_ohms = (r_matrix_ohms.magnitude + r_matrix_ohms.T.magnitude) / 2
self.opendss_dict["RMatrix"] = r_matrix_ohms

def map_x_matrix(self):
x_matrix_ohms = self.model.x_matrix.to("ohm/km")
self.opendss_dict["XMatrix"] = x_matrix_ohms.magnitude
assert (
abs(x_matrix_ohms.magnitude - x_matrix_ohms.T.magnitude).max() < 1e-6
), "XMatrix must be symmetric"
x_matrix_ohms = (x_matrix_ohms.magnitude + x_matrix_ohms.T.magnitude) / 2
self.opendss_dict["XMatrix"] = x_matrix_ohms

def map_c_matrix(self):
c_matrix_nf = self.model.c_matrix.to("nanofarad/km")
self.opendss_dict["CMatrix"] = c_matrix_nf.magnitude
assert (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, are these best as assertions or GDM validations?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's a good question. I would be fine moving this exact assert to GDM validation if we don't want non-symmetric matrices. But i'm not sure that is a restriction that needs to be in GDM- i'm sort of fine for them to exist.

The reason this is adjustment is here is because altdss has a very strict rule for symmetry. Even if we were to add this as a validation to GDM, I would tend to not go so strict to avoid these floating point equality issues. And then this check would be required anyway.

abs(c_matrix_nf.magnitude - c_matrix_nf.T.magnitude).max() < 1e-6
), "CMatrix must be symmetric"
c_matrix_nf = (c_matrix_nf.magnitude + c_matrix_nf.T.magnitude) / 2
self.opendss_dict["CMatrix"] = c_matrix_nf

def map_ampacity(self):
ampacity_amps = self.model.ampacity.to("ampere")
Expand Down
Loading