Skip to content

Add new node-type for OpenDSS #889

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ indent_size = 4
[*.{nix,json}]
indent_style = space
indent_size = 2

[*.patch]
charset = unset
end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ repos:
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: \.patch$
- id: end-of-file-fixer
exclude: \.svg$
exclude: \.(svg|patch)$
- id: check-ast
- id: check-xml
- id: check-yaml
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ find_package(Etherlab)
find_package(Lua)
find_package(LibDataChannel)
find_package(re)
find_package(OpenDSSC)

# Check for tools
find_program(PASTE NAMES paste)
Expand Down Expand Up @@ -203,6 +204,7 @@ cmake_dependent_option(WITH_NODE_ULDAQ "Build with uldaq node-type"
cmake_dependent_option(WITH_NODE_WEBRTC "Build with webrtc node-type" "${WITH_DEFAULTS}" "WITH_WEB; LibDataChannel_FOUND" OFF)
cmake_dependent_option(WITH_NODE_WEBSOCKET "Build with websocket node-type" "${WITH_DEFAULTS}" "WITH_WEB" OFF)
cmake_dependent_option(WITH_NODE_ZEROMQ "Build with zeromq node-type" "${WITH_DEFAULTS}" "LIBZMQ_FOUND; NOT WITHOUT_GPL" OFF)
cmake_dependent_option(WITH_NODE_OPENDSS "Build with opendss node-type" "${WITH_DEFAULTS}" "OpenDSSC_FOUND" OFF)

# set a default for the build type
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
Expand Down Expand Up @@ -296,6 +298,7 @@ add_feature_info(NODE_MODBUS WITH_NODE_MODBUS "Build with
add_feature_info(NODE_MQTT WITH_NODE_MQTT "Build with mqtt node-type")
add_feature_info(NODE_NANOMSG WITH_NODE_NANOMSG "Build with nanomsg node-type")
add_feature_info(NODE_NGSI WITH_NODE_NGSI "Build with ngsi node-type")
add_feature_info(NODE_OPENDSS WITH_NODE_OPENDSS "Build with opendss node-type")
add_feature_info(NODE_REDIS WITH_NODE_REDIS "Build with redis node-type")
add_feature_info(NODE_RTP WITH_NODE_RTP "Build with rtp node-type")
add_feature_info(NODE_SHMEM WITH_NODE_SHMEM "Build with shmem node-type")
Expand Down
11 changes: 10 additions & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# SPDX-FileCopyrightText: 2025 OPAL-RT Germany GmbH
# SPDX-License-Identifier: Apache-2.0

version = 1
SPDX-PackageName = "VILLASnode"
SPDX-PackageSupplier = "Steffen Vogel <[email protected]>"
SPDX-PackageDownloadLocation = "https://fein-aachen.org/en/projects/villas-node/"

[[annotations]]
path = ["**.prefs", "**.vi", "**.opal", "**.dft", "**.sib", "**.json", "**.ipynb", "**_pb2.py", "doc/pictures/**", "doc/favicon.png", "clients/opal-rt/rtlab-asyncip/models/send_receive/eonerc_logo.png", "clients/rtds/**/**.txt", "clients/opal-rt/hypersim-ucm/**.ecf", "etc/labs/lab3.pcap", "packaging/live-iso/files/etc/**", "flake.lock", "tests/valgrind.supp", "packaging/archlinux/villas-node.install"]
path = ["**.prefs", "**.vi", "**.opal", "**.dft", "**.sib", "**.json", "**.ipynb", "**_pb2.py", "doc/pictures/**", "doc/favicon.png", "clients/opal-rt/rtlab-asyncip/models/send_receive/eonerc_logo.png", "clients/rtds/**/**.txt", "clients/opal-rt/hypersim-ucm/**.ecf", "etc/labs/lab3.pcap", "packaging/live-iso/files/etc/**", "tests/valgrind.supp", "packaging/archlinux/villas-node.install"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2018-2023, Institute for Automation of Complex Power Systems, RWTH Aachen University"
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ["flake.lock", "**.patch"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 OPAL-RT Germany GmbH"
SPDX-License-Identifier = "Apache-2.0"
29 changes: 29 additions & 0 deletions cmake/FindOpenDSSC.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# CMakeLists.txt.
#
# Author: Jitpanu Maneeratpongsuk <[email protected]>
# SPDX-FileCopyrightText: 2025 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0

find_path(OPENDSSC_INCLUDE_DIR
NAMES OpenDSSCDLL.h
PATH_SUFFIXES OpenDSSC
)

find_library(OPENDSSC_LIBRARY_DIR
NAMES OpenDSSC
PATH_SUFFIXES openDSSC/bin
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OpenDSSC DEFAULT_MSG OPENDSSC_LIBRARY_DIR OPENDSSC_INCLUDE_DIR)

mark_as_advanced(OPENDSSC_INCLUDE_DIR OPENDSS_LIBRARY_DIR)

if(OpenDSSC_FOUND)
add_library(OpenDSSC SHARED IMPORTED)
set_target_properties(OpenDSSC PROPERTIES
IMPORTED_LOCATION "${OPENDSSC_LIBRARY_DIR}"
INTERFACE_INCLUDE_DIRECTORIES "${OPENDSSC_INCLUDE_DIR}"
INTERFACE_COMPILE_OPTIONS "-D_D2C_SYSFILE_H_LONG_IS_INT64"
)
endif()
7 changes: 7 additions & 0 deletions doc/openapi/components/schemas/config/nodes/_opendss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# yaml-language-server: $schema=http://json-schema.org/draft-07/schema
# SPDX-FileCopyrightText: 2025 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0
---
allOf:
- $ref: ../node_obj.yaml
- $ref: opendss.yaml
49 changes: 49 additions & 0 deletions doc/openapi/components/schemas/config/nodes/opendss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# yaml-language-server: $schema=http://json-schema.org/draft-07/schema
# SPDX-FileCopyrightText: 2025 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0
---
allOf:
- type: object
properties:

path:
file_path: string
format: uri
description: |
Specifies the URI to a OpenDSS file.

in:
type: array
item:
type: object
properties:
name:
type: string
description: |
Name of the element.
type:
type: string
enum:
- load
- generator
- isource
description: |
Type of the element.
data:
type: string
description: |
Data to be input. Possible option are depent on element type.

- load: kV, kW, kVA, Pf
- generator: kV, kW, kVA, Pf
- isource: Amps, AngleDeg, f

out:
description: |
Name of the monitor to be read.
type: array
items:
type: string

- $ref: ../node_signals.yaml
- $ref: ../node.yaml
74 changes: 74 additions & 0 deletions etc/examples/nodes/opendss.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SPDX-FileCopyrightText: 2025 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0

nodes = {
opendss_node = {
type = "opendss"

# Path to OpenDSS file
file_path = "OpenDSS_file/sample.DSS"

# Example input configuration. Input data will be used to set Active power and power factor
# of load element name load1 and Active power of the generator element name gen1.
# Element name are deleared in OpenDSS file.
# Available input type and data:
# - load: kV, kW, kVA, Pf
# - generator: kV, kW, kVA, Pf
# - isource: Amps, AngleDeg, f
in = {
list = (
{name = "load1", type = "load", data = ("kW", "Pf")},
{name = "gen1", type = "generator", data = ("kW")}
)
}

# Example output configuration. Output data will be read from monitor name load1_power and load1_v.
# Monitor name are declared in OpenDSS file.
out = {
list = ["load1_power", "load1_v"]
}
}

udp_node = {
type = "socket"

layer = "udp"

format = "villas.human"

in = {
address = "*:12000"
}
out = {
address = "127.0.0.1:12001"
}
}

file_node1 = {
type = "file"

uri = "load.dat"

in = {
epoch_mode = "direct"
epoch = 10
rate = 2
buffer_size = 0
}
}
}

paths = (
{
# Get input for file node type
in = "file_node1"
out = "opendss_node"
hooks = ( { type = "print" } )
},
{
# Output to udp node type
in = "opendss_node"
out = "udp_node"
hooks = ( { type = "print" } )
}
)
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
contents = [ villas-node ];
config.ENTRYPOINT = "/bin/villas";
};

opendssc = pkgs.callPackage (nixDir + "/opendssc.nix") { };
};
in
{
Expand Down
77 changes: 77 additions & 0 deletions include/villas/nodes/opendss.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* OpenDSS node-type for electric power distribution system simulator OpenDSS.
*
* Author: Jitpanu Maneeratpongsuk <[email protected]>
* SPDX-FileCopyrightText: 2025 Institute for Automation of Complex Power Systems, RWTH Aachen University
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <unordered_set>

#include <villas/format.hpp>
#include <villas/node.hpp>
#include <villas/node/config.hpp>
#include <villas/node_compat.hpp>
#include <villas/timing.hpp>

namespace villas {
namespace node {

// Forward declarations
struct Sample;

class OpenDSS : public Node {
protected:
enum ElementType { generator, load, monitor, isource };

struct Element {
std::string
name; // Name of the element must be that same as declared in OpenDSS file
ElementType type; // Type of the element
std::vector<int>
mode; // Mode is set according to the function mode in OpenDSS
};

bool writingTurn;
const char *path;
timespec ts;

std::vector<Element> dataIn; // Vector of elements to be written.
std::vector<std::string> monitorNames;
std::unordered_set<std::string> loads, generators, monitors,
isource_set; // Set of corresponding element type.

pthread_mutex_t mutex;
pthread_cond_t cv;

virtual int _read(struct Sample *smps[], unsigned cnt);

virtual int _write(struct Sample *smps[], unsigned cnt);

void parseData(json_t *json, bool in);

void getElementName(ElementType type, std::unordered_set<std::string> *set);

int extractMonitorData(struct Sample *const *smps);

std::string dssPutCommand(const std::string &cmd) const;

public:
OpenDSS(const uuid_t &id = {}, const std::string &name = "");

virtual ~OpenDSS();

virtual int prepare();

virtual int parse(json_t *json);

virtual int check();

virtual int start();

virtual int stop();
};

} // namespace node
} // namespace villas
10 changes: 8 additions & 2 deletions lib/nodes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,14 @@ endif()
if(WITH_NODE_EXAMPLE)
list(APPEND NODE_SRC example.cpp)

# Add your library dependencies for the new node-type here
# list(APPEND LIBRARIES PkgConfig::EXAMPLELIB)
# Add your library dependencies for the new node-type here
# list(APPEND LIBRARIES PkgConfig::EXAMPLELIB)
endif()

# Enable OpenDSS node type
if(WITH_NODE_OPENDSS)
list(APPEND NODE_SRC opendss.cpp)
list(APPEND LIBRARIES OpenDSSC)
endif()

# Enable Ethercat support
Expand Down
Loading