Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ResStock-HPXML: HES Workflow Generator #252

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
45ee2f9
make copy of residential_hpxml workflow generator to get started on h…
aspeake Oct 5, 2021
a1efb5a
first pass at connecting OS-HEScore using filepath yml argument
aspeake Oct 27, 2021
a270ff6
try to pull local Docker image first
aspeake Nov 11, 2021
d8cd306
update yml field name; some updates to HES workflow generator
aspeake Nov 11, 2021
7483a12
fix a couple of osw arguments; temporary changes for testing
aspeake Nov 19, 2021
a748763
mount OS-HEScore weather directory; update hardcoded workflow generat…
aspeake Nov 30, 2021
570d5cd
new argument passed to BuildExistingModel
aspeake Dec 8, 2021
633ae54
style fixes
aspeake Dec 10, 2021
bd2ba32
fix check for OS_HEScore directory path
aspeake Dec 10, 2021
21a74ef
Merge remote-tracking branch 'origin/restructure-v3' into restructure…
aspeake Dec 10, 2021
ced0abc
output timeseries for hes workflow generator
aspeake Dec 10, 2021
0b5324e
change opestudio docker image
aspeake Dec 10, 2021
553d5fe
apply defaults in BuildExistingModel
aspeake Dec 15, 2021
20317ea
updated HEScoreHPXML arguments
aspeake Jan 11, 2022
ed499b4
unit test for hes workflow generator
aspeake Jan 11, 2022
110742e
use bool types in argument dict
aspeake Jan 11, 2022
708b8ca
inherit residential_hpxml in residential_hpxml_hes workflow generator
aspeake Jan 25, 2022
f81e138
specify OS-HEScore measure path to use the correct reporting measure
aspeake Jan 26, 2022
a209fe6
update test for hpxml hes workflow generator
aspeake Jan 26, 2022
54f7f22
style fixes
aspeake Jan 26, 2022
c001c97
Merge remote-tracking branch 'origin/restructure-v3' into restructure…
aspeake Feb 1, 2022
26efc6f
Merge remote-tracking branch 'origin/restructure-v3' into restructure…
aspeake Feb 8, 2022
30ba6f5
Merge branch 'restructure-v3' into restructure-v3-hes
joseph-robertson Feb 16, 2022
bccc0c1
Fix syntax.
joseph-robertson Feb 16, 2022
837098e
bind OS-HEScore/hescore-hpxml to local docker image
aspeake Feb 16, 2022
c0539c0
Merge branch 'restructure-v3-hes' of github.com:NREL/buildstockbatch …
aspeake Feb 16, 2022
947df50
address pytest errors
aspeake Feb 17, 2022
8bc4b4f
temporary yml for hes workflow testing
aspeake Feb 17, 2022
a22e02c
fix relative path for OS-HEScore directory
aspeake Feb 17, 2022
5bdb4e5
mounting into /opt/hescore-hpxml
nmerket Mar 1, 2022
955f610
keeping the whole batch from failing on a single simulation failure
nmerket Mar 2, 2022
0a91a21
Merge branch 'develop' into restructure-v3-hes
nmerket Mar 4, 2022
21fcba0
Merge remote-tracking branch 'origin/develop' into restructure-v3-hes
nmerket Mar 4, 2022
5a141be
minor formatting change
nmerket Mar 4, 2022
2a00232
building hescore image on the fly
nmerket Mar 8, 2022
0ac675a
adding resstock hescore reporting measure
nmerket Mar 9, 2022
82f4c7f
Revert "adding resstock hescore reporting measure"
shorowit Apr 7, 2022
afeaa40
Merge remote-tracking branch 'origin/develop' into restructure-v3-hes
aspeake Oct 25, 2022
35ba146
add buildarg to hes docker image, minor updates to hes workflow yml
aspeake Nov 10, 2022
7cd801c
break up yml validation method in order to edit schema in child class
aspeake Nov 10, 2022
e256c7e
Merge remote-tracking branch 'origin/develop' into restructure-v3-hes
aspeake Nov 10, 2022
653f6d2
style fixes
aspeake Nov 10, 2022
7bc8030
more style fixes
aspeake Nov 11, 2022
50125e3
update hes workflow generator test
aspeake Nov 11, 2022
0af1325
Fix missing os_hes_dir variable in some cases
aspeake Nov 11, 2022
d8fa15f
remove debug statement
aspeake Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions buildstockbatch/aws/awsbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ def delete_role(self, role_name):

for policy in response['AttachedPolicies']:
self.iam.detach_role_policy(
RoleName=role_name,
PolicyArn=policy['PolicyArn']
)
RoleName=role_name,
PolicyArn=policy['PolicyArn']
)

logger.info(f'Policies detached from role {role_name}.')

Expand Down
8 changes: 7 additions & 1 deletion buildstockbatch/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ def __init__(self, project_filename):
if not os.path.isdir(self.project_dir):
raise FileNotFoundError(f'project_directory = {self.project_dir} is not a directory.')

self.os_hescore_dir = None
if 'build_existing_model' in self.cfg['workflow_generator']['args']:
if 'os_hescore_directory' in self.cfg['workflow_generator']['args']['build_existing_model']:
self.os_hescore_dir = path_rel_to_file(
project_filename, self.cfg['workflow_generator']['args']['build_existing_model']['os_hescore_directory'])

# Load in OS_VERSION and OS_SHA arguments if they exist in the YAML,
# otherwise use defaults specified here.
self.os_version = self.cfg.get('os_version', self.DEFAULT_OS_VERSION)
Expand Down Expand Up @@ -737,7 +743,7 @@ def get_dask_client(self):
def process_results(self, skip_combine=False, force_upload=False):
self.get_dask_client() # noqa: F841

if self.cfg['workflow_generator']['type'] == 'residential_hpxml':
if 'residential_hpxml' in self.cfg['workflow_generator']['type']:
Comment on lines -740 to +746
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I learned something today that you can search for a substring using in.

if 'simulation_output_report' in self.cfg['workflow_generator']['args'].keys():
if 'timeseries_frequency' in self.cfg['workflow_generator']['args']['simulation_output_report'].keys():
do_timeseries = \
Expand Down
64 changes: 32 additions & 32 deletions buildstockbatch/eagle.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ def singularity_image_url(self):
else:
prefix_ver = self.os_version
return 'https://s3.amazonaws.com/openstudio-builds/{prefix_ver}/OpenStudio-{ver}.{sha}-Singularity.simg'.format(
prefix_ver=prefix_ver,
ver=self.os_version,
sha=self.os_sha
)
prefix_ver=prefix_ver,
ver=self.os_version,
sha=self.os_sha
)

@property
def singularity_image(self):
Expand Down Expand Up @@ -653,35 +653,35 @@ def rerun_failed_jobs(self, hipri=False):


logging_config = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'defaultfmt': {
'format': '%(levelname)s:%(asctime)s:%(name)s:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'defaultfmt',
'level': 'DEBUG',
'stream': 'ext://sys.stdout',
}
},
'loggers': {
'__main__': {
'level': 'DEBUG',
'propagate': True,
'handlers': ['console']
},
'buildstockbatch': {
'level': 'DEBUG',
'propagate': True,
'handlers': ['console']
}
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'defaultfmt': {
'format': '%(levelname)s:%(asctime)s:%(name)s:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'defaultfmt',
'level': 'DEBUG',
'stream': 'ext://sys.stdout',
}
},
'loggers': {
'__main__': {
'level': 'DEBUG',
'propagate': True,
'handlers': ['console']
},
}
'buildstockbatch': {
'level': 'DEBUG',
'propagate': True,
'handlers': ['console']
}
},
}


def user_cli(argv=sys.argv[1:]):
Expand Down
61 changes: 51 additions & 10 deletions buildstockbatch/localdocker.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from buildstockbatch.base import BuildStockBatchBase, SimulationExists
from buildstockbatch import postprocessing
from .utils import log_error_details, ContainerRuntime
from docker.errors import ImageNotFound
from buildstockbatch.__version__ import __version__ as bsb_version

logger = logging.getLogger(__name__)
Expand All @@ -52,22 +53,41 @@ def __init__(self, project_filename):

self._weather_dir = None

# FIXME: something better and more general than os_hescore_dir
if self.os_hescore_dir:
logger.debug("Building HEScore docker image")
self.docker_client.images.build(
path=self.os_hescore_dir,
tag=self.docker_image,
dockerfile="resstock/Dockerfile",
rm=True,
buildargs={'os_version': self.os_version}
)
logger.debug("Docker image built")
else:
try:
self.docker_client.images.get(self.docker_image)
except ImageNotFound:
self.docker_client.images.pull(self.docker_image)

@staticmethod
def validate_project(project_file):
super(DockerBatchBase, DockerBatchBase).validate_project(project_file)
# LocalDocker specific code goes here

@property
def docker_image(self):
return 'nrel/openstudio:{}'.format(self.os_version)
if self.os_hescore_dir:
return 'nrel/hescore-hpxml-openstudio'
else:
return 'nrel/openstudio:{}'.format(self.os_version)


class LocalDockerBatch(DockerBatchBase):

def __init__(self, project_filename):
super().__init__(project_filename)
logger.debug(f'Pulling docker image: {self.docker_image}')
self.docker_client.images.pull(self.docker_image)

# Create simulation_output dir
sim_out_ts_dir = os.path.join(self.results_dir, 'simulation_output', 'timeseries')
Expand Down Expand Up @@ -149,28 +169,44 @@ def weather_dir(self):
return self._weather_dir.name

@classmethod
def run_building(cls, project_dir, buildstock_dir, weather_dir, docker_image, results_dir, measures_only,
n_datapoints, cfg, i, upgrade_idx=None):
def run_building(cls, project_dir, buildstock_dir, weather_dir, os_hescore_dir, docker_image, results_dir,
measures_only, n_datapoints, cfg, i, upgrade_idx=None):
Comment on lines -152 to +173
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this was necessary to get it working, but I don't like it.


upgrade_id = 0 if upgrade_idx is None else upgrade_idx + 1

try:
sim_id, sim_dir = cls.make_sim_dir(i, upgrade_idx, os.path.join(results_dir, 'simulation_output'))
except SimulationExists:
return

bind_mounts = [
(sim_dir, '', 'rw'),
(os.path.join(buildstock_dir, 'measures'), 'measures', 'ro'),
(os.path.join(buildstock_dir, 'resources'), 'lib/resources', 'ro'),
(os.path.join(project_dir, 'housing_characteristics'), 'lib/housing_characteristics', 'ro'),
(weather_dir, 'weather', 'ro')
]

# ResStock-hpxml measure directory
if os.path.exists(os.path.join(buildstock_dir, 'resources', 'hpxml-measures')):
bind_mounts += [(os.path.join(buildstock_dir, 'resources', 'hpxml-measures'),
'resources/hpxml-measures', 'ro')]
docker_volume_mounts = dict([(key, {'bind': f'/var/simdata/openstudio/{bind}', 'mode': mode}) for key, bind, mode in bind_mounts]) # noqa E501
bind_mounts.append(
(os.path.join(buildstock_dir, 'resources', 'hpxml-measures'), 'resources/hpxml-measures', 'ro')
)

# OS-HEScore weather directory
if os_hescore_dir and os.path.exists(os_hescore_dir):
bind_mounts.append((os.path.join(os_hescore_dir, 'weather'), '/opt/OpenStudio-HEScore/weather', 'ro'))

docker_volume_mounts = {
key: {
'bind': bind if bind.startswith('/') else f'/var/simdata/openstudio/{bind}',
'mode': mode
}
for key, bind, mode in bind_mounts
}

for bind in bind_mounts:
if bind[1].startswith('/'):
continue
dir_to_make = os.path.join(sim_dir, *bind[1].split('/'))
if not os.path.exists(dir_to_make):
os.makedirs(dir_to_make)
Expand Down Expand Up @@ -211,17 +247,20 @@ def run_building(cls, project_dir, buildstock_dir, weather_dir, docker_image, re
extra_kws = {}
if sys.platform.startswith('linux'):
extra_kws['user'] = f'{os.getuid()}:{os.getgid()}'
container_output = docker_client.containers.run(

container = docker_client.containers.run(
docker_image,
run_cmd,
remove=True,
detach=True,
volumes=docker_volume_mounts,
name=sim_id,
environment=env_vars,
**extra_kws
)
with open(os.path.join(sim_dir, 'docker_output.log'), 'wb') as f_out:
f_out.write(container_output)
for x in container.logs(stream=True):
f_out.write(x)

# Clean up directories created with the docker mounts
for dirname in ('lib', 'measures', 'weather'):
Expand Down Expand Up @@ -255,6 +294,7 @@ def run_batch(self, n_jobs=None, measures_only=False, sampling_only=False):
self.project_dir,
self.buildstock_dir,
self.weather_dir,
self.os_hescore_dir,
self.docker_image,
self.results_dir,
measures_only,
Expand All @@ -281,6 +321,7 @@ def run_batch(self, n_jobs=None, measures_only=False, sampling_only=False):
json.dump(dpouts, f)
del dpouts

# FIXME temporarily comment out for testing
sim_out_tarfile_name = os.path.join(sim_out_dir, 'simulations_job0.tar.gz')
logger.debug(f'Compressing simulation outputs to {sim_out_tarfile_name}')
with tarfile.open(sim_out_tarfile_name, 'w:gz') as tarf:
Expand Down
Loading