Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Add create_childs tool (#1335)
Browse files Browse the repository at this point in the history
Signed-off-by: David P. Chassin <david.chassin@me.com>
  • Loading branch information
dchassin committed Sep 22, 2023
1 parent b9707df commit 2d3408f
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 8 deletions.
2 changes: 1 addition & 1 deletion geodata/geodata_powerline.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def TODO(value=float('nan')):
}

default_config = {
"cabletype_file" : f"{GLD_ETC}/gridlabd/geodata_powerline_cabletypes.csv",
"cabletype_file" : f"{GLD_ETC}/geodata_powerline_cabletypes.csv",
}

OPTIONS = default_options
Expand Down
15 changes: 8 additions & 7 deletions tools/Makefile.mk
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
dist_pkgdata_DATA += tools/create_childs.py
dist_pkgdata_DATA += tools/create_ductbank.py
dist_pkgdata_DATA += tools/create_filter.py
dist_pkgdata_DATA += tools/create_player.py
dist_pkgdata_DATA += tools/create_meters.py
dist_pkgdata_DATA += tools/create_player.py
dist_pkgdata_DATA += tools/create_poles.py
dist_pkgdata_DATA += tools/create_schedule.py
dist_pkgdata_DATA += tools/eia_recs.py
dist_pkgdata_DATA += tools/find_location.py
dist_pkgdata_DATA += tools/fire_report.py
dist_pkgdata_DATA += tools/fire_danger.py
dist_pkgdata_DATA += tools/fire_report.py
dist_pkgdata_DATA += tools/fit_filter.py
dist_pkgdata_DATA += tools/gldserver.py
dist_pkgdata_DATA += tools/gridlabd-editor.png
dist_pkgdata_DATA += tools/gridlabd-editor.py
dist_pkgdata_DATA += tools/group.py
dist_pkgdata_DATA += tools/insights.py
dist_pkgdata_DATA += tools/install.py
dist_pkgdata_DATA += tools/isone.py
dist_pkgdata_DATA += tools/market_data.py
dist_pkgdata_DATA += tools/mdb_info.py
dist_pkgdata_DATA += tools/market_model.py
dist_pkgdata_DATA += tools/meteostat_weather.py
dist_pkgdata_DATA += tools/mdb_info.py
dist_pkgdata_DATA += tools/metar2glm.py
dist_pkgdata_DATA += tools/read_dlp.py
dist_pkgdata_DATA += tools/gldserver.py
dist_pkgdata_DATA += tools/meteostat_weather.py
dist_pkgdata_DATA += tools/noaa_forecast.py
dist_pkgdata_DATA += tools/nsrdb_weather.py
dist_pkgdata_DATA += tools/ucar_weather.py
dist_pkgdata_DATA += tools/pole_analysis.py
dist_pkgdata_DATA += tools/read_dlp.py
dist_pkgdata_DATA += tools/ucar_weather.py
186 changes: 186 additions & 0 deletions tools/create_childs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Syntax: create_childs [-i|--input=INPUTFILE] [-o|--output=GLMFILE] [OPTIONS ...]
"""Syntax: create_childs [-i|--input=INPUTFILE] [-o|--output=GLMFILE] [OPTIONS ...]
Options
-------
-P|--parents=NAME:VALUE,... specify parent property pattern to match (required)
-C|--childs=NAME:VALUE,... specify child property list to assign (required)
-N|--names=STRING specify object naming convention (default is '{class}_{name}')
-M|--modules=NAME,... specify module names to use (defaults to those found)
Description
-----------
The `create_childs` tool adds child objects to all objects that match the
parent object pattern specified.
Parent patterns and child properties as specified as a comma-separate list of
`NAME:VALUE` strings, e.g., `class:node` or `nominal_voltage:2.4kV`. Parent
patterns use `regex` pattern matching. Child properties may include `{NAME}`
format strings where `NAME` is a property of the parent object. This
allows copying of values from the parent object. This formatting also can be
applied to the naming string, e.g., `-N='{name}_L' to append '_L' to the
parent object name.
Example
-------
The following creates a GLM file containing a `triplex_load` objects attached
to `triplex_node` objects with names starting as `N_` in the file `my-network.json`:
~~~
$ gridlabd create_childs -i=my-network.json -o=loads.glm -P='class:triplex_node,name:^N_' -C='class:triplex_load,nominal_voltage:{nominal_voltage},phases:{phases},constant_power_B:1.2+0.1jkVA'
~~~
"""

import sys, os
import json
import re
import datetime
import subprocess
import random

EXENAME = os.path.splitext(os.path.basename(sys.argv[0]))[0]

DEBUG = False
WARNING = True
QUIET = False
VERBOSE = False

E_OK = 0
E_INVALID = 1
E_FAILED = 2
E_SYNTAX = 8
E_EXCEPTION = 9
EXITCODE = E_OK

class GldException(Exception):
pass

def error(msg,code=None):
if type(code) is int:
global EXITCODE
EXITCODE = code
if DEBUG:
raise GldException(msg)
print("ERROR [create_childs]:",msg,file=sys.stderr)
exit(code)


def load():

if not INPUTFILE.endswith(".json"):
tmpfile = "."
while os.path.exists(tmpfile):
tmpfile = f"tmp{hex(random.randint(1e30,1e31))[2:]}.json"
try:
result = subprocess.run(["gridlabd","-C",INPUTFILE,"-o",tmpfile])
assert(result.returncode==0)
with open(tmpfile,"r") as fh:
model = json.load(fh)
except:
raise
finally:
os.remove(tmpfile)
pass
else:
with open(INPUTFILE,"r") as fh:
model = json.load(fh)
return model

def save(fh):
print(f"// generated by '{' '.join(sys.argv)}' at {datetime.datetime.now()}",file=fh)
for name in MODULES:
print(f"module {name};",file=fh)
classname = CHILDS["class"]
for obj,data in OBJECTS.items():
print(f"object {classname} {{",file=fh)
for prop,value in data.items():
print(f" {prop} \"{value}\";",file=fh)
print("}",file=fh)

def main():

PATTERN = {}
for name,pattern in PARENTS.items():
PATTERN[name] = re.compile(pattern)

if "class" not in CHILDS:
error("you must include a class name in the child properties",E_INVALID)
classname = CHILDS["class"]
model = load()
assert(model['application']=='gridlabd')
global MODULES
if not MODULES:
MODULES = list(model['modules'])

for obj,data in model['objects'].items():
data['name'] = obj
ok = True
for name,pattern in PATTERN.items():
if not pattern.match(data[name]):
ok = False
break
if ok:
name = f"{classname}_{obj}" if NAMING is None else NAMING.format(**data)
OBJECTS[name] = dict(parent=obj,name=name)
for prop,value in CHILDS.items():
if not prop in ["class"]:
OBJECTS[name][prop] = value.format(**data)

if OUTPUTFILE.endswith(".glm"):
with open(OUTPUTFILE,"w") as fh:
save(fh)
else:
error("invalid output file format")

return E_OK

INPUTFILE = "/dev/stdin"
OUTPUTFILE = "/dev/stdout"
PARENTS = None
CHILDS = None
NAMING = None
OBJECTS = {}
MODULES = []

if __name__ == "__main__":

if len(sys.argv) == 1:
print(__doc__.split('\n')[0],file=sys.stderr)
exit(E_SYNTAX)

for arg in sys.argv[1:]:
spec = arg.split("=")
if len(spec) == 1:
tag = arg
value = None
else:
tag = spec[0]
value = '='.join(spec[1:])

if tag in ["-h","--help","help"]:
print(__doc__)
exit(E_OK)
if tag in ["-i","--input"]:
INPUTFILE = value if value else "/dev/stdin"
elif tag in ["-o","--output"]:
OUTPUTFILE = value if value else "/dev/stdout"
elif tag in ["-P","--parent"]:
PARENTS = dict([x.split(":") for x in value.split(",")])
elif tag in ["-C","--childs"]:
CHILDS = dict([x.split(":") for x in value.split(",")])
elif tag in ["-N","--names"]:
NAMING = value
elif tag in ["-M","--modules"]:
MODULES = value.split(",")
else:
error(f"option '{arg}' is invalid",E_INVALID)

if PARENTS is None:
error("you must specify the parent patterns to match")
if CHILDS is None:
error("you must specify the child properties to define")

EXITCODE = main()
exit(EXITCODE)

0 comments on commit 2d3408f

Please sign in to comment.