Question-and-Answer Resource for the Building Energy Modeling Community
Get started with the Help page
Ask Your Question

Using Eppy to replace recurring strings in an IDF file

asked 2016-08-15 10:40:50 -0500

andrea.botti's avatar

updated 2016-08-16 13:28:26 -0500

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


Block1:B1_GroundFloor_0_0_0 0.0

Block1:B1_Roof_1_0_0 0.0

Block1:B1_Partition_2_0_0 90.0

Block2:LR_Partition_4_0_10000 270.0

Block1:B1_Wall_3_0_0 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 flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted

answered 2016-08-18 05:04:44 -0500

santoshphilip's avatar

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

It is documented at

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

edit flag offensive delete link 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.

Jamie Bull's avatar Jamie Bull  ( 2016-08-18 05:37:30 -0500 )edit

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

andrea.botti's avatar andrea.botti  ( 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

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.

santoshphilip's avatar santoshphilip  ( 2016-08-19 19:52:51 -0500 )edit

answered 2016-08-15 12:41:26 -0500

updated 2016-08-19 05:19:19 -0500

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).

    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+]': ""}
edit flag offensive delete link 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)
andrea.botti's avatar andrea.botti  ( 2016-08-16 10:57:45 -0500 )edit

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

andrea.botti's avatar andrea.botti  ( 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.

andrea.botti's avatar andrea.botti  ( 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..

Jamie Bull's avatar Jamie Bull  ( 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?

andrea.botti's avatar andrea.botti  ( 2016-08-17 05:47:06 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools



Asked: 2016-08-15 10:40:50 -0500

Seen: 401 times

Last updated: Aug 19 '16