Skip to content

Commit c5f23cc

Browse files
authored
Merge pull request #28 from sogno-platform/villas-import
The villas import didn't work for powerflow simulations with PQ loads due to two reasons: The SP load didn't use the P and Q attributes internally so that it didn't matter if they pointed to the attributes used by the villas import. The villas import tasks was dropped by the scheduler since the task system is not fully developed for the PF solver yet. Both issues have been fixed in this PR.
2 parents 68325bc + 7827bba commit c5f23cc

File tree

2 files changed

+133
-8
lines changed

2 files changed

+133
-8
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# This example reads a sine signal from villas that modifies the active power set point
2+
# of the PQ load. The n2 voltage is exported to a file via villas.
3+
4+
import sys
5+
import os.path
6+
import logging
7+
import json
8+
9+
from datetime import datetime
10+
from villas.node.node import Node as VILLASnode
11+
12+
import dpsimpy
13+
import dpsimpyvillas
14+
15+
base = os.path.splitext(os.path.basename(sys.argv[0]))[0]
16+
log = logging.getLogger(base)
17+
18+
def villas(intf):
19+
20+
log_filename=datetime.now().strftime(f'{base}-villas-%y-%m-%d_%H_%M_%S.log')
21+
22+
nodes = {
23+
'dpsim1': intf.get_config(),
24+
'file1': {
25+
'type': 'file',
26+
'uri': f'{base}-results-%y-%m-%d_%H_%M_%S.csv'
27+
},
28+
'sine' : {
29+
'type': 'signal',
30+
'signal': 'sine',
31+
'rate': 1,
32+
'frequency': 0.1,
33+
'amplitude': 50000,
34+
'offset': 100000
35+
},
36+
}
37+
38+
paths = [
39+
{
40+
'in': 'dpsim1',
41+
'out': 'file1'
42+
},
43+
{
44+
'in': 'sine',
45+
'out': 'dpsim1',
46+
'hooks': [{'type':'print'}]
47+
}
48+
]
49+
50+
config = {
51+
'nodes': nodes,
52+
'paths': paths
53+
}
54+
config['nodes']['dpsim1']['out']['hooks'] = [{'type':'print'}]
55+
56+
log.info('VILLASnode config: \n%s', json.dumps(config, indent=2))
57+
58+
return VILLASnode(config=config, log_filename=log_filename)
59+
60+
def dpsim():
61+
# Parameters
62+
V_nom = 20e3
63+
p_load_nom = 100e3
64+
q_load_nom = 50e3
65+
line_resistance = 0.05
66+
line_inductance = 0.1
67+
line_capacitance = 0.1e-6
68+
name = 'test_shmem_import_export'
69+
70+
# Nodes and Components
71+
n1 = dpsimpy.sp.SimNode('n1', dpsimpy.PhaseType.Single)
72+
n2 = dpsimpy.sp.SimNode('n2', dpsimpy.PhaseType.Single)
73+
74+
extnet = dpsimpy.sp.ph1.NetworkInjection('Slack')
75+
extnet.set_parameters(voltage_set_point=V_nom)
76+
extnet.set_base_voltage(V_nom)
77+
extnet.modify_power_flow_bus_type(dpsimpy.PowerflowBusType.VD)
78+
79+
line = dpsimpy.sp.ph1.PiLine('PiLine')
80+
line.set_parameters(R=line_resistance, L=line_inductance, C=line_capacitance)
81+
line.set_base_voltage(V_nom)
82+
83+
load = dpsimpy.sp.ph1.Load('Load')
84+
load.set_parameters(active_power=p_load_nom, reactive_power=q_load_nom, nominal_voltage=V_nom)
85+
load.modify_power_flow_bus_type(dpsimpy.PowerflowBusType.PQ)
86+
87+
extnet.connect([n1])
88+
line.connect([n1, n2])
89+
load.connect([n2])
90+
system = dpsimpy.SystemTopology(50, [n1, n2], [extnet, line, load])
91+
92+
sim = dpsimpy.RealTimeSimulation(name, dpsimpy.LogLevel.debug)
93+
sim.set_system(system)
94+
sim.set_domain(dpsimpy.Domain.SP)
95+
sim.set_solver(dpsimpy.Solver.NRP)
96+
sim.set_time_step(1)
97+
sim.set_final_time(10)
98+
sim.do_init_from_nodes_and_terminals(False)
99+
100+
logger = dpsimpy.Logger(name)
101+
sim.add_logger(logger)
102+
sim.log_attr('n1', 'v')
103+
sim.log_attr('n2', 'v')
104+
105+
intf = dpsimpyvillas.InterfaceShmem()
106+
sim.add_interface(intf)
107+
sim.import_attr('Load', 'P', 0)
108+
sim.export_attr('n2', 'v', 0, dpsimpy.AttrModifier.mag)
109+
110+
return sim, intf
111+
112+
def test_shmem_import_export():
113+
logging.basicConfig(format='[%(asctime)s %(name)s %(levelname)s] %(message)s', datefmt='%H:%M:%S', level=logging.INFO)
114+
115+
sim, intf = dpsim()
116+
node = villas(intf)
117+
118+
node.start()
119+
120+
sim.run(1)
121+
122+
node.stop()
123+
124+
if __name__ == '__main__':
125+
test_shmem_import_export()

models/Source/SP/SP_Ph1_Load.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ void SP::Ph1::Load::calculatePerUnitParameters(Real baseApparentPower, Real base
5656
mBaseOmega = baseOmega;
5757
mSLog->info("Base Power={} [VA] Base Omega={} [1/s]", mBaseApparentPower, mBaseOmega);
5858

59-
mActivePowerPerUnit = mActivePower/mBaseApparentPower;
60-
mReactivePowerPerUnit = mReactivePower/mBaseApparentPower;
59+
mActivePowerPerUnit = attribute<Real>("P")->get()/mBaseApparentPower;
60+
mReactivePowerPerUnit = attribute<Real>("Q")->get()/mBaseApparentPower;
6161
mSLog->info("Active Power={} [pu] Reactive Power={} [pu]", mActivePowerPerUnit, mReactivePowerPerUnit);
6262
mSLog->flush();
6363
}
@@ -108,8 +108,8 @@ void SP::Ph1::Load::initializeFromNodesAndTerminals(Real frequency) {
108108
}
109109

110110
// instantiate subResistor for active power consumption
111-
if (mActivePower != 0) {
112-
mResistance = std::pow(mNomVoltage, 2) / mActivePower;
111+
if (attribute<Real>("P")->get() != 0) {
112+
mResistance = std::pow(mNomVoltage, 2) / attribute<Real>("P")->get();
113113
mConductance = 1.0 / mResistance;
114114
mSubResistor = std::make_shared<SP::Ph1::Resistor>(mUID + "_res", mName + "_res", Logger::Level::off);
115115
mSubResistor->setParameters(mResistance);
@@ -118,8 +118,8 @@ void SP::Ph1::Load::initializeFromNodesAndTerminals(Real frequency) {
118118
mSubResistor->initializeFromNodesAndTerminals(frequency);
119119
}
120120

121-
if (mReactivePower != 0)
122-
mReactance = std::pow(mNomVoltage, 2) / mReactivePower;
121+
if (attribute<Real>("Q")->get() != 0)
122+
mReactance = std::pow(mNomVoltage, 2) / attribute<Real>("Q")->get();
123123
else
124124
mReactance = 0;
125125

@@ -141,7 +141,7 @@ void SP::Ph1::Load::initializeFromNodesAndTerminals(Real frequency) {
141141
}
142142

143143
mIntfVoltage(0, 0) = mTerminals[0]->initialSingleVoltage();
144-
mIntfCurrent(0, 0) = std::conj(Complex(mActivePower, mReactivePower) / mIntfVoltage(0, 0));
144+
mIntfCurrent(0, 0) = std::conj(Complex(attribute<Real>("P")->get(), attribute<Real>("Q")->get()) / mIntfVoltage(0, 0));
145145

146146
mSLog->info(
147147
"\n--- Initialization from powerflow ---"
@@ -154,7 +154,7 @@ void SP::Ph1::Load::initializeFromNodesAndTerminals(Real frequency) {
154154
Logger::phasorToString(initialSingleVoltage(0)));
155155
mSLog->info(
156156
"Updated parameters according to powerflow:\n"
157-
"Active Power={} [W] Reactive Power={} [VAr]", mActivePower, mReactivePower);
157+
"Active Power={} [W] Reactive Power={} [VAr]", attribute<Real>("P")->get(), attribute<Real>("Q")->get());
158158
mSLog->flush();
159159
}
160160

0 commit comments

Comments
 (0)