Question-and-Answer Resource for the Building Energy Modeling Community
Get s tarted with the Help page

# Using Eppy to replace recurring strings in an IDF file

Hello, I am trying to pre-process an IDF file (exported from DesignBuilder or OpenStudio) to use it with jEPlus.

As I am trying to 'assemble' a custom IDF file, using bits and pieces from different sources, I would like to manipulate an IDF, exported from DB, to combine it with objects that have customised names.

For surfaces, I managed to replace each surface name with a simpler name, the latter including the name of the zone, the type of surface and its azimuth.

The code:

s_names_azm = [(sf.Name, sf.azimuth) for sf in surfaces]
for name, azimuth in s_names_azm[:5]:
print '\n', name, azimuth
name = name.split(':',1)[1]
name = name.split('_',2)[0] + '_' + name.split('_',2)[1] + '_' + str(azimuth)
print name


returns:

Block1:B1_GroundFloor_0_0_0 0.0
B1_GroundFloor_0.0

Block1:B1_Roof_1_0_0 0.0
B1_Roof_0.0

Block1:B1_Partition_2_0_0 90.0
B1_Partition_90.0

Block2:LR_Partition_4_0_10000 270.0
LR_Partition_270.0

Block1:B1_Wall_3_0_0 0.0
B1_Wall_0.0


which is what I wanted.

However, you all know that names are always cross referenced in a IDF file, thus what I am after is really a script capable of running a find and replace function throughout the IDF file.

Is there a way to run the string.replace(old_name, new_name) for an entire IDF file? My aim is that every string 'Block*:' is replaced with '' (so effectively deleted).

Thanks, Andrea

EDIT: The code:

surfaces = idf1.idfobjects['BUILDINGSURFACE:DETAILED']
s_names_azm = [(sf.Name, sf.azimuth) for sf in surfaces]
for name, azimuth in s_names_azm:
name = name.split(':',1)[1]
name = name.split('_',2)[0] + '_' + name.split('_',2)[1] + '_' + str(azimuth)


does achieve the renaming I am after (where the input happens before Jamie Bull's substitution function).

However it does not seem to modify any object in the idf file (even though a saveas command is included at the end of the script). Why??

edit retag close merge delete

Sort by » oldest newest most voted

Andrea, You may want to look at the function modeleditor.rename

It is documented at http://pythonhosted.org/eppy/newfunct...

if this is what you are looking for, I'll flesh out this answer some more so that it is self explanatory

more

@santoshphilip Ah, I didn't know that existed - although now you mention it, the documentation does ring a bell - "peanut butter" everywhere! It might be even more useful if it could accept a regex argument rather than just a string.

( 2016-08-18 05:37:30 -0500 )edit

now we have peanut butter everywhere! I love it. Santos, that does indeed answer my question!

( 2016-08-19 04:36:36 -0500 )edit

Andrea, Jamie,

The reason no one found this function is because of bad API design :-( Why would you think of looking for it in modeleditor.rename. It does not even sound right. This function really should be look like

surface.rename(newname)
zone.rename(newname)
anyIDFobject.rename(newname)
Peanutbutter_object.rename(newname) # ha !


I'll open an issue in github. If this can be implemented, I'll add an update to this answer once it is released.

( 2016-08-19 19:52:51 -0500 )edit

If you want to use eppy (and I would recommend it!), @santoshphilip's answer is the way to go.

However, if you want an option that doesn't use eppy, for example if you want to use regular expressions in your search, this should do what you want. It's based on something I used for preparing IDFs for jEPlus.

import fileinput
import re

def edit_idf(substitutions, idf_path):
"""
Replace items in an IDF in place (this changes the file directly so be careful).

Parameters
----------
substitutions: dict
A dictionary mapping the variable to be substituted to the value
to be substitute in its place, e.g. {'Block*:': ''}
will replace 'Block*:' with '' in the input file. You may also use regular
expressions so {'Block[\d+]:': ''} would replace 'Block1:', 'Block2:',
'Block999:', etc.
idf_path : str
The path to the IDF or IMF.

"""
for key in substitutions:
for line in fileinput.FileInput(idf_path, inplace=True):
for s in substitutions:
line = re.sub("%s" % s), str(substitutions[s]), line.rstrip())
print line,


Your substitutions dict should be:

subs = {r'Block[\d+]': ""}

more

Jamie, that is great, thank you loads!

As using string 'Block*:' did not seem to work (I wanted to rename Block1:B1 into B1, Block2:LR into LR etc.) I resorted to the following code, which does the job:

for num in list(range(1,10)):
idf_substitutions({'Block'+str(num)+':' : ''}, path_imf_je)

( 2016-08-16 10:57:45 -0500 )edit

Jamie, can you also please note my comment in the edited question above?

( 2016-08-16 10:59:15 -0500 )edit

Actually forget about that. I have just realised how replacing only the field: Name in BuildingSurface:Detailed would leave old names in the field !- Outside Boundary Condition (for internal partitions), resulting in some errors.

( 2016-08-16 11:11:31 -0500 )edit

I haven't tested the new version but this ought to work in the way you at first expected using regex to catch any Block<some number>: strings. As to your edited question, yes, that's why I didn't suggest an eppy solution. There's no tracking of references within the IDF (which makes sense as they may not all be present, and it might not always be desirable to change them). With this regex solution, you need to be sure that all matches actually need to be changed, but you don't have to remember every place that needs to be changed. It's a trade-off which one is better in a given case..

( 2016-08-16 13:44:07 -0500 )edit

Jamie, thanks again. I've tried to run your function and unfortunately I get errors on the line:

line = re.sub("%s" % s), str(substitutions[s]), line)


Should that be:

line = re.sub("%s" % s, str(substitutions[s]), line)


I am also getting the error NameError: global name 're' is not defined.

PS I am calling the function with the command:

edit_idf({r'Block[\d+]':''}, path_imf_je)


What am I doing wrong?

( 2016-08-17 05:47:06 -0500 )edit