Skip to content

Commit 2c6ed55

Browse files
authored
Merge pull request #11 from idaholab/develop
Merge in Develop into Main branch (v.1.1)
2 parents ea3e8ff + a5c4b46 commit 2c6ed55

11 files changed

Lines changed: 239 additions & 23 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ outputs/*.csv
1414

1515
CMakeSettings.json
1616
.vs/
17+
.vscode/

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ endif()
1818
# Find pybind11
1919
find_package(pybind11 CONFIG)
2020

21+
# Make sure C++11 flag is set for macOS.
22+
if(APPLE)
23+
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang\$")
24+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
25+
endif()
26+
endif()
27+
2128
# Update submodules
2229
find_package(Git QUIET)
2330
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,51 @@ Caldera Grid has the following requirements to be able to compile on windows
8585
build -> Install Grid
8686
```
8787

88+
89+
##### On Ubuntu Linux
90+
91+
```
92+
First, installed Ubuntu.
93+
sudo apt inatall git
94+
mkdir ~/Documents/dev
95+
Checked out the repos, put in ~/Documents/dev
96+
sudo apt install cmake
97+
sudo apt install build-essential
98+
99+
cd ~/Documents/
100+
wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-Linux-x86_64.sh
101+
bash Miniconda3-py39_4.12.0-Linux-x86_64.sh
102+
103+
(installed anaconda)
104+
(then restarted the terminal)
105+
106+
conda create -n caldera python=3.7
107+
conda activate caldera
108+
pip install helics
109+
conda install pandas numpy scipy cvxopt
110+
pip install cython
111+
pip install 'OpenDSSDirect.py[extras]'
112+
pip install "pybind11[global]"
113+
114+
cd Caldera_Grid
115+
git switch develop
116+
mkdir build
117+
cd build
118+
cmake -DPROJECT=eMosaic -DICM=ON ../
119+
make -j 4
120+
make install
121+
```
122+
123+
##### Notes for macOS
124+
```
125+
To install anaconda:
126+
-------
127+
brew install --cask anaconda
128+
source /usr/local/anaconda3/bin/activate
129+
conda create -n caldera python=3.7
130+
conda activate caldera
131+
```
132+
88133
#### Running Caldera Grid
89134

90135
1. Open Anaconda prompt

inputs/parameters/VS100.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Key,Value,Description
22
target_P3_reference__percent_of_maxP3,90,
33
max_delta_kW_per_min,1000,
4-
volt_delta_kW_curve,0.95|-40 ; 0.99|-2 ; 1|0 ; 1.03|0 ; 1.05|10,V1|deltaP1 ; V2|deltaP2 ; V3|deltaP3 ; V4|deltaP4 Vn|deltaPn
4+
volt_delta_kW_curve,0.95|-40 ; 0.99|-2 ; 1|0 ; 1.03|0 ; 1.05|10,V1|deltaP1 ; V2|deltaP2 ; V3|deltaP3 ; V4|deltaP4 ; Vn|deltaPn
55
voltage_LPF,is_active|True ; seed|40 ; window_size_LB|2 ; window_size_UB|18 ; window_type|Rectangular,

inputs/parameters/VS200-A.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Key,Value,Description
22
target_P3_reference__percent_of_maxP3,70,
33
max_delta_kVAR_per_min,1000,
4-
volt_var_curve,0.95|-100 ; 0.975|-25 ; 0.99|-5 ; 1|0 ; 1.03|0 ; 1.05|20,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 Vn|Qn
4+
volt_var_curve,0.95|-100 ; 0.975|-25 ; 0.99|-5 ; 1|0 ; 1.03|0 ; 1.05|20,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 ; Vn|Qn
55
voltage_LPF,is_active|True ; seed|35 ; window_size_LB|2 ; window_size_UB|18 ; window_type|Rectangular,
66
can_provide_reactive_power_after_battery_full,FALSE,

inputs/parameters/VS200-B.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Key,Value,Description
22
target_P3_reference__percent_of_maxP3,70,
33
max_delta_kVAR_per_min,0.25,
4-
volt_var_curve,0.95|-90 ; 0.98|0 ; 1.02|0 ; 1.05|90,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 Vn|Qn
4+
volt_var_curve,0.95|-90 ; 0.98|0 ; 1.02|0 ; 1.05|90,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 ; Vn|Qn
55
voltage_LPF,is_active|True ; seed|35 ; window_size_LB|25 ; window_size_UB|30 ; window_type|Rectangular,
66
can_provide_reactive_power_after_battery_full,TRUE,

inputs/parameters/VS200-C.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Key,Value,Description
22
target_P3_reference__percent_of_maxP3,70,
33
max_delta_kVAR_per_min,0.25,
4-
volt_var_curve,0.95|-90 ; 0.98|0 ; 1.02|0 ; 1.05|90,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 Vn|Qn
4+
volt_var_curve,0.95|-90 ; 0.98|0 ; 1.02|0 ; 1.05|90,V1|Q1 ; V2|Q2 ; V3|Q3 ; V4|Q4 ; Vn|Qn
55
voltage_LPF,is_active|True ; seed|35 ; window_size_LB|20 ; window_size_UB|30 ; window_type|Rectangular,
66
can_provide_reactive_power_after_battery_full,TRUE,

source/base/OpenDSS_aux.py

Lines changed: 175 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,75 @@
11

22
import opendssdirect as dss
33
import os, math
4+
import pandas as pd
45

56
from global_aux import OpenDSS_message_types, input_datasets, non_pev_feeder_load
67

78

89
class open_dss:
910

11+
# NOTE: The 'open_dss' class does two things: Use OpenDSS, and do logging.
12+
# if the boolean 'use_opendss' is false, then this class will just do the logging.
13+
# TODO: Separate the logging into a separate federate.
14+
def __init__(self, base_dir, use_opendss):
15+
16+
if use_opendss == True:
17+
self.helper = open_dss_helper(base_dir)
18+
else:
19+
self.helper = logger_helper(base_dir)
20+
21+
22+
def get_input_dataset_enum_list(self):
23+
return self.helper.get_request_list()
24+
25+
26+
def load_input_datasets(self, datasets_dict):
27+
self.helper.load_input_datasets(datasets_dict)
28+
29+
30+
def initialize(self):
31+
return self.helper.initialize()
32+
33+
34+
def process_control_messages(self, simulation_unix_time, message_dict):
35+
return self.helper.process_control_messages(simulation_unix_time, message_dict)
36+
37+
38+
def set_caldera_pev_charging_loads(self, node_pevPQ):
39+
self.helper.set_caldera_pev_charging_loads(node_pevPQ)
40+
41+
42+
def get_pu_node_voltages_for_caldera(self):
43+
return self.helper.get_pu_node_voltages_for_caldera()
44+
45+
46+
def solve(self, simulation_unix_time):
47+
self.helper.solve(simulation_unix_time)
48+
49+
50+
def log_data(self, simulation_unix_time):
51+
self.helper.log_data(simulation_unix_time)
52+
53+
54+
def post_simulation(self):
55+
self.helper.post_simulation()
56+
57+
58+
class open_dss_helper:
59+
1060
def __init__(self, base_dir):
1161
self.base_dir = base_dir
1262
self.dss_file_name = 'ieee34.dss'
1363

14-
15-
def get_input_dataset_enum_list(self):
64+
def get_request_list(self):
1665
return [input_datasets.baseLD_data_obj, input_datasets.all_caldera_node_names, input_datasets.HPSE_caldera_node_names]
1766

18-
1967
def load_input_datasets(self, datasets_dict):
2068
# datasets_dict is a dictionary with input_datasets as keys.
2169
self.datasets_dict = datasets_dict
2270

71+
def initialize(self):
2372

24-
def initialize(self):
2573
baseLD_data_obj = self.datasets_dict[input_datasets.baseLD_data_obj]
2674
all_caldera_node_names = self.datasets_dict[input_datasets.all_caldera_node_names]
2775
HPSE_caldera_node_names = self.datasets_dict[input_datasets.HPSE_caldera_node_names]
@@ -46,29 +94,83 @@ def initialize(self):
4694
self.dss_logger = open_dss_logger_A(self.base_dir, all_caldera_node_names, HPSE_caldera_node_names)
4795

4896
return is_successful
49-
50-
97+
5198
def process_control_messages(self, simulation_unix_time, message_dict):
5299
return self.dss_external_control.process_control_messages(simulation_unix_time, message_dict)
53100

54-
55101
def set_caldera_pev_charging_loads(self, node_pevPQ):
56102
self.node_pevPQ = node_pevPQ
57103
self.dss_Caldera.set_caldera_pev_charging_loads(node_pevPQ)
58-
59-
104+
60105
def get_pu_node_voltages_for_caldera(self):
61106
return self.dss_Caldera.get_pu_node_voltages_for_caldera()
62-
63-
107+
64108
def solve(self, simulation_unix_time):
65109
self.dss_core.solve(simulation_unix_time)
66-
67-
110+
68111
def log_data(self, simulation_unix_time):
69112
self.dss_logger.log_data(simulation_unix_time, self.node_pevPQ)
70113

114+
def post_simulation(self):
115+
pass
116+
117+
class logger_helper:
118+
119+
def __init__(self, base_dir):
120+
self.base_dir = base_dir
121+
122+
def get_request_list(self):
123+
return [input_datasets.baseLD_data_obj, input_datasets.all_caldera_node_names]
124+
125+
def load_input_datasets(self, datasets_dict):
126+
# datasets_dict is a dictionary with input_datasets as keys.
127+
self.datasets_dict = datasets_dict
128+
129+
def initialize(self):
130+
131+
self.baseLD_data_obj = self.datasets_dict[input_datasets.baseLD_data_obj]
132+
self.all_caldera_node_names = self.datasets_dict[input_datasets.all_caldera_node_names]
133+
134+
self.logger_obj = logger(self.base_dir, self.baseLD_data_obj, self.all_caldera_node_names)
135+
136+
is_successful = True
137+
return is_successful
71138

139+
def process_control_messages(self, simulation_unix_time, message_dict):
140+
141+
return_dict = {}
142+
143+
for (msg_enum, parameters) in message_dict.items():
144+
if msg_enum == OpenDSS_message_types.get_all_node_voltages:
145+
return_dict[msg_enum] = self.get_pu_node_voltages_for_caldera()
146+
147+
else:
148+
raise ValueError('Invalid message in caldera_ICM_aux::process_message.')
149+
150+
# The return value (return_dict) must be a dictionary with OpenDSS_message_types as keys.
151+
# If there is nothing to return, return an empty dictionary.
152+
return return_dict
153+
154+
155+
def set_caldera_pev_charging_loads(self, node_pevPQ):
156+
self.node_pevPQ = node_pevPQ
157+
158+
def get_pu_node_voltages_for_caldera(self):
159+
return_dict = {}
160+
for node_name in self.all_caldera_node_names:
161+
return_dict[node_name] = 1.0
162+
163+
return return_dict
164+
165+
def solve(self, simulation_unix_time):
166+
self.logger_obj.compute_total_load_profiles(self.node_pevPQ, simulation_unix_time)
167+
168+
def log_data(self, simulation_unix_time):
169+
return None
170+
171+
def post_simulation(self):
172+
self.logger_obj.write_data_to_disk()
173+
72174

73175
class open_dss_external_control:
74176

@@ -380,4 +482,63 @@ def log_data(self, simulation_unix_time, node_pevPQ):
380482
node_V = node_V/len(X)
381483

382484
tmp_str = '{}, {}, {}, {}'.format(simulation_time_hrs, node_V, pevQ_kVAR, pevP_kW)
383-
f_node.write(tmp_str + '\n')
485+
f_node.write(tmp_str + '\n')
486+
487+
class logger:
488+
489+
def __init__(self, base_dir, baseLD_data_obj, all_caldera_node_names):
490+
491+
self.base_dir = base_dir
492+
self.all_caldera_node_names = all_caldera_node_names
493+
self.baseLD_data_obj = baseLD_data_obj
494+
#print("all_caldera_node_names : {}".format(all_caldera_node_names))
495+
496+
self.real_power_profiles = {}
497+
self.reactive_power_profiles = {}
498+
self.real_power_profiles["simulation_time_hrs"] = []
499+
self.real_power_profiles["base_load_kW"] = []
500+
self.reactive_power_profiles["simulation_time_hrs"] = []
501+
self.reactive_power_profiles["base_load_kW"] = []
502+
503+
for node_name in all_caldera_node_names:
504+
self.real_power_profiles[node_name] = []
505+
self.reactive_power_profiles[node_name] = []
506+
507+
508+
def compute_total_load_profiles(self, node_pevPQ, simulation_unix_time):
509+
510+
simulation_time_hrs = simulation_unix_time/3600.0
511+
512+
index = math.floor((simulation_unix_time - self.baseLD_data_obj.data_start_unix_time) / self.baseLD_data_obj.data_timestep_sec)
513+
514+
if (index < 0) or (index >= len(self.baseLD_data_obj.actual_load_akW)):
515+
print("Error : base_LD index computed not in data range")
516+
exit()
517+
518+
base_LD_kW = self.baseLD_data_obj.actual_load_akW[index]
519+
self.real_power_profiles["simulation_time_hrs"].append(simulation_time_hrs)
520+
self.real_power_profiles["base_load_kW"].append(base_LD_kW)
521+
522+
self.reactive_power_profiles["simulation_time_hrs"].append(simulation_time_hrs)
523+
self.reactive_power_profiles["base_load_kW"].append(base_LD_kW)
524+
525+
for (node_name, (P_kW, Q_kVAR)) in node_pevPQ.items():
526+
self.real_power_profiles[node_name].append(P_kW)
527+
self.reactive_power_profiles[node_name].append(Q_kVAR)
528+
529+
def write_data_to_disk(self):
530+
Output_dir = self.base_dir + "/outputs/"
531+
532+
df = pd.DataFrame(self.real_power_profiles)
533+
df.to_csv(Output_dir + "real_power_profiles.csv", index=False)
534+
535+
df = pd.DataFrame(self.reactive_power_profiles)
536+
df.to_csv(Output_dir + "reactive_power_profiles.csv", index=False)
537+
538+
def get_pu_node_voltages_for_caldera(self):
539+
540+
return_dict = {}
541+
for node_name in self.all_caldera_node_names:
542+
return_dict[node_name] = 1.0
543+
544+
return return_dict

source/federates/OpenDSS_federate.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from OpenDSS_aux import open_dss
33
from Helics_Helper import send, receive, cleanup
44

5-
def open_dss_federate(base_dir, json_config_file_name, simulation_time_constraints):
5+
def open_dss_federate(base_dir, json_config_file_name, simulation_time_constraints, use_opendss):
66

77
print_communication = False
88
#=====================================
@@ -61,7 +61,7 @@ def open_dss_federate(base_dir, json_config_file_name, simulation_time_constrain
6161
#=====================================
6262
# Initialize OpenDSS
6363
#=====================================
64-
dss_obj = open_dss(base_dir)
64+
dss_obj = open_dss(base_dir, use_opendss)
6565

6666
#-------------------------------------
6767
# Get Information from Load Input Files
@@ -157,7 +157,6 @@ def open_dss_federate(base_dir, json_config_file_name, simulation_time_constrain
157157
if len(msg_dict) != 0:
158158
send(msg_dict, typeB_control_endpoint, source)
159159

160-
161160
#=====================================
162161
# Sub Step 4
163162
#=====================================
@@ -183,6 +182,8 @@ def open_dss_federate(base_dir, json_config_file_name, simulation_time_constrain
183182
if federate_time >= end_simulation_unix_time:
184183
break
185184

185+
dss_obj.post_simulation()
186+
186187
#=====================================
187188
# Terminate Federate
188189
#=====================================

0 commit comments

Comments
 (0)