├── README.md
├── agents
    ├── models.py
    ├── policies.py
    └── utils.py
├── configs
    ├── parameters_der20.py
    ├── parameters_der4.py
    ├── parameters_der40.py
    └── parameters_der6.py
├── docs
    └── microgrid.png
├── envs
    ├── Grid_envs.py
    ├── der20_RL_fn.py
    ├── der40_RL_fn.py
    └── der6_RL_fn.py
├── main.py
├── requirements.txt
└── trainer.py
/README.md:
--------------------------------------------------------------------------------
 1 | # PowerNet: Multi-agent Deep ReinforcementLearning for Scalable Powergrid Control
 2 | 
 3 | An on-policy, cooperative MARL algorithm for voltage control problem in the isolated Microgrid system by incorporating  a  differentiable,  learning-based communication  protocol,  a  spatial  discount factor, and an action smoothing scheme. 
 4 | 
 5 | ## Installation
 6 | - create an python virtual environment: `conda create -n powernet python=3.6`
 7 | - active the virtul environment: `conda activate powernet`
 8 | - install pytorch (torch>=1.2.0): `pip install torch===1.7.0 torchvision===0.8.1 torchaudio===0.7.0 -f https://download.pytorch.org/whl/torch_stable.html`
 9 | - install the requirements: `pip install -r requirements.txt`
10 | 
11 | ## PGSIM
12 | A python implementation of IEEE-34 bus and a 20-DGs Microgrid system with Numba [5] which is an open source JIT compiler that translates a subset of Python and NumPy code into fast machine code.  The simulation platform we developed is based on the line and load specifications detailed in [3].
13 | 
14 | 
15 |       16 |
16 |      
Fig.1 System diagrams of the two microgrid simulation platforms: (a) microgrid-6; and (b) microgrid-20.
17 | 
18 | 
19 | ## Usage
20 | To run the code, just run it via `python main.py`.  The config files contain the parameters for MARL policies.
21 | 
22 | ## Cite
23 | ```
24 | @misc{chen2020powernet,
25 |       title={PowerNet: Multi-agent Deep Reinforcement Learning for Scalable Powergrid Control}, 
26 |       author={Dong Chen and Zhaojian Li and Tianshu Chu and Rui Yao and Feng Qiu and Kaixiang Lin},
27 |       year={2020},
28 |       eprint={2011.12354},
29 |       archivePrefix={arXiv},
30 |       primaryClass={eess.SY}
31 | }
32 | ```
33 | 
34 | ## Reference
35 | [1] Bidram, Ali, et al. "Distributed cooperative secondary control of microgrids using feedback linearization." IEEE Transactions on Power Systems 28.3 (2013): 3462-3470.
36 | 
37 | [2] Bidram, Ali, et al. "Secondary control of microgrids based on distributed cooperative control of multi-agent systems." IET Generation, Transmission & Distribution 7.8 (2013): 822-831.
38 | 
39 | [3] Mustafa, Aquib, et al. "Detection and Mitigation of Data Manipulation Attacks in AC Microgrids." IEEE Transactions on Smart Grid 11.3 (2019): 2588-2603.
40 | 
41 | [4] Chu, Tianshu, et al. "Multi-agent deep reinforcement learning for large-scale traffic signal control." IEEE Transactions on Intelligent Transportation Systems 21.3 (2019): 1086-1095.
42 | 
43 | [5] [Numba: an open source JIT compiler that translates a subset of Python and NumPy code into fast machine code.](https://numba.pydata.org/)
44 | 
--------------------------------------------------------------------------------
/agents/models.py:
--------------------------------------------------------------------------------
  1 | """
  2 | Model functions
  3 | """
  4 | import os
  5 | import torch
  6 | import torch.nn as nn
  7 | import torch.optim as optim
  8 | from agents.utils import OnPolicyBuffer, MultiAgentOnPolicyBuffer, Scheduler
  9 | from agents.policies import (LstmPolicy, FPPolicy, ConsensusPolicy, NCMultiAgentPolicy,
 10 |                              CommNetMultiAgentPolicy, DIALMultiAgentPolicy)
 11 | import logging
 12 | import numpy as np
 13 | 
 14 | 
 15 | class IA2C:
 16 |     """
 17 |     The basic IA2C implementation with decentralized actor and centralized critic,
 18 |     limited to neighborhood area only.
 19 |     """
 20 | 
 21 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
 22 |                  total_step, model_config, seed=0, use_gpu=False):
 23 |         """
 24 |         n_s_ls: list of agents' state dimension. e.g., n_s_ls = [17, 17, 17, 17]
 25 |         n_a_ls: list of agents' action dimension. e.g., n_a_ls = [5, 5, 5, 5]
 26 |         neighbor_mask: matrix for neighbors: e.g., [[0,1,0,0], [1,0,1,0], [0,1,0,1], [0,0,1,0]]
 27 |         distance_mask: matrix for distance: e.g., [[0,1,2,3], [1,0,1,2], [2,1,0,1], [3,2,1,0]]
 28 |         coop_gamma: coop discount is used to discount the neighbors' impact
 29 |         """
 30 |         self.name = 'ia2c'
 31 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
 32 |                         total_step, seed, use_gpu, model_config)
 33 | 
 34 |     def add_transition(self, ob, naction, action, reward, value, done):
 35 |         if self.reward_norm > 0:
 36 |             reward = reward / self.reward_norm
 37 |         if self.reward_clip > 0:
 38 |             reward = np.clip(reward, -self.reward_clip, self.reward_clip)
 39 |         for i in range(self.n_agent):
 40 |             self.trans_buffer[i].add_transition(ob[i], naction[i], action[i], reward, value[i], done)
 41 | 
 42 |     def backward(self, Rends, dt, summary_writer=None, global_step=None):
 43 |         self.optimizer.zero_grad()
 44 |         for i in range(self.n_agent):
 45 |             obs, nas, acts, dones, Rs, Advs = self.trans_buffer[i].sample_transition(Rends[i], dt)
 46 |             if i == 0:
 47 |                 self.policy[i].backward(obs, nas, acts, dones, Rs, Advs,
 48 |                                         self.e_coef, self.v_coef,
 49 |                                         summary_writer=summary_writer, global_step=global_step)
 50 |             else:
 51 |                 self.policy[i].backward(obs, nas, acts, dones, Rs, Advs,
 52 |                                         self.e_coef, self.v_coef)
 53 |         if self.max_grad_norm > 0:
 54 |             nn.utils.clip_grad_norm_(self.policy.parameters(), self.max_grad_norm)
 55 |         self.optimizer.step()
 56 |         if self.lr_decay != 'constant':
 57 |             self._update_lr()
 58 | 
 59 |     def forward(self, obs, done, nactions=None, out_type='p'):
 60 |         out = []
 61 |         if nactions is None:
 62 |             nactions = [None] * self.n_agent
 63 |         for i in range(self.n_agent):
 64 |             cur_out = self.policy[i](obs[i], done, nactions[i], out_type)
 65 |             out.append(cur_out)
 66 |         return out
 67 | 
 68 |     def load(self, model_dir, global_step=None, train_mode=False):
 69 |         save_file = None
 70 |         save_step = 0
 71 |         if os.path.exists(model_dir):
 72 |             if global_step is None:
 73 |                 for file in os.listdir(model_dir):
 74 |                     if file.startswith('checkpoint'):
 75 |                         tokens = file.split('.')[0].split('-')
 76 |                         if len(tokens) != 2:
 77 |                             continue
 78 |                         cur_step = int(tokens[1])
 79 |                         if cur_step > save_step:
 80 |                             save_file = file
 81 |                             save_step = cur_step
 82 |             else:
 83 |                 save_file = 'checkpoint-{:d}.pt'.format(global_step)
 84 |         if save_file is not None:
 85 |             file_path = model_dir + save_file
 86 |             checkpoint = torch.load(file_path)
 87 |             logging.info('Checkpoint loaded: {}'.format(file_path))
 88 |             self.policy.load_state_dict(checkpoint['model_state_dict'])
 89 |             if train_mode:
 90 |                 self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
 91 |                 self.policy.train()
 92 |             else:
 93 |                 self.policy.eval()
 94 |             return True
 95 |         logging.error('Can not find checkpoint for {}'.format(model_dir))
 96 |         return False
 97 | 
 98 |     def reset(self):
 99 |         for i in range(self.n_agent):
100 |             self.policy[i]._reset()
101 | 
102 |     def save(self, model_dir, global_step):
103 |         file_path = model_dir + 'checkpoint-{:d}.pt'.format(global_step)
104 |         torch.save({'global_step': global_step,
105 |                     'model_state_dict': self.policy.state_dict(),
106 |                     'optimizer_state_dict': self.optimizer.state_dict()},
107 |                    file_path)
108 |         # logging.info('Checkpoint saved: {}'.format(file_path))
109 | 
110 |     def _init_algo(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
111 |                    total_step, seed, use_gpu, model_config):
112 |         # init params
113 |         self.n_s_ls = n_s_ls
114 |         self.n_a_ls = n_a_ls
115 |         self.identical_agent = False
116 |         if max(self.n_a_ls) == min(self.n_a_ls):
117 |             # note for identical IA2C, n_s_ls may have varient dims
118 |             self.identical_agent = True
119 |             self.n_s = n_s_ls[0]
120 |             self.n_a = n_a_ls[0]
121 |         else:
122 |             self.n_s = max(self.n_s_ls)
123 |             self.n_a = max(self.n_a_ls)
124 |         self.neighbor_mask = neighbor_mask
125 |         self.n_agent = len(self.neighbor_mask)
126 |         self.reward_clip = model_config.getfloat('reward_clip')
127 |         self.reward_norm = model_config.getfloat('reward_norm')
128 |         self.n_step = model_config.getint('batch_size')
129 |         self.n_fc = model_config.getint('num_fc')
130 |         self.n_lstm = model_config.getint('num_lstm')
131 |         # init torch
132 |         if use_gpu and torch.cuda.is_available():
133 |             torch.cuda.manual_seed_all(seed)
134 |             torch.backends.cudnn.benchmark = False
135 |             torch.backends.cudnn.deterministic = True
136 |             self.device = torch.device("cuda:0")
137 |             logging.info('Use gpu for pytorch...')
138 |         else:
139 |             torch.manual_seed(seed)
140 |             torch.set_num_threads(1)
141 |             self.device = torch.device("cpu")
142 |             logging.info('Use cpu for pytorch...')
143 | 
144 |         self.policy = self._init_policy()
145 |         self.policy.to(self.device)
146 | 
147 |         # init exp buffer and lr scheduler for training
148 |         if total_step:
149 |             self.total_step = total_step
150 |             self._init_train(model_config, distance_mask, coop_gamma)
151 | 
152 |     def _init_policy(self):
153 |         policy = []
154 |         for i in range(self.n_agent):
155 |             n_n = np.sum(self.neighbor_mask[i])
156 |             if self.identical_agent:
157 |                 local_policy = LstmPolicy(self.n_s_ls[i], self.n_a_ls[i], n_n, self.n_step,
158 |                                           n_fc=self.n_fc, n_lstm=self.n_lstm, name='{:d}'.format(i))
159 |             else:
160 |                 na_dim_ls = []
161 |                 for j in np.where(self.neighbor_mask[i] == 1)[0]:
162 |                     na_dim_ls.append(self.n_a_ls[j])
163 |                 local_policy = LstmPolicy(self.n_s_ls[i], self.n_a_ls[i], n_n, self.n_step,
164 |                                           n_fc=self.n_fc, n_lstm=self.n_lstm, name='{:d}'.format(i),
165 |                                           na_dim_ls=na_dim_ls, identical=False)
166 |                 # local_policy.to(self.device)
167 |             policy.append(local_policy)
168 |         return nn.ModuleList(policy)
169 | 
170 |     def _init_scheduler(self, model_config):
171 |         # init lr scheduler
172 |         self.lr_init = model_config.getfloat('lr_init')
173 |         self.lr_decay = model_config.get('lr_decay')
174 |         if self.lr_decay == 'constant':
175 |             self.lr_scheduler = Scheduler(self.lr_init, decay=self.lr_decay)
176 |         else:
177 |             lr_min = model_config.getfloat('lr_min')
178 |             self.lr_scheduler = Scheduler(self.lr_init, lr_min, self.total_step, decay=self.lr_decay)
179 | 
180 |     def _init_train(self, model_config, distance_mask, coop_gamma):
181 |         # init lr scheduler
182 |         self._init_scheduler(model_config)
183 |         # init parameters for grad computation
184 |         self.v_coef = model_config.getfloat('value_coef')
185 |         self.e_coef = model_config.getfloat('entropy_coef')
186 |         self.max_grad_norm = model_config.getfloat('max_grad_norm')
187 |         # init optimizer
188 |         alpha = model_config.getfloat('rmsp_alpha')
189 |         epsilon = model_config.getfloat('rmsp_epsilon')
190 |         gamma = model_config.getfloat('gamma')
191 |         self.optimizer = optim.RMSprop(self.policy.parameters(), self.lr_init,
192 |                                        eps=epsilon, alpha=alpha)
193 |         # init transition buffer
194 |         self._init_trans_buffer(gamma, distance_mask, coop_gamma)
195 | 
196 |     def _init_trans_buffer(self, gamma, distance_mask, coop_gamma):
197 |         self.trans_buffer = []
198 |         for i in range(self.n_agent):
199 |             # init replay buffer
200 |             self.trans_buffer.append(OnPolicyBuffer(gamma, coop_gamma, distance_mask[i]))
201 | 
202 |     def _update_lr(self):
203 |         # TODO: refactor this using optim.lr_scheduler
204 |         cur_lr = self.lr_scheduler.get(self.n_step)
205 |         for param_group in self.optimizer.param_groups:
206 |             param_group['lr'] = cur_lr
207 | 
208 | 
209 | class IA2C_FP(IA2C):
210 |     """
211 |     In fingerprint IA2C, neighborhood policies (fingerprints) are also included.
212 |     """
213 | 
214 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
215 |                  total_step, model_config, seed=0, use_gpu=False):
216 |         self.name = 'ia2c_fp'
217 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
218 |                         total_step, seed, use_gpu, model_config)
219 | 
220 |     def _init_policy(self):
221 |         policy = []
222 |         for i in range(self.n_agent):
223 |             n_n = np.sum(self.neighbor_mask[i])
224 |             # neighborhood policies are included in local state
225 |             if self.identical_agent:
226 |                 n_s1 = int(self.n_s_ls[i] + self.n_a * n_n)
227 |                 policy.append(FPPolicy(n_s1, self.n_a, int(n_n), self.n_step, n_fc=self.n_fc,
228 |                                        n_lstm=self.n_lstm, name='{:d}'.format(i)))
229 |             else:
230 |                 na_dim_ls = []
231 |                 for j in np.where(self.neighbor_mask[i] == 1)[0]:
232 |                     na_dim_ls.append(self.n_a_ls[j])
233 |                 n_s1 = int(self.n_s_ls[i] + sum(na_dim_ls))
234 |                 policy.append(FPPolicy(n_s1, self.n_a_ls[i], int(n_n), self.n_step, n_fc=self.n_fc,
235 |                                        n_lstm=self.n_lstm, name='{:d}'.format(i),
236 |                                        na_dim_ls=na_dim_ls, identical=False))
237 |         return nn.ModuleList(policy)
238 | 
239 | 
240 | class MA2C_NC(IA2C):
241 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
242 |                  total_step, model_config, seed=0, use_gpu=False):
243 |         """
244 |         n_s_ls: list of agents' state dimension. e.g., n_s_ls = [17, 17, 17, 17]
245 |         n_a_ls: list of agents' action dimension. e.g., n_a_ls = [5, 5, 5, 5]
246 |         neighbor_mask: matrix for neighbors: e.g., [[0,1,0,0], [1,0,1,0], [0,1,0,1], [0,0,1,0]]
247 |         distance_mask: matrix for distance: e.g., [[0,1,2,3], [1,0,1,2], [2,1,0,1], [3,2,1,0]]
248 |         coop_gamma: coop discount is used to discount the neighbors' impact
249 |         """
250 |         self.name = 'ma2c_nc'
251 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
252 |                         total_step, seed, use_gpu, model_config)
253 | 
254 |     def add_transition(self, ob, p, action, reward, value, done):
255 |         if self.reward_norm > 0:
256 |             reward = reward / self.reward_norm
257 |         if self.reward_clip > 0:
258 |             reward = np.clip(reward, -self.reward_clip, self.reward_clip)
259 |         if self.identical_agent:
260 |             self.trans_buffer.add_transition(np.array(ob), np.array(p), action,
261 |                                              reward, value, done)
262 |         else:
263 |             pad_ob, pad_p = self._convert_hetero_states(ob, p)
264 |             self.trans_buffer.add_transition(pad_ob, pad_p, action,
265 |                                              reward, value, done)
266 | 
267 |     def backward(self, Rends, dt, summary_writer=None, global_step=None):
268 |         self.optimizer.zero_grad()
269 |         obs, ps, acts, dones, Rs, Advs = self.trans_buffer.sample_transition(Rends, dt)
270 |         self.policy.backward(obs, ps, acts, dones, Rs, Advs, self.e_coef, self.v_coef,
271 |                              summary_writer=summary_writer, global_step=global_step)
272 |         if self.max_grad_norm > 0:
273 |             nn.utils.clip_grad_norm_(self.policy.parameters(), self.max_grad_norm)
274 |         self.optimizer.step()
275 |         if self.lr_decay != 'constant':
276 |             self._update_lr()
277 | 
278 |     def forward(self, obs, done, ps, actions=None, out_type='p'):
279 |         if self.identical_agent:
280 |             return self.policy.forward(np.array(obs), done, np.array(ps),
281 |                                        actions, out_type)
282 |         else:
283 |             pad_ob, pad_p = self._convert_hetero_states(obs, ps)
284 |             return self.policy.forward(pad_ob, done, pad_p,
285 |                                        actions, out_type)
286 | 
287 |     def reset(self):
288 |         self.policy._reset()
289 | 
290 |     def _convert_hetero_states(self, ob, p):
291 |         pad_ob = np.zeros((self.n_agent, self.n_s))
292 |         pad_p = np.zeros((self.n_agent, self.n_a))
293 |         for i in range(self.n_agent):
294 |             pad_ob[i, :len(ob[i])] = ob[i]
295 |             pad_p[i, :len(p[i])] = p[i]
296 |         return pad_ob, pad_p
297 | 
298 |     def _init_policy(self):
299 |         if self.identical_agent:
300 |             return NCMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
301 |                                       self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm)
302 |         else:
303 |             return NCMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
304 |                                       self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm,
305 |                                       n_s_ls=self.n_s_ls, n_a_ls=self.n_a_ls, identical=False)
306 | 
307 |     def _init_trans_buffer(self, gamma, distance_mask, coop_gamma):
308 |         self.trans_buffer = MultiAgentOnPolicyBuffer(gamma, coop_gamma, distance_mask)
309 | 
310 | 
311 | class IA2C_CU(MA2C_NC):
312 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
313 |                  total_step, model_config, seed=0, use_gpu=False):
314 |         self.name = 'ma2c_cu'
315 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
316 |                         total_step, seed, use_gpu, model_config)
317 | 
318 |     def _init_policy(self):
319 |         if self.identical_agent:
320 |             return ConsensusPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
321 |                                    self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm)
322 |         else:
323 |             return ConsensusPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
324 |                                    self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm,
325 |                                    n_s_ls=self.n_s_ls, n_a_ls=self.n_a_ls, identical=False)
326 | 
327 |     def backward(self, Rends, dt, summary_writer=None, global_step=None):
328 |         super(IA2C_CU, self).backward(Rends, dt, summary_writer, global_step)
329 |         self.policy.consensus_update()
330 | 
331 | 
332 | class MA2C_CNET(MA2C_NC):
333 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
334 |                  total_step, model_config, seed=0, use_gpu=False):
335 |         self.name = 'ma2c_ic3'
336 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
337 |                         total_step, seed, use_gpu, model_config)
338 | 
339 |     def _init_policy(self):
340 |         if self.identical_agent:
341 |             return CommNetMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
342 |                                            self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm)
343 |         else:
344 |             return CommNetMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
345 |                                            self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm,
346 |                                            n_s_ls=self.n_s_ls, n_a_ls=self.n_a_ls, identical=False)
347 | 
348 | 
349 | class MA2C_DIAL(MA2C_NC):
350 |     def __init__(self, n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
351 |                  total_step, model_config, seed=0, use_gpu=False):
352 |         self.name = 'ma2c_dial'
353 |         self._init_algo(n_s_ls, n_a_ls, neighbor_mask, distance_mask, coop_gamma,
354 |                         total_step, seed, use_gpu, model_config)
355 | 
356 |     def _init_policy(self):
357 |         if self.identical_agent:
358 |             return DIALMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
359 |                                         self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm)
360 |         else:
361 |             return DIALMultiAgentPolicy(self.n_s, self.n_a, self.n_agent, self.n_step,
362 |                                         self.neighbor_mask, n_fc=self.n_fc, n_h=self.n_lstm,
363 |                                         n_s_ls=self.n_s_ls, n_a_ls=self.n_a_ls, identical=False)
364 | 
--------------------------------------------------------------------------------
/agents/policies.py:
--------------------------------------------------------------------------------
  1 | """
  2 | Policy functions for training and update
  3 | """
  4 | import numpy as np
  5 | import torch
  6 | import torch.nn as nn
  7 | import torch.nn.functional as F
  8 | from agents.utils import batch_to_seq, init_layer, one_hot, run_rnn
  9 | 
 10 | 
 11 | class Policy(nn.Module):
 12 |     def __init__(self, n_a, n_s, n_step, policy_name, agent_name, identical):
 13 |         super(Policy, self).__init__()
 14 |         self.name = policy_name
 15 |         if agent_name is not None:
 16 |             # for multi-agent system
 17 |             self.name += '_' + str(agent_name)
 18 |         self.n_a = n_a
 19 |         self.n_s = n_s
 20 |         self.n_step = n_step
 21 |         self.identical = identical
 22 | 
 23 |     def forward(self, ob, *_args, **_kwargs):
 24 |         raise NotImplementedError()
 25 | 
 26 |     def _init_actor_head(self, n_h, n_a=None):
 27 |         if n_a is None:
 28 |             n_a = self.n_a
 29 |         # only discrete control is supported for now
 30 |         self.actor_head = nn.Linear(n_h, n_a)
 31 |         init_layer(self.actor_head, 'fc')
 32 | 
 33 |     def _init_critic_head(self, n_h, n_n=None):
 34 |         if n_n is None:
 35 |             n_n = int(self.n_n)
 36 |         if n_n:
 37 |             if self.identical:
 38 |                 n_na_sparse = self.n_a * n_n
 39 |             else:
 40 |                 n_na_sparse = sum(self.na_dim_ls)
 41 |             n_h += n_na_sparse
 42 |         self.critic_head = nn.Linear(n_h, 1)
 43 |         init_layer(self.critic_head, 'fc')
 44 | 
 45 |     def _run_critic_head(self, h, na, n_n=None):
 46 |         if n_n is None:
 47 |             n_n = int(self.n_n)
 48 |         if n_n:
 49 |             na = torch.from_numpy(na).long()
 50 |             if self.identical:
 51 |                 na_sparse = one_hot(na, self.n_a)
 52 |                 na_sparse = na_sparse.view(-1, self.n_a * n_n)
 53 |             else:
 54 |                 na_sparse = []
 55 |                 na_ls = torch.chunk(na, n_n, dim=1)
 56 |                 for na_val, na_dim in zip(na_ls, self.na_dim_ls):
 57 |                     na_sparse.append(torch.squeeze(one_hot(na_val, na_dim), dim=1))
 58 |                 na_sparse = torch.cat(na_sparse, dim=1)
 59 |             h = torch.cat([h, na_sparse], dim=1)
 60 |         return self.critic_head(h).squeeze()
 61 | 
 62 |     def _run_loss(self, actor_dist, e_coef, v_coef, vs, As, Rs, Advs):
 63 |         log_probs = actor_dist.log_prob(As)
 64 |         policy_loss = -(log_probs * Advs).mean()
 65 |         entropy_loss = -(actor_dist.entropy()).mean() * e_coef
 66 |         value_loss = (Rs - vs).pow(2).mean() * v_coef
 67 |         return policy_loss, value_loss, entropy_loss
 68 | 
 69 |     def _update_tensorboard(self, summary_writer, global_step):
 70 |         # monitor training
 71 |         summary_writer.add_scalar('loss/entropy_loss', self.entropy_loss,
 72 |                                   global_step=global_step)
 73 |         summary_writer.add_scalar('loss/policy_loss', self.policy_loss,
 74 |                                   global_step=global_step)
 75 |         summary_writer.add_scalar('loss/value_loss', self.value_loss,
 76 |                                   global_step=global_step)
 77 |         summary_writer.add_scalar('loss/total_loss', self.loss,
 78 |                                   global_step=global_step)
 79 | 
 80 | 
 81 | class LstmPolicy(Policy):
 82 |     def __init__(self, n_s, n_a, n_n, n_step, n_fc=64, n_lstm=64, name=None,
 83 |                  na_dim_ls=None, identical=True):
 84 |         super(LstmPolicy, self).__init__(n_a, n_s, n_step, 'lstm', name, identical)
 85 |         if not self.identical:
 86 |             self.na_dim_ls = na_dim_ls
 87 |         self.n_lstm = n_lstm
 88 |         self.n_fc = n_fc
 89 |         self.n_n = n_n
 90 |         self._init_net()
 91 |         self._reset()
 92 | 
 93 |     def backward(self, obs, nactions, acts, dones, Rs, Advs,
 94 |                  e_coef, v_coef, summary_writer=None, global_step=None):
 95 |         obs = torch.from_numpy(obs).float()
 96 |         dones = torch.from_numpy(dones).float()
 97 |         xs = self._encode_ob(obs)
 98 |         hs, new_states = run_rnn(self.lstm_layer, xs, dones, self.states_bw)
 99 |         # backward grad is limited to the minibatch
100 |         self.states_bw = new_states.detach()
101 |         actor_dist = torch.distributions.categorical.Categorical(logits=F.log_softmax(self.actor_head(hs), dim=1))
102 |         vs = self._run_critic_head(hs, nactions)
103 |         self.policy_loss, self.value_loss, self.entropy_loss = \
104 |             self._run_loss(actor_dist, e_coef, v_coef, vs,
105 |                            torch.from_numpy(acts).long(),
106 |                            torch.from_numpy(Rs).float(),
107 |                            torch.from_numpy(Advs).float())
108 |         self.loss = self.policy_loss + self.value_loss + self.entropy_loss
109 |         self.loss.backward()
110 |         if summary_writer is not None:
111 |             self._update_tensorboard(summary_writer, global_step)
112 | 
113 |     def forward(self, ob, done, naction=None, out_type='p'):
114 |         ob = torch.from_numpy(np.expand_dims(ob, axis=0)).float()
115 |         done = torch.from_numpy(np.expand_dims(done, axis=0)).float()
116 |         x = self._encode_ob(ob)
117 |         h, new_states = run_rnn(self.lstm_layer, x, done, self.states_fw)
118 |         if out_type.startswith('p'):
119 |             self.states_fw = new_states.detach()
120 |             return F.softmax(self.actor_head(h), dim=1).squeeze().detach().numpy()
121 |         else:
122 |             return self._run_critic_head(h, np.array([naction])).detach().numpy()
123 | 
124 |     def _encode_ob(self, ob):
125 |         return F.relu(self.fc_layer(ob))
126 | 
127 |     def _init_net(self):
128 |         self.fc_layer = nn.Linear(self.n_s, self.n_fc)
129 |         init_layer(self.fc_layer, 'fc')
130 |         self.lstm_layer = nn.LSTMCell(self.n_fc, self.n_lstm)
131 |         init_layer(self.lstm_layer, 'lstm')
132 |         self._init_actor_head(self.n_lstm)
133 |         self._init_critic_head(self.n_lstm)
134 | 
135 |     def _reset(self):
136 |         # forget the cumulative states every cum_step
137 |         self.states_fw = torch.zeros(self.n_lstm * 2)
138 |         self.states_bw = torch.zeros(self.n_lstm * 2)
139 | 
140 | 
141 | class FPPolicy(LstmPolicy):
142 |     def __init__(self, n_s, n_a, n_n, n_step, n_fc=64, n_lstm=64, name=None,
143 |                  na_dim_ls=None, identical=True):
144 |         super(FPPolicy, self).__init__(n_s, n_a, n_n, n_step, n_fc, n_lstm, name,
145 |                                        na_dim_ls, identical)
146 | 
147 |     def _init_net(self):
148 |         if self.identical:
149 |             # dim of obs except the fingerprints
150 |             self.n_x = self.n_s - self.n_n * self.n_a
151 |         else:
152 |             self.n_x = int(self.n_s - sum(self.na_dim_ls))
153 |         self.fc_x_layer = nn.Linear(self.n_x, self.n_fc)
154 |         init_layer(self.fc_x_layer, 'fc')
155 |         n_h = self.n_fc
156 |         if self.n_n:
157 |             self.fc_p_layer = nn.Linear(self.n_s - self.n_x, self.n_fc)
158 |             init_layer(self.fc_p_layer, 'fc')
159 |             n_h += self.n_fc
160 |         self.lstm_layer = nn.LSTMCell(n_h, self.n_lstm)
161 |         init_layer(self.lstm_layer, 'lstm')
162 |         self._init_actor_head(self.n_lstm)
163 |         self._init_critic_head(self.n_lstm)
164 | 
165 |     def _encode_ob(self, ob):
166 |         x = F.relu(self.fc_x_layer(ob[:, :self.n_x]))
167 |         if self.n_n:
168 |             p = F.relu(self.fc_p_layer(ob[:, self.n_x:]))
169 |             x = torch.cat([x, p], dim=1)
170 |         return x
171 | 
172 | 
173 | class NCMultiAgentPolicy(Policy):
174 |     """ Implemented as a centralized meta-DNN. To simplify the implementation, all input
175 |     and output dimensions are identical among all agents, and invalid values are casted as
176 |     zeros during runtime."""
177 | 
178 |     def __init__(self, n_s, n_a, n_agent, n_step, neighbor_mask, n_fc=64, n_h=64,
179 |                  n_s_ls=None, n_a_ls=None, identical=True):
180 |         """
181 |         n_h: dim of hidden states
182 |         """
183 |         super(NCMultiAgentPolicy, self).__init__(n_a, n_s, n_step, 'nc', None, identical)
184 |         if not self.identical:
185 |             self.n_s_ls = n_s_ls
186 |             self.n_a_ls = n_a_ls
187 |         self.n_agent = n_agent
188 |         self.neighbor_mask = neighbor_mask
189 |         self.n_fc = n_fc
190 |         self.n_h = n_h
191 |         self._init_net()
192 |         self._reset()
193 | 
194 |     def _get_neighbor_dim(self, i_agent):
195 |         # return num of neighbors, dim of overall states, sum of neigbor's action dim,
196 |         # list of neighbor-information's dim, list of neighbor-action's dim
197 |         n_n = int(np.sum(self.neighbor_mask[i_agent]))
198 |         if self.identical:
199 |             return n_n, self.n_s * (n_n + 1), self.n_a * n_n, [self.n_s] * n_n, [self.n_a] * n_n
200 |         else:
201 |             ns_ls = []
202 |             na_ls = []
203 |             for j in np.where(self.neighbor_mask[i_agent])[0]:
204 |                 ns_ls.append(self.n_s_ls[j])
205 |                 na_ls.append(self.n_a_ls[j])
206 |             return n_n, self.n_s_ls[i_agent] + sum(ns_ls), sum(na_ls), ns_ls, na_ls
207 | 
208 |     def _init_actor_head(self, n_a):
209 |         # only discrete control is supported for now
210 |         actor_head = nn.Linear(self.n_h, n_a)
211 |         init_layer(actor_head, 'fc')
212 |         self.actor_heads.append(actor_head)
213 | 
214 |     def _init_critic_head(self, n_na):
215 |         # TODO: can we include more information,like neighbor's hidden states
216 |         critic_head = nn.Linear(self.n_h + n_na, 1)
217 |         init_layer(critic_head, 'fc')
218 |         self.critic_heads.append(critic_head)
219 | 
220 |     def _init_net(self):
221 |         self.fc_x_layers = nn.ModuleList()
222 |         self.fc_p_layers = nn.ModuleList()
223 |         self.fc_m_layers = nn.ModuleList()
224 |         self.lstm_layers = nn.ModuleList()
225 |         self.actor_heads = nn.ModuleList()
226 |         self.critic_heads = nn.ModuleList()
227 |         self.ns_ls_ls = []
228 |         self.na_ls_ls = []
229 |         self.n_n_ls = []
230 |         for i in range(self.n_agent):
231 |             n_n, n_ns, n_na, ns_ls, na_ls = self._get_neighbor_dim(i)
232 |             self.ns_ls_ls.append(ns_ls)
233 |             self.na_ls_ls.append(na_ls)
234 |             self.n_n_ls.append(n_n)
235 |             self._init_comm_layer(n_n, n_ns, n_na)
236 |             n_a = self.n_a if self.identical else self.n_a_ls[i]
237 |             self._init_actor_head(n_a)
238 |             self._init_critic_head(n_na)
239 | 
240 |     def _reset(self):
241 |         self.states_fw = torch.zeros(self.n_agent, self.n_h * 2)
242 |         self.states_bw = torch.zeros(self.n_agent, self.n_h * 2)
243 | 
244 |     def _run_actor_heads(self, hs, detach=False):
245 |         ps = []
246 |         for i in range(self.n_agent):
247 |             if detach:
248 |                 p_i = F.softmax(self.actor_heads[i](hs[i]), dim=1).squeeze().detach().numpy()
249 |             else:
250 |                 p_i = F.log_softmax(self.actor_heads[i](hs[i]), dim=1)
251 |             ps.append(p_i)
252 |         return ps
253 | 
254 |     def _run_critic_heads(self, hs, actions, detach=False):
255 |         # include neighbor's actions (one-hot) to get the value fn
256 |         vs = []
257 |         for i in range(self.n_agent):
258 |             n_n = self.n_n_ls[i]
259 |             if n_n:
260 |                 js = torch.from_numpy(np.where(self.neighbor_mask[i])[0]).long()
261 |                 na_i = torch.index_select(actions, 0, js)
262 |                 na_i_ls = []
263 |                 for j in range(n_n):
264 |                     na_i_ls.append(one_hot(na_i[j], self.na_ls_ls[i][j]))
265 |                 h_i = torch.cat([hs[i]] + na_i_ls, dim=1)
266 |             else:
267 |                 h_i = hs[i]
268 |             v_i = self.critic_heads[i](h_i).squeeze()
269 |             if detach:
270 |                 vs.append(v_i.detach().numpy())
271 |             else:
272 |                 vs.append(v_i)
273 |         return vs
274 | 
275 |     def backward(self, obs, fps, acts, dones, Rs, Advs,
276 |                  e_coef, v_coef, summary_writer=None, global_step=None):
277 |         obs = torch.from_numpy(obs).float().transpose(0, 1)
278 |         dones = torch.from_numpy(dones).float()
279 |         fps = torch.from_numpy(fps).float().transpose(0, 1)
280 |         acts = torch.from_numpy(acts).long()
281 |         hs, new_states = self._run_comm_layers(obs, dones, fps, self.states_bw)
282 |         # backward grad is limited to the minibatch
283 |         self.states_bw = new_states.detach()
284 |         ps = self._run_actor_heads(hs)
285 |         vs = self._run_critic_heads(hs, acts)
286 |         self.policy_loss = 0
287 |         self.value_loss = 0
288 |         self.entropy_loss = 0
289 |         Rs = torch.from_numpy(Rs).float()
290 |         Advs = torch.from_numpy(Advs).float()
291 |         for i in range(self.n_agent):
292 |             actor_dist_i = torch.distributions.categorical.Categorical(logits=ps[i])
293 |             policy_loss_i, value_loss_i, entropy_loss_i = \
294 |                 self._run_loss(actor_dist_i, e_coef, v_coef, vs[i],
295 |                                acts[i], Rs[i], Advs[i])
296 |             self.policy_loss += policy_loss_i
297 |             self.value_loss += value_loss_i
298 |             self.entropy_loss += entropy_loss_i
299 |         self.loss = self.policy_loss + self.value_loss + self.entropy_loss
300 |         self.loss.backward()
301 |         if summary_writer is not None:
302 |             self._update_tensorboard(summary_writer, global_step)
303 | 
304 |     def forward(self, ob, done, fp, action=None, out_type='p'):
305 |         # TxNxm
306 |         ob = torch.from_numpy(np.expand_dims(ob, axis=0)).float()
307 |         done = torch.from_numpy(np.expand_dims(done, axis=0)).float()
308 |         fp = torch.from_numpy(np.expand_dims(fp, axis=0)).float()
309 |         # h dim: NxTxm
310 |         h, new_states = self._run_comm_layers(ob, done, fp, self.states_fw)
311 |         if out_type.startswith('p'):
312 |             self.states_fw = new_states.detach()
313 |             return self._run_actor_heads(h, detach=True)
314 |         else:
315 |             action = torch.from_numpy(np.expand_dims(action, axis=1)).long()
316 |             return self._run_critic_heads(h, action, detach=True)
317 | 
318 |     def _init_comm_layer(self, n_n, n_ns, n_na):
319 |         # num of neighbors, neighbor-information's dim, neighbor-policy's dim
320 |         n_lstm_in = 3 * self.n_fc
321 |         fc_x_layer = nn.Linear(n_ns, self.n_fc)
322 |         init_layer(fc_x_layer, 'fc')
323 |         self.fc_x_layers.append(fc_x_layer)
324 |         if n_n:
325 |             fc_p_layer = nn.Linear(n_na, self.n_fc)
326 |             init_layer(fc_p_layer, 'fc')
327 |             fc_m_layer = nn.Linear(self.n_h * n_n, self.n_fc)
328 |             init_layer(fc_m_layer, 'fc')
329 |             self.fc_m_layers.append(fc_m_layer)
330 |             self.fc_p_layers.append(fc_p_layer)
331 |             lstm_layer = nn.LSTMCell(n_lstm_in, self.n_h)
332 |         else:
333 |             self.fc_m_layers.append(None)
334 |             self.fc_p_layers.append(None)
335 |             lstm_layer = nn.LSTMCell(self.n_fc, self.n_h)
336 |         init_layer(lstm_layer, 'lstm')
337 |         self.lstm_layers.append(lstm_layer)
338 | 
339 |     def _run_comm_layers(self, obs, dones, fps, states):
340 |         # encode comm info(s_i) and (h_(t-1), c_(t-1)) to the h_t and c_t
341 |         # states (h_(t-1), c_(t-1)): t-1 hidden states, (self.n_agent, self.n_h * 2)
342 |         obs = batch_to_seq(obs)
343 |         dones = batch_to_seq(dones)
344 |         fps = batch_to_seq(fps)
345 |         h, c = torch.chunk(states, 2, dim=1)
346 |         outputs = []
347 |         for t, (x, p, done) in enumerate(zip(obs, fps, dones)):
348 |             next_h = []
349 |             next_c = []
350 |             x = x.squeeze(0)
351 |             p = p.squeeze(0)
352 |             for i in range(self.n_agent):
353 |                 n_n = self.n_n_ls[i]
354 |                 if n_n:
355 |                     s_i = self._get_comm_s(i, n_n, x, h, p)
356 |                 else:
357 |                     s_i = F.relu(self.fc_x_layers[i](x[i].unsqueeze(0)))
358 |                 h_i, c_i = h[i].unsqueeze(0) * (1 - done), c[i].unsqueeze(0) * (1 - done)
359 |                 next_h_i, next_c_i = self.lstm_layers[i](s_i, (h_i, c_i))
360 |                 next_h.append(next_h_i)
361 |                 next_c.append(next_c_i)
362 |             h, c = torch.cat(next_h), torch.cat(next_c)
363 |             outputs.append(h.unsqueeze(0))
364 |         outputs = torch.cat(outputs)
365 |         return outputs.transpose(0, 1), torch.cat([h, c], dim=1)
366 | 
367 |     def _get_comm_s(self, i, n_n, x, h, p):
368 |         # get the S_(i-1), h_(i-1), Pi_(i-1)
369 |         js = torch.from_numpy(np.where(self.neighbor_mask[i])[0]).long()
370 |         m_i = torch.index_select(h, 0, js).view(1, self.n_h * n_n)  # neighbor's hidden states
371 |         p_i = torch.index_select(p, 0, js)  # neighbor's policy
372 |         nx_i = torch.index_select(x, 0, js)  # neighbor's obs
373 |         if self.identical:
374 |             p_i = p_i.view(1, self.n_a * n_n)
375 |             nx_i = nx_i.view(1, self.n_s * n_n)
376 |         else:
377 |             p_i_ls = []
378 |             nx_i_ls = []
379 |             for j in range(n_n):
380 |                 p_i_ls.append(p_i[j].narrow(0, 0, self.na_ls_ls[i][j]))
381 |                 nx_i_ls.append(nx_i[j].narrow(0, 0, self.ns_ls_ls[i][j]))
382 |             p_i = torch.cat(p_i_ls).unsqueeze(0)
383 |             nx_i = torch.cat(nx_i_ls).unsqueeze(0)
384 | 
385 |         # encode neighbor's states along with its own states;
386 |         # encode neighbor's policies;
387 |         # encode neighbor's hidden states.
388 |         s_i = [F.relu(self.fc_x_layers[i](torch.cat([x[i].unsqueeze(0), nx_i], dim=1))),
389 |                F.relu(self.fc_p_layers[i](p_i)),
390 |                F.relu(self.fc_m_layers[i](m_i))]
391 |         return torch.cat(s_i, dim=1)
392 | 
393 | 
394 | class ConsensusPolicy(NCMultiAgentPolicy):
395 |     def __init__(self, n_s, n_a, n_agent, n_step, neighbor_mask, n_fc=64, n_h=64,
396 |                  n_s_ls=None, n_a_ls=None, identical=True):
397 |         Policy.__init__(self, n_a, n_s, n_step, 'cu', None, identical)
398 |         if not self.identical:
399 |             self.n_s_ls = n_s_ls
400 |             self.n_a_ls = n_a_ls
401 |         self.n_agent = n_agent
402 |         self.neighbor_mask = neighbor_mask
403 |         self.n_fc = n_fc
404 |         self.n_h = n_h
405 |         self._init_net()
406 |         self._reset()
407 | 
408 |     def consensus_update(self):
409 |         consensus_update = []
410 |         with torch.no_grad():
411 |             for i in range(self.n_agent):
412 |                 mean_wts = self._get_critic_wts(i)
413 |                 for param, wt in zip(self.lstm_layers[i].parameters(), mean_wts):
414 |                     param.copy_(wt)
415 | 
416 |     def _init_net(self):
417 |         self.fc_x_layers = nn.ModuleList()
418 |         self.lstm_layers = nn.ModuleList()
419 |         self.actor_heads = nn.ModuleList()
420 |         self.critic_heads = nn.ModuleList()
421 |         self.na_ls_ls = []
422 |         self.n_n_ls = []
423 |         for i in range(self.n_agent):
424 |             n_n, _, n_na, _, na_ls = self._get_neighbor_dim(i)
425 |             n_s = self.n_s if self.identical else self.n_s_ls[i]
426 |             self.na_ls_ls.append(na_ls)
427 |             self.n_n_ls.append(n_n)
428 |             fc_x_layer = nn.Linear(n_s, self.n_fc)
429 |             init_layer(fc_x_layer, 'fc')
430 |             self.fc_x_layers.append(fc_x_layer)
431 |             lstm_layer = nn.LSTMCell(self.n_fc, self.n_h)
432 |             init_layer(lstm_layer, 'lstm')
433 |             self.lstm_layers.append(lstm_layer)
434 |             n_a = self.n_a if self.identical else self.n_a_ls[i]
435 |             self._init_actor_head(n_a)
436 |             self._init_critic_head(n_na)
437 | 
438 |     def _get_critic_wts(self, i_agent):
439 |         wts = []
440 |         for wt in self.lstm_layers[i_agent].parameters():
441 |             wts.append(wt.detach())
442 |         neighbors = list(np.where(self.neighbor_mask[i_agent] == 1)[0])
443 |         for j in neighbors:
444 |             for k, wt in enumerate(self.lstm_layers[j].parameters()):
445 |                 wts[k] += wt.detach()
446 |         n = 1 + len(neighbors)
447 |         for k in range(len(wts)):
448 |             wts[k] /= n
449 |         return wts
450 | 
451 |     def _run_comm_layers(self, obs, dones, fps, states):
452 |         # NxTxm
453 |         obs = obs.transpose(0, 1)
454 |         hs = []
455 |         new_states = []
456 |         for i in range(self.n_agent):
457 |             xs_i = F.relu(self.fc_x_layers[i](obs[i]))
458 |             hs_i, new_states_i = run_rnn(self.lstm_layers[i], xs_i, dones, states[i])
459 |             hs.append(hs_i.unsqueeze(0))
460 |             new_states.append(new_states_i.unsqueeze(0))
461 |         return torch.cat(hs), torch.cat(new_states)
462 | 
463 | 
464 | class CommNetMultiAgentPolicy(NCMultiAgentPolicy):
465 |     """Reference code: https://github.com/IC3Net/IC3Net/blob/master/comm.py.
466 |        Note in CommNet, the message is generated from hidden state only, so current state
467 |        and neigbor policies are not included in the inputs."""
468 | 
469 |     def __init__(self, n_s, n_a, n_agent, n_step, neighbor_mask, n_fc=64, n_h=64,
470 |                  n_s_ls=None, n_a_ls=None, identical=True):
471 |         Policy.__init__(self, n_a, n_s, n_step, 'cnet', None, identical)
472 |         if not self.identical:
473 |             self.n_s_ls = n_s_ls
474 |             self.n_a_ls = n_a_ls
475 |         self.n_agent = n_agent
476 |         self.neighbor_mask = neighbor_mask
477 |         self.n_fc = n_fc
478 |         self.n_h = n_h
479 |         self._init_net()
480 |         self._reset()
481 | 
482 |     def _init_comm_layer(self, n_n, n_ns, n_na):
483 |         fc_x_layer = nn.Linear(n_ns, self.n_fc)
484 |         init_layer(fc_x_layer, 'fc')
485 |         self.fc_x_layers.append(fc_x_layer)
486 |         if n_n:
487 |             fc_m_layer = nn.Linear(self.n_h, self.n_fc)
488 |             init_layer(fc_m_layer, 'fc')
489 |             self.fc_m_layers.append(fc_m_layer)
490 |         else:
491 |             self.fc_m_layers.append(None)
492 |         lstm_layer = nn.LSTMCell(self.n_fc, self.n_h)
493 |         init_layer(lstm_layer, 'lstm')
494 |         self.lstm_layers.append(lstm_layer)
495 | 
496 |     def _get_comm_s(self, i, n_n, x, h, p):
497 |         js = torch.from_numpy(np.where(self.neighbor_mask[i])[0]).long()
498 |         m_i = torch.index_select(h, 0, js).mean(dim=0, keepdim=True)
499 |         nx_i = torch.index_select(x, 0, js)
500 |         if self.identical:
501 |             nx_i = nx_i.view(1, self.n_s * n_n)
502 |         else:
503 |             nx_i_ls = []
504 |             for j in range(n_n):
505 |                 nx_i_ls.append(nx_i[j].narrow(0, 0, self.ns_ls_ls[i][j]))
506 |             nx_i = torch.cat(nx_i_ls).unsqueeze(0)
507 |         return F.relu(self.fc_x_layers[i](torch.cat([x[i].unsqueeze(0), nx_i], dim=1))) + \
508 |                self.fc_m_layers[i](m_i)
509 | 
510 | 
511 | class DIALMultiAgentPolicy(NCMultiAgentPolicy):
512 |     def __init__(self, n_s, n_a, n_agent, n_step, neighbor_mask, n_fc=64, n_h=64,
513 |                  n_s_ls=None, n_a_ls=None, identical=True):
514 |         Policy.__init__(self, n_a, n_s, n_step, 'dial', None, identical)
515 |         if not self.identical:
516 |             self.n_s_ls = n_s_ls
517 |             self.n_a_ls = n_a_ls
518 |         self.n_agent = n_agent
519 |         self.neighbor_mask = neighbor_mask
520 |         self.n_fc = n_fc
521 |         self.n_h = n_h
522 |         self._init_net()
523 |         self._reset()
524 | 
525 |     def _init_comm_layer(self, n_n, n_ns, n_na):
526 |         fc_x_layer = nn.Linear(n_ns, self.n_fc)
527 |         init_layer(fc_x_layer, 'fc')
528 |         self.fc_x_layers.append(fc_x_layer)
529 |         if n_n:
530 |             fc_m_layer = nn.Linear(self.n_h * n_n, self.n_fc)
531 |             init_layer(fc_m_layer, 'fc')
532 |             self.fc_m_layers.append(fc_m_layer)
533 |         else:
534 |             self.fc_m_layers.append(None)
535 |         lstm_layer = nn.LSTMCell(self.n_fc, self.n_h)
536 |         init_layer(lstm_layer, 'lstm')
537 |         self.lstm_layers.append(lstm_layer)
538 | 
539 |     def _get_comm_s(self, i, n_n, x, h, p):
540 |         # different with NC, include only it's own action, no other's policies.
541 |         # summation all the info
542 |         js = torch.from_numpy(np.where(self.neighbor_mask[i])[0]).long()
543 |         m_i = torch.index_select(h, 0, js).view(1, self.n_h * n_n)
544 |         nx_i = torch.index_select(x, 0, js)
545 |         if self.identical:
546 |             nx_i = nx_i.view(1, self.n_s * n_n)
547 |         else:
548 |             nx_i_ls = []
549 |             for j in range(n_n):
550 |                 nx_i_ls.append(nx_i[j].narrow(0, 0, self.ns_ls_ls[i][j]))
551 |             nx_i = torch.cat(nx_i_ls).unsqueeze(0)
552 |         a_i = one_hot(p[i].argmax().unsqueeze(0), self.n_fc)
553 |         return F.relu(self.fc_x_layers[i](torch.cat([x[i].unsqueeze(0), nx_i], dim=1))) + \
554 |                F.relu(self.fc_m_layers[i](m_i)) + a_i
555 | 
--------------------------------------------------------------------------------
/agents/utils.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import torch
  3 | import torch.nn as nn
  4 | 
  5 | 
  6 | def init_layer(layer, layer_type):
  7 |     """
  8 |     initializers
  9 |     """
 10 |     if layer_type == 'fc':
 11 |         nn.init.orthogonal_(layer.weight.data)
 12 |         nn.init.constant_(layer.bias.data, 0)
 13 |     elif layer_type == 'lstm':
 14 |         nn.init.orthogonal_(layer.weight_ih.data)
 15 |         nn.init.orthogonal_(layer.weight_hh.data)
 16 |         nn.init.constant_(layer.bias_ih.data, 0)
 17 |         nn.init.constant_(layer.bias_hh.data, 0)
 18 | 
 19 | 
 20 | def batch_to_seq(x):
 21 |     """
 22 |     layer helpers
 23 |     """
 24 |     n_step = x.shape[0]
 25 |     if len(x.shape) == 1:
 26 |         x = torch.unsqueeze(x, -1)
 27 |     return torch.chunk(x, n_step)
 28 | 
 29 | 
 30 | def run_rnn(layer, xs, dones, s):
 31 |     # run the LSTM (RNN) module
 32 |     xs = batch_to_seq(xs)
 33 |     # need dones to reset states
 34 |     dones = batch_to_seq(dones)
 35 |     n_in = int(xs[0].shape[1])
 36 |     n_out = int(s.shape[0]) // 2
 37 |     s = torch.unsqueeze(s, 0)
 38 |     h, c = torch.chunk(s, 2, dim=1)
 39 |     outputs = []
 40 |     for _, (x, done) in enumerate(zip(xs, dones)):
 41 |         c = c * (1 - done)
 42 |         h = h * (1 - done)
 43 |         h, c = layer(x, (h, c))
 44 |         outputs.append(h)
 45 |     s = torch.cat([h, c], dim=1)
 46 |     return torch.cat(outputs), torch.squeeze(s)
 47 | 
 48 | 
 49 | def one_hot(x, oh_dim, dim=-1):
 50 |     oh_shape = list(x.shape)
 51 |     if dim == -1:
 52 |         oh_shape.append(oh_dim)
 53 |     else:
 54 |         oh_shape = oh_shape[:dim + 1] + [oh_dim] + oh_shape[dim + 1:]
 55 |     x_oh = torch.zeros(oh_shape)
 56 |     x = torch.unsqueeze(x, -1)
 57 |     if dim == -1:
 58 |         x_oh = x_oh.scatter(dim, x, 1)
 59 |     else:
 60 |         x_oh = x_oh.scatter(dim + 1, x, 1)
 61 |     return x_oh
 62 | 
 63 | 
 64 | class TransBuffer:
 65 |     """
 66 |     buffers
 67 |     """
 68 |     def reset(self):
 69 |         self.buffer = []
 70 | 
 71 |     @property
 72 |     def size(self):
 73 |         return len(self.buffer)
 74 | 
 75 |     def add_transition(self, ob, a, r, *_args, **_kwargs):
 76 |         raise NotImplementedError()
 77 | 
 78 |     def sample_transition(self, *_args, **_kwargs):
 79 |         raise NotImplementedError()
 80 | 
 81 | 
 82 | class OnPolicyBuffer(TransBuffer):
 83 |     def __init__(self, gamma, alpha, distance_mask):
 84 |         self.gamma = gamma
 85 |         self.alpha = alpha
 86 |         if alpha > 0:
 87 |             self.distance_mask = distance_mask
 88 |             self.max_distance = np.max(distance_mask, axis=-1)
 89 |         self.reset()
 90 | 
 91 |     def reset(self, done=False):
 92 |         # the done before each step is required
 93 |         self.obs = []
 94 |         self.acts = []
 95 |         self.rs = []
 96 |         self.vs = []
 97 |         self.adds = []
 98 |         self.dones = [done]
 99 | 
100 |     def add_transition(self, ob, na, a, r, v, done):
101 |         self.obs.append(ob)
102 |         self.adds.append(na)
103 |         self.acts.append(a)
104 |         self.rs.append(r)
105 |         self.vs.append(v)
106 |         self.dones.append(done)
107 | 
108 |     def sample_transition(self, R, dt=0):
109 |         if self.alpha < 0:
110 |             self._add_R_Adv(R)
111 |         else:
112 |             self._add_s_R_Adv(R)
113 |         obs = np.array(self.obs, dtype=np.float32)
114 |         nas = np.array(self.adds, dtype=np.int32)
115 |         acts = np.array(self.acts, dtype=np.int32)
116 |         Rs = np.array(self.Rs, dtype=np.float32)
117 |         Advs = np.array(self.Advs, dtype=np.float32)
118 |         # use pre-step dones here
119 |         dones = np.array(self.dones[:-1], dtype=np.bool)
120 |         self.reset(self.dones[-1])
121 |         return obs, nas, acts, dones, Rs, Advs
122 | 
123 |     def _add_R_Adv(self, R):
124 |         Rs = []
125 |         Advs = []
126 |         # use post-step dones here
127 |         for r, v, done in zip(self.rs[::-1], self.vs[::-1], self.dones[:0:-1]):
128 |             R = r + self.gamma * R * (1. - done)
129 |             Adv = R - v
130 |             Rs.append(R)
131 |             Advs.append(Adv)
132 |         Rs.reverse()
133 |         Advs.reverse()
134 |         self.Rs = Rs
135 |         self.Advs = Advs
136 | 
137 |     def _add_st_R_Adv(self, R, dt):
138 |         Rs = []
139 |         Advs = []
140 |         # use post-step dones here
141 |         tdiff = dt
142 |         for r, v, done in zip(self.rs[::-1], self.vs[::-1], self.dones[:0:-1]):
143 |             R = self.gamma * R * (1. - done)
144 |             if done:
145 |                 tdiff = 0
146 |             # additional spatial rewards
147 |             tmax = min(tdiff, self.max_distance)
148 |             for t in range(tmax + 1):
149 |                 rt = np.sum(r[self.distance_mask == t])
150 |                 R += (self.gamma * self.alpha) ** t * rt
151 |             Adv = R - v
152 |             tdiff += 1
153 |             Rs.append(R)
154 |             Advs.append(Adv)
155 |         Rs.reverse()
156 |         Advs.reverse()
157 |         self.Rs = Rs
158 |         self.Advs = Advs
159 | 
160 |     def _add_s_R_Adv(self, R):
161 |         Rs = []
162 |         Advs = []
163 |         # use post-step dones here
164 |         for r, v, done in zip(self.rs[::-1], self.vs[::-1], self.dones[:0:-1]):
165 |             R = self.gamma * R * (1. - done)
166 |             # additional spatial rewards
167 |             for t in range(self.max_distance + 1):
168 |                 rt = np.sum(r[self.distance_mask == t])
169 |                 R += (self.alpha ** t) * rt
170 |             Adv = R - v
171 |             Rs.append(R)
172 |             Advs.append(Adv)
173 |         Rs.reverse()
174 |         Advs.reverse()
175 |         self.Rs = Rs
176 |         self.Advs = Advs
177 | 
178 | 
179 | class MultiAgentOnPolicyBuffer(OnPolicyBuffer):
180 |     def __init__(self, gamma, alpha, distance_mask):
181 |         super().__init__(gamma, alpha, distance_mask)
182 | 
183 |     def sample_transition(self, R, dt=0):
184 |         if self.alpha < 0:
185 |             self._add_R_Adv(R)
186 |         else:
187 |             self._add_s_R_Adv(R)
188 |         obs = np.transpose(np.array(self.obs, dtype=np.float32), (1, 0, 2))
189 |         policies = np.transpose(np.array(self.adds, dtype=np.float32), (1, 0, 2))
190 |         acts = np.transpose(np.array(self.acts, dtype=np.int32))
191 |         Rs = np.array(self.Rs, dtype=np.float32)
192 |         Advs = np.array(self.Advs, dtype=np.float32)
193 |         dones = np.array(self.dones[:-1], dtype=np.bool)
194 |         self.reset(self.dones[-1])
195 |         return obs, policies, acts, dones, Rs, Advs
196 | 
197 |     def _add_R_Adv(self, R):
198 |         Rs = []
199 |         Advs = []
200 |         vs = np.array(self.vs)
201 |         for i in range(vs.shape[1]):
202 |             cur_Rs = []
203 |             cur_Advs = []
204 |             cur_R = R[i]
205 |             for r, v, done in zip(self.rs[::-1], vs[::-1, i], self.dones[:0:-1]):
206 |                 cur_R = r + self.gamma * cur_R * (1. - done)
207 |                 cur_Adv = cur_R - v
208 |                 cur_Rs.append(cur_R)
209 |                 cur_Advs.append(cur_Adv)
210 |             cur_Rs.reverse()
211 |             cur_Advs.reverse()
212 |             Rs.append(cur_Rs)
213 |             Advs.append(cur_Advs)
214 |         self.Rs = np.array(Rs)
215 |         self.Advs = np.array(Advs)
216 | 
217 |     def _add_st_R_Adv(self, R, dt):
218 |         Rs = []
219 |         Advs = []
220 |         vs = np.array(self.vs)
221 |         for i in range(vs.shape[1]):
222 |             cur_Rs = []
223 |             cur_Advs = []
224 |             cur_R = R[i]
225 |             tdiff = dt
226 |             distance_mask = self.distance_mask[i]
227 |             max_distance = self.max_distance[i]
228 |             for r, v, done in zip(self.rs[::-1], vs[::-1, i], self.dones[:0:-1]):
229 |                 cur_R = self.gamma * cur_R * (1. - done)
230 |                 if done:
231 |                     tdiff = 0
232 |                 # additional spatial rewards
233 |                 tmax = min(tdiff, max_distance)
234 |                 for t in range(tmax + 1):
235 |                     rt = np.sum(r[distance_mask == t])
236 |                     cur_R += (self.gamma * self.alpha) ** t * rt
237 |                 cur_Adv = cur_R - v
238 |                 tdiff += 1
239 |                 cur_Rs.append(cur_R)
240 |                 cur_Advs.append(cur_Adv)
241 |             cur_Rs.reverse()
242 |             cur_Advs.reverse()
243 |             Rs.append(cur_Rs)
244 |             Advs.append(cur_Advs)
245 |         self.Rs = np.array(Rs)
246 |         self.Advs = np.array(Advs)
247 | 
248 |     def _add_s_R_Adv(self, R):
249 |         Rs = []
250 |         Advs = []
251 |         vs = np.array(self.vs)
252 |         for i in range(vs.shape[1]):
253 |             cur_Rs = []
254 |             cur_Advs = []
255 |             cur_R = R[i]
256 |             distance_mask = self.distance_mask[i]
257 |             max_distance = self.max_distance[i]
258 |             for r, v, done in zip(self.rs[::-1], vs[::-1, i], self.dones[:0:-1]):
259 |                 cur_R = self.gamma * cur_R * (1. - done)
260 |                 # additional spatial rewards
261 |                 for t in range(max_distance + 1):
262 |                     rt = np.sum(r[distance_mask == t])
263 |                     cur_R += (self.alpha ** t) * rt
264 |                 cur_Adv = cur_R - v
265 |                 cur_Rs.append(cur_R)
266 |                 cur_Advs.append(cur_Adv)
267 |             cur_Rs.reverse()
268 |             cur_Advs.reverse()
269 |             Rs.append(cur_Rs)
270 |             Advs.append(cur_Advs)
271 |         self.Rs = np.array(Rs)
272 |         self.Advs = np.array(Advs)
273 | 
274 | 
275 | class Scheduler:
276 |     """
277 |     util functions
278 |     """
279 |     def __init__(self, val_init, val_min=0, total_step=0, decay='linear'):
280 |         self.val = val_init
281 |         self.N = float(total_step)
282 |         self.val_min = val_min
283 |         self.decay = decay
284 |         self.n = 0
285 | 
286 |     def get(self, n_step):
287 |         self.n += n_step
288 |         if self.decay == 'linear':
289 |             return max(self.val_min, self.val * (1 - self.n / self.N))
290 |         else:
291 |             return self.val
292 | 
--------------------------------------------------------------------------------
/configs/parameters_der20.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import math
  3 | 
  4 | DER_num = 20
  5 | lines_num = 20
  6 | loads_num = 10
  7 | sampling_time = 0.05
  8 | state_dim = 9
  9 | 
 10 | pi = math.pi
 11 | # --------Parameter values--------------
 12 | Lc = .35e-3
 13 | rLc = .03
 14 | wc = 31.41
 15 | 
 16 | rN = 1e4
 17 | wref = 2 * pi * 60
 18 | Vnom = 480.0
 19 | 
 20 | mp1 = 9.4e-5
 21 | mp2 = mp1
 22 | mp6 = 12.5e-5
 23 | mp7 = mp6
 24 | mp3 = 9.4e-5
 25 | mp4 = mp3
 26 | mp8 = 12.5e-5
 27 | mp9 = mp8
 28 | mp5 = 9.4e-5
 29 | mp11 = mp5
 30 | mp10 = 12.5e-5
 31 | mp16 = mp10
 32 | mp12 = 9.4e-5
 33 | mp13 = mp12
 34 | mp17 = 12.5e-5
 35 | mp18 = mp17
 36 | mp14 = 9.4e-5
 37 | mp15 = mp14
 38 | mp19 = 12.5e-5
 39 | mp20 = mp19
 40 | 
 41 | nq1 = 1.3e-3
 42 | nq2 = nq1
 43 | nq6 = 1.5e-3
 44 | nq7 = nq6
 45 | nq3 = 1.3e-3
 46 | nq4 = nq3
 47 | nq8 = 1.5e-3
 48 | nq9 = nq8
 49 | nq5 = 1.3e-3
 50 | nq11 = nq5
 51 | nq10 = 1.5e-3
 52 | nq16 = nq10
 53 | nq12 = 1.3e-3
 54 | nq13 = nq12
 55 | nq17 = 1.5e-3
 56 | nq18 = nq17
 57 | nq14 = 1.3e-3
 58 | nq15 = nq14
 59 | nq19 = 1.5e-3
 60 | nq20 = nq19
 61 | 
 62 | # ---------------
 63 | kp = 4
 64 | ki = 60
 65 | 
 66 | # ----Network Data-------
 67 | rline1 = .23
 68 | Lline1 = .1 / (2 * pi * 60)
 69 | rline2 = .35
 70 | Lline2 = .58 / (2 * pi * 60)
 71 | rline3 = .23
 72 | Lline3 = .1 / (2 * pi * 60)
 73 | rline4 = .23
 74 | Lline4 = .1 / (2 * pi * 60)
 75 | rline5 = .35
 76 | Lline5 = .58 / (2 * pi * 60)
 77 | rline6 = .23
 78 | Lline6 = .1 / (2 * pi * 60)
 79 | rline7 = .23
 80 | Lline7 = .1 / (2 * pi * 60)
 81 | rline8 = .35
 82 | Lline8 = .58 / (2 * pi * 60)
 83 | rline9 = .23
 84 | Lline9 = .1 / (2 * pi * 60)
 85 | rline10 = .23
 86 | Lline10 = .1 / (2 * pi * 60)
 87 | rline11 = .35
 88 | Lline11 = .58 / (2 * pi * 60)
 89 | rline12 = .23
 90 | Lline12 = .1 / (2 * pi * 60)
 91 | rline13 = .23
 92 | Lline13 = .1 / (2 * pi * 60)
 93 | rline14 = .35
 94 | Lline14 = .58 / (2 * pi * 60)
 95 | rline15 = .23
 96 | Lline15 = .1 / (2 * pi * 60)
 97 | rline16 = .23
 98 | Lline16 = .1 / (2 * pi * 60)
 99 | rline17 = .35
100 | Lline17 = .58 / (2 * pi * 60)
101 | rline18 = .23
102 | Lline18 = .1 / (2 * pi * 60)
103 | rline19 = .23
104 | Lline19 = .1 / (2 * pi * 60)
105 | rline20 = .35
106 | Lline20 = .58 / (2 * pi * 60)
107 | 
108 | QF = 0.5
109 | Rload1 = 2
110 | Lload1 = QF * 2 / (2 * pi * 60)
111 | Rload2 = 2
112 | Lload2 = QF * 1 / (2 * pi * 60)
113 | Rload3 = 2
114 | Lload3 = QF * 2 / (2 * pi * 60)
115 | Rload4 = 2
116 | Lload4 = QF * 1 / (2 * pi * 60)
117 | Rload5 = 2
118 | Lload5 = QF * 2 / (2 * pi * 60)
119 | Rload6 = 2
120 | Lload6 = QF * 1 / (2 * pi * 60)
121 | Rload7 = 2
122 | Lload7 = QF * 2 / (2 * pi * 60)
123 | Rload8 = 2
124 | Lload8 = QF * 1 / (2 * pi * 60)
125 | Rload9 = 2
126 | Lload9 = QF * 2 / (2 * pi * 60)
127 | Rload10 = 2
128 | Lload10 = QF * 1 / (2 * pi * 60)
129 | 
130 | # ------------Controller Parameters------------------------
131 | a_ctrl = 400
132 | 
133 | AP = 60 * np.array([[.0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
134 |                     [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
135 |                     [0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
136 |                     [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
137 |                     [0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
138 |                     [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
139 |                     [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
140 |                     [0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
141 |                     [0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
142 |                     [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
143 |                     [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
144 |                     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0],
145 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
146 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0],
147 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
148 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
149 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0],
150 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0],
151 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],
152 |                     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]])
153 | 
154 | D = np.array([[sum(AP[0]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
155 |               [0, sum(AP[1]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
156 |               [0, 0, sum(AP[2]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
157 |               [0, 0, 0, sum(AP[3]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
158 |               [0, 0, 0, 0, sum(AP[4]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
159 |               [0, 0, 0, 0, 0, sum(AP[5]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
160 |               [0, 0, 0, 0, 0, 0, sum(AP[6]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
161 |               [0, 0, 0, 0, 0, 0, 0, sum(AP[7]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
162 |               [0, 0, 0, 0, 0, 0, 0, 0, sum(AP[8]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
163 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[9]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
164 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[10]), 0, 0, 0, 0, 0, 0, 0, 0, 0],
165 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[11]), 0, 0, 0, 0, 0, 0, 0, 0],
166 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[12]), 0, 0, 0, 0, 0, 0, 0],
167 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[13]), 0, 0, 0, 0, 0, 0],
168 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[14]), 0, 0, 0, 0, 0],
169 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[15]), 0, 0, 0, 0],
170 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[16]), 0, 0, 0],
171 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[17]), 0, 0],
172 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[18]), 0],
173 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[19])]])
174 | 
175 | L = D - AP
176 | 
177 | # Pinning gain to the reference frequency
178 | G = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
179 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
180 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
181 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
182 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
183 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
184 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
185 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
186 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
187 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
188 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
189 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
190 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
191 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
192 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
193 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
194 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
195 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
196 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
197 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
198 | 
199 | # Fig 17
200 | Physical_Graph = np.array([[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 1
201 |                            [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 2
202 |                            [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 3
203 |                            [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 4
204 |                            [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 5
205 |                            [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 6
206 |                            [0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 7
207 |                            [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 8
208 |                            [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 9
209 |                            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],  # 10
210 |                            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],  # 11
211 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],  # 12
212 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],  # 13
213 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0],  # 14
214 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],  # 15
215 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],  # 16
216 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],  # 17
217 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0],  # 18
218 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],  # 19
219 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]])  # 20
220 | 
221 | Distance_Mask = np.array([[0, 3, 4, 7, 8, 1, 2, 5, 6, 9, 6, 9, 10, 13, 14, 7, 8, 11, 12, 15],  # 1
222 |                           [3, 0, 1, 4, 5, 2, 1, 2, 3, 6, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13],  # 2
223 |                           [4, 1, 0, 3, 4, 3, 2, 1, 2, 5, 2, 5, 6, 9, 10, 3, 4, 7, 8, 11],  # 3
224 |                           [7, 4, 3, 0, 1, 6, 5, 2, 1, 2, 3, 6, 3, 6, 7, 4, 5, 4, 5, 8],  # 4
225 |                           [8, 5, 4, 1, 0, 7, 6, 3, 2, 1, 4, 3, 2, 5, 6, 5, 4, 3, 4, 7],  # 5
226 |                           [1, 2, 3, 6, 7, 0, 1, 4, 5, 8, 5, 8, 9, 12, 13, 6, 7, 10, 11, 14],  # 6
227 |                           [2, 1, 2, 5, 6, 1, 0, 3, 4, 7, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13],  # 7
228 |                           [5, 2, 1, 2, 3, 4, 3, 0, 1, 4, 1, 4, 5, 8, 9, 2, 3, 5, 6, 9],  # 8
229 |                           [6, 3, 2, 1, 2, 5, 4, 1, 0, 3, 2, 5, 4, 7, 8, 3, 4, 5, 6, 9],  # 9
230 |                           [9, 6, 5, 2, 1, 8, 7, 4, 3, 0, 5, 2, 1, 4, 5, 4, 3, 2, 3, 6],  # 10
231 |                           [6, 3, 2, 3, 4, 5, 4, 1, 2, 5, 0, 3, 4, 7, 8, 1, 2, 5, 6, 9],  # 11
232 |                           [9, 6, 5, 4, 3, 8, 7, 4, 5, 2, 3, 0, 1, 4, 5, 2, 1, 2, 3, 6],  # 12
233 |                           [10, 7, 6, 3, 2, 9, 8, 5, 4, 1, 4, 1, 0, 3, 4, 3, 2, 1, 2, 5],  # 13
234 |                           [13, 10, 9, 6, 5, 12, 11, 8, 7, 4, 7, 4, 3, 0, 1, 6, 5, 2, 1, 2],  # 14
235 |                           [14, 11, 10, 7, 6, 13, 12, 9, 8, 5, 8, 5, 4, 1, 0, 7, 6, 3, 2, 1],  # 15
236 |                           [7, 4, 3, 4, 5, 6, 5, 2, 3, 6, 1, 2, 3, 6, 7, 0, 1, 4, 5, 8],  # 16
237 |                           [8, 5, 4, 5, 4, 7, 6, 3, 4, 3, 2, 1, 2, 5, 6, 1, 0, 3, 4, 7],  # 17
238 |                           [11, 8, 7, 4, 3, 10, 9, 6, 5, 2, 5, 2, 1, 2, 3, 4, 3, 0, 1, 4],  # 18
239 |                           [12, 9, 8, 5, 4, 11, 10, 7, 6, 3, 6, 3, 2, 1, 2, 5, 4, 1, 0, 3],  # 19
240 |                           [15, 12, 11, 8, 7, 14, 13, 10, 9, 6, 9, 6, 5, 2, 1, 8, 7, 4, 3, 0]])  # 20
241 | 
242 | # add 0, to meet the difference between MATLAB and python, shape=(201,)
243 | x0 = np.array([0, 0, 0, 0, 0, 0,  # 1
244 |                0, 0, 0, 0, 0,  # 2
245 |                0, 0, 0, 0, 0,  # 3
246 |                0, 0, 0, 0, 0,  # 4
247 |                0, 0, 0, 0, 0,  # 5
248 |                0, 0, 0, 0, 0,  # 6
249 |                0, 0, 0, 0, 0,  # 7
250 |                0, 0, 0, 0, 0,  # 8
251 |                0, 0, 0, 0, 0,  # 9
252 |                0, 0, 0, 0, 0,  # 10
253 |                0, 0, 0, 0, 0,  # 11
254 |                0, 0, 0, 0, 0,  # 12
255 |                0, 0, 0, 0, 0,  # 13
256 |                0, 0, 0, 0, 0,  # 14
257 |                0, 0, 0, 0, 0,  # 15
258 |                0, 0, 0, 0, 0,  # 16
259 |                0, 0, 0, 0, 0,  # 17
260 |                0, 0, 0, 0, 0,  # 18
261 |                0, 0, 0, 0, 0,  # 19
262 |                0, 0, 0, 0, 0,  # 20
263 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
264 |                0, 0, 0,
265 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
266 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
267 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
268 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
269 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
270 |                Vnom, Vnom, Vnom,
271 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
272 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
273 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ])
274 | 
275 | # add 0, to meet the difference between MATLAB and python, shape=(201,)
276 | x_critic = np.array([0, 0, 0, 0, 0, 0,  # 1
277 |                      0, 0, 0, 0, 0,  # 2
278 |                      0, 0, 0, 0, 0,  # 3
279 |                      0, 0, 0, 0, 0,  # 4
280 |                      0, 0, 0, 0, 0,  # 5
281 |                      0, 0, 0, 0, 0,  # 6
282 |                      0, 0, 0, 0, 0,  # 7
283 |                      0, 0, 0, 0, 0,  # 8
284 |                      0, 0, 0, 0, 0,  # 9
285 |                      0, 0, 0, 0, 0,  # 10
286 |                      0, 0, 0, 0, 0,  # 11
287 |                      0, 0, 0, 0, 0,  # 12
288 |                      0, 0, 0, 0, 0,  # 13
289 |                      0, 0, 0, 0, 0,  # 14
290 |                      0, 0, 0, 0, 0,  # 15
291 |                      0, 0, 0, 0, 0,  # 16
292 |                      0, 0, 0, 0, 0,  # 17
293 |                      0, 0, 0, 0, 0,  # 18
294 |                      0, 0, 0, 0, 0,  # 19
295 |                      0, 0, 0, 0, 0,  # 20
296 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297 |                      0, 0, 0, 0, 0,
298 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
299 |                      2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
300 |                      2 * pi * 60,
301 |                      2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
302 |                      2 * pi * 60,
303 |                      2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
304 |                      Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
305 |                      Vnom, Vnom,
306 |                      Vnom,
307 |                      Vnom, 0])
308 | 
309 | mp = np.array(
310 |     [mp1, mp2, mp3, mp4, mp5, mp6, mp7, mp8, mp9, mp10, mp11, mp12, mp13, mp14, mp15, mp16, mp17, mp18, mp19, mp20])
311 | nq = np.array(
312 |     [nq1, nq2, nq3, nq4, nq5, nq6, nq7, nq8, nq9, nq10, nq11, nq12, nq13, nq14, nq15, nq16, nq17, nq18, nq19, nq20])
313 | 
314 | obs_mean = np.array([[0.00000000e+00, 6.29283743e+04, 3.07978839e+04, 1.32348191e+02,
315 |                       -6.47528904e+01, 1.03796796e+02, -5.21592059e+01, 3.68194779e+02,
316 |                       -1.20152670e+01],
317 |                      [-4.93041786e-02, 6.30173923e+04, 1.68234121e+04, 1.27650177e+02,
318 |                       -3.41183888e+01, 9.99311072e+01, -3.05826380e+01, 3.84754303e+02,
319 |                       -2.75800537e+01],
320 |                      [-4.83026940e-02, 6.30138967e+04, 2.64078047e+04, 1.30929256e+02,
321 |                       -5.48771644e+01, 1.01772581e+02, -4.67882379e+01, 3.73359007e+02,
322 |                       -2.59512455e+01],
323 |                      [-8.82804061e-02, 6.31662493e+04, 1.48097410e+04, 1.27243570e+02,
324 |                       -3.00282412e+01, 9.88151381e+01, -2.75418624e+01, 3.86580620e+02,
325 |                       -3.58185231e+01],
326 |                      [-1.53396305e-01, 6.32074782e+04, 2.45682959e+04, 1.30650136e+02,
327 |                       -5.09303028e+01, 9.88664977e+01, -5.14727440e+01, 3.70418223e+02,
328 |                       -5.65501408e+01],
329 |                      [-1.65011027e-02, 4.73166932e+04, 1.84625127e+04, 9.69607871e+01,
330 |                       -3.78968366e+01, 7.58555115e+01, -3.08587258e+01, 3.81494347e+02,
331 |                       -1.44883493e+01],
332 |                      [-7.03528928e-02, 4.73602159e+04, 2.49348027e+04, 9.90546714e+01,
333 |                       -5.22085498e+01, 7.60480094e+01, -4.77392993e+01, 3.70103481e+02,
334 |                       -3.20859402e+01],
335 |                      [-7.80410413e-02, 4.74392625e+04, 1.68590902e+04, 9.67741550e+01,
336 |                       -3.44178654e+01, 7.50861449e+01, -3.12799559e+01, 3.81936553e+02,
337 |                       -3.18280343e+01],
338 |                      [-9.81964209e-02, 4.74772788e+04, 2.48865169e+04, 9.92333620e+01,
339 |                       -5.21513496e+01, 7.53031111e+01, -4.64068288e+01, 3.70441241e+02,
340 |                       -3.64576923e+01],
341 |                      [-1.52710271e-01, 4.75412263e+04, 1.36380038e+04, 9.60072464e+01,
342 |                       -2.76367498e+01, 7.37299287e+01, -2.95498280e+01, 3.82774251e+02,
343 |                       -5.52793130e+01],
344 |                      [-6.63784012e-02, 6.30905432e+04, 2.42016821e+04, 1.30314870e+02,
345 |                       -5.00216150e+01, 1.01273178e+02, -4.52626412e+01, 3.74384415e+02,
346 |                       -3.10691635e+01],
347 |                      [1.31971170e-02, 6.30522068e+04, 1.06244599e+04, 1.25673273e+02,
348 |                       -2.12483097e+01, 9.79990848e+01, -1.29564891e+01, 3.93142820e+02,
349 |                       -9.59958079e+00],
350 |                      [9.89911093e-04, 6.30372815e+04, 2.22167508e+04, 1.29546967e+02,
351 |                       -4.57372659e+01, 1.00918663e+02, -3.41846513e+01, 3.78791916e+02,
352 |                       -9.59958079e+00],
353 |                      [1.29618049e-01, 6.27656182e+04, 1.44435942e+04, 1.26417018e+02,
354 |                       -2.92002087e+01, 9.71299612e+01, -1.42892440e+01, 3.84691938e+02,
355 |                       2.45889420e+01],
356 |                      [1.43425530e-01, 6.27289664e+04, 1.94882799e+04, 1.28056372e+02,
357 |                       -4.00042387e+01, 9.88248607e+01, -2.35289490e+01, 3.77023871e+02,
358 |                       2.75940875e+01],
359 |                      [-5.75664523e-02, 4.74206821e+04, 1.52145138e+04, 9.62322446e+01,
360 |                       -3.09076068e+01, 7.50619493e+01, -2.75737065e+01, 3.84408967e+02,
361 |                       -2.60070071e+01],
362 |                      [-4.62132310e-02, 4.74006002e+04, 2.24274566e+04, 9.83433097e+01,
363 |                       -4.65498907e+01, 7.62699283e+01, -4.00471076e+01, 3.74414440e+02,
364 |                       -2.20451640e+01],
365 |                      [3.50348076e-02, 4.73514947e+04, 9.46583650e+03, 9.44818202e+01,
366 |                       -1.89488035e+01, 7.35617397e+01, -1.25175327e+01, 3.92288555e+02,
367 |                       3.46725897e+00],
368 |                      [9.29765789e-02, 4.72303747e+04, 2.63115101e+04, 9.92533169e+01,
369 |                       -5.53249064e+01, 7.80638969e+01, -3.85288092e+01, 3.68578201e+02,
370 |                       1.52290973e+01],
371 |                      [2.44430771e-01, 4.71629073e+04, 1.22011703e+03, 9.19220885e+01,
372 |                       -2.49415110e+00, 6.66335088e+01, 1.07921775e+01, 3.94242595e+02,
373 |                       6.97643296e+01]])
374 | 
375 | obs_std = np.array([[1e-12, 4.20343356e+03, 4.85700984e+03, 8.49569114e+00,
376 |                      1.04435108e+01, 3.09905688e+01, 1.64983659e+01, 1.07705269e+02,
377 |                      3.62599666e+00],
378 |                     [3.53208644e-02, 4.07268993e+03, 3.18474039e+03, 7.27486594e+00,
379 |                      6.59024116e+00, 2.94062352e+01, 1.07673233e+01, 1.12420029e+02,
380 |                      1.43182321e+01],
381 |                     [4.50219271e-02, 4.07628579e+03, 3.84826542e+03, 7.78605599e+00,
382 |                      8.02164152e+00, 2.99804139e+01, 1.58040164e+01, 1.08851426e+02,
383 |                      1.57230310e+01],
384 |                     [1.08836450e-01, 4.19079661e+03, 5.57892569e+03, 8.56541981e+00,
385 |                      1.16624186e+01, 2.88013185e+01, 1.74233657e+01, 1.12298972e+02,
386 |                      3.36366445e+01],
387 |                     [1.37059517e-01, 4.33290558e+03, 6.12791477e+03, 9.14265967e+00,
388 |                      1.33285040e+01, 2.80931036e+01, 2.36151694e+01, 1.07532260e+02,
389 |                      4.17724974e+01],
390 |                     [7.36926786e-03, 3.19718414e+03, 2.92929707e+03, 5.97164442e+00,
391 |                      6.09308020e+00, 2.25476621e+01, 9.85390513e+00, 1.11499200e+02,
392 |                      5.05572909e+00],
393 |                     [2.78848864e-02, 3.11031174e+03, 3.97391116e+03, 5.79699603e+00,
394 |                      8.91482441e+00, 2.22632525e+01, 1.49408615e+01, 1.08441720e+02,
395 |                      1.35471693e+01],
396 |                     [8.25305955e-02, 3.04606208e+03, 2.92804158e+03, 5.75932221e+00,
397 |                      6.03682404e+00, 2.19137623e+01, 1.16520326e+01, 1.11262948e+02,
398 |                      2.60898841e+01],
399 |                     [9.79098227e-02, 3.09173758e+03, 5.23944524e+03, 6.58828741e+00,
400 |                      1.15435474e+01, 2.17736019e+01, 1.89274478e+01, 1.07758569e+02,
401 |                      2.97496664e+01],
402 |                     [1.37595975e-01, 3.22258710e+03, 4.08098380e+03, 6.47070128e+00,
403 |                      8.53185325e+00, 2.09757867e+01, 1.47363294e+01, 1.11029807e+02,
404 |                      4.25592585e+01],
405 |                     [8.21029285e-02, 4.00905106e+03, 3.80086720e+03, 7.79257515e+00,
406 |                      8.12332877e+00, 2.96808581e+01, 1.55003639e+01, 1.09240975e+02,
407 |                      2.52447521e+01],
408 |                     [1.02595739e-01, 4.03706055e+03, 2.49241366e+03, 7.33073790e+00,
409 |                      4.97502660e+00, 2.93381675e+01, 9.14063564e+00, 1.14486565e+02,
410 |                      2.99601396e+01],
411 |                     [1.07966938e-01, 4.05754667e+03, 4.11356167e+03, 8.05455891e+00,
412 |                      8.76190441e+00, 3.01553432e+01, 1.54748166e+01, 1.10186587e+02,
413 |                      2.99601396e+01],
414 |                     [1.38518044e-01, 4.22761865e+03, 3.50591976e+03, 6.62981281e+00,
415 |                      7.38719628e+00, 2.95937703e+01, 1.50819504e+01, 1.12070762e+02,
416 |                      4.20976493e+01],
417 |                     [1.44113015e-01, 4.28464751e+03, 6.48124316e+03, 6.51512713e+00,
418 |                      1.41503352e+01, 3.01614611e+01, 1.94874888e+01, 1.10038587e+02,
419 |                      4.35260379e+01],
420 |                     [8.27426818e-02, 3.01275981e+03, 2.29503144e+03, 5.45061519e+00,
421 |                      4.72076804e+00, 2.20351945e+01, 9.73276151e+00, 1.12169970e+02,
422 |                      2.48839111e+01],
423 |                     [8.48676710e-02, 3.01884033e+03, 2.93932933e+03, 5.51880851e+00,
424 |                      6.35914398e+00, 2.24510839e+01, 1.33921664e+01, 1.09320916e+02,
425 |                      2.41679942e+01],
426 |                     [1.09723468e-01, 3.06290265e+03, 2.10642429e+03, 5.12431400e+00,
427 |                      4.27448769e+00, 2.20158690e+01, 9.04678262e+00, 1.14585687e+02,
428 |                      3.18676060e+01],
429 |                     [1.30148700e-01, 3.14843530e+03, 3.05573700e+03, 5.70614986e+00,
430 |                      6.79232126e+00, 2.42166221e+01, 1.54501623e+01, 1.07152775e+02,
431 |                      3.70785000e+01],
432 |                     [1.47446439e-01, 3.23645184e+03, 3.54838371e+03, 4.82558091e+00,
433 |                      7.05528823e+00, 2.06150534e+01, 1.21554693e+01, 1.14626963e+02,
434 |                      5.10992820e+01]])
435 | 
--------------------------------------------------------------------------------
/configs/parameters_der4.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import math
  3 | 
  4 | pi = math.pi
  5 | 
  6 | Lc = .35e-3
  7 | rLc = .03
  8 | wc = 31.41
  9 | 
 10 | DER_num = 4
 11 | lines_num = 3
 12 | loads_num = 2
 13 | 
 14 | mp1 = 9.4 * 1e-5
 15 | nq1 = 1.3 * 1e-3
 16 | mp2 = mp1
 17 | nq2 = nq1
 18 | mp3 = 12.5 * 1e-5
 19 | nq3 = 1.5 * 1e-3
 20 | mp4 = mp3
 21 | nq4 = nq3
 22 | 
 23 | rN = 1e4
 24 | wref = 2 * pi * 60
 25 | Vnom = 380.0
 26 | 
 27 | # for critical bus control
 28 | kp = 4
 29 | ki = 40
 30 | 
 31 | # Network Data
 32 | rline1 = 0.23
 33 | Lline1 = 0.318 / (2 * pi * 60)
 34 | rline2 = 0.35
 35 | Lline2 = 1.847 / (2 * pi * 60)
 36 | rline3 = 0.23
 37 | Lline3 = 0.318 / (2 * pi * 60)
 38 | 
 39 | Rload1 = 2.5
 40 | Lload1 = 1 / (2 * pi * 60)
 41 | Rload2 = 3
 42 | Lload2 = 2 / (2 * pi * 60)
 43 | 
 44 | # Controller Parameters
 45 | a_ctrl = 40
 46 | 
 47 | AP = 4 * np.array([[.0, 0, 0, 0],
 48 |                    [1, 0, 0, 0],
 49 |                    [0, 1, 0, 0],
 50 |                    [0, 0, 1, 0],
 51 |                    ])
 52 | 
 53 | # Pinning gain to the reference frequency
 54 | G = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
 55 | 
 56 | # matrix for bus and load connection
 57 | BUS_LOAD = np.array([1, 0, 1, 0])
 58 | 
 59 | # matrix for bus connection
 60 | BUSES = np.array([[0, 1, 0, 0],
 61 |                   [1, 0, 1, 0],
 62 |                   [0, 1, 0, 1],
 63 |                   [0, 0, 1, 0]])
 64 | 
 65 | Physical_Graph = np.array([[0, 1, 0, 0],
 66 |                            [1, 0, 1, 0],
 67 |                            [0, 1, 0, 1],
 68 |                            [0, 0, 1, 0]])
 69 | 
 70 | Distance_Mask = np.array([[0, 1, 2, 3],
 71 |                           [1, 0, 1, 2],
 72 |                           [2, 1, 0, 1],
 73 |                           [3, 2, 1, 0]])
 74 | 
 75 | DER_dic = np.array([[Rload1, Lload1, mp1, nq1],
 76 |                     [0, 0, mp2, nq2],
 77 |                     [Rload2, Lload2, mp3, nq3],
 78 |                     [0, 0, mp4, nq4]])
 79 | 
 80 | rline = np.array([rline1, rline2, rline3])
 81 | Lline = np.array([Lline1, Lline2, Lline3])
 82 | 
 83 | # add 0, to meet the difference between MATLAB and python, shape=(35,)
 84 | x0 = np.array([0, 0, 0, 0, 0, 0,
 85 |                0, 0, 0, 0, 0,
 86 |                0, 0, 0, 0, 0,
 87 |                0, 0, 0, 0, 0,
 88 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 89 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
 90 |                Vnom, Vnom, Vnom, Vnom])
 91 | 
 92 | mp = np.array([mp1, mp2, mp3, mp4])
 93 | nq = np.array([nq1, nq2, nq3, nq4])
 94 | 
 95 | obs_mean = np.array([[0.00000000e+00, -1.25472351e+03, -2.12519352e+03, 4.10819053e+00,
 96 |                       -8.73258639e-01, 7.06497949e-01, -4.92866602e-01, 3.72485264e+02,
 97 |                       -6.83244265e+00],
 98 |                      [-3.01979698e-03, 4.14551913e+02, -2.07692677e+03, -2.13223582e+00,
 99 |                       -1.34217855e+00, 0.00000000e+00, 0.00000000e+00, 3.89795909e+02,
100 |                       1.26289101e+01],
101 |                      [-6.87461608e-03, -1.07905479e+02, -1.55205693e+03, 8.49008251e+00,
102 |                       -4.62387385e-01, -3.16647345e-01, -1.19334490e+00, 3.81486416e+02,
103 |                       2.29906559e+01],
104 |                      [-8.41740074e-03, -1.03054684e+03, -8.92739050e+02, -3.68726862e+00,
105 |                       -9.41845252e-01, 0.00000000e+00, 0.00000000e+00, 3.95523327e+02,
106 |                       3.52166136e+01]])
107 | 
108 | obs_std = np.array([[1e-12, 9.03935509e+04, 4.07198509e+04, 3.09694785e+02,
109 |                      3.63693026e+01, 1.14880758e+01, 2.14060149e+01, 1.26208840e+01,
110 |                      1.04869236e+00],
111 |                     [4.90044782e-01, 1.06416791e+05, 2.64131909e+04, 3.33947461e+02,
112 |                      6.00689740e+01, 1e-12, 1e-12, 1.18473960e+01,
113 |                      6.53970631e+00],
114 |                     [3.60300508e-01, 1.23964177e+05, 2.30459256e+04, 3.85219327e+02,
115 |                      9.32747376e+01, 2.21537447e+01, 2.56743784e+01, 1.18200888e+01,
116 |                      1.39858282e+01],
117 |                     [3.52282040e-01, 1.14521148e+05, 2.90240489e+04, 2.66153315e+02,
118 |                      8.41119329e+01, 1e-12, 1e-12, 1.29708915e+01,
119 |                      1.47636637e+01]])
120 | 
--------------------------------------------------------------------------------
/configs/parameters_der40.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import math
  3 | 
  4 | DER_num = 40
  5 | lines_num = 42
  6 | loads_num = 20
  7 | sampling_time = 0.05
  8 | state_dim = 9
  9 | 
 10 | pi = math.pi
 11 | # --------Parameter values--------------
 12 | Lc = .35e-3
 13 | rLc = .03
 14 | wc = 31.41
 15 | 
 16 | rN = 1e4
 17 | wref = 2 * pi * 60
 18 | Vnom = 480.0
 19 | 
 20 | mp1 = 9.4e-5
 21 | mp2 = mp1
 22 | mp6 = 12.5e-5
 23 | mp7 = mp6
 24 | mp3 = 9.4e-5
 25 | mp4 = mp3
 26 | mp8 = 12.5e-5
 27 | mp9 = mp8
 28 | mp5 = 9.4e-5
 29 | mp11 = mp5
 30 | mp10 = 12.5e-5
 31 | mp16 = mp10
 32 | mp12 = 9.4e-5
 33 | mp13 = mp12
 34 | mp17 = 12.5e-5
 35 | mp18 = mp17
 36 | mp14 = 9.4e-5
 37 | mp15 = mp14
 38 | mp19 = 12.5e-5
 39 | mp20 = mp19
 40 | 
 41 | mp21 = 9.4e-5
 42 | mp22 = mp21
 43 | mp26 = 12.5e-5
 44 | mp27 = mp26
 45 | mp23 = 9.4e-5
 46 | mp24 = mp23
 47 | mp28 = 12.5e-5
 48 | mp29 = mp28
 49 | mp25 = 9.4e-5
 50 | mp31 = mp25
 51 | mp30 = 12.5e-5
 52 | mp36 = mp30
 53 | mp32 = 9.4e-5
 54 | mp33 = mp32
 55 | mp37 = 12.5e-5
 56 | mp38 = mp37
 57 | mp34 = 9.4e-5
 58 | mp35 = mp34
 59 | mp39 = 12.5e-5
 60 | mp40 = mp39
 61 | 
 62 | nq1 = 1.3e-3
 63 | nq2 = nq1
 64 | nq6 = 1.5e-3
 65 | nq7 = nq6
 66 | nq3 = 1.3e-3
 67 | nq4 = nq3
 68 | nq8 = 1.5e-3
 69 | nq9 = nq8
 70 | nq5 = 1.3e-3
 71 | nq11 = nq5
 72 | nq10 = 1.5e-3
 73 | nq16 = nq10
 74 | nq12 = 1.3e-3
 75 | nq13 = nq12
 76 | nq17 = 1.5e-3
 77 | nq18 = nq17
 78 | nq14 = 1.3e-3
 79 | nq15 = nq14
 80 | nq19 = 1.5e-3
 81 | nq20 = nq19
 82 | 
 83 | nq21 = 1.3e-3
 84 | nq22 = nq21
 85 | nq26 = 1.5e-3
 86 | nq27 = nq26
 87 | nq23 = 1.3e-3
 88 | nq24 = nq23
 89 | nq28 = 1.5e-3
 90 | nq29 = nq28
 91 | nq25 = 1.3e-3
 92 | nq31 = nq25
 93 | nq30 = 1.5e-3
 94 | nq36 = nq30
 95 | nq32 = 1.3e-3
 96 | nq33 = nq32
 97 | nq37 = 1.5e-3
 98 | nq38 = nq37
 99 | nq34 = 1.3e-3
100 | nq35 = nq34
101 | nq39 = 1.5e-3
102 | nq40 = nq39
103 | 
104 | # ---------------
105 | kp = 4
106 | ki = 60
107 | 
108 | # ----Network Data-------
109 | rline1 = .23
110 | Lline1 = .1 / (2 * pi * 60)
111 | rline2 = .35
112 | Lline2 = .58 / (2 * pi * 60)
113 | rline3 = .23
114 | Lline3 = .1 / (2 * pi * 60)
115 | rline4 = .23
116 | Lline4 = .1 / (2 * pi * 60)
117 | rline5 = .35
118 | Lline5 = .58 / (2 * pi * 60)
119 | rline6 = .23
120 | Lline6 = .1 / (2 * pi * 60)
121 | rline7 = .23
122 | Lline7 = .1 / (2 * pi * 60)
123 | rline8 = .35
124 | Lline8 = .58 / (2 * pi * 60)
125 | rline9 = .23
126 | Lline9 = .1 / (2 * pi * 60)
127 | rline10 = .23
128 | Lline10 = .1 / (2 * pi * 60)
129 | rline11 = .35
130 | Lline11 = .58 / (2 * pi * 60)
131 | rline12 = .23
132 | Lline12 = .1 / (2 * pi * 60)
133 | rline13 = .23
134 | Lline13 = .1 / (2 * pi * 60)
135 | rline14 = .35
136 | Lline14 = .58 / (2 * pi * 60)
137 | rline15 = .23
138 | Lline15 = .1 / (2 * pi * 60)
139 | rline16 = .23
140 | Lline16 = .1 / (2 * pi * 60)
141 | rline17 = .35
142 | Lline17 = .58 / (2 * pi * 60)
143 | rline18 = .23
144 | Lline18 = .1 / (2 * pi * 60)
145 | rline19 = .23
146 | Lline19 = .1 / (2 * pi * 60)
147 | rline20 = .35
148 | Lline20 = .58 / (2 * pi * 60)
149 | 
150 | # ----Network Data-------
151 | rline21 = .23
152 | Lline21 = .1 / (2 * pi * 60)
153 | rline22 = .35
154 | Lline22 = .58 / (2 * pi * 60)
155 | rline23 = .23
156 | Lline23 = .1 / (2 * pi * 60)
157 | rline24 = .23
158 | Lline24 = .1 / (2 * pi * 60)
159 | rline25 = .35
160 | Lline25 = .58 / (2 * pi * 60)
161 | rline26 = .23
162 | Lline26 = .1 / (2 * pi * 60)
163 | rline27 = .23
164 | Lline27 = .1 / (2 * pi * 60)
165 | rline28 = .35
166 | Lline28 = .58 / (2 * pi * 60)
167 | rline29 = .23
168 | Lline29 = .1 / (2 * pi * 60)
169 | rline30 = .23
170 | Lline30 = .1 / (2 * pi * 60)
171 | rline31 = .35
172 | Lline31 = .58 / (2 * pi * 60)
173 | rline32 = .23
174 | Lline32 = .1 / (2 * pi * 60)
175 | rline33 = .23
176 | Lline33 = .1 / (2 * pi * 60)
177 | rline34 = .35
178 | Lline34 = .58 / (2 * pi * 60)
179 | rline35 = .23
180 | Lline35 = .1 / (2 * pi * 60)
181 | rline36 = .23
182 | Lline36 = .1 / (2 * pi * 60)
183 | rline37 = .35
184 | Lline37 = .58 / (2 * pi * 60)
185 | rline38 = .23
186 | Lline38 = .1 / (2 * pi * 60)
187 | rline39 = .23
188 | Lline39 = .1 / (2 * pi * 60)
189 | rline40 = .35
190 | Lline40 = .58 / (2 * pi * 60)
191 | 
192 | # vb 18 --> 21
193 | rline41 = .23
194 | Lline41 = .1 / (2 * pi * 60)
195 | # vb 20 --> 23
196 | rline42 = .35
197 | Lline42 = .58 / (2 * pi * 60)
198 | 
199 | QF = 0.5
200 | Rload1 = 2
201 | Lload1 = QF * 2 / (2 * pi * 60)
202 | Rload2 = 2
203 | Lload2 = QF * 1 / (2 * pi * 60)
204 | Rload3 = 2
205 | Lload3 = QF * 2 / (2 * pi * 60)
206 | Rload4 = 2
207 | Lload4 = QF * 1 / (2 * pi * 60)
208 | Rload5 = 2
209 | Lload5 = QF * 2 / (2 * pi * 60)
210 | Rload6 = 2
211 | Lload6 = QF * 1 / (2 * pi * 60)
212 | Rload7 = 2
213 | Lload7 = QF * 2 / (2 * pi * 60)
214 | Rload8 = 2
215 | Lload8 = QF * 1 / (2 * pi * 60)
216 | Rload9 = 2
217 | Lload9 = QF * 2 / (2 * pi * 60)
218 | Rload10 = 2
219 | Lload10 = QF * 1 / (2 * pi * 60)
220 | Rload11 = 2
221 | Lload11 = QF * 2 / (2 * pi * 60)
222 | Rload12 = 2
223 | Lload12 = QF * 1 / (2 * pi * 60)
224 | Rload13 = 2
225 | Lload13 = QF * 2 / (2 * pi * 60)
226 | Rload14 = 2
227 | Lload14 = QF * 1 / (2 * pi * 60)
228 | Rload15 = 2
229 | Lload15 = QF * 2 / (2 * pi * 60)
230 | Rload16 = 2
231 | Lload16 = QF * 1 / (2 * pi * 60)
232 | Rload17 = 2
233 | Lload17 = QF * 2 / (2 * pi * 60)
234 | Rload18 = 2
235 | Lload18 = QF * 1 / (2 * pi * 60)
236 | Rload19 = 2
237 | Lload19 = QF * 2 / (2 * pi * 60)
238 | Rload20 = 2
239 | Lload20 = QF * 1 / (2 * pi * 60)
240 | 
241 | # ------------Controller Parameters------------------------
242 | a_ctrl = 400
243 | # Physical Graph
244 | AP = 60 * np.array([[.0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
245 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 1
246 |                     [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
247 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 2
248 |                     [0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
249 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 3
250 |                     [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
251 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 4
252 |                     [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 5
254 |                     [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 6
256 |                     [0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
257 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 7
258 |                     [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
259 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 8
260 |                     [0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
261 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 9
262 |                     [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
263 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 10
264 |                     [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
265 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 11
266 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0,
267 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 12
268 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0,
269 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 13
270 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0,
271 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 14
272 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
273 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 15
274 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
275 |                      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 16
276 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0,
277 |                      0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 17
278 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0,
279 |                      1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 18
280 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1,
281 |                      0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 19
282 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
283 |                      0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 20
284 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
285 |                      0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 21
286 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
287 |                      1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 22
288 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1,
289 |                      0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 23
290 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
291 |                      0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 24
292 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
293 |                      0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 25
294 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
295 |                      1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 26
296 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297 |                      0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 27
298 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
299 |                      0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 28
300 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
301 |                      0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 29
302 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
303 |                      0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],  # 30
304 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
305 |                      0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],  # 31
306 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
307 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0],  # 32
308 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
309 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0],  # 33
310 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
311 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0],  # 34
312 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],  # 35
314 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
315 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],  # 36
316 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
317 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0],  # 37
318 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
319 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0],  # 38
320 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
321 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],  # 39
322 |                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
323 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0]  # 40
324 |                     ])
325 | 
326 | D = np.array([[sum(AP[0]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
327 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
328 |               [0, sum(AP[1]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
329 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
330 |               [0, 0, sum(AP[2]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
331 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
332 |               [0, 0, 0, sum(AP[3]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
333 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
334 |               [0, 0, 0, 0, sum(AP[4]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
335 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
336 |               [0, 0, 0, 0, 0, sum(AP[5]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
337 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
338 |               [0, 0, 0, 0, 0, 0, sum(AP[6]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
340 |               [0, 0, 0, 0, 0, 0, 0, sum(AP[7]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
341 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
342 |               [0, 0, 0, 0, 0, 0, 0, 0, sum(AP[8]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
343 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
344 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[9]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
346 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[10]), 0, 0, 0, 0, 0, 0, 0, 0, 0,
347 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
348 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[11]), 0, 0, 0, 0, 0, 0, 0, 0,
349 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
350 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[12]), 0, 0, 0, 0, 0, 0, 0,
351 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
352 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[13]), 0, 0, 0, 0, 0, 0,
353 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
354 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[14]), 0, 0, 0, 0, 0,
355 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
356 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[15]), 0, 0, 0, 0,
357 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
358 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[16]), 0, 0, 0,
359 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
360 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[17]), 0, 0,
361 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
362 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[18]), 0,
363 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
364 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[19]),
365 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
366 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
367 |                sum(AP[20]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
368 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
369 |                0, sum(AP[21]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
370 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
371 |                0, 0, sum(AP[22]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
372 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
373 |                0, 0, 0, sum(AP[23]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
374 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
375 |                0, 0, 0, 0, sum(AP[24]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
376 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
377 |                0, 0, 0, 0, 0, sum(AP[25]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
378 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
379 |                0, 0, 0, 0, 0, 0, sum(AP[26]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
380 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
381 |                0, 0, 0, 0, 0, 0, 0, sum(AP[27]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
382 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
383 |                0, 0, 0, 0, 0, 0, 0, 0, sum(AP[28]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
384 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
385 |                0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[29]), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
386 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
387 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[30]), 0, 0, 0, 0, 0, 0, 0, 0, 0],
388 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
389 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[31]), 0, 0, 0, 0, 0, 0, 0, 0],
390 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
391 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[32]), 0, 0, 0, 0, 0, 0, 0],
392 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
393 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[33]), 0, 0, 0, 0, 0, 0],
394 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
395 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[34]), 0, 0, 0, 0, 0],
396 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
397 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[35]), 0, 0, 0, 0],
398 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
399 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[36]), 0, 0, 0],
400 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
401 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[37]), 0, 0],
402 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
403 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[38]), 0],
404 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
405 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sum(AP[39])]
406 |               ])
407 | 
408 | L = D - AP
409 | 
410 | # Pinning gain to the reference frequency
411 | G = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412 |                0, 0, 0, 0, 0],
413 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
414 |                0, 0, 0, 0, 0],
415 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
416 |                0, 0, 0, 0, 0],
417 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
418 |                0, 0, 0, 0, 0],
419 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
420 |                0, 0, 0, 0, 0],
421 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
422 |                0, 0, 0, 0, 0],
423 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
424 |                0, 0, 0, 0, 0],
425 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
426 |                0, 0, 0, 0, 0],
427 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
428 |                0, 0, 0, 0, 0],
429 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
430 |                0, 0, 0, 0, 0],
431 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
432 |                0, 0, 0, 0, 0],
433 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
434 |                0, 0, 0, 0, 0],
435 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
436 |                0, 0, 0, 0, 0],
437 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
438 |                0, 0, 0, 0, 0],
439 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
440 |                0, 0, 0, 0, 0],
441 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
442 |                0, 0, 0, 0, 0],
443 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
444 |                0, 0, 0, 0, 0],
445 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446 |                0, 0, 0, 0, 0],
447 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
448 |                0, 0, 0, 0, 0],
449 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
450 |                0, 0, 0, 0, 0],
451 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
452 |                0, 0, 0, 0, 0],
453 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
454 |                0, 0, 0, 0, 0],
455 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
456 |                0, 0, 0, 0, 0],
457 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
458 |                0, 0, 0, 0, 0],
459 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
460 |                0, 0, 0, 0, 0],
461 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
462 |                0, 0, 0, 0, 0],
463 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
464 |                0, 0, 0, 0, 0],
465 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
466 |                0, 0, 0, 0, 0],
467 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
468 |                0, 0, 0, 0, 0],
469 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
470 |                0, 0, 0, 0, 0],
471 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
472 |                0, 0, 0, 0, 0],
473 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
474 |                0, 0, 0, 0, 0],
475 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
476 |                0, 0, 0, 0, 0],
477 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
478 |                0, 0, 0, 0, 0],
479 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
480 |                0, 0, 0, 0, 0],
481 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
482 |                0, 0, 0, 0, 0],
483 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484 |                0, 0, 0, 0, 0],
485 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
486 |                0, 0, 0, 0, 0],
487 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488 |                0, 0, 0, 0, 0],
489 |               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
490 |                0, 0, 0, 0, 0]
491 |               ])
492 | 
493 | Physical_Graph = np.array([[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
494 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 1
495 |                            [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
496 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 2
497 |                            [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
498 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 3
499 |                            [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
500 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 4
501 |                            [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
502 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 5
503 |                            [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
504 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 6
505 |                            [0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
506 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 7
507 |                            [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
508 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 8
509 |                            [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
510 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 9
511 |                            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
512 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 10
513 |                            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
514 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 11
515 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
516 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 12
517 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
518 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 13
519 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
520 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 14
521 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
522 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 15
523 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
524 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 16
525 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
526 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 17
527 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
528 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 18
529 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
530 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 19
531 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
532 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 20
533 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
534 |                             0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 21
535 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
536 |                             0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 22
537 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
538 |                             0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 23
539 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
540 |                             0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 24
541 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
542 |                             0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 25
543 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
544 |                             1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 26
545 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
546 |                             0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 27
547 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
548 |                             0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 28
549 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
550 |                             0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 29
551 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
552 |                             0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],  # 30
553 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
554 |                             0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],  # 31
555 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
556 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],  # 32
557 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
558 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],  # 33
559 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
560 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0],  # 34
561 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
562 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],  # 35
563 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
564 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],  # 36
565 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
566 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],  # 37
567 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
568 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0],  # 38
569 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
570 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],  # 39
571 |                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
572 |                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]  # 40
573 |                            ])
574 | 
575 | Distance_Mask = np.array([[0, 3, 4, 7, 8, 1, 2, 5, 6, 9, 6, 9, 10, 13, 14, 7, 8, 11, 12, 15,
576 |                            12, 15, 16, 19, 20, 13, 14, 17, 18, 21, 18, 20, 20, 20, 20, 20, 20, 20, 20, 20],  # 1
577 |                           [3, 0, 1, 4, 5, 2, 1, 2, 3, 6, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13,
578 |                            10, 13, 14, 17, 18, 11, 12, 15, 16, 19, 16, 18, 20, 20, 20, 20, 20, 20, 20, 20],  # 2
579 |                           [4, 1, 0, 3, 4, 3, 2, 1, 2, 5, 2, 5, 6, 9, 10, 3, 4, 7, 8, 11,
580 |                            9, 12, 13, 16, 17, 10, 11, 14, 15, 18, 15, 17, 19, 20, 20, 20, 20, 20, 20, 20],  # 3
581 |                           [7, 4, 3, 0, 1, 6, 5, 2, 1, 2, 3, 6, 3, 6, 7, 4, 5, 4, 5, 8,
582 |                            5, 8, 9, 12, 13, 6, 7, 10, 11, 14, 11, 14, 15, 18, 19, 12, 13, 16, 17, 20],  # 4
583 |                           [8, 5, 4, 1, 0, 7, 6, 3, 2, 1, 4, 3, 2, 5, 6, 5, 4, 3, 4, 7,
584 |                            4, 7, 8, 11, 12, 5, 6, 9, 10, 13, 10, 13, 14, 17, 18, 11, 12, 15, 16, 19],  # 5
585 |                           [1, 2, 3, 6, 7, 0, 1, 4, 5, 8, 5, 8, 9, 12, 13, 6, 7, 10, 11, 14,
586 |                            11, 14, 15, 18, 19, 12, 13, 16, 17, 20, 17, 19, 20, 20, 20, 20, 20, 20, 20, 20],  # 6
587 |                           [2, 1, 2, 5, 6, 1, 0, 3, 4, 7, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13,
588 |                            10, 13, 14, 17, 18, 11, 12, 15, 16, 19, 16, 18, 19, 19, 20, 20, 20, 20, 20, 20],  # 7
589 |                           [5, 2, 1, 2, 3, 4, 3, 0, 1, 4, 1, 4, 5, 8, 9, 2, 3, 5, 6, 9,
590 |                            8, 11, 12, 15, 16, 9, 10, 13, 14, 17, 14, 16, 18, 19, 20, 20, 20, 20, 20, 20],  # 8
591 |                           [6, 3, 2, 1, 2, 5, 4, 1, 0, 3, 2, 5, 4, 7, 8, 3, 4, 5, 6, 9,
592 |                            9, 12, 13, 16, 17, 10, 11, 14, 15, 18, 15, 17, 19, 20, 20, 20, 20, 20, 20, 20],  # 9
593 |                           [9, 6, 5, 2, 1, 8, 7, 4, 3, 0, 5, 2, 1, 4, 5, 4, 3, 2, 3, 6,
594 |                            3, 6, 7, 10, 11, 4, 5, 8, 9, 12, 9, 12, 13, 16, 17, 10, 11, 14, 15, 18],  # 10
595 |                           [6, 3, 2, 3, 4, 5, 4, 1, 2, 5, 0, 3, 4, 7, 8, 1, 2, 5, 6, 9,
596 |                            6, 9, 10, 13, 14, 7, 8, 11, 12, 15, 12, 14, 16, 17, 18, 20, 20, 20, 20, 20],  # 11
597 |                           [9, 6, 5, 4, 3, 8, 7, 4, 5, 2, 3, 0, 1, 4, 5, 2, 1, 2, 3, 6,
598 |                            3, 6, 7, 10, 11, 4, 5, 8, 9, 12, 9, 12, 13, 16, 17, 13, 14, 17, 18, 21],  # 12
599 |                           [10, 7, 6, 3, 2, 9, 8, 5, 4, 1, 4, 1, 0, 3, 4, 3, 2, 1, 2, 5,
600 |                            2, 5, 6, 9, 10, 3, 4, 7, 8, 10, 8, 11, 12, 15, 16, 12, 13, 16, 17, 20],  # 13
601 |                           [13, 10, 9, 6, 5, 12, 11, 8, 7, 4, 7, 4, 3, 0, 1, 6, 5, 2, 1, 2,
602 |                            3, 4, 3, 6, 7, 4, 5, 4, 5, 8, 5, 9, 12, 13, 6, 7, 10, 11, 12, 15],  # 14
603 |                           [14, 11, 10, 7, 6, 13, 12, 9, 8, 5, 8, 5, 4, 1, 0, 7, 6, 3, 2, 1,
604 |                            4, 3, 2, 5, 6, 5, 4, 3, 4, 7, 4, 7, 6, 9, 10, 5, 6, 8, 9, 13],  # 15
605 |                           [7, 4, 3, 4, 5, 6, 5, 2, 3, 6, 1, 2, 3, 6, 7, 0, 1, 4, 5, 8,
606 |                            5, 8, 9, 12, 13, 6, 7, 10, 11, 14, 11, 14, 15, 18, 19, 12, 13, 16, 17, 20],  # 16
607 |                           [8, 5, 4, 5, 4, 7, 6, 3, 4, 3, 2, 1, 2, 5, 6, 1, 0, 3, 4, 7,
608 |                            4, 7, 8, 11, 12, 5, 6, 9, 10, 13, 10, 13, 14, 17, 18, 11, 12, 15, 16, 19],  # 17
609 |                           [11, 8, 7, 4, 3, 10, 9, 6, 5, 2, 5, 2, 1, 2, 3, 4, 3, 0, 1, 4,
610 |                            1, 4, 5, 8, 9, 2, 3, 6, 7, 10, 7, 10, 11, 14, 15, 8, 9, 12, 13, 16],  # 18
611 |                           [12, 9, 8, 5, 4, 11, 10, 7, 6, 3, 6, 3, 2, 1, 2, 5, 4, 1, 0, 3,
612 |                            2, 5, 4, 7, 8, 3, 4, 7, 8, 11, 8, 11, 12, 15, 16, 9, 10, 13, 14, 17],  # 19
613 |                           [15, 12, 11, 8, 7, 14, 13, 10, 9, 6, 9, 6, 5, 2, 1, 8, 7, 4, 3, 0,
614 |                            5, 2, 1, 4, 5, 4, 3, 2, 3, 6, 3, 6, 7, 10, 11, 4, 5, 8, 9, 12],  # 20
615 |                           [12, 10, 9, 5, 4, 11, 10, 8, 9, 3, 6, 3, 2, 3, 4, 5, 4, 1, 2, 5,
616 |                            0, 3, 4, 7, 8, 1, 2, 5, 6, 9, 6, 9, 10, 13, 14, 7, 8, 11, 12, 15],  # 21
617 |                           [15, 13, 12, 8, 7, 14, 13, 11, 12, 6, 9, 6, 5, 4, 3, 8, 7, 4, 5, 2,
618 |                            3, 0, 1, 4, 5, 2, 1, 2, 3, 6, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13],  # 22
619 |                           [16, 14, 13, 9, 8, 15, 14, 12, 13, 7, 10, 7, 6, 3, 2, 9, 8, 5, 4, 1,
620 |                            4, 1, 0, 3, 4, 3, 2, 1, 2, 5, 2, 5, 6, 9, 10, 3, 4, 7, 8, 11],  # 23
621 |                           [19, 17, 16, 12, 11, 18, 17, 15, 16, 10, 13, 10, 9, 6, 5, 12, 11, 8, 7, 4,
622 |                            7, 4, 3, 0, 1, 6, 5, 2, 1, 2, 3, 6, 3, 6, 7, 4, 5, 4, 5, 8],  # 24
623 |                           [20, 18, 17, 13, 12, 19, 18, 16, 17, 11, 14, 11, 10, 7, 6, 13, 12, 9, 8, 5,
624 |                            8, 5, 4, 1, 0, 7, 6, 3, 2, 1, 4, 3, 2, 5, 6, 5, 4, 3, 4, 7],  # 25
625 |                           [13, 11, 10, 6, 5, 12, 11, 9, 10, 4, 7, 4, 3, 4, 5, 6, 5, 2, 3, 4,
626 |                            1, 2, 3, 6, 7, 0, 1, 4, 5, 8, 5, 8, 9, 12, 13, 6, 7, 10, 11, 14],  # 26
627 |                           [14, 12, 11, 7, 6, 13, 12, 10, 11, 5, 8, 5, 4, 5, 4, 7, 6, 3, 4, 5,
628 |                            2, 1, 2, 5, 6, 1, 0, 3, 4, 7, 4, 7, 8, 11, 12, 5, 6, 9, 10, 13],  # 27
629 |                           [17, 15, 14, 10, 9, 16, 15, 13, 14, 8, 11, 8, 7, 4, 3, 10, 9, 6, 7, 2,
630 |                            5, 2, 1, 2, 3, 4, 3, 0, 1, 4, 1, 4, 5, 8, 9, 2, 3, 5, 6, 9],  # 28
631 |                           [18, 16, 15, 11, 10, 17, 16, 14, 15, 9, 12, 9, 8, 5, 4, 11, 10, 7, 8, 3,
632 |                            6, 3, 2, 1, 2, 5, 4, 1, 0, 3, 2, 5, 4, 7, 8, 3, 4, 5, 6, 9],  # 29
633 |                           [21, 19, 18, 14, 13, 20, 19, 17, 18, 12, 15, 12, 10, 8, 7, 14, 13, 10, 11, 6,
634 |                            9, 6, 5, 2, 1, 8, 7, 4, 3, 0, 5, 2, 1, 4, 5, 4, 3, 2, 3, 6],  # 30
635 |                           [18, 16, 15, 11, 10, 20, 16, 14, 15, 9, 12, 9, 8, 5, 4, 11, 10, 7, 8, 3,
636 |                            6, 3, 2, 3, 4, 5, 4, 1, 2, 5, 0, 3, 4, 7, 8, 1, 2, 5, 6, 9],  # 31
637 |                           [20, 18, 17, 14, 13, 20, 18, 16, 17, 12, 14, 12, 11, 8, 7, 14, 13, 10, 11, 6,
638 |                            9, 6, 5, 4, 3, 8, 7, 4, 5, 2, 3, 0, 1, 4, 5, 2, 1, 2, 3, 6],  # 32
639 |                           [20, 20, 19, 15, 14, 20, 19, 18, 19, 13, 16, 13, 12, 9, 6, 15, 14, 11, 12, 7,
640 |                            10, 7, 6, 3, 2, 9, 8, 5, 4, 1, 4, 1, 0, 3, 4, 3, 2, 1, 2, 5],  # 33
641 |                           [20, 20, 20, 18, 17, 20, 19, 19, 20, 16, 17, 16, 15, 12, 9, 18, 17, 14, 15, 10,
642 |                            13, 10, 9, 6, 5, 12, 11, 8, 7, 4, 7, 4, 3, 0, 1, 6, 5, 2, 1, 2],  # 34
643 |                           [20, 20, 20, 19, 18, 20, 20, 20, 20, 17, 18, 17, 16, 13, 10, 19, 18, 15, 16, 11,
644 |                            14, 11, 10, 7, 6, 13, 12, 9, 8, 5, 8, 5, 4, 1, 0, 7, 6, 3, 2, 1],  # 35
645 |                           [20, 20, 20, 12, 11, 20, 20, 20, 20, 10, 20, 13, 12, 6, 5, 12, 11, 8, 9, 4,
646 |                            7, 4, 3, 4, 5, 6, 5, 2, 3, 6, 1, 2, 3, 6, 7, 0, 1, 4, 5, 8],  # 36
647 |                           [20, 20, 20, 13, 12, 20, 20, 20, 20, 11, 20, 14, 13, 7, 6, 13, 12, 9, 10, 5,
648 |                            8, 5, 4, 5, 4, 7, 6, 3, 4, 3, 2, 1, 2, 5, 6, 1, 0, 3, 4, 7],  # 37
649 |                           [20, 20, 20, 16, 15, 20, 20, 20, 20, 14, 20, 17, 16, 10, 8, 16, 15, 12, 13, 8,
650 |                            11, 8, 7, 4, 3, 10, 9, 6, 5, 2, 5, 2, 1, 2, 3, 4, 3, 0, 1, 4],  # 38
651 |                           [20, 20, 20, 17, 16, 20, 20, 20, 20, 15, 20, 18, 17, 11, 9, 17, 16, 13, 14, 9,
652 |                            12, 9, 8, 5, 4, 11, 10, 7, 6, 3, 6, 3, 2, 1, 2, 5, 4, 1, 0, 3],  # 39
653 |                           [20, 20, 20, 20, 19, 20, 20, 20, 20, 18, 20, 21, 20, 12, 13, 20, 19, 16, 17, 12,
654 |                            15, 12, 11, 8, 7, 14, 13, 10, 9, 6, 9, 6, 5, 2, 1, 8, 7, 4, 3, 0]  # 40
655 |                           ])
656 | 
657 | # add 0, to meet the difference between MATLAB and python, shape=(201,)
658 | x0 = np.array([0, 0, 0, 0, 0, 0,  # 1
659 |                0, 0, 0, 0, 0,  # 2
660 |                0, 0, 0, 0, 0,  # 3
661 |                0, 0, 0, 0, 0,  # 4
662 |                0, 0, 0, 0, 0,  # 5
663 |                0, 0, 0, 0, 0,  # 6
664 |                0, 0, 0, 0, 0,  # 7
665 |                0, 0, 0, 0, 0,  # 8
666 |                0, 0, 0, 0, 0,  # 9
667 |                0, 0, 0, 0, 0,  # 10
668 |                0, 0, 0, 0, 0,  # 11
669 |                0, 0, 0, 0, 0,  # 12
670 |                0, 0, 0, 0, 0,  # 13
671 |                0, 0, 0, 0, 0,  # 14
672 |                0, 0, 0, 0, 0,  # 15
673 |                0, 0, 0, 0, 0,  # 16
674 |                0, 0, 0, 0, 0,  # 17
675 |                0, 0, 0, 0, 0,  # 18
676 |                0, 0, 0, 0, 0,  # 19
677 |                0, 0, 0, 0, 0,  # 20
678 |                0, 0, 0, 0, 0,  # 21
679 |                0, 0, 0, 0, 0,  # 22
680 |                0, 0, 0, 0, 0,  # 23
681 |                0, 0, 0, 0, 0,  # 24
682 |                0, 0, 0, 0, 0,  # 25
683 |                0, 0, 0, 0, 0,  # 26
684 |                0, 0, 0, 0, 0,  # 27
685 |                0, 0, 0, 0, 0,  # 28
686 |                0, 0, 0, 0, 0,  # 29
687 |                0, 0, 0, 0, 0,  # 30
688 |                0, 0, 0, 0, 0,  # 31
689 |                0, 0, 0, 0, 0,  # 32
690 |                0, 0, 0, 0, 0,  # 33
691 |                0, 0, 0, 0, 0,  # 34
692 |                0, 0, 0, 0, 0,  # 35
693 |                0, 0, 0, 0, 0,  # 36
694 |                0, 0, 0, 0, 0,  # 37
695 |                0, 0, 0, 0, 0,  # 38
696 |                0, 0, 0, 0, 0,  # 39
697 |                0, 0, 0, 0, 0,  # 40
698 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
699 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
700 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
701 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
702 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
703 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
704 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
705 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
706 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
707 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
708 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
709 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
710 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
711 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
712 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
713 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
714 |                2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60, 2 * pi * 60,
715 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
716 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
717 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
718 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
719 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
720 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
721 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
722 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
723 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
724 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
725 | 
726 | mp = np.array(
727 |     [mp1, mp2, mp3, mp4, mp5, mp6, mp7, mp8, mp9, mp10, mp11, mp12, mp13, mp14, mp15, mp16, mp17, mp18, mp19, mp20,
728 |      mp21, mp22, mp23, mp24, mp25, mp26, mp27, mp28, mp29, mp30, mp31, mp32, mp33, mp34, mp35, mp36, mp37, mp38, mp39,
729 |      mp40])
730 | nq = np.array(
731 |     [nq1, nq2, nq3, nq4, nq5, nq6, nq7, nq8, nq9, nq10, nq11, nq12, nq13, nq14, nq15, nq16, nq17, nq18, nq19, nq20,
732 |      nq21, nq22, nq23, nq24, nq25, nq26, nq27, nq28, nq29, nq30, nq31, nq32, nq33, nq34, nq35, nq36, nq37, nq38, nq39,
733 |      nq40])
734 | 
735 | obs_mean = 0
736 | 
737 | obs_std = 1
--------------------------------------------------------------------------------
/configs/parameters_der6.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import math
  3 | 
  4 | DER_num = 6
  5 | lines_num = 10
  6 | loads_num = 4
  7 | sampling_time = 0.05
  8 | state_dim = 9
  9 | 
 10 | pi = math.pi
 11 | 
 12 | # --------Parameter values--------------
 13 | Lc = .35e-3
 14 | rLc = .03
 15 | wc = 31.41
 16 | mp1 = 0.3 * 18.8e-5
 17 | mp2 = mp1
 18 | mp3 = 0.3 * 25e-5
 19 | mp4 = mp3
 20 | mp5 = mp1
 21 | mp6 = mp1
 22 | nq1 = 0.2 * 2.6e-3
 23 | nq2 = nq1
 24 | nq3 = 0.2 * 3e-3
 25 | nq4 = nq3
 26 | nq5 = nq1
 27 | nq6 = nq1
 28 | 
 29 | # --------------
 30 | kp = 4
 31 | ki = 40
 32 | 
 33 | rN = 1e4
 34 | wb = 2 * pi * 60
 35 | wref = 2 * pi * 60
 36 | Vnom = 480
 37 | 
 38 | # -----Network Data-------
 39 | r300 = ((480 / 24900) ** 2) * 1.3368
 40 | x300 = ((480 / 24900) ** 2) * 1.3343
 41 | r301 = ((480 / 24900) ** 2) * 1.9300
 42 | x301 = ((480 / 24900) ** 2) * 1.4115
 43 | r302 = ((480 / 24900) ** 2) * 2.7995
 44 | x302 = ((480 / 24900) ** 2) * 1.4855
 45 | r303 = ((480 / 24900) ** 2) * 2.7995
 46 | x303 = ((480 / 24900) ** 2) * 1.4855
 47 | r304 = ((480 / 24900) ** 2) * 1.9217
 48 | x304 = ((480 / 24900) ** 2) * 1.4212
 49 | 
 50 | rline1 = 0.000189394 * ((37500 + 29730) * r300 + 320 * r301)
 51 | Lline1 = 0.000189394 * ((37500 + 29730) * x300 + 320 * x301) / wref
 52 | rline2 = 0.000189394 * (10210 * r301)
 53 | Lline2 = 0.000189394 * (10210 * x301) / wref
 54 | rline3 = 0.000189394 * (3030 * r303)
 55 | Lline3 = 0.000189394 * (3030 * x303) / wref
 56 | rline4 = 0.000189394 * ((840 + 20440 + 520) * r301)
 57 | Lline4 = 0.000189394 * ((840 + 20440 + 520) * x301) / wref
 58 | rline5 = 0.000189394 * (23330 * r303)
 59 | Lline5 = 0.000189394 * (23330 * x303) / wref
 60 | rline6 = 0.000189394 * ((36830 + 10) * r301)
 61 | Lline6 = 0.000189394 * ((36830 + 10) * x301) / wref
 62 | rline7 = 0.000189394 * (1620 * r303)
 63 | Lline7 = 0.000189394 * (1620 * x303) / wref
 64 | rline8 = 0.000189394 * (5830 * r301)
 65 | Lline8 = 0.000189394 * (5830 * x301) / wref
 66 | rline9 = 0.000189394 * ((280 + 1350 + 3640 + 530) * r301)
 67 | Lline9 = 0.000189394 * ((280 + 1350 + 3640 + 530) * x301) / wref
 68 | rline10 = 0.000189394 * ((2020 + 2680) * r301)
 69 | Lline10 = 0.000189394 * ((2020 + 2680) * x301) / wref
 70 | rline11 = 0.000189394 * (860 * r301)
 71 | Lline11 = 0.000189394 * (860 * x301) / wref
 72 | rline12 = 0.000189394 * (280 * r301 + 4860 * r304)
 73 | Lline12 = 0.000189394 * (280 * x301 + 4860 * x304) / wref
 74 | rline13 = 0.000189394 * ((1710 + 48150 + 13740) * r302)
 75 | Lline13 = 0.000189394 * ((1710 + 48150 + 13740) * x302) / wref
 76 | 
 77 | Rload1 = 1.5
 78 | Lload1 = 1 / wref
 79 | Rload2 = 0.5
 80 | Lload2 = 0.5 / wref
 81 | Rload3 = 1
 82 | Lload3 = 1 / wref
 83 | Rload4 = 0.8
 84 | Lload4 = 0.8 / wref
 85 | 
 86 | # Controller Parameters
 87 | R1 = pi / 2
 88 | R2 = pi / 50
 89 | Etha = 10
 90 | a_ctrl = 40
 91 | 
 92 | 
 93 | # Physical Graph
 94 | AP = 6 * np.array([[.0, 1, 1, 0, 0, 0],
 95 |                    [1, 0, 1, 1, 0, 0],
 96 |                    [1, 1, 0, 0, 1, 0],
 97 |                    [0, 1, 0, 0, 1, 1],
 98 |                    [0, 0, 0, 1, 0, 1],
 99 |                    [0, 0, 0, 1, 1, 0]])
100 | 
101 | Physical_Graph = np.array([[0, 1, 1, 0, 0, 0],  # 1
102 |                            [1, 0, 1, 1, 0, 0],  # 2
103 |                            [1, 1, 0, 0, 1, 0],  # 3
104 |                            [0, 1, 0, 0, 1, 1],  # 4
105 |                            [0, 0, 0, 1, 0, 1],  # 5
106 |                            [0, 0, 0, 1, 1, 0]])  # 6
107 | 
108 | Distance_Mask = np.array([[0, 1, 1, 2, 2, 3],  # 1
109 |                           [1, 0, 1, 1, 2, 2],  # 2
110 |                           [1, 1, 0, 2, 1, 2],  # 3
111 |                           [2, 1, 2, 0, 1, 1],  # 4
112 |                           [2, 2, 1, 1, 0, 1],  # 5
113 |                           [3, 2, 2, 1, 1, 0]])  # 6
114 | 
115 | D = np.array([[sum(AP[0]), 0, 0, 0, 0, 0],
116 |               [0, sum(AP[1]), 0, 0, 0, 0],
117 |               [0, 0, sum(AP[2]), 0, 0, 0],
118 |               [0, 0, 0, sum(AP[3]), 0, 0],
119 |               [0, 0, 0, 0, sum(AP[4]), 0],
120 |               [0, 0, 0, 0, 0, sum(AP[5])]])
121 | 
122 | L = D - AP
123 | 
124 | # Pinning gain to the reference frequency
125 | G = np.array([[1, 0, 0, 0, 0, 0],
126 |               [0, 0, 0, 0, 0, 0],
127 |               [0, 0, 0, 0, 0, 0],
128 |               [0, 0, 0, 0, 0, 0],
129 |               [0, 0, 0, 0, 0, 0],
130 |               [0, 0, 0, 0, 0, 0]])
131 | 
132 | # Initial Conditions
133 | x0 = np.array([0, 0, 0, 0, 0, 0,
134 |                0, 0, 0, 0, 0,
135 |                0, 0, 0, 0, 0,
136 |                0, 0, 0, 0, 0,
137 |                0, 0, 0, 0, 0,
138 |                0, 0, 0, 0, 0,
139 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 |                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141 |                wref, wref, wref, wref, wref, wref,
142 |                Vnom, Vnom, Vnom, Vnom, Vnom, Vnom,
143 |                0, 0, 0, 0,
144 |                0, 0, 0, 0,
145 |                0, 0, 0, 0,
146 |                0, 0, 0, 0,
147 |                0, 0, 0, 0,
148 |                0, 0, 0, 0, ])
149 | 
150 | # Initial Conditions
151 | x_critic = np.array([0, 0, 0, 0, 0, 0,
152 |                      0, 0, 0, 0, 0,
153 |                      0, 0, 0, 0, 0,
154 |                      0, 0, 0, 0, 0,
155 |                      0, 0, 0, 0, 0,
156 |                      0, 0, 0, 0, 0,
157 |                      0, 0, 0, 0, 0,
158 |                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 |                      wref, wref, wref, wref, wref, wref,
160 |                      Vnom, Vnom, Vnom, Vnom, Vnom, Vnom, 0,
161 |                      0, 0, 0, 0,
162 |                      0, 0, 0, 0,
163 |                      0, 0, 0, 0,
164 |                      0, 0, 0, 0,
165 |                      0, 0, 0, 0,
166 |                      0, 0, 0, 0])
167 | 
168 | mp = [mp1, mp2, mp3, mp4, mp5, mp6]
169 | nq = [nq1, nq2, nq3, nq4, nq5, nq6]
170 | 
171 | obs_mean = np.array([[0.00000000e+00, 5.94750957e+04, 5.18826718e+04, 1.59484495e+02,
172 |                       -1.39131987e+02, 1.58977121e+02, -1.37752942e+02, 3.50326969e+02,
173 |                       -1.66857296e+01],
174 |                      [-2.94964702e-03, 5.84485099e+04, 5.03222993e+04, 1.56643756e+02,
175 |                       -1.34910554e+02, 1.56337295e+02, -1.33966792e+02, 3.51290037e+02,
176 |                       -1.74565790e+01],
177 |                      [-1.56892081e-02, 4.39054908e+04, 5.11862523e+04, 1.19121086e+02,
178 |                       -1.38904635e+02, 1.17458840e+02, -1.40130448e+02, 3.47164367e+02,
179 |                       -1.68323952e+01],
180 |                      [-1.58780872e-02, 4.34048292e+04, 5.02168674e+04, 1.17707465e+02,
181 |                       -1.36224549e+02, 1.16293641e+02, -1.37484136e+02, 3.47843176e+02,
182 |                       -1.67953707e+01],
183 |                      [-2.72605340e-04, 5.74754480e+04, 5.96003551e+04, 1.56265833e+02,
184 |                       -1.62105917e+02, 1.56587639e+02, -1.61667359e+02, 3.42553468e+02,
185 |                       -1.56752104e+01],
186 |                      [-3.92235639e-04, 5.74755015e+04, 5.93884888e+04, 1.56220429e+02,
187 |                       -1.61483242e+02, 1.56530411e+02, -1.61057580e+02, 3.42740639e+02,
188 |                       -1.57280775e+01]])
189 | 
190 | obs_std = np.array([[1e-12, 3.86420056e+03, 4.16700891e+03, 1.05387470e+01,
191 |                      1.09296704e+01, 4.65795588e+01, 4.12278816e+01, 1.01437007e+02,
192 |                      4.95697466e+00],
193 |                     [1.16677122e-03, 4.02882556e+03, 4.24164329e+03, 1.01733946e+01,
194 |                      1.09124226e+01, 4.58190749e+01, 4.01796093e+01, 1.01715036e+02,
195 |                      5.17413404e+00],
196 |                     [1.98886559e-03, 3.04426481e+03, 4.03934214e+03, 7.77323806e+00,
197 |                      1.05744803e+01, 3.43026169e+01, 4.16269469e+01, 1.00521725e+02,
198 |                      5.02423347e+00],
199 |                     [2.31861878e-03, 3.22645256e+03, 4.06658900e+03, 7.98839539e+00,
200 |                      1.04757837e+01, 3.39905023e+01, 4.08615239e+01, 1.00718850e+02,
201 |                      5.00494945e+00],
202 |                     [2.81557239e-03, 4.38046968e+03, 4.75347347e+03, 1.08754296e+01,
203 |                      1.22423939e+01, 4.59981011e+01, 4.79306463e+01, 9.91901033e+01,
204 |                      4.74497259e+00],
205 |                     [2.78095118e-03, 4.38070528e+03, 4.75270109e+03, 1.08728819e+01,
206 |                      1.22383163e+01, 4.59788637e+01, 4.77614296e+01, 9.92454851e+01,
207 |                      4.75863648e+00]])
208 | 
--------------------------------------------------------------------------------
/docs/microgrid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EnergyQuantResearch/PGSIM/e5ce48bc2730678eb4b119fb10f5e1d24e6b69d8/docs/microgrid.png
--------------------------------------------------------------------------------
/envs/Grid_envs.py:
--------------------------------------------------------------------------------
  1 | import sys
  2 | from abc import ABC
  3 | 
  4 | sys.path.append("../")
  5 | from scipy.integrate import odeint
  6 | 
  7 | DER_num = 40
  8 | 
  9 | if DER_num == 4:
 10 |     from configs.parameters_der4 import *
 11 |     from envs.der4_RL_fn import der_fn
 12 | elif DER_num == 6:
 13 |     from configs.parameters_der6 import *
 14 |     from envs.der6_RL_fn import der_fn
 15 | elif DER_num == 19:
 16 |     from configs.parameters_der19 import *
 17 |     from envs.der19_RL_fn import der_fn
 18 | elif DER_num == 20:
 19 |     from configs.parameters_der20 import *
 20 |     from envs.der20_RL_fn import der_fn
 21 | elif DER_num == 21:
 22 |     from configs.parameters_der21 import *
 23 |     from envs.der21_RL_fn import der_fn
 24 | elif DER_num == 40:
 25 |     from configs.parameters_der40 import *
 26 |     from envs.der40_RL_fn import der_fn
 27 | else:
 28 |     pass
 29 | 
 30 | 
 31 | class GridEnv(ABC):
 32 |     def __init__(self, config, discrete=True, rendering=False, episode_length=20, random_seed=0, train_mode=True):
 33 |         # Setup gym environment
 34 |         self.discrete = discrete
 35 |         self.rendering = rendering
 36 |         self.T = episode_length
 37 |         self.seed = random_seed
 38 |         self.agent = config.get('agent')
 39 |         self.dt = config.getfloat('sampling_time')
 40 |         self.coop_gamma = config.getfloat('coop_gamma')
 41 |         self.train_mode = train_mode
 42 |         test_seeds = config.get('test_seeds').split(',')
 43 |         test_seeds = [int(s) for s in test_seeds]
 44 |         self.init_test_seeds(test_seeds)
 45 |         self.min_action = 490
 46 |         self.max_action = 540
 47 |         self.states = []
 48 |         self.n_a = 10
 49 |         self.n_s = 9
 50 |         self.obs_mean = obs_mean
 51 |         self.obs_std = obs_std
 52 |         self.DER_num, self.lines_num, self.loads_num = DER_num, lines_num, loads_num
 53 |         self.n_agent = self.DER_num
 54 |         self.der_fn = der_fn
 55 |         self._init_space()
 56 |         self.step_list = []
 57 |         self.action_smoothing = 0
 58 |         try:
 59 |             self.reset()
 60 |         except Exception as e:
 61 |             self.close()
 62 |             raise e
 63 | 
 64 |     def reset(self, test_ind=0):
 65 |         # for initial random disturbance
 66 |         if self.train_mode:
 67 |             seed = self.seed
 68 |         else:
 69 |             seed = self.test_seeds[test_ind]
 70 |         np.random.seed(seed)
 71 |         # random seed for step function
 72 |         self.seed += 1
 73 |         self.t = 1
 74 |         self.pf_fail = False
 75 |         # initialize fingerprint
 76 |         self.update_fingerprint(self._init_policy())
 77 |         self.index = 0
 78 |         self.vbuses = []
 79 |         self.time = []
 80 |         self.step_list.append(1)  # used to store steps when all the voltages are in normal range
 81 |         self.action_list = [[] for x in range(self.DER_num)]
 82 |         self.x0 = x0
 83 |         # 0.4s for stabling the primary control
 84 |         t = np.linspace(0, 0.4, 11)
 85 |         # random disturbance
 86 |         self.disturbance_R = np.random.rand(self.loads_num) * 0.4 + 0.8
 87 |         self.disturbance_L = np.random.rand(self.loads_num) * 0.4 + 0.8
 88 | 
 89 |         self.x0 = odeint(self.der_fn, self.x0, t, args=(self.disturbance_R, self.disturbance_L), atol=1e-10,
 90 |                          rtol=1e-11, mxstep=5000)
 91 |         for j in range(self.DER_num):
 92 |             # voltage of buses
 93 |             self.vbuses.append(((self.x0[:, self.DER_num * 6 + self.lines_num * 2 + self.loads_num * 2 + j + 1] - nq[j] * self.x0[:,
 94 |                                                                                                                     5 * j + 3]) / Vnom).tolist())
 95 |         self.time.extend(t)
 96 |         self.x0 = (self.x0[-1]).tolist()
 97 |         return self._get_state()
 98 | 
 99 |     def step(self, action, mode='train'):
100 |         if self.discrete:
101 |             # discrete control
102 |             action = (self.max_action - self.min_action) / self.n_a * action + self.min_action
103 |         else:
104 |             # continuous control
105 |             action = 0.5 * (action + 1) * (self.max_action - self.min_action) + self.min_action
106 |         action = np.clip(action, self.min_action, self.max_action).astype(np.float32)
107 | 
108 |         # used to record actions
109 |         for i in range(self.DER_num):
110 |             self.action_list[i].append(action[i])
111 | 
112 |         # action smoothing
113 |         if self.t == 1:
114 |             self.action_past = action
115 |         action = self.action_past * self.action_smoothing + action * (
116 |                 1.0 - self.action_smoothing)
117 | 
118 |         self.x0[(6 * self.DER_num + 2 * self.lines_num + 2 * self.loads_num + 1):(
119 |                     7 * self.DER_num + 2 * self.lines_num + 2 * self.loads_num + 1)] = action.tolist()
120 |         if mode == 'train':
121 |             # random disturbance
122 |             self.disturbance_R = (np.random.rand(self.loads_num) * 0.1 - 0.05) + self.disturbance_R
123 |             self.disturbance_L = (np.random.rand(self.loads_num) * 0.1 - 0.05) + self.disturbance_L
124 |         else:
125 |             self.disturbance_R = 0 + self.disturbance_R
126 |             self.disturbance_L = 0 + self.disturbance_L
127 | 
128 |         try:
129 |             self.x0 = odeint(self.der_fn, self.x0,
130 |                              np.array([(self.t - 1) * self.dt, self.t * self.dt]),
131 |                              args=(self.disturbance_R, self.disturbance_L),
132 |                              atol=1e-10, rtol=1e-11, mxstep=5000)
133 |         except:
134 |             self.pf_fail = True
135 |             print("no solution")
136 |         finally:
137 |             self.x0 = self.x0[-1]
138 |             obs = self._get_state()
139 |             self.states.append(obs)
140 |             reward = self.get_reward()
141 |             done = self._check_termination()
142 |             global_reward = np.sum(reward)
143 |             self.t += 1
144 |             # use original rewards in test
145 |             if not self.train_mode:
146 |                 return obs, reward, done, global_reward
147 |             if (self.agent == 'greedy') or (self.coop_gamma < 0):
148 |                 reward = global_reward
149 |             self.action_past = action
150 |             return obs, reward, done, global_reward
151 | 
152 |     def _check_termination(self, ):
153 |         # achieve the maximum time or no powerflow solution
154 |         if self.t >= self.T or self.pf_fail == True:
155 |             return True
156 |         return False
157 | 
158 |     def get_fingerprint(self):
159 |         return self.fingerprint
160 | 
161 |     def update_fingerprint(self, policy):
162 |         self.fingerprint = policy
163 | 
164 |     def _init_policy(self, ):
165 |         return [np.ones(self.n_a_ls[i]) / self.n_a_ls[i] for i in range(self.n_agent)]
166 | 
167 |     def get_der_state(self):
168 |         self.der_state = np.zeros(shape=(self.n_agent, self.n_s))
169 |         for i in range(self.n_agent):
170 |             self.der_state[i] = (np.array(
171 |                 [self.x0[1 + 5 * i], self.x0[2 + 5 * i], self.x0[3 + 5 * i], self.x0[4 + 5 * i],
172 |                  self.x0[5 + 5 * i], x0[7 * DER_num + 2 * lines_num + 2 * loads_num + 1 + 4 * i],
173 |                  x0[7 * DER_num + 2 * lines_num + 2 * loads_num + 2 + 4 * i],
174 |                  x0[7 * DER_num + 2 * lines_num + 2 * loads_num + 3 + 4 * i],
175 |                  x0[7 * DER_num + 2 * lines_num + 2 * loads_num + 4 + 4 * i]]) -
176 |                                  self.obs_mean[i]) / self.obs_std[i]
177 | 
178 |     def _get_state(self, ):
179 |         state = []
180 |         self.get_der_state()
181 |         if self.agent == 'greedy':
182 |             state = self.der_state
183 |         elif self.agent == 'centralized':
184 |             state = np.array(self.x0[1:(5 * self.DER_num + 2 * self.lines_num + 2 * self.loads_num + 1)])
185 |         else:
186 |             for n in range(self.n_agent):
187 |                 cur_state = self.der_state[n]
188 |                 n_n = np.sum(self.neighbor_mask[n])
189 |                 if self.agent.startswith('ia2c'):
190 |                     for j in range(int(n_n)):
191 |                         index = np.where(self.neighbor_mask[n] == 1)[0]
192 |                         cur_state = np.concatenate((cur_state, self.der_state[index[j]]))
193 |                 if self.agent == 'ia2c_fp':
194 |                     for j in range(int(n_n)):
195 |                         index = np.where(self.neighbor_mask[n] == 1)[0]
196 |                         cur_state = np.concatenate((cur_state, self.fingerprint[index[j]]))
197 |                 state.append(cur_state)
198 |         return state
199 | 
200 |     def get_neighbor_action(self, action):
201 |         naction = []
202 |         for i in range(self.n_agent):
203 |             naction.append(action[self.neighbor_mask[i] == 1])
204 |         return naction
205 | 
206 |     def get_reward(self, ):
207 |         reward = []
208 |         count = 0
209 |         x = np.array(self.x0)
210 |         for j in range(self.DER_num):
211 |             # voltage of buses
212 |             vi = (x[self.DER_num * 6 + self.lines_num * 2 + self.loads_num * 2 + j + 1] - nq[j] * x[5 * j + 3]) / Vnom
213 |             self.vbuses[j].append(vi)
214 |             if self.pf_fail:
215 |                 reward.append(-10)
216 |             elif 0.95 <= vi <= 1.05:
217 |                 count += 1
218 |                 reward.append(0.05 - np.abs(1 - vi))
219 |             elif vi <= 0.8 or vi >= 1.25:
220 |                 reward.append(-10)
221 |             else:
222 |                 reward.append(- np.abs(1 - vi))
223 |         if count == self.n_agent and self.index == 0:
224 |             self.step_list[-1] = self.t
225 |             self.index = 1
226 |         elif (count != self.n_agent) and (self.index == 0) and (self.t == self.T):
227 |             self.step_list[-1] = self.t
228 |         else:
229 |             pass
230 |         self.time.append(self.t * self.dt + 0.4)
231 |         return np.array(reward)
232 | 
233 |     def _init_space(self):
234 |         self.n_s_ls = []
235 |         # declare for IA2C
236 |         self.neighbor_mask = Physical_Graph
237 |         self.distance_mask = Distance_Mask
238 |         for i in range(self.n_agent):
239 |             # initialize state space
240 |             if not self.agent.startswith('ma2c'):
241 |                 n_n = np.sum(self.neighbor_mask[i])
242 |                 self.n_s_ls.append(self.n_s * (1 + n_n))
243 |             else:
244 |                 self.n_s_ls.append(self.n_s)  # DER state*5 + load_state*2 + Bus_state*2
245 |         self.n_a_ls = [self.n_a] * self.n_agent
246 | 
247 |     def init_test_seeds(self, test_seeds):
248 |         self.test_num = len(test_seeds)
249 |         self.test_seeds = test_seeds
250 | 
251 |     def output_data(self, output_path, test_ind):
252 |         np.save(output_path + 'voltage/voltage_' + '{}'.format(test_ind), self.vbuses)
253 |         np.save(output_path + 'voltage/times_' + '{}'.format(test_ind), self.time)
254 |         np.save(output_path + 'voltage/action_list_' + '{}'.format(test_ind), self.action_list)
255 | 
256 |     def terminate(self):
257 |         return
258 | 
259 |     def render(self, mode='human'):
260 |         pass
261 | 
--------------------------------------------------------------------------------
/envs/der20_RL_fn.py:
--------------------------------------------------------------------------------
  1 | """""*******************************************************
  2 |  * Copyright (C) 2020 {Dong Chen} <{chendon9@msu.edu}>
  3 |  * this function is used to interact with RL agent.
  4 | """
  5 | 
  6 | from configs.parameters_der20 import *
  7 | import numba as nb
  8 | 
  9 | 
 10 | @nb.jit
 11 | def der_fn(x, t, disturbance_R, disturbance_L):
 12 |     # ---------Transferring Inv. Output Currents to Global DQ-----------------
 13 |     ioD1 = math.cos(0) * x[4] - math.sin(0) * x[5]
 14 |     ioQ1 = math.sin(0) * x[4] + math.cos(0) * x[5]
 15 |     ioD2 = math.cos(x[6]) * x[9] - math.sin(x[6]) * x[10]
 16 |     ioQ2 = math.sin(x[6]) * x[9] + math.cos(x[6]) * x[10]
 17 |     ioD3 = math.cos(x[11]) * x[14] - math.sin(x[11]) * x[15]
 18 |     ioQ3 = math.sin(x[11]) * x[14] + math.cos(x[11]) * x[15]
 19 |     ioD4 = math.cos(x[16]) * x[19] - math.sin(x[16]) * x[20]
 20 |     ioQ4 = math.sin(x[16]) * x[19] + math.cos(x[16]) * x[20]
 21 |     ioD5 = math.cos(x[21]) * x[24] - math.sin(x[21]) * x[25]
 22 |     ioQ5 = math.sin(x[21]) * x[24] + math.cos(x[21]) * x[25]
 23 |     ioD6 = math.cos(x[26]) * x[29] - math.sin(x[26]) * x[30]
 24 |     ioQ6 = math.sin(x[26]) * x[29] + math.cos(x[26]) * x[30]
 25 |     ioD7 = math.cos(x[31]) * x[34] - math.sin(x[31]) * x[35]
 26 |     ioQ7 = math.sin(x[31]) * x[34] + math.cos(x[31]) * x[35]
 27 |     ioD8 = math.cos(x[36]) * x[39] - math.sin(x[36]) * x[40]
 28 |     ioQ8 = math.sin(x[36]) * x[39] + math.cos(x[36]) * x[40]
 29 |     ioD9 = math.cos(x[41]) * x[44] - math.sin(x[41]) * x[45]
 30 |     ioQ9 = math.sin(x[41]) * x[44] + math.cos(x[41]) * x[45]
 31 |     ioD10 = math.cos(x[46]) * x[49] - math.sin(x[46]) * x[50]
 32 |     ioQ10 = math.sin(x[46]) * x[49] + math.cos(x[46]) * x[50]
 33 |     ioD11 = math.cos(x[51]) * x[54] - math.sin(x[51]) * x[55]
 34 |     ioQ11 = math.sin(x[51]) * x[54] + math.cos(x[51]) * x[55]
 35 |     ioD12 = math.cos(x[56]) * x[59] - math.sin(x[56]) * x[60]
 36 |     ioQ12 = math.sin(x[56]) * x[59] + math.cos(x[56]) * x[60]
 37 |     ioD13 = math.cos(x[61]) * x[64] - math.sin(x[61]) * x[65]
 38 |     ioQ13 = math.sin(x[61]) * x[64] + math.cos(x[61]) * x[65]
 39 |     ioD14 = math.cos(x[66]) * x[69] - math.sin(x[66]) * x[70]
 40 |     ioQ14 = math.sin(x[66]) * x[69] + math.cos(x[66]) * x[70]
 41 |     ioD15 = math.cos(x[71]) * x[74] - math.sin(x[71]) * x[75]
 42 |     ioQ15 = math.sin(x[71]) * x[74] + math.cos(x[71]) * x[75]
 43 |     ioD16 = math.cos(x[76]) * x[79] - math.sin(x[76]) * x[80]
 44 |     ioQ16 = math.sin(x[76]) * x[79] + math.cos(x[76]) * x[80]
 45 |     ioD17 = math.cos(x[81]) * x[84] - math.sin(x[81]) * x[85]
 46 |     ioQ17 = math.sin(x[81]) * x[84] + math.cos(x[81]) * x[85]
 47 |     ioD18 = math.cos(x[86]) * x[89] - math.sin(x[86]) * x[90]
 48 |     ioQ18 = math.sin(x[86]) * x[89] + math.cos(x[86]) * x[90]
 49 |     ioD19 = math.cos(x[91]) * x[94] - math.sin(x[91]) * x[95]
 50 |     ioQ19 = math.sin(x[91]) * x[94] + math.cos(x[91]) * x[95]
 51 |     ioD20 = math.cos(x[96]) * x[99] - math.sin(x[96]) * x[100]
 52 |     ioQ20 = math.sin(x[96]) * x[99] + math.cos(x[96]) * x[100]
 53 | 
 54 |     # ---------Defining Bus Voltages-----------------
 55 |     vbD1 = rN * (ioD1 - x[141] - x[101])
 56 |     vbQ1 = rN * (ioQ1 - x[142] - x[102])
 57 |     vbD2 = rN * (ioD2 + x[105] - x[107])
 58 |     vbQ2 = rN * (ioQ2 + x[106] - x[108])
 59 |     vbD3 = rN * (ioD3 + x[107] - x[109] - x[145])
 60 |     vbQ3 = rN * (ioQ3 + x[108] - x[110] - x[146])
 61 |     vbD4 = rN * (ioD4 + x[113] - x[115])
 62 |     vbQ4 = rN * (ioQ4 + x[114] - x[116])
 63 |     vbD5 = rN * (ioD5 + x[115] - x[117] - x[147])
 64 |     vbQ5 = rN * (ioQ5 + x[116] - x[118] - x[148])
 65 |     vbD6 = rN * (ioD6 + x[101] - x[103])
 66 |     vbQ6 = rN * (ioQ6 + x[102] - x[104])
 67 |     vbD7 = rN * (ioD7 + x[103] - x[105] - x[143])
 68 |     vbQ7 = rN * (ioQ7 + x[104] - x[106] - x[144])
 69 |     vbD8 = rN * (ioD8 + x[109] - x[111] - x[119])
 70 |     vbQ8 = rN * (ioQ8 + x[110] - x[112] - x[120])
 71 |     vbD9 = rN * (ioD9 + x[111] - x[113] - x[147])
 72 |     vbQ9 = rN * (ioQ9 + x[112] - x[114] - x[148])
 73 |     vbD10 = rN * (ioD10 + x[117] - x[119])
 74 |     vbQ10 = rN * (ioQ10 + x[118] - x[120])
 75 |     vbD11 = rN * (ioD11 + x[119] - x[123] - x[151])
 76 |     vbQ11 = rN * (ioQ11 + x[120] - x[124] - x[152])
 77 |     vbD12 = rN * (ioD12 + x[127] - x[129])
 78 |     vbQ12 = rN * (ioQ12 + x[128] - x[130])
 79 |     vbD13 = rN * (ioD13 + x[121] + x[129] - x[131] - x[155])
 80 |     vbQ13 = rN * (ioQ13 + x[122] + x[130] - x[132] - x[156])
 81 |     vbD14 = rN * (ioD14 + x[135] - x[137])
 82 |     vbQ14 = rN * (ioQ14 + x[136] - x[138])
 83 |     vbD15 = rN * (ioD15 + x[137] - x[139] - x[159])
 84 |     vbQ15 = rN * (ioQ15 + x[138] - x[140] - x[160])
 85 |     vbD16 = rN * (ioD16 + x[123] - x[125])
 86 |     vbQ16 = rN * (ioQ16 + x[124] - x[126])
 87 |     vbD17 = rN * (ioD17 + x[125] - x[127] - x[153])
 88 |     vbQ17 = rN * (ioQ17 + x[126] - x[128] - x[154])
 89 |     vbD18 = rN * (ioD18 + x[131] - x[133])
 90 |     vbQ18 = rN * (ioQ18 + x[132] - x[134])
 91 |     vbD19 = rN * (ioD19 + x[133] - x[135] - x[157])
 92 |     vbQ19 = rN * (ioQ19 + x[134] - x[136] - x[158])
 93 |     vbD20 = rN * (ioD20 + x[139])
 94 |     vbQ20 = rN * (ioQ20 + x[140])
 95 | 
 96 |     # ---------Transferring Bus Voltages to Inv. dq-----------------
 97 |     vbd1 = math.cos(0) * vbD1 + math.sin(0) * vbQ1
 98 |     vbq1 = -math.sin(0) * vbD1 + math.cos(0) * vbQ1
 99 |     vbd2 = math.cos(x[6]) * vbD2 + math.sin(x[6]) * vbQ2
100 |     vbq2 = -math.sin(x[6]) * vbD2 + math.cos(x[6]) * vbQ2
101 |     vbd3 = math.cos(x[11]) * vbD3 + math.sin(x[11]) * vbQ3
102 |     vbq3 = -math.sin(x[11]) * vbD3 + math.cos(x[11]) * vbQ3
103 |     vbd4 = math.cos(x[16]) * vbD4 + math.sin(x[16]) * vbQ4
104 |     vbq4 = -math.sin(x[16]) * vbD4 + math.cos(x[16]) * vbQ4
105 |     vbd5 = math.cos(x[21]) * vbD5 + math.sin(x[21]) * vbQ5
106 |     vbq5 = -math.sin(x[21]) * vbD5 + math.cos(x[21]) * vbQ5
107 |     vbd6 = math.cos(x[26]) * vbD6 + math.sin(x[26]) * vbQ6
108 |     vbq6 = -math.sin(x[26]) * vbD6 + math.cos(x[26]) * vbQ6
109 |     vbd7 = math.cos(x[31]) * vbD7 + math.sin(x[31]) * vbQ7
110 |     vbq7 = -math.sin(x[31]) * vbD7 + math.cos(x[31]) * vbQ7
111 |     vbd8 = math.cos(x[36]) * vbD8 + math.sin(x[36]) * vbQ8
112 |     vbq8 = -math.sin(x[36]) * vbD8 + math.cos(x[36]) * vbQ8
113 |     vbd9 = math.cos(x[41]) * vbD9 + math.sin(x[41]) * vbQ9
114 |     vbq9 = -math.sin(x[41]) * vbD9 + math.cos(x[41]) * vbQ9
115 |     vbd10 = math.cos(x[46]) * vbD10 + math.sin(x[46]) * vbQ10
116 |     vbq10 = -math.sin(x[46]) * vbD10 + math.cos(x[46]) * vbQ10
117 |     vbd11 = math.cos(x[51]) * vbD11 + math.sin(x[51]) * vbQ11
118 |     vbq11 = -math.sin(x[51]) * vbD11 + math.cos(x[51]) * vbQ11
119 |     vbd12 = math.cos(x[56]) * vbD12 + math.sin(x[56]) * vbQ12
120 |     vbq12 = -math.sin(x[56]) * vbD12 + math.cos(x[56]) * vbQ12
121 |     vbd13 = math.cos(x[61]) * vbD13 + math.sin(x[61]) * vbQ13
122 |     vbq13 = -math.sin(x[61]) * vbD13 + math.cos(x[61]) * vbQ13
123 |     vbd14 = math.cos(x[66]) * vbD14 + math.sin(x[66]) * vbQ14
124 |     vbq14 = -math.sin(x[66]) * vbD14 + math.cos(x[66]) * vbQ14
125 |     vbd15 = math.cos(x[71]) * vbD15 + math.sin(x[71]) * vbQ15
126 |     vbq15 = -math.sin(x[71]) * vbD15 + math.cos(x[71]) * vbQ15
127 |     vbd16 = math.cos(x[76]) * vbD16 + math.sin(x[76]) * vbQ16
128 |     vbq16 = -math.sin(x[76]) * vbD16 + math.cos(x[76]) * vbQ16
129 |     vbd17 = math.cos(x[81]) * vbD17 + math.sin(x[81]) * vbQ17
130 |     vbq17 = -math.sin(x[81]) * vbD17 + math.cos(x[81]) * vbQ17
131 |     vbd18 = math.cos(x[86]) * vbD18 + math.sin(x[86]) * vbQ18
132 |     vbq18 = -math.sin(x[86]) * vbD18 + math.cos(x[86]) * vbQ18
133 |     vbd19 = math.cos(x[91]) * vbD19 + math.sin(x[91]) * vbQ19
134 |     vbq19 = -math.sin(x[91]) * vbD19 + math.cos(x[91]) * vbQ19
135 |     vbd20 = math.cos(x[96]) * vbD20 + math.sin(x[96]) * vbQ20
136 |     vbq20 = -math.sin(x[96]) * vbD20 + math.cos(x[96]) * vbQ20
137 | 
138 |     wcom = x[161] - mp1 * x[2]
139 |     # ---------------DG1--------------------
140 |     vod1_star = x[181] - nq1 * x[3]
141 |     voq1_star = 0
142 |     xdot1 = x[161] - mp1 * x[2] - wcom
143 |     xdot2 = wc * (vod1_star * x[4] + voq1_star * x[5] - x[2])
144 |     xdot3 = wc * (-vod1_star * x[5] + voq1_star * x[4] - x[3])
145 |     xdot4 = (-rLc / Lc) * x[4] + wcom * x[5] + (1 / Lc) * (vod1_star - vbd1)
146 |     xdot5 = (-rLc / Lc) * x[5] - wcom * x[4] + (1 / Lc) * (voq1_star - vbq1)
147 | 
148 |     # ----------------DG2-------------------
149 |     vod2_star = x[182] - nq2 * x[8]
150 |     voq2_star = 0
151 |     xdot6 = x[162] - mp2 * x[7] - wcom
152 |     xdot7 = wc * (vod2_star * x[9] + voq2_star * x[10] - x[7])
153 |     xdot8 = wc * (-vod2_star * x[10] + voq2_star * x[9] - x[8])
154 |     xdot9 = (-rLc / Lc) * x[9] + wcom * x[10] + (1 / Lc) * (vod2_star - vbd2)
155 |     xdot10 = (-rLc / Lc) * x[10] - wcom * x[9] + (1 / Lc) * (voq2_star - vbq2)
156 | 
157 |     # ----------------DG3-------------------
158 |     vod3_star = x[183] - nq3 * x[13]
159 |     voq3_star = 0
160 |     xdot11 = x[163] - mp3 * x[12] - wcom
161 |     xdot12 = wc * (vod3_star * x[14] + voq3_star * x[15] - x[12])
162 |     xdot13 = wc * (-vod3_star * x[15] + voq3_star * x[14] - x[13])
163 |     xdot14 = (-rLc / Lc) * x[14] + wcom * x[15] + (1 / Lc) * (vod3_star - vbd3)
164 |     xdot15 = (-rLc / Lc) * x[15] - wcom * x[14] + (1 / Lc) * (voq3_star - vbq3)
165 | 
166 |     # ----------------DG4-------------------
167 |     vod4_star = x[184] - nq4 * x[18]
168 |     voq4_star = 0
169 |     xdot16 = x[164] - mp4 * x[17] - wcom
170 |     xdot17 = wc * (vod4_star * x[19] + voq4_star * x[20] - x[17])
171 |     xdot18 = wc * (-vod4_star * x[20] + voq4_star * x[19] - x[18])
172 |     xdot19 = (-rLc / Lc) * x[19] + wcom * x[20] + (1 / Lc) * (vod4_star - vbd4)
173 |     xdot20 = (-rLc / Lc) * x[20] - wcom * x[19] + (1 / Lc) * (voq4_star - vbq4)
174 | 
175 |     # ----------------DG5-------------------
176 |     vod5_star = x[185] - nq5 * x[23]
177 |     voq5_star = 0
178 |     xdot21 = x[165] - mp5 * x[22] - wcom
179 |     xdot22 = wc * (vod5_star * x[24] + voq5_star * x[25] - x[22])
180 |     xdot23 = wc * (-vod5_star * x[25] + voq5_star * x[24] - x[23])
181 |     xdot24 = (-rLc / Lc) * x[24] + wcom * x[25] + (1 / Lc) * (vod5_star - vbd5)
182 |     xdot25 = (-rLc / Lc) * x[25] - wcom * x[24] + (1 / Lc) * (voq5_star - vbq5)
183 | 
184 |     # ----------------DG6-------------------
185 |     vod6_star = x[186] - nq6 * x[28]
186 |     voq6_star = 0
187 |     xdot26 = x[166] - mp6 * x[27] - wcom
188 |     xdot27 = wc * (vod6_star * x[29] + voq6_star * x[30] - x[27])
189 |     xdot28 = wc * (-vod6_star * x[30] + voq6_star * x[29] - x[28])
190 |     xdot29 = (-rLc / Lc) * x[29] + wcom * x[30] + (1 / Lc) * (vod6_star - vbd6)
191 |     xdot30 = (-rLc / Lc) * x[30] - wcom * x[29] + (1 / Lc) * (voq6_star - vbq6)
192 | 
193 |     # ----------------DG7-------------------
194 |     vod7_star = x[187] - nq7 * x[33]
195 |     voq7_star = 0
196 |     xdot31 = x[167] - mp7 * x[32] - wcom
197 |     xdot32 = wc * (vod7_star * x[34] + voq7_star * x[35] - x[32])
198 |     xdot33 = wc * (-vod7_star * x[35] + voq7_star * x[34] - x[33])
199 |     xdot34 = (-rLc / Lc) * x[34] + wcom * x[35] + (1 / Lc) * (vod7_star - vbd7)
200 |     xdot35 = (-rLc / Lc) * x[35] - wcom * x[34] + (1 / Lc) * (voq7_star - vbq7)
201 | 
202 |     # ----------------DG8-------------------
203 |     vod8_star = x[188] - nq8 * x[38]
204 |     voq8_star = 0
205 |     xdot36 = x[168] - mp8 * x[37] - wcom
206 |     xdot37 = wc * (vod8_star * x[39] + voq8_star * x[40] - x[37])
207 |     xdot38 = wc * (-vod8_star * x[40] + voq8_star * x[39] - x[38])
208 |     xdot39 = (-rLc / Lc) * x[39] + wcom * x[40] + (1 / Lc) * (vod8_star - vbd8)
209 |     xdot40 = (-rLc / Lc) * x[40] - wcom * x[39] + (1 / Lc) * (voq8_star - vbq8)
210 | 
211 |     # ----------------DG9-------------------
212 |     vod9_star = x[189] - nq9 * x[43]
213 |     voq9_star = 0
214 |     xdot41 = x[169] - mp9 * x[42] - wcom
215 |     xdot42 = wc * (vod9_star * x[44] + voq9_star * x[45] - x[42])
216 |     xdot43 = wc * (-vod9_star * x[45] + voq9_star * x[44] - x[43])
217 |     xdot44 = (-rLc / Lc) * x[44] + wcom * x[45] + (1 / Lc) * (vod9_star - vbd9)
218 |     xdot45 = (-rLc / Lc) * x[45] - wcom * x[44] + (1 / Lc) * (voq9_star - vbq9)
219 | 
220 |     # ----------------DG10-------------------
221 |     vod10_star = x[190] - nq10 * x[48]
222 |     voq10_star = 0
223 |     xdot46 = x[170] - mp10 * x[47] - wcom
224 |     xdot47 = wc * (vod10_star * x[49] + voq10_star * x[50] - x[47])
225 |     xdot48 = wc * (-vod10_star * x[50] + voq10_star * x[49] - x[48])
226 |     xdot49 = (-rLc / Lc) * x[49] + wcom * x[50] + (1 / Lc) * (vod10_star - vbd10)
227 |     xdot50 = (-rLc / Lc) * x[50] - wcom * x[49] + (1 / Lc) * (voq10_star - vbq10)
228 | 
229 |     # ----------------DG11-------------------
230 |     vod11_star = x[191] - nq11 * x[53]
231 |     voq11_star = 0
232 |     xdot51 = x[171] - mp11 * x[52] - wcom
233 |     xdot52 = wc * (vod11_star * x[54] + voq11_star * x[55] - x[52])
234 |     xdot53 = wc * (-vod11_star * x[55] + voq11_star * x[54] - x[53])
235 |     xdot54 = (-rLc / Lc) * x[54] + wcom * x[55] + (1 / Lc) * (vod11_star - vbd11)
236 |     xdot55 = (-rLc / Lc) * x[55] - wcom * x[54] + (1 / Lc) * (voq11_star - vbq11)
237 | 
238 |     # ----------------DG12-------------------
239 |     vod12_star = x[192] - nq12 * x[58]
240 |     voq12_star = 0
241 |     xdot56 = x[172] - mp12 * x[57] - wcom
242 |     xdot57 = wc * (vod12_star * x[59] + voq12_star * x[60] - x[57])
243 |     xdot58 = wc * (-vod12_star * x[60] + voq12_star * x[59] - x[58])
244 |     xdot59 = (-rLc / Lc) * x[59] + wcom * x[60] + (1 / Lc) * (vod12_star - vbd12)
245 |     xdot60 = (-rLc / Lc) * x[60] - wcom * x[59] + (1 / Lc) * (voq12_star - vbq12)
246 | 
247 |     # ----------------DG13-------------------
248 |     vod13_star = x[193] - nq13 * x[63]
249 |     voq13_star = 0
250 |     xdot61 = x[173] - mp13 * x[62] - wcom
251 |     xdot62 = wc * (vod13_star * x[64] + voq13_star * x[65] - x[62])
252 |     xdot63 = wc * (-vod13_star * x[65] + voq13_star * x[54] - x[63])
253 |     xdot64 = (-rLc / Lc) * x[64] + wcom * x[65] + (1 / Lc) * (vod13_star - vbd13)
254 |     xdot65 = (-rLc / Lc) * x[65] - wcom * x[64] + (1 / Lc) * (voq13_star - vbq13)
255 | 
256 |     # ----------------DG14-------------------
257 |     vod14_star = x[194] - nq14 * x[68]
258 |     voq14_star = 0
259 |     xdot66 = x[174] - mp14 * x[67] - wcom
260 |     xdot67 = wc * (vod14_star * x[69] + voq14_star * x[70] - x[67])
261 |     xdot68 = wc * (-vod14_star * x[70] + voq14_star * x[69] - x[68])
262 |     xdot69 = (-rLc / Lc) * x[69] + wcom * x[70] + (1 / Lc) * (vod14_star - vbd14)
263 |     xdot70 = (-rLc / Lc) * x[70] - wcom * x[69] + (1 / Lc) * (voq14_star - vbq14)
264 | 
265 |     # ----------------DG15-------------------
266 |     vod15_star = x[195] - nq15 * x[73]
267 |     voq15_star = 0
268 |     xdot71 = x[175] - mp15 * x[72] - wcom
269 |     xdot72 = wc * (vod15_star * x[74] + voq15_star * x[75] - x[72])
270 |     xdot73 = wc * (-vod15_star * x[75] + voq15_star * x[74] - x[73])
271 |     xdot74 = (-rLc / Lc) * x[74] + wcom * x[75] + (1 / Lc) * (vod15_star - vbd15)
272 |     xdot75 = (-rLc / Lc) * x[75] - wcom * x[74] + (1 / Lc) * (voq15_star - vbq15)
273 | 
274 |     # ----------------DG16-------------------
275 |     vod16_star = x[196] - nq16 * x[78]
276 |     voq16_star = 0
277 |     xdot76 = x[176] - mp16 * x[77] - wcom
278 |     xdot77 = wc * (vod16_star * x[79] + voq16_star * x[80] - x[77])
279 |     xdot78 = wc * (-vod16_star * x[80] + voq16_star * x[79] - x[78])
280 |     xdot79 = (-rLc / Lc) * x[79] + wcom * x[80] + (1 / Lc) * (vod16_star - vbd16)
281 |     xdot80 = (-rLc / Lc) * x[80] - wcom * x[79] + (1 / Lc) * (voq16_star - vbq16)
282 | 
283 |     # ----------------DG17-------------------
284 |     vod17_star = x[197] - nq17 * x[83]
285 |     voq17_star = 0
286 |     xdot81 = x[177] - mp17 * x[82] - wcom
287 |     xdot82 = wc * (vod17_star * x[84] + voq17_star * x[85] - x[82])
288 |     xdot83 = wc * (-vod17_star * x[85] + voq17_star * x[84] - x[83])
289 |     xdot84 = (-rLc / Lc) * x[84] + wcom * x[85] + (1 / Lc) * (vod17_star - vbd17)
290 |     xdot85 = (-rLc / Lc) * x[85] - wcom * x[84] + (1 / Lc) * (voq17_star - vbq17)
291 | 
292 |     # ----------------DG18-------------------
293 |     vod18_star = x[198] - nq18 * x[88]
294 |     voq18_star = 0
295 |     xdot86 = x[178] - mp18 * x[87] - wcom
296 |     xdot87 = wc * (vod18_star * x[89] + voq18_star * x[90] - x[87])
297 |     xdot88 = wc * (-vod18_star * x[90] + voq18_star * x[89] - x[88])
298 |     xdot89 = (-rLc / Lc) * x[89] + wcom * x[90] + (1 / Lc) * (vod18_star - vbd18)
299 |     xdot90 = (-rLc / Lc) * x[90] - wcom * x[89] + (1 / Lc) * (voq18_star - vbq18)
300 | 
301 |     # ----------------DG19-------------------
302 |     vod19_star = x[199] - nq19 * x[93]
303 |     voq19_star = 0
304 |     xdot91 = x[179] - mp19 * x[92] - wcom
305 |     xdot92 = wc * (vod19_star * x[94] + voq19_star * x[95] - x[92])
306 |     xdot93 = wc * (-vod19_star * x[95] + voq19_star * x[94] - x[93])
307 |     xdot94 = (-rLc / Lc) * x[94] + wcom * x[95] + (1 / Lc) * (vod19_star - vbd19)
308 |     xdot95 = (-rLc / Lc) * x[95] - wcom * x[94] + (1 / Lc) * (voq19_star - vbq19)
309 | 
310 |     # ----------------DG20-------------------
311 |     vod20_star = x[200] - nq20 * x[98]
312 |     voq20_star = 0
313 |     xdot96 = x[180] - mp20 * x[97] - wcom
314 |     xdot97 = wc * (vod20_star * x[99] + voq20_star * x[100] - x[97])
315 |     xdot98 = wc * (-vod20_star * x[100] + voq20_star * x[99] - x[98])
316 |     xdot99 = (-rLc / Lc) * x[99] + wcom * x[100] + (1 / Lc) * (vod20_star - vbd20)
317 |     xdot100 = (-rLc / Lc) * x[100] - wcom * x[99] + (1 / Lc) * (voq20_star - vbq20)
318 | 
319 |     # -------------------------Lines------------------
320 |     # ----1 -> 6-----
321 |     xdot101 = (-rline1 / Lline1) * x[101] + wcom * x[102] + (1 / Lline1) * (vbD1 - vbD6)
322 |     xdot102 = (-rline1 / Lline1) * x[102] - wcom * x[101] + (1 / Lline1) * (vbQ1 - vbQ6)
323 |     # ----6 -> 7-----
324 |     xdot103 = (-rline2 / Lline2) * x[103] + wcom * x[104] + (1 / Lline2) * (vbD6 - vbD7)
325 |     xdot104 = (-rline2 / Lline2) * x[104] - wcom * x[103] + (1 / Lline2) * (vbQ6 - vbQ7)
326 |     # ----7 -> 2-----
327 |     xdot105 = (-rline3 / Lline3) * x[105] + wcom * x[106] + (1 / Lline3) * (vbD7 - vbD2)
328 |     xdot106 = (-rline3 / Lline3) * x[106] - wcom * x[105] + (1 / Lline3) * (vbQ7 - vbQ2)
329 |     # ----2 -> 3-----
330 |     xdot107 = (-rline4 / Lline4) * x[107] + wcom * x[108] + (1 / Lline4) * (vbD2 - vbD3)
331 |     xdot108 = (-rline4 / Lline4) * x[108] - wcom * x[107] + (1 / Lline4) * (vbQ2 - vbQ3)
332 |     # ----3 -> 8-----
333 |     xdot109 = (-rline5 / Lline5) * x[109] + wcom * x[110] + (1 / Lline5) * (vbD3 - vbD8)
334 |     xdot110 = (-rline5 / Lline5) * x[110] - wcom * x[109] + (1 / Lline5) * (vbQ3 - vbQ8)
335 |     # ----8 -> 9-----
336 |     xdot111 = (-rline6 / Lline6) * x[111] + wcom * x[112] + (1 / Lline6) * (vbD8 - vbD9)
337 |     xdot112 = (-rline6 / Lline6) * x[112] - wcom * x[111] + (1 / Lline6) * (vbQ8 - vbQ9)
338 |     # ----9 -> 4-----
339 |     xdot113 = (-rline7 / Lline7) * x[113] + wcom * x[114] + (1 / Lline7) * (vbD9 - vbD4)
340 |     xdot114 = (-rline7 / Lline7) * x[114] - wcom * x[113] + (1 / Lline7) * (vbQ9 - vbQ4)
341 |     # ----4 -> 5-----
342 |     xdot115 = (-rline8 / Lline8) * x[115] + wcom * x[116] + (1 / Lline8) * (vbD4 - vbD5)
343 |     xdot116 = (-rline8 / Lline8) * x[116] - wcom * x[115] + (1 / Lline8) * (vbQ4 - vbQ5)
344 |     # ----5 -> 10-----
345 |     xdot117 = (-rline9 / Lline9) * x[117] + wcom * x[118] + (1 / Lline9) * (vbD5 - vbD10)
346 |     xdot118 = (-rline9 / Lline9) * x[118] - wcom * x[117] + (1 / Lline9) * (vbQ5 - vbQ10)
347 |     # ----8 -> 11-----
348 |     xdot119 = (-rline10 / Lline10) * x[119] + wcom * x[120] + (1 / Lline10) * (vbD8 - vbD11)
349 |     xdot120 = (-rline10 / Lline10) * x[120] - wcom * x[119] + (1 / Lline10) * (vbQ8 - vbQ11)
350 |     # ----10 -> 13-----
351 |     xdot121 = (-rline11 / Lline11) * x[121] + wcom * x[122] + (1 / Lline11) * (vbD10 - vbD13)
352 |     xdot122 = (-rline11 / Lline11) * x[122] - wcom * x[121] + (1 / Lline11) * (vbQ10 - vbQ13)
353 |     # ----11 -> 16-----
354 |     xdot123 = (-rline12 / Lline12) * x[123] + wcom * x[124] + (1 / Lline12) * (vbD11 - vbD16)
355 |     xdot124 = (-rline12 / Lline12) * x[124] - wcom * x[123] + (1 / Lline12) * (vbQ11 - vbQ16)
356 |     # ----16 -> 17-----
357 |     xdot125 = (-rline13 / Lline13) * x[125] + wcom * x[126] + (1 / Lline13) * (vbD16 - vbD17)
358 |     xdot126 = (-rline13 / Lline13) * x[126] - wcom * x[125] + (1 / Lline13) * (vbQ16 - vbQ17)
359 |     # ----17 -> 12-----
360 |     xdot127 = (-rline14 / Lline14) * x[127] + wcom * x[128] + (1 / Lline14) * (vbD17 - vbD12)
361 |     xdot128 = (-rline14 / Lline14) * x[128] - wcom * x[127] + (1 / Lline14) * (vbQ17 - vbQ12)
362 |     # ----12 -> 13-----
363 |     xdot129 = (-rline15 / Lline15) * x[129] + wcom * x[130] + (1 / Lline15) * (vbD12 - vbD13)
364 |     xdot130 = (-rline15 / Lline15) * x[130] - wcom * x[129] + (1 / Lline15) * (vbQ12 - vbQ13)
365 |     # ----13 -> 18-----
366 |     xdot131 = (-rline16 / Lline16) * x[131] + wcom * x[132] + (1 / Lline16) * (vbD13 - vbD18)
367 |     xdot132 = (-rline16 / Lline16) * x[132] - wcom * x[131] + (1 / Lline16) * (vbQ13 - vbQ18)
368 |     # ----18 -> 19-----
369 |     xdot133 = (-rline17 / Lline17) * x[133] + wcom * x[134] + (1 / Lline17) * (vbD18 - vbD19)
370 |     xdot134 = (-rline17 / Lline17) * x[134] - wcom * x[133] + (1 / Lline17) * (vbQ18 - vbQ19)
371 |     # ----19 -> 14-----
372 |     xdot135 = (-rline18 / Lline18) * x[135] + wcom * x[136] + (1 / Lline18) * (vbD19 - vbD14)
373 |     xdot136 = (-rline18 / Lline18) * x[136] - wcom * x[135] + (1 / Lline18) * (vbQ19 - vbQ14)
374 |     # ----14 -> 15-----
375 |     xdot137 = (-rline19 / Lline19) * x[137] + wcom * x[138] + (1 / Lline19) * (vbD14 - vbD15)
376 |     xdot138 = (-rline19 / Lline19) * x[138] - wcom * x[137] + (1 / Lline19) * (vbQ14 - vbQ15)
377 |     # ----15 -> 20-----
378 |     xdot139 = (-rline20 / Lline20) * x[139] + wcom * x[140] + (1 / Lline20) * (vbD15 - vbD20)
379 |     xdot140 = (-rline20 / Lline20) * x[140] - wcom * x[139] + (1 / Lline20) * (vbQ15 - vbQ20)
380 | 
381 |     # -------------------------Loads------------------
382 |     # ------Load1--------
383 |     xdot141 = (-disturbance_R[0] * Rload1 / (disturbance_L[0] * Lload1)) * x[141] + wcom * x[142] + (
384 |                 1 / (disturbance_L[0] * Lload1)) * vbD1
385 |     xdot142 = (-disturbance_R[0] * Rload1 / (disturbance_L[0] * Lload1)) * x[142] - wcom * x[141] + (
386 |                 1 / (disturbance_L[0] * Lload1)) * vbQ1
387 |     # ------Load2--------
388 |     xdot143 = (-disturbance_R[1] * Rload2 / (disturbance_L[1] * Lload2)) * x[143] + wcom * x[144] + (
389 |                 1 / (disturbance_L[1] * Lload2)) * vbD7
390 |     xdot144 = (-disturbance_R[1] * Rload2 / (disturbance_L[1] * Lload2)) * x[144] - wcom * x[143] + (
391 |                 1 / (disturbance_L[1] * Lload2)) * vbQ7
392 |     # ------Load3--------
393 |     xdot145 = (-disturbance_R[2] * Rload3 / (disturbance_L[2] * Lload3)) * x[145] + wcom * x[146] + (
394 |                 1 / (disturbance_L[2] * Lload3)) * vbD3
395 |     xdot146 = (-disturbance_R[2] * Rload3 / (disturbance_L[2] * Lload3)) * x[146] - wcom * x[145] + (
396 |                 1 / (disturbance_L[2] * Lload3)) * vbQ3
397 |     # ------Load4--------
398 |     xdot147 = (-disturbance_R[3] * Rload4 / (disturbance_L[3] * Lload4)) * x[147] + wcom * x[148] + (
399 |                 1 / (disturbance_L[3] * Lload4)) * vbD9
400 |     xdot148 = (-disturbance_R[3] * Rload4 / (disturbance_L[3] * Lload4)) * x[148] - wcom * x[147] + (
401 |                 1 / (disturbance_L[3] * Lload4)) * vbQ9
402 |     # ------Load5--------
403 |     xdot149 = (-disturbance_R[4] * Rload5 / (disturbance_L[4] * Lload5)) * x[149] + wcom * x[150] + (
404 |                 1 / (disturbance_L[4] * Lload5)) * vbD5
405 |     xdot150 = (-disturbance_R[4] * Rload5 / (disturbance_L[4] * Lload5)) * x[150] - wcom * x[149] + (
406 |                 1 / (disturbance_L[4] * Lload5)) * vbQ5
407 |     # ------Load6--------
408 |     xdot151 = (-disturbance_R[5] * Rload6 / (disturbance_L[5] * Lload6)) * x[151] + wcom * x[152] + (
409 |                 1 / (disturbance_L[5] * Lload6)) * vbD11
410 |     xdot152 = (-disturbance_R[5] * Rload6 / (disturbance_L[5] * Lload6)) * x[152] - wcom * x[151] + (
411 |                 1 / (disturbance_L[5] * Lload6)) * vbQ11
412 |     # ------Load7--------
413 |     xdot153 = (-disturbance_R[6] * Rload7 / (disturbance_L[6] * Lload7)) * x[153] + wcom * x[154] + (
414 |                 1 / (disturbance_L[6] * Lload7)) * vbD17
415 |     xdot154 = (-disturbance_R[6] * Rload7 / (disturbance_L[6] * Lload7)) * x[154] - wcom * x[153] + (
416 |                 1 / (disturbance_L[6] * Lload7)) * vbQ17
417 |     # ------Load8--------
418 |     xdot155 = (-disturbance_R[7] * Rload8 / (disturbance_L[7] * Lload8)) * x[155] + wcom * x[156] + (
419 |                 1 / (disturbance_L[7] * Lload8)) * vbD13
420 |     xdot156 = (-disturbance_R[7] * Rload8 / (disturbance_L[7] * Lload8)) * x[156] - wcom * x[155] + (
421 |                 1 / (disturbance_L[7] * Lload8)) * vbQ13
422 |     # ------Load9--------
423 |     xdot157 = (-disturbance_R[8] * Rload9 / (disturbance_L[8] * Lload9)) * x[157] + wcom * x[158] + (
424 |                 1 / (disturbance_L[8] * Lload9)) * vbD19
425 |     xdot158 = (-disturbance_R[8] * Rload9 / (disturbance_L[8] * Lload9)) * x[158] - wcom * x[157] + (
426 |                 1 / (disturbance_L[8] * Lload9)) * vbQ19
427 |     # ------Load10--------
428 |     xdot159 = (-disturbance_R[9] * Rload10 / (disturbance_L[9] * Lload10)) * x[159] + wcom * x[160] + (
429 |                 1 / (disturbance_L[9] * Lload10)) * vbD15
430 |     xdot160 = (-disturbance_R[9] * Rload10 / (disturbance_L[9] * Lload10)) * x[160] - wcom * x[159] + (
431 |                 1 / (disturbance_L[9] * Lload10)) * vbQ15
432 | 
433 |     # ----------------------------------------------------
434 |     # Controller Parameters
435 |     if t <= 0.4:
436 |         # this time is for the primary control
437 |         xdot161 = 0
438 |         xdot162 = 0
439 |         xdot163 = 0
440 |         xdot164 = 0
441 |         xdot165 = 0
442 |         xdot166 = 0
443 |         xdot167 = 0
444 |         xdot168 = 0
445 |         xdot169 = 0
446 |         xdot170 = 0
447 |         xdot171 = 0
448 |         xdot172 = 0
449 |         xdot173 = 0
450 |         xdot174 = 0
451 |         xdot175 = 0
452 |         xdot176 = 0
453 |         xdot177 = 0
454 |         xdot178 = 0
455 |         xdot179 = 0
456 |         xdot180 = 0
457 |         xdot181 = 0
458 |         xdot182 = 0
459 |         xdot183 = 0
460 |         xdot184 = 0
461 |         xdot185 = 0
462 |         xdot186 = 0
463 |         xdot187 = 0
464 |         xdot188 = 0
465 |         xdot189 = 0
466 |         xdot190 = 0
467 |         xdot191 = 0
468 |         xdot192 = 0
469 |         xdot193 = 0
470 |         xdot194 = 0
471 |         xdot195 = 0
472 |         xdot196 = 0
473 |         xdot197 = 0
474 |         xdot198 = 0
475 |         xdot199 = 0
476 |         xdot200 = 0
477 |     else:
478 |         Pratio = np.array([[mp1 * x[2]], [mp2 * x[7]], [mp3 * x[12]], [mp4 * x[17]], [mp5 * x[22]],
479 |                            [mp6 * x[27]], [mp7 * x[37]], [mp8 * x[42]], [mp9 * x[47]], [mp10 * x[52]],
480 |                            [mp11 * x[57]], [mp12 * x[62]], [mp13 * x[67]], [mp14 * x[72]], [mp15 * x[77]],
481 |                            [mp16 * x[82]], [mp17 * x[87]], [mp18 * x[92]], [mp19 * x[97]], [mp20 * x[102]]])
482 | 
483 |         w_array = np.array(
484 |             [[x[161] - Pratio[0][0]], [x[162] - Pratio[1][0]], [x[163] - Pratio[2][0]], [x[164] - Pratio[3][0]],
485 |              [x[165] - Pratio[4][0]], [x[166] - Pratio[5][0]], [x[167] - Pratio[6][0]], [x[168] - Pratio[7][0]],
486 |              [x[169] - Pratio[8][0]], [x[170] - Pratio[9][0]], [x[171] - Pratio[10][0]], [x[172] - Pratio[11][0]],
487 |              [x[173] - Pratio[12][0]], [x[174] - Pratio[13][0]], [x[175] - Pratio[14][0]], [x[176] - Pratio[15][0]],
488 |              [x[177] - Pratio[16][0]], [x[178] - Pratio[17][0]], [x[179] - Pratio[18][0]], [x[180] - Pratio[19][0]]])
489 | 
490 |         # Conventional Freq Control
491 |         Synch_Mat = -1 * a_ctrl * (
492 |                 np.dot(L + G, w_array - np.array([[wref], [wref], [wref], [wref],
493 |                                                   [wref], [wref], [wref], [wref],
494 |                                                   [wref], [wref], [wref], [wref],
495 |                                                   [wref], [wref], [wref], [wref],
496 |                                                   [wref], [wref], [wref], [wref]])) + np.dot(L, Pratio))
497 | 
498 |         # ---frequency input---
499 |         xdot161 = Synch_Mat[0][0]
500 |         xdot162 = Synch_Mat[1][0]
501 |         xdot163 = Synch_Mat[2][0]
502 |         xdot164 = Synch_Mat[3][0]
503 |         xdot165 = Synch_Mat[4][0]
504 |         xdot166 = Synch_Mat[5][0]
505 |         xdot167 = Synch_Mat[6][0]
506 |         xdot168 = Synch_Mat[7][0]
507 |         xdot169 = Synch_Mat[8][0]
508 |         xdot170 = Synch_Mat[9][0]
509 |         xdot171 = Synch_Mat[10][0]
510 |         xdot172 = Synch_Mat[11][0]
511 |         xdot173 = Synch_Mat[12][0]
512 |         xdot174 = Synch_Mat[13][0]
513 |         xdot175 = Synch_Mat[14][0]
514 |         xdot176 = Synch_Mat[15][0]
515 |         xdot177 = Synch_Mat[16][0]
516 |         xdot178 = Synch_Mat[17][0]
517 |         xdot179 = Synch_Mat[18][0]
518 |         xdot180 = Synch_Mat[19][0]
519 |         # ---voltage input---
520 |         xdot181 = 0
521 |         xdot182 = 0
522 |         xdot183 = 0
523 |         xdot184 = 0
524 |         xdot185 = 0
525 |         xdot186 = 0
526 |         xdot187 = 0
527 |         xdot188 = 0
528 |         xdot189 = 0
529 |         xdot190 = 0
530 |         xdot191 = 0
531 |         xdot192 = 0
532 |         xdot193 = 0
533 |         xdot194 = 0
534 |         xdot195 = 0
535 |         xdot196 = 0
536 |         xdot197 = 0
537 |         xdot198 = 0
538 |         xdot199 = 0
539 |         xdot200 = 0
540 | 
541 |     return np.array(
542 |         [0, xdot1, xdot2, xdot3, xdot4, xdot5, xdot6, xdot7, xdot8, xdot9, xdot10, xdot11, xdot12, xdot13, xdot14,
543 |          xdot15, xdot16, xdot17, xdot18, xdot19, xdot20, xdot21, xdot22, xdot23, xdot24, xdot25, xdot26, xdot27,
544 |          xdot28, xdot29, xdot30, xdot31, xdot32, xdot33, xdot34, xdot35, xdot36, xdot37, xdot38, xdot39, xdot40,
545 |          xdot41, xdot42, xdot43, xdot44, xdot45, xdot46, xdot47, xdot48, xdot49, xdot50, xdot51, xdot52, xdot53,
546 |          xdot54, xdot55, xdot56, xdot57, xdot58, xdot59, xdot60, xdot61, xdot62, xdot63, xdot64, xdot65, xdot66,
547 |          xdot67, xdot68, xdot69, xdot70, xdot71, xdot72, xdot73, xdot74, xdot75, xdot76, xdot77, xdot78, xdot79,
548 |          xdot80, xdot81, xdot82, xdot83, xdot84, xdot85, xdot86, xdot87, xdot88, xdot89, xdot90, xdot91, xdot92,
549 |          xdot93, xdot94, xdot95, xdot96, xdot97, xdot98, xdot99, xdot100, xdot101, xdot102, xdot103, xdot104,
550 |          xdot105, xdot106, xdot107, xdot108, xdot109, xdot110, xdot111, xdot112, xdot113, xdot114, xdot115,
551 |          xdot116, xdot117, xdot118, xdot119, xdot120, xdot121, xdot122, xdot123, xdot124, xdot125, xdot126,
552 |          xdot127, xdot128, xdot129, xdot130, xdot131, xdot132, xdot133, xdot134, xdot135, xdot136, xdot137,
553 |          xdot138, xdot139, xdot140, xdot141, xdot142, xdot143, xdot144, xdot145, xdot146, xdot147, xdot148,
554 |          xdot149, xdot150, xdot151, xdot152, xdot153, xdot154, xdot155, xdot156, xdot157, xdot158, xdot159,
555 |          xdot160, xdot161, xdot162, xdot163, xdot164, xdot165, xdot166, xdot167, xdot168, xdot169, xdot170,
556 |          xdot171, xdot172, xdot173, xdot174, xdot175, xdot176, xdot177, xdot178, xdot179, xdot180, xdot181,
557 |          xdot182, xdot183, xdot184, xdot185, xdot186, xdot187, xdot188, xdot189, xdot190, xdot191, xdot192,
558 |          xdot193, xdot194, xdot195, xdot196, xdot197, xdot198, xdot199, xdot200, ioD1, ioQ1, vbD1, vbQ1,
559 |          ioD2, ioQ2, vbD2, vbQ2, ioD3, ioQ3, vbD3, vbQ3, ioD4, ioQ4, vbD4, vbQ4, ioD5, ioQ5, vbD5, vbQ5,
560 |          ioD6, ioQ6, vbD6, vbQ6, ioD7, ioQ7, vbD7, vbQ7, ioD8, ioQ8, vbD8, vbQ8, ioD9, ioQ9, vbD9, vbQ9,
561 |          ioD10, ioQ10, vbD10, vbQ10, ioD11, ioQ11, vbD11, vbQ11, ioD12, ioQ12, vbD12, vbQ13, ioD13, ioQ13, vbD13, vbQ13,
562 |          ioD14, ioQ14, vbD14, vbQ14, ioD15, ioQ15, vbD15, vbQ15, ioD16, ioQ16, vbD16, vbQ16, ioD17, ioQ17, vbD17, vbQ17,
563 |          ioD18, ioQ18, vbD18, vbQ18, ioD19, ioQ19, vbD19, vbQ19, ioD20, ioQ20, vbD20, vbQ20
564 |          ])
--------------------------------------------------------------------------------
/envs/der6_RL_fn.py:
--------------------------------------------------------------------------------
  1 | """""*******************************************************
  2 |  * Copyright (C) 2020 {Dong Chen} <{chendon9@msu.edu}>
  3 |  * this function is used to interact with RL agent.
  4 | """
  5 | 
  6 | from configs.parameters_der6 import *
  7 | import numba as nb
  8 | 
  9 | 
 10 | @nb.jit
 11 | def der_fn(x, t, disturbance_R, disturbance_L):
 12 |     # ----------Transferring Inv. Output Currents to Global DQ-----------------
 13 |     # dq --> \alpha \beta
 14 |     ioD1 = math.cos(0) * x[4] - math.sin(0) * x[5]
 15 |     ioQ1 = math.sin(0) * x[4] + math.cos(0) * x[5]
 16 |     ioD2 = math.cos(x[6]) * x[9] - math.sin(x[6]) * x[10]
 17 |     ioQ2 = math.sin(x[6]) * x[9] + math.cos(x[6]) * x[10]
 18 |     ioD3 = math.cos(x[11]) * x[14] - math.sin(x[11]) * x[15]
 19 |     ioQ3 = math.sin(x[11]) * x[14] + math.cos(x[11]) * x[15]
 20 |     ioD4 = math.cos(x[16]) * x[19] - math.sin(x[16]) * x[20]
 21 |     ioQ4 = math.sin(x[16]) * x[19] + math.cos(x[16]) * x[20]
 22 |     ioD5 = math.cos(x[21]) * x[24] - math.sin(x[21]) * x[25]
 23 |     ioQ5 = math.sin(x[21]) * x[24] + math.cos(x[21]) * x[25]
 24 |     ioD6 = math.cos(x[26]) * x[29] - math.sin(x[26]) * x[30]
 25 |     ioQ6 = math.sin(x[26]) * x[29] + math.cos(x[26]) * x[30]
 26 | 
 27 |     # ----------Defining Bus Voltages-----------------
 28 |     vbD1 = rN * (ioD1 - x[31])
 29 |     vbQ1 = rN * (ioQ1 - x[32])
 30 |     vbD2 = rN * (x[31] + x[49] - x[33])
 31 |     vbQ2 = rN * (x[32] + x[50] - x[34])
 32 |     vbD3 = rN * (ioD2 - x[49])
 33 |     vbQ3 = rN * (ioQ2 - x[50])
 34 |     vbD4 = rN * (x[33] + x[35] - x[37] - x[51])
 35 |     vbQ4 = rN * (x[34] + x[36] - x[38] - x[52])
 36 |     vbD5 = rN * (ioD4 - x[35])
 37 |     vbQ5 = rN * (ioQ4 - x[36])
 38 |     vbD6 = rN * (x[37] + x[39] - x[41])
 39 |     vbQ6 = rN * (x[38] + x[40] - x[42])
 40 |     vbD7 = rN * (ioD6 + x[41] - x[53] - x[43])
 41 |     vbQ7 = rN * (ioQ6 + x[42] - x[54] - x[44])
 42 |     vbD9 = rN * (x[43] + x[45] - x[55])
 43 |     vbQ9 = rN * (x[44] + x[46] - x[56])
 44 |     vbD11 = rN * (x[47] - x[45] - x[57])
 45 |     vbQ11 = rN * (x[48] - x[46] - x[58])
 46 |     vbD13 = rN * (ioD5 - x[47])
 47 |     vbQ13 = rN * (ioQ5 - x[48])
 48 |     vbD14 = rN * (ioD3 - x[39])
 49 |     vbQ14 = rN * (ioQ3 - x[40])
 50 | 
 51 |     # ----------Transferring Bus Voltages to Inv. dq-----------------
 52 |     # \alpha \beta --> dq
 53 |     vbd1 = math.cos(0) * vbD1 + math.sin(0) * vbQ1
 54 |     vbq1 = -math.sin(0) * vbD1 + math.cos(0) * vbQ1
 55 |     vbd3 = math.cos(x[6]) * vbD3 + math.sin(x[6]) * vbQ3
 56 |     vbq3 = -math.sin(x[6]) * vbD3 + math.cos(x[6]) * vbQ3
 57 |     vbd14 = math.cos(x[11]) * vbD14 + math.sin(x[11]) * vbQ14
 58 |     vbq14 = -math.sin(x[11]) * vbD14 + math.cos(x[11]) * vbQ14
 59 |     vbd5 = math.cos(x[16]) * vbD5 + math.sin(x[16]) * vbQ5
 60 |     vbq5 = -math.sin(x[16]) * vbD5 + math.cos(x[16]) * vbQ5
 61 |     vbd13 = math.cos(x[21]) * vbD13 + math.sin(x[21]) * vbQ13
 62 |     vbq13 = -math.sin(x[21]) * vbD13 + math.cos(x[21]) * vbQ13
 63 |     vbd7 = math.cos(x[26]) * vbD7 + math.sin(x[26]) * vbQ7
 64 |     vbq7 = -math.sin(x[26]) * vbD7 + math.cos(x[26]) * vbQ7
 65 | 
 66 |     wcom = x[59] - mp1 * x[2]
 67 |     ## ----------------DG1--------------------
 68 |     vod1_star = x[65] - nq1 * x[3]
 69 |     voq1_star = 0
 70 |     xdot1 = x[59] - mp1 * x[2] - wcom
 71 |     xdot2 = wc * (vod1_star * x[4] + voq1_star * x[5] - x[2])
 72 |     xdot3 = wc * (-vod1_star * x[5] + voq1_star * x[4] - x[3])
 73 |     xdot4 = (-rLc / Lc) * x[4] + wcom * x[5] + (1 / Lc) * (vod1_star - vbd1)
 74 |     xdot5 = (-rLc / Lc) * x[5] - wcom * x[4] + (1 / Lc) * (voq1_star - vbq1)
 75 | 
 76 |     ## ----------------DG2-------------------
 77 |     vod2_star = x[66] - nq2 * x[8]
 78 |     voq2_star = 0
 79 |     xdot6 = x[60] - mp2 * x[7] - wcom
 80 |     xdot7 = wc * (vod2_star * x[9] + voq2_star * x[10] - x[7])
 81 |     xdot8 = wc * (-vod2_star * x[10] + voq2_star * x[9] - x[8])
 82 |     xdot9 = (-rLc / Lc) * x[9] + wcom * x[10] + (1 / Lc) * (vod2_star - vbd3)
 83 |     xdot10 = (-rLc / Lc) * x[10] - wcom * x[9] + (1 / Lc) * (voq2_star - vbq3)
 84 | 
 85 |     ## ----------------DG3-------------------
 86 |     vod3_star = x[67] - nq3 * x[13]
 87 |     voq3_star = 0
 88 |     xdot11 = x[61] - mp3 * x[12] - wcom
 89 |     xdot12 = wc * (vod3_star * x[14] + voq3_star * x[15] - x[12])
 90 |     xdot13 = wc * (-vod3_star * x[15] + voq3_star * x[14] - x[13])
 91 |     xdot14 = (-rLc / Lc) * x[14] + wcom * x[15] + (1 / Lc) * (vod3_star - vbd14)
 92 |     xdot15 = (-rLc / Lc) * x[15] - wcom * x[14] + (1 / Lc) * (voq3_star - vbq14)
 93 | 
 94 |     ## ----------------DG4-------------------
 95 |     vod4_star = x[68] - nq4 * x[18]
 96 |     voq4_star = 0
 97 |     xdot16 = x[62] - mp4 * x[17] - wcom
 98 |     xdot17 = wc * (vod4_star * x[19] + voq4_star * x[20] - x[17])
 99 |     xdot18 = wc * (-vod4_star * x[20] + voq4_star * x[19] - x[18])
100 |     xdot19 = (-rLc / Lc) * x[19] + wcom * x[20] + (1 / Lc) * (vod4_star - vbd5)
101 |     xdot20 = (-rLc / Lc) * x[20] - wcom * x[19] + (1 / Lc) * (voq4_star - vbq5)
102 | 
103 |     # ----------------DG5-------------------
104 |     vod5_star = x[69] - nq5 * x[23]
105 |     voq5_star = 0
106 |     xdot21 = x[63] - mp5 * x[22] - wcom
107 |     xdot22 = wc * (vod5_star * x[24] + voq5_star * x[25] - x[22])
108 |     xdot23 = wc * (-vod5_star * x[25] + voq5_star * x[24] - x[23])
109 |     xdot24 = (-rLc / Lc) * x[24] + wcom * x[25] + (1 / Lc) * (vod5_star - vbd13)
110 |     xdot25 = (-rLc / Lc) * x[25] - wcom * x[24] + (1 / Lc) * (voq5_star - vbq13)
111 | 
112 |     # ----------------DG6-------------------
113 |     vod6_star = x[70] - nq6 * x[28]
114 |     voq6_star = 0
115 |     xdot26 = x[64] - mp6 * x[27] - wcom
116 |     xdot27 = wc * (vod6_star * x[29] + voq6_star * x[30] - x[27])
117 |     xdot28 = wc * (-vod6_star * x[30] + voq6_star * x[29] - x[28])
118 |     xdot29 = (-rLc / Lc) * x[29] + wcom * x[30] + (1 / Lc) * (vod6_star - vbd7)
119 |     xdot30 = (-rLc / Lc) * x[30] - wcom * x[29] + (1 / Lc) * (voq6_star - vbq7)
120 | 
121 |     # ----------------------------------------------------
122 |     # ---------line1
123 |     xdot31 = (-rline1 / Lline1) * x[31] + wcom * x[32] + (1 / Lline1) * (vbD1 - vbD2)
124 |     xdot32 = (-rline1 / Lline1) * x[32] - wcom * x[31] + (1 / Lline1) * (vbQ1 - vbQ2)
125 |     # ---------line2
126 |     xdot33 = (-rline2 / Lline2) * x[33] + wcom * x[34] + (1 / Lline2) * (vbD2 - vbD4)
127 |     xdot34 = (-rline2 / Lline2) * x[34] - wcom * x[33] + (1 / Lline2) * (vbQ2 - vbQ4)
128 |     # ---------line3
129 |     xdot35 = (-rline3 / Lline3) * x[35] + wcom * x[36] + (1 / Lline3) * (vbD5 - vbD4)
130 |     xdot36 = (-rline3 / Lline3) * x[36] - wcom * x[35] + (1 / Lline3) * (vbQ5 - vbQ4)
131 |     # ---------line4
132 |     xdot37 = (-rline4 / Lline4) * x[37] + wcom * x[38] + (1 / Lline4) * (vbD4 - vbD6)
133 |     xdot38 = (-rline4 / Lline4) * x[38] - wcom * x[37] + (1 / Lline4) * (vbQ4 - vbQ6)
134 |     # ---------line5
135 |     xdot39 = (-rline5 / Lline5) * x[39] + wcom * x[40] + (1 / Lline5) * (vbD14 - vbD6)
136 |     xdot40 = (-rline5 / Lline5) * x[40] - wcom * x[39] + (1 / Lline5) * (vbQ14 - vbQ6)
137 |     # ---------line6
138 |     xdot41 = (-rline6 / Lline6) * x[41] + wcom * x[42] + (1 / Lline6) * (vbD6 - vbD7)
139 |     xdot42 = (-rline6 / Lline6) * x[42] - wcom * x[41] + (1 / Lline6) * (vbQ6 - vbQ7)
140 |     # ---------line8
141 |     xdot43 = (-rline8 / Lline8) * x[43] + wcom * x[44] + (1 / Lline8) * (vbD7 - vbD9)
142 |     xdot44 = (-rline8 / Lline8) * x[44] - wcom * x[43] + (1 / Lline8) * (vbQ7 - vbQ9)
143 |     # ---------line10
144 |     xdot45 = (-rline10 / Lline10) * x[45] + wcom * x[46] + (1 / Lline10) * (vbD11 - vbD9)
145 |     xdot46 = (-rline10 / Lline10) * x[46] - wcom * x[45] + (1 / Lline10) * (vbQ11 - vbQ9)
146 |     # ---------line12
147 |     xdot47 = (-rline12 / Lline12) * x[47] + wcom * x[48] + (1 / Lline12) * (vbD13 - vbD11)
148 |     xdot48 = (-rline12 / Lline12) * x[48] - wcom * x[47] + (1 / Lline12) * (vbQ13 - vbQ11)
149 |     # ---------line13
150 |     xdot49 = (-rline13 / Lline13) * x[49] + wcom * x[50] + (1 / Lline13) * (vbD3 - vbD2)
151 |     xdot50 = (-rline13 / Lline13) * x[50] - wcom * x[49] + (1 / Lline13) * (vbQ3 - vbQ2)
152 | 
153 |     # -------------------------Loads------------------
154 |     # ------Load1--------
155 |     xdot51 = (-(disturbance_R[0] * Rload1) / (disturbance_L[0] * Lload1)) * x[51] + wcom * x[52] + (
156 |             1 / (disturbance_L[0] * Lload1)) * vbD4
157 |     xdot52 = (-(disturbance_R[0] * Rload1) / (disturbance_L[0] * Lload1)) * x[52] - wcom * x[51] + (
158 |             1 / (disturbance_L[0] * Lload1)) * vbQ4
159 |     # ------Load2--------
160 |     xdot53 = (-(disturbance_R[1] * Rload2 + rline7) / (disturbance_L[1] * Lload2 + Lline7)) * \
161 |              x[53] + wcom * x[54] + (1 / (disturbance_L[1] * Lload2 + Lline7)) * vbD7
162 |     xdot54 = (-(disturbance_R[1] * Rload2 + rline7) / (disturbance_L[1] * Lload2 + Lline7)) * \
163 |              x[54] - wcom * x[53] + (1 / (disturbance_L[1] * Lload2 + Lline7)) * vbQ7
164 |     # ------Load3--------
165 |     xdot55 = (-(disturbance_R[2] * Rload3 + rline9) / (disturbance_L[2] * Lload3 + Lline9)) * \
166 |              x[55] + wcom * x[56] + (1 / (disturbance_L[2] * Lload3 + Lline9)) * vbD9
167 |     xdot56 = (-(disturbance_R[2] * Rload3 + rline9) / (disturbance_L[2] * Lload3 + Lline9)) * \
168 |              x[56] - wcom * x[55] + (1 / (disturbance_L[2] * Lload3 + Lline9)) * vbQ9
169 |     # ------Load4--------
170 |     xdot57 = (-(disturbance_R[3] * Rload4 + rline11) / (disturbance_L[3] * Lload4 + Lline11)) * \
171 |              x[57] + wcom * x[58] + (1 / (disturbance_L[3] * Lload4 + Lline11)) * vbD11
172 |     xdot58 = (-(disturbance_R[3] * Rload4 + rline11) / (disturbance_L[3] * Lload4 + Lline11)) * \
173 |              x[58] - wcom * x[57] + (1 / (disturbance_L[3] * Lload4 + Lline11)) * vbQ11
174 | 
175 |     if t <= 0.4:
176 |         # this time is for the primary control
177 |         xdot59 = 0
178 |         xdot60 = 0
179 |         xdot61 = 0
180 |         xdot62 = 0
181 |         xdot63 = 0
182 |         xdot64 = 0
183 |         xdot65 = 0
184 |         xdot66 = 0
185 |         xdot67 = 0
186 |         xdot68 = 0
187 |         xdot69 = 0
188 |         xdot70 = 0
189 |     else:
190 |         # --------Controller Parameters--------
191 |         w_array = np.array([[x[59] - mp1 * x[2]],
192 |                             [x[60] - mp2 * x[7]],
193 |                             [x[61] - mp3 * x[12]],
194 |                             [x[62] - mp4 * x[17]],
195 |                             [x[63] - mp5 * x[22]],
196 |                             [x[64] - mp6 * x[27]]])
197 | 
198 |         Pratio = np.array([[mp1 * x[2]], [mp2 * x[7]], [mp3 * x[12]], [mp4 * x[17]], [mp5 * x[22]], [mp6 * x[27]]])
199 | 
200 |         # Conventional Freq Control
201 |         Synch_Mat = -1 * a_ctrl * (np.dot(L + G, w_array - np.array([[wref], [wref], [wref], [wref], [wref], [wref]]))
202 |                                    + np.dot(L, Pratio))
203 | 
204 |         xdot59 = Synch_Mat[0][0]
205 |         xdot60 = Synch_Mat[1][0]
206 |         xdot61 = Synch_Mat[2][0]
207 |         xdot62 = Synch_Mat[3][0]
208 |         xdot63 = Synch_Mat[4][0]
209 |         xdot64 = Synch_Mat[5][0]
210 |         xdot65 = 0
211 |         xdot66 = 0
212 |         xdot67 = 0
213 |         xdot68 = 0
214 |         xdot69 = 0
215 |         xdot70 = 0
216 | 
217 |     return np.array(
218 |         [0, xdot1, xdot2, xdot3, xdot4, xdot5, xdot6, xdot7, xdot8, xdot9, xdot10, xdot11, xdot12, xdot13, xdot14,
219 |          xdot15, xdot16, xdot17, xdot18, xdot19, xdot20, xdot21, xdot22, xdot23, xdot24, xdot25, xdot26, xdot27,
220 |          xdot28, xdot29, xdot30, xdot31, xdot32, xdot33, xdot34, xdot35, xdot36, xdot37, xdot38, xdot39, xdot40,
221 |          xdot41, xdot42, xdot43, xdot44, xdot45, xdot46, xdot47, xdot48, xdot49, xdot50, xdot51, xdot52, xdot53,
222 |          xdot54, xdot55, xdot56, xdot57, xdot58, xdot59, xdot60, xdot61, xdot62, xdot63, xdot64, xdot65, xdot66,
223 |          xdot67, xdot68, xdot69, xdot70, ioD1, ioQ1, vbD1, vbQ1, ioD2, ioQ2, vbD3, vbQ3, ioD3, ioQ3, vbD14, vbQ14, ioD4,
224 |          ioQ4, vbD5, vbQ5, ioD5, ioQ5, vbD13, vbQ13, ioD6, ioQ6, vbD7, vbQ7])
225 | 
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
  1 | """
  2 | Main function for training and evaluating MARL algorithms in Powernet
  3 | """
  4 | from __future__ import print_function, division
  5 | import argparse
  6 | import configparser
  7 | import logging
  8 | from torch.utils.tensorboard.writer import SummaryWriter
  9 | from envs.Grid_envs import GridEnv
 10 | from agents.models import IA2C, IA2C_FP, MA2C_NC, IA2C_CU, MA2C_CNET, MA2C_DIAL
 11 | from trainer import (Counter, Trainer, Tester, Evaluator,
 12 |                      check_dir, copy_file, find_file,
 13 |                      init_dir, init_log, init_test_flag)
 14 | 
 15 | 
 16 | def parse_args():
 17 |     default_base_dir = './ma2c_cnet_der6'
 18 |     default_config_dir = 'configs/config_ma2c_cnet_DER6.ini'
 19 |     parser = argparse.ArgumentParser(description=('Train or evaluate policy on RL environment '
 20 |                                                   'using A2C'))
 21 |     parser.add_argument('--base-dir', type=str, required=False,
 22 |                         default=default_base_dir, help="experiment base dir")
 23 |     parser.add_argument('--option', type=str, required=False,
 24 |                         default='train', help="train or evaluate")
 25 |     parser.add_argument('--config-dir', type=str, required=False,
 26 |                         default=default_config_dir, help="experiment config path")
 27 |     parser.add_argument('--evaluation-seeds', type=str, required=False,
 28 |                         default=','.join([str(i) for i in range(2000, 2500, 100)]),
 29 |                         help="random seeds for evaluation, split by ,")
 30 |     args = parser.parse_args()
 31 |     return args
 32 | 
 33 | 
 34 | def init_agent(env, config, total_step, seed):
 35 |     if env.agent == 'ia2c':
 36 |         return IA2C(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 37 |                     total_step, config, seed=seed)
 38 |     elif env.agent == 'ia2c_fp':
 39 |         return IA2C_FP(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 40 |                        total_step, config, seed=seed)
 41 |     elif env.agent == 'ma2c_nc':
 42 |         return MA2C_NC(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 43 |                        total_step, config, seed=seed)
 44 |     elif env.agent == 'ma2c_cnet':
 45 |         # CommNet, it calculates the mean of all messages instead of encoding them
 46 |         return MA2C_CNET(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 47 |                          total_step, config, seed=seed)
 48 |     elif env.agent == 'ma2c_cu':
 49 |         """
 50 |         ConseNet: the critic is fully decentralized
 51 |         but each takes global observations and performs consensus updates
 52 |         """
 53 |         return IA2C_CU(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 54 |                        total_step, config, seed=seed)
 55 |     elif env.agent == 'ma2c_dial':
 56 |         """
 57 |         the message is generated together with action-value estimation 
 58 |         by each DQN agent, then it is encoded and summed with other 
 59 |         input signals at the receiver side.
 60 |         """
 61 |         return MA2C_DIAL(env.n_s_ls, env.n_a_ls, env.neighbor_mask, env.distance_mask, env.coop_gamma,
 62 |                          total_step, config, seed=seed)
 63 |     else:
 64 |         return None
 65 | 
 66 | 
 67 | def train(args):
 68 |     base_dir = args.base_dir
 69 |     dirs = init_dir(base_dir)
 70 |     init_log(dirs['log'])
 71 |     config_dir = args.config_dir
 72 |     copy_file(config_dir, dirs['data'])
 73 |     config = configparser.ConfigParser()
 74 |     config.read(config_dir)
 75 | 
 76 |     # init env
 77 |     seed = config.getint('ENV_CONFIG', 'seed')
 78 |     env = GridEnv(config['ENV_CONFIG'], random_seed=seed)
 79 |     logging.info('Training: a dim %r, agent dim: %d' % (env.n_a_ls, env.n_agent))
 80 | 
 81 |     # init step counter
 82 |     total_step = int(config.getfloat('TRAIN_CONFIG', 'total_step'))
 83 |     test_step = int(config.getfloat('TRAIN_CONFIG', 'test_interval'))
 84 |     log_step = int(config.getfloat('TRAIN_CONFIG', 'log_interval'))
 85 |     global_counter = Counter(total_step, test_step, log_step)
 86 | 
 87 |     # init centralized or multi agent
 88 |     torch_seed = config.getint('MODEL_CONFIG', 'torch_seed')
 89 |     model = init_agent(env, config['MODEL_CONFIG'], total_step, torch_seed)
 90 |     model.load(dirs['model'], train_mode=True)
 91 | 
 92 |     # disable multi-threading for safe SUMO implementation
 93 |     summary_writer = SummaryWriter(dirs['log'], flush_secs=10000)
 94 |     trainer = Trainer(env, model, global_counter, summary_writer, output_path=dirs['data'], model_path=dirs['model'])
 95 |     trainer.run()
 96 | 
 97 |     # save model
 98 |     final_step = global_counter.cur_step
 99 |     model.save(dirs['model'], final_step)
100 |     summary_writer.close()
101 | 
102 | 
103 | def evaluate_fn(agent_dir, output_dir, seeds):
104 |     agent = agent_dir.split('/')[-1]
105 |     if not check_dir(agent_dir):
106 |         logging.error('Evaluation: %s does not exist!' % agent)
107 |         return
108 |     # load config file 
109 |     config_dir = find_file(agent_dir + '/data/')
110 |     if not config_dir:
111 |         return
112 |     config = configparser.ConfigParser()
113 |     config.read(config_dir)
114 | 
115 |     # init env
116 |     env = GridEnv(config['ENV_CONFIG'], train_mode=False)
117 |     env.init_test_seeds(seeds)
118 | 
119 |     # load model for agent
120 |     model = init_agent(env, config['MODEL_CONFIG'], 0, 0)
121 |     if model is None:
122 |         return
123 |     model_dir = agent_dir + '/model/'
124 |     if not model.load(model_dir):
125 |         return
126 |     # collect evaluation data
127 |     evaluator = Evaluator(env, model, output_dir)
128 |     evaluator.run()
129 | 
130 | 
131 | def evaluate(args):
132 |     base_dir = args.base_dir
133 |     dirs = init_dir(base_dir, pathes=['eva_data', 'eva_log', 'eva_data/voltage'])
134 |     init_log(dirs['eva_log'])
135 |     output_dir = dirs['eva_data']
136 |     # enforce the same evaluation seeds across agents
137 |     seeds = args.evaluation_seeds
138 |     logging.info('Evaluation: random seeds: %s' % seeds)
139 |     if not seeds:
140 |         seeds = []
141 |     else:
142 |         seeds = [int(s) for s in seeds.split(',')]
143 |     evaluate_fn(base_dir, output_dir, seeds)
144 | 
145 | 
146 | if __name__ == '__main__':
147 |     args = parse_args()
148 |     if args.option == 'train':
149 |         train(args)
150 |     else:
151 |         evaluate(args)
152 | 
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | numba
3 | pandas
--------------------------------------------------------------------------------
/trainer.py:
--------------------------------------------------------------------------------
  1 | import itertools
  2 | import logging
  3 | import numpy as np
  4 | import time
  5 | import os
  6 | import pandas as pd
  7 | import subprocess
  8 | from shutil import copy
  9 | 
 10 | 
 11 | def check_dir(cur_dir):
 12 |     if not os.path.exists(cur_dir):
 13 |         return False
 14 |     return True
 15 | 
 16 | 
 17 | def copy_file(src_dir, tar_dir):
 18 |     copy(src_dir, tar_dir)
 19 |     env = 'envs/Grid_envs.py'
 20 |     copy(env, tar_dir)
 21 |     policies = 'agents/policies.py'
 22 |     copy(policies, tar_dir)
 23 |     models = 'agents/models.py'
 24 |     copy(models, tar_dir)
 25 |     main = 'main.py'
 26 |     copy(main, tar_dir)
 27 | 
 28 | 
 29 | def find_file(cur_dir, suffix='.ini'):
 30 |     for file in os.listdir(cur_dir):
 31 |         if file.endswith(suffix):
 32 |             return cur_dir + '/' + file
 33 |     logging.error('Cannot find %s file' % suffix)
 34 |     return None
 35 | 
 36 | 
 37 | def init_dir(base_dir, pathes=['log', 'data', 'model']):
 38 |     if not os.path.exists(base_dir):
 39 |         os.mkdir(base_dir)
 40 |     dirs = {}
 41 |     for path in pathes:
 42 |         cur_dir = base_dir + '/%s/' % path
 43 |         if not os.path.exists(cur_dir):
 44 |             os.mkdir(cur_dir)
 45 |         dirs[path] = cur_dir
 46 |     return dirs
 47 | 
 48 | 
 49 | def init_log(log_dir):
 50 |     logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s',
 51 |                         level=logging.INFO,
 52 |                         handlers=[
 53 |                             logging.FileHandler('%s/%d.log' % (log_dir, time.time())),
 54 |                             logging.StreamHandler()
 55 |                         ])
 56 | 
 57 | 
 58 | def init_test_flag(test_mode):
 59 |     if test_mode == 'no_test':
 60 |         return False, False
 61 |     if test_mode == 'in_train_test':
 62 |         return True, False
 63 |     if test_mode == 'after_train_test':
 64 |         return False, True
 65 |     if test_mode == 'all_test':
 66 |         return True, True
 67 |     return False, False
 68 | 
 69 | 
 70 | class Counter:
 71 |     def __init__(self, total_step, test_step, log_step):
 72 |         self.counter = itertools.count(1)
 73 |         self.cur_step = 0
 74 |         self.cur_test_step = 0
 75 |         self.total_step = total_step
 76 |         self.test_step = test_step
 77 |         self.log_step = log_step
 78 |         self.stop = False
 79 | 
 80 |     def next(self):
 81 |         self.cur_step = next(self.counter)
 82 |         return self.cur_step
 83 | 
 84 |     def should_test(self):
 85 |         test = False
 86 |         if (self.cur_step - self.cur_test_step) >= self.test_step:
 87 |             test = True
 88 |             self.cur_test_step = self.cur_step
 89 |         return test
 90 | 
 91 |     def should_log(self):
 92 |         return self.cur_step % self.log_step == 0
 93 | 
 94 |     def should_stop(self):
 95 |         if self.cur_step >= self.total_step:
 96 |             return True
 97 |         return self.stop
 98 | 
 99 | 
100 | class Trainer:
101 |     def __init__(self, env, model, global_counter, summary_writer, output_path=None, model_path=None):
102 |         self.cur_step = 0
103 |         self.global_counter = global_counter
104 |         self.env = env
105 |         self.agent = self.env.agent
106 |         self.model = model
107 |         self.n_step = self.model.n_step
108 |         self.summary_writer = summary_writer
109 |         assert self.env.T % self.n_step == 0
110 |         self.data = []
111 |         self.episode_rewards = [0]
112 |         self.output_path = output_path
113 |         self.model_path = model_path
114 |         self.env.train_mode = True
115 | 
116 |     def _add_summary(self, reward, global_step, is_train=True):
117 |         if is_train:
118 |             self.summary_writer.add_scalar('train_reward', reward, global_step=global_step)
119 |         else:
120 |             self.summary_writer.add_scalar('test_reward', reward, global_step=global_step)
121 | 
122 |     def _get_policy(self, ob, done, mode='train'):
123 |         if self.agent.startswith('ma2c'):
124 |             self.ps = self.env.get_fingerprint()
125 |             policy = self.model.forward(ob, done, self.ps)
126 |         else:
127 |             policy = self.model.forward(ob, done)
128 |         action = []
129 |         for pi in policy:
130 |             if mode == 'train':
131 |                 action.append(np.random.choice(np.arange(len(pi)), p=pi))
132 |             else:
133 |                 action.append(np.argmax(pi))
134 |         return policy, np.array(action)
135 | 
136 |     def _get_value(self, ob, done, action):
137 |         if self.agent.startswith('ma2c'):
138 |             value = self.model.forward(ob, done, self.ps, np.array(action), 'v')
139 |         else:
140 |             self.naction = self.env.get_neighbor_action(action)  # action=[2,3,2,2]; nactions =[[3], [2,2], [3,2], [2]]
141 |             if not self.naction:
142 |                 self.naction = np.nan
143 |             value = self.model.forward(ob, done, self.naction, 'v')
144 |         return value
145 | 
146 |     def _log_episode(self, global_step, mean_reward, std_reward):
147 |         log = {'agent': self.agent,
148 |                'step': global_step,
149 |                'test_id': -1,
150 |                'avg_reward': mean_reward,
151 |                'std_reward': std_reward}
152 |         self.data.append(log)
153 |         self._add_summary(mean_reward, global_step)
154 |         self.summary_writer.flush()
155 | 
156 |     def explore(self, prev_ob, prev_done):
157 |         # run a batch of steps
158 |         ob = prev_ob
159 |         done = prev_done
160 |         for _ in range(self.n_step):
161 |             # pre-decision
162 |             policy, action = self._get_policy(ob, done)
163 |             # post-decision
164 |             value = self._get_value(ob, done, action)
165 |             # transition
166 |             self.env.update_fingerprint(policy)
167 |             next_ob, reward, done, global_reward = self.env.step(action)
168 |             self.episode_rewards[-1] += global_reward
169 |             self.global_counter.next()
170 |             self.cur_step += 1
171 |             # collect experience
172 |             if self.agent.startswith('ma2c'):
173 |                 self.model.add_transition(ob, self.ps, action, reward, value, done)
174 |             else:
175 |                 self.model.add_transition(ob, self.naction, action, reward, value, done)
176 |             if done:
177 |                 break
178 |             ob = next_ob
179 |         if done:
180 |             R = np.zeros(self.model.n_agent)
181 |         else:
182 |             _, action = self._get_policy(ob, done)
183 |             R = self._get_value(ob, done, action)
184 |         return ob, done, R
185 | 
186 |     def perform(self, test_ind):
187 |         # do a test
188 |         ob = self.env.reset(test_ind=test_ind)
189 |         rewards = []
190 |         # note this done is pre-decision to reset LSTM states!
191 |         done = True
192 |         self.model.reset()
193 |         while True:
194 |             if self.agent == 'greedy':
195 |                 action = self.model.forward(ob)
196 |             else:
197 |                 policy, action = self._get_policy(ob, done, mode='test')
198 |                 self.env.update_fingerprint(policy)
199 |             next_ob, reward, done, global_reward = self.env.step(action, mode='train')  # with disturbance
200 |             rewards.append(global_reward)
201 |             if done:
202 |                 break
203 |             ob = next_ob
204 |         mean_reward = np.mean(np.array(rewards))
205 |         std_reward = np.std(np.array(rewards))
206 |         return mean_reward, std_reward
207 | 
208 |     def run(self):
209 |         while not self.global_counter.should_stop():
210 |             ob = self.env.reset()
211 |             # note this done is pre-decision to reset LSTM states!
212 |             done = True
213 |             self.model.reset()
214 |             self.cur_step = 0
215 |             while True:
216 |                 ob, done, R = self.explore(ob, done)
217 |                 dt = self.env.T - self.cur_step
218 |                 global_step = self.global_counter.cur_step
219 |                 self.model.backward(R, dt, self.summary_writer, global_step)
220 |                 # termination
221 |                 if done:
222 |                     if self.global_counter.should_log():
223 |                         # logging
224 |                         mean_reward = round(np.mean(self.episode_rewards[-101:-1]) / self.env.T, 3)
225 |                         logging.info('''Training: global step %d, episode step %d,train r: %.2f, done: %r''' %
226 |                                      (global_step, len(self.episode_rewards), mean_reward, done))
227 |                         np.save('{}'.format('states'), np.array(self.env.states))
228 |                         # save the counter for steps for each epoch when first time reach a normal voltage
229 |                         np.save(self.output_path + '{}'.format('step_count'), self.env.step_list)
230 |                         np.save(self.output_path + '{}'.format('episode_rewards'), self.episode_rewards)
231 |                         # save model
232 |                         self.model.save(self.model_path, global_step)
233 |                     self.env.terminate()
234 |                     self.episode_rewards.append(0)
235 |                     break
236 |             rewards = np.array(self.episode_rewards[-101:-1]) / self.env.T
237 |             mean_reward = np.mean(rewards)
238 |             std_reward = np.std(rewards)
239 |             self._log_episode(global_step, mean_reward, std_reward)
240 |         df = pd.DataFrame(self.data)
241 |         df.to_csv(self.output_path + 'train_reward.csv')
242 | 
243 | 
244 | class Tester(Trainer):
245 |     def __init__(self, env, model, global_counter, summary_writer, output_path):
246 |         super().__init__(env, model, global_counter, summary_writer)
247 |         self.env.train_mode = False
248 |         self.test_num = self.env.test_num
249 |         self.output_path = output_path
250 |         self.data = []
251 |         logging.info('Testing: total test num: %d' % self.test_num)
252 | 
253 |     def run_offline(self):
254 |         self.env.cur_episode = 0
255 |         rewards = []
256 |         for test_ind in range(self.test_num):
257 |             rewards.append(self.perform(test_ind))
258 |         avg_reward = np.mean(np.array(rewards))
259 |         logging.info('Offline testing: avg R: %.2f' % avg_reward)
260 |         self.env.output_data()
261 | 
262 |     def run_online(self, coord):
263 |         self.env.cur_episode = 0
264 |         while not coord.should_stop():
265 |             time.sleep(30)
266 |             if self.global_counter.should_test():
267 |                 rewards = []
268 |                 global_step = self.global_counter.cur_step
269 |                 for test_ind in range(self.test_num):
270 |                     cur_reward = self.perform(test_ind)
271 |                     self.env.terminate()
272 |                     rewards.append(cur_reward)
273 |                     log = {'agent': self.agent,
274 |                            'step': global_step,
275 |                            'test_id': test_ind,
276 |                            'reward': cur_reward}
277 |                     self.data.append(log)
278 |                 avg_reward = np.mean(np.array(rewards))
279 |                 self._add_summary(avg_reward, global_step)
280 |                 logging.info('Testing: global step %d, avg R: %.2f' %
281 |                              (global_step, avg_reward))
282 |                 # self.global_counter.update_test(avg_reward)
283 |         df = pd.DataFrame(self.data)
284 |         df.to_csv(self.output_path + 'train_reward.csv')
285 | 
286 | 
287 | class Evaluator(Tester):
288 |     def __init__(self, env, model, output_path):
289 |         self.env = env
290 |         self.model = model
291 |         self.agent = self.env.agent
292 |         self.env.train_mode = False
293 |         self.test_num = self.env.test_num
294 |         self.output_path = output_path
295 | 
296 |     def run(self):
297 |         self.env.cur_episode = 0
298 |         time.sleep(1)
299 |         rewards = 0
300 |         for test_ind in range(self.test_num):
301 |             reward, _ = self.perform(test_ind)
302 |             logging.info('test %i, avg reward %.2f' % (test_ind, reward))
303 |             rewards += reward
304 |             time.sleep(0.5)
305 |             self.env.output_data(self.output_path, test_ind)
306 |         print('Testing finished, avg reward %.2f' % (rewards / self.test_num))
307 | 
--------------------------------------------------------------------------------