Skip to content

Commit 1628d19

Browse files
authored
Update README file (#5)
* Update README file. * Move scripts for contingency file manipulations to one place.
1 parent d308872 commit 1628d19

File tree

6 files changed

+138
-22
lines changed

6 files changed

+138
-22
lines changed

LICENSE

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
Copyright (c) 2026, UT-Battelle.
22
All rights reserved.
33

4-
ExaGO™ version 2
4+
This work uses code from the ExaGO v1.6.0:
5+
Copyright (c) 2020-2025, Battelle Memorial Institute
6+
All rights reserved.
57

68

9+
ExaGO™ version 2
10+
711
Open Source License:
812

913
Subject to the conditions of this License, UT-Battelle, LLC, hereby grants

README.md

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ Instructions for executing the different ExaGO<sup>TM</sup> applications is give
6464
- [SCOPFLOW](docs/web/scopflow.md)
6565
- [PFLOW](docs/web/pflow.md)
6666

67-
We also provide our user manual as a pdf [manual.pdf](docs/manual/manual.pdf)
68-
-> need to update this regularly with CI / move to quarto docs.
67+
We also provide our user manual as a pdf [manual.pdf](docs/manual/manual.pdf).
6968

7069
## Tutorials
7170

@@ -84,7 +83,11 @@ Each application has a different set of options that are described in depth in
8483
the usage notes. These options can be passed optionally through an options file
8584
(`-optionsfile <option_file>`), or directly on the command line.
8685

87-
Since options may be specified in more than one location (on the command line, and through an options file), it is worth noting that the option specified on the command line supersede those in the options file. For example, if `opflowoptions` options file set the network file via the option `-netfile case9mod.m`, the following behavior occurs:
86+
Since options may be specified in more than one location (on the command line,
87+
and through an options file), it is worth noting that the option specified on
88+
the command line supersede those in the options file. For example, if
89+
`opflowoptions` options file set the network file via the option
90+
`-netfile case9mod.m`, the following behavior occurs:
8891

8992
```bash
9093
# This uses case9mod.m
@@ -106,22 +109,11 @@ attempting to contribute. Feel free to raise an issue or contact the team if
106109
the guidelines are ambiguous or you have a particular question.
107110

108111
## Authors
109-
ExaGO<sup>TM</sup> was designed and implemented by Shrirang Abhyankar. It has
110-
received significant from following developers:
111-
- Slaven Peles
112-
- Nicholson Koukpaizan
113-
- Maksudul Alam
114-
- Asher Mancinelli
115-
- Cameron Rutherford
116-
- Joshua Hambrick
117-
- Philip Fackler
118-
- Eve Tsybina
119-
- Bruce Palmer
120-
- Jaelyn Litzinger
121-
- William Perkins
122-
- Sayef Azad Sakin
123-
- Joseph Macam
124-
- Ryan Danehy
112+
ExaGO<sup>TM</sup> was designed and implemented by Shrirang Abhyankar and has
113+
received significant from Slaven Peles, Nicholson Koukpaizan, Maksudul Alam,
114+
Asher Mancinelli, Cameron Rutherford, Joshua Hambrick, Philip Fackler, Eve
115+
Tsybina, Bruce Palmer, Jaelyn Litzinger, William Perkins, Sayef Azad Sakin,
116+
Joseph Macam, and Ryan Danehy.
125117

126118
## Acknowledgement
127119
ExaGO<sup>TM</sup> has been originally developed as a part of
@@ -135,8 +127,7 @@ Copyright &copy; 2026, UT Battelle.
135127
ExaGO<sup>TM</sup> (v2) is a free software distributed under a BSD-style
136128
license. See the [LICENSE](LICENSE) and [NOTICE](NOTICE) files for details.
137129
All new contributions to ExaGO<sup>TM</sup> must be made under the same
138-
licensing terms. ExaGO<sup>TM</sup> v2 contains code from ExaGO v1.6.0,
139-
licensed under 2-clause BSD [license](LICENSE-1.6.0).
130+
licensing terms.
140131

141132
Please Note If you are using ExaGO<sup>TM</sup> with any third party libraries
142133
linked in (e.g., CoinHSL), be sure to review the respective license of the
File renamed without changes.
File renamed without changes.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
input_file = r'case_ACTIVSg70k.m'
2+
output_folder = r'out'
3+
4+
import os
5+
import re
6+
import pandas as pd
7+
8+
def extract_matrix_with_headers(text, key):
9+
pattern = rf"(?:%%[^\r\n]*[\r\n])?%[ \t]*([^\r\n]*)[\r\n]\s*mpc\.{key}\s*=\s*\[(.*?)\];"
10+
match = re.search(pattern, text, re.DOTALL)
11+
header_line = match.group(1).strip()
12+
raw_headers = re.split(r'[ \t]+', header_line)
13+
headers = raw_headers if any(not re.fullmatch(r'\d+', h) for h in raw_headers) else None
14+
block = match.group(2)
15+
lines = [line.strip().rstrip(';') for line in block.strip().split('\n') if line.strip()]
16+
data = [list(map(float, re.split(r'[ \t]+', line))) for line in lines]
17+
df = pd.DataFrame(data)
18+
if headers and len(headers) == df.shape[1]:
19+
df.columns = headers
20+
return df
21+
22+
def process_m_with_headers_df(m_file_path):
23+
with open(m_file_path, 'r') as f:
24+
content = f.read()
25+
summary_index = content.find('mpc.summary')
26+
if summary_index != -1:
27+
content = content[:summary_index]
28+
dfs = {}
29+
for key in ['bus', 'gen', 'branch']:
30+
df = extract_matrix_with_headers(content, key)
31+
output_path = os.path.join(output_folder, f"{key}.csv")
32+
#df.to_csv(output_path, index=False)
33+
dfs[key] = df
34+
df_load = dfs.get('bus')
35+
df_gen = dfs.get('gen')
36+
df_branch = dfs.get('branch')
37+
return df_load, df_gen, df_branch
38+
39+
df_load_initial, df_gen_initial, df_branch_initial = process_m_with_headers_df(input_file)
40+
41+
df_load = df_load_initial[df_load_initial['Pd'] > 0].reset_index(drop=True)
42+
n_loads = df_load.shape[0]
43+
df_load = df_load.sort_values('bus_i').reset_index(drop=True)
44+
df_load['id_or_circuit'] = df_load.groupby('bus_i').cumcount() + 1
45+
46+
df_gen = df_gen_initial[df_gen_initial['status'] == 1].reset_index(drop=True)
47+
n_gen = df_gen.shape[0]
48+
df_gen = df_gen.sort_values('bus').reset_index(drop=True)
49+
df_gen['id_or_circuit'] = df_gen.groupby('bus').cumcount() + 1
50+
51+
df_branch = df_branch_initial[(df_branch_initial['status'] == 1) & (df_branch_initial['ratio'] == 0)].reset_index(drop=True)
52+
n_branch = df_branch.shape[0]
53+
df_branch = df_branch.sort_values(['fbus', 'tbus']).reset_index(drop=True)
54+
df_branch['id_or_circuit'] = df_branch.groupby(['fbus', 'tbus']).cumcount() + 1
55+
56+
df_tr = df_branch_initial[(df_branch_initial['status'] == 1) & (df_branch_initial['ratio'] > 0)].reset_index(drop=True)
57+
n_tr = df_tr.shape[0]
58+
df_tr = df_tr.sort_values(['fbus', 'tbus']).reset_index(drop=True)
59+
df_tr['id_or_circuit'] = df_tr.groupby(['fbus', 'tbus']).cumcount() + 1
60+
61+
def create_contingency_df(df, df_type):
62+
cont_df = pd.DataFrame()
63+
cont_df['Num'] = df.index + 1 # contingency numbers starting from 1
64+
type_mapping = {'gen': 1, 'branch': 2, 'tr': 3, 'load': 4}
65+
cont_df['Type'] = type_mapping[df_type]
66+
if df_type == 'gen':
67+
cont_df['Bus'] = df['bus']
68+
elif df_type == 'load':
69+
cont_df['Bus'] = df['bus_i']
70+
else: # branch or transformer
71+
cont_df['Bus'] = 0
72+
if df_type in ['branch', 'tr']:
73+
cont_df['Fbus'] = df['fbus']
74+
else:
75+
cont_df['Fbus'] = 0
76+
if df_type in ['branch', 'tr']:
77+
cont_df['Tbus'] = df['tbus']
78+
else:
79+
cont_df['Tbus'] = 0
80+
cont_df['Id'] = df['id_or_circuit']
81+
cont_df['Status'] = 0
82+
cont_df['Prob'] = 0.001
83+
return cont_df
84+
85+
df_gen_cont = create_contingency_df(df_gen, 'gen')
86+
df_load_cont = create_contingency_df(df_load, 'load')
87+
df_branch_cont = create_contingency_df(df_branch, 'branch')
88+
df_tr_cont = create_contingency_df(df_tr, 'tr')
89+
90+
df_all = pd.concat([df_gen_cont, df_load_cont, df_branch_cont, df_tr_cont], ignore_index=True)
91+
df_all = df_all.sample(frac=1, random_state=42).reset_index(drop=True) # random_state for reproducibility
92+
df_all['Num'] = df_all.index + 1 # start from 1 if you like
93+
#df_all.to_csv('cont.csv', header=False, index=False)
94+
95+
columns_order = ['Num', 'Type', 'Bus', 'Fbus', 'Tbus', 'Id', 'Status', 'Prob']
96+
df_all['Num'] = df_all['Num'].astype(int)
97+
df_all['Type'] = df_all['Type'].astype(int)
98+
df_all['Bus'] = df_all['Bus'].astype(int)
99+
df_all['Fbus'] = df_all['Fbus'].astype(int)
100+
df_all['Tbus'] = df_all['Tbus'].astype(int)
101+
df_all['Id'] = df_all['Id'].astype(str)
102+
df_all['Status'] = df_all['Status'].astype(int)
103+
df_all['Prob'] = df_all['Prob'].astype(float)
104+
df_txt = df_all[columns_order]
105+
106+
df_txt.to_csv('70thousandbus.cont', sep=',', header=False, index=False, float_format='%.3f', lineterminator='\n')
107+
108+
with open('70thousandbus.cont', 'r') as f:
109+
lines = f.readlines()
110+
with open('70thousandbus.cont', 'w') as f:
111+
for line in lines:
112+
parts = [p.strip() for p in line.strip().split(',')]
113+
padded = []
114+
for i, p in enumerate(parts):
115+
if i in [0,1,2,3,4,6]: # integer columns
116+
padded.append(f'{int(p):>5}')
117+
elif i == 5: # string column
118+
padded.append(f"'{p}'")
119+
else: # float column
120+
padded.append(f'{float(p):>6.3f}')
121+
f.write(', '.join(padded) + '\n')

0 commit comments

Comments
 (0)