import os

Build FDS Input Lines Using Python String Manipulation#

This example demonstrates some basic string manipulation methods in Python. It is used to create FDS input lines. Furthermore, it is demonstrated how changes to input files could be conducted.

String Basics in Python#

In Python, strings are used to work with text data. They are written as text that is encapsulated with quotes. One can use single or double quotes to write a string.

# Different ways to write strings.

single_quotes = 'Hello world! (single quotes)'
print(single_quotes)

double_quotes = "Hello world! (double quotes)"
print(double_quotes)
Hello world! (single quotes)
Hello world! (double quotes)

Issues may arise if the string contains qoutes. Here, one type of quotes can be used to mark a character chain as being the string and the other type of quotes to highlight the quote inside the string itself. This is important to keep in mind when working on FDS input.

For example consider the definition of a basic REAC-line, where the fuel is set as a string:

# Basic REAC line with 
reac_line = "&REAC FUEL = 'METHANE' /"
print(reac_line)
&REAC FUEL = 'METHANE' /

Using a given quote-character three times to encapsulate the string allows to cover multiple lines with the string. This strategy is used for example in docstrings in definitions of functions.

# Multi-line strings with triple qoutes.
reac_line_2 = """&REAC FUEL               = 'METHANE',
      SOOT_YIELD         = 0.022,
      RADIATIVE_FRACTION = 0.2"""
print(reac_line_2)
&REAC FUEL               = 'METHANE',
      SOOT_YIELD         = 0.022,
      RADIATIVE_FRACTION = 0.2

Basic String Manipulation with Python#

In the following a couple of basic string manipulation methods are presented.

# Example string.
file_name = "BUW_Cone_HRR_50kW_1.csv"

Individual characters can be accessed by their location, using similar syntax like arrays.

# Individual character.
print(file_name[2])

# Series of characters - substring.
print(file_name[:8])
W
BUW_Cone

The split() method of string objects can be used to break down a string, using a specific character as separator. It gives back a list, where every element is a substring.

As an example, we use a file name of some experiment data. Parts of it denote specific parts of information, like the type of experiment and the recorded parameter.

file_name.split("_")
['BUW', 'Cone', 'HRR', '50kW', '1.csv']

If one were interested only the file name itself, one could either skip the last four characters. This would be under the assumption that the end would be known.

file_name[:-4].split("_")
['BUW', 'Cone', 'HRR', '50kW', '1']

Or one could first split around the period first. Then the first element of the resulting list could be split around the underscore.

period_split = file_name.split(".")
period_split
['BUW_Cone_HRR_50kW_1', 'csv']
period_split[0].split("_")
['BUW', 'Cone', 'HRR', '50kW', '1']

Strings can be added together using “+” operator.

# Initial strings.
string_a = "Hello "
print(string_a)

string_b = "world!"
print(string_b)

# Combine strings.
string_combined = string_a + string_b
print(string_combined)
Hello 
world!
Hello world!

One can add substrings into another string, for example using f-strings. The f-strings are created by writing an “f” directly infront of the string, like f"this is an f-string". Using curly brackets, locations can be marked in which new infromation can be written. Inside these brackets a varibale can be placed to add its content there.

# Set a variable to add to a string.
add_in = 42

# The f-string to add the information into.
f"BUW_Cone_HRR_{add_in}kW_1"
'BUW_Cone_HRR_42kW_1'

Multiple markers can be set at once.

# Set a variable to add to a string.
exp_condition = 42
exp_type = "Cone"

# The f-string to add the information into.
f"BUW_{exp_type}_HRR_{exp_condition}kW_1"
'BUW_Cone_HRR_42kW_1'

Similar results could be achieved using format() method. Here in the string the curly brackets are also used as markers, yet the content to be added will be provided later on. Multiple brackets can be used, they are filled in order.

# Initialiseg the string 
string_base = "BUW_{}_HRR_{}kW_1"

# Fill in the information.
string_base.format("Cone", 42)
'BUW_Cone_HRR_42kW_1'

With the information above, we could procedurally generate FDS input lines.

Let’s take devices as an example. Two different quantities are to be recorded, 'TEMPERATURE' and 'THERMOCOUPLE'. They also are supposed to be stacked vertically with an equal distance of \(\SI{0.1}{\meter}\).

# Initialise data collection.
devc_lines = str()

# Set the quantities.
quantities = ["TEMPERATURE", "THERMOCOUPLE"]

# Set the number od DEVC.
n_devc = 10

# Set the vertical distance.
z_dist = 0.1

# Set the base input line string.
devc_base = "&DEVC ID = '{}', XYZ = 0.0, 0.0, {:.2f}, QUANTITY = '{}' /"


# Go over the quantities.
for quantity in quantities:
    
    # Go over the DEVC locations.
    for devc_idx in range(n_devc):
        # Define DEVC ID.
        devc_id = f"{quantity[:4]}_z{devc_idx:02d}"
        
        # Compute height, shifted by a half cell.
        loc_z = z_dist * devc_idx + z_dist / 2
        
        # Create the DEVC line.
        devc_line = devc_base.format(devc_id, loc_z, quantity) + os.linesep
        # print(devc_line)
        
        # Collect lines.
        devc_lines += devc_line
        
    # Separation between quantities.
    devc_lines += os.linesep


# Check result.
print(devc_lines)
&DEVC ID = 'TEMP_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'TEMPERATURE' /

&DEVC ID = 'THER_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'THERMOCOUPLE' /

Within a string, substrings can be overwritten using the replace() method. It takes two parameters, the first is the substring to look for and the second is the new string to replace it with.

# Define the two substrings.
search_for = "TEMPERATURE"
replace_with = "VELOCITY"

# Replace substring.
new_input_line = devc_lines.replace(search_for,
                                    replace_with)
print(new_input_line)
&DEVC ID = 'TEMP_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'VELOCITY' /
&DEVC ID = 'TEMP_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'VELOCITY' /

&DEVC ID = 'THER_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'THERMOCOUPLE' /

Reading and Writing Text Files#

Text files can be read using the read() method. It will be provided as a single string.

One could set up an FDS input file using it as a template. Consider, if most of the input remains the same, markers could be added such that sections could be adjusted. For example, let’s read some basic FDS input template. In it, there is a substring, #Devices#, that is to be replaced with procedurally generated DEVC lines.

First read the example file.

# Set path to file to read.
file_name = "FDSInputTemplate.fds"
file_path = os.path.join("data", "simple_fds_template", file_name)

# Read text file.
with open(file_path) as file_content:
    content = file_content.read()
    
    
# Check results.
print(content)
&HEAD CHID  = 'InputTemplate',
      TITLE = 'Basic template file for learning string replacement' /

&TIME T_END = 30.0 /



&MESH ID  = 'Domain',
      IJK = 10,10,10,
      XB  = 0.00,2.00, 0.00,2.00, 0.00,2.00 /



# ---------------------------------------
# Gas burner: methane
# ---------------------------------------

&REAC FUEL       = 'METHANE',
      SOOT_YIELD = 0.022 /

&SURF ID         = 'BURNER',
      HRRPUA     = 160.0,
      COLOR      = 'RASPBERRY' /


&VENT ID      = 'BurnerOutlet',
      SURF_ID = 'BURNER',
      XB      = 0.80,1.20, 0.80,1.20, 0.00,0.00 /



# ---------------------------------------
# Analytics
# ---------------------------------------

#Devices#



&TAIL /

Now, using the replace() method to exchange the marker and add in the DEVC lines.

# Define substring to be replaced (markker).
marker = "#Devices#"

# Exchange 
new_content = content.replace(marker, devc_lines)


# Check results.
print(new_content)
&HEAD CHID  = 'InputTemplate',
      TITLE = 'Basic template file for learning string replacement' /

&TIME T_END = 30.0 /



&MESH ID  = 'Domain',
      IJK = 10,10,10,
      XB  = 0.00,2.00, 0.00,2.00, 0.00,2.00 /



# ---------------------------------------
# Gas burner: methane
# ---------------------------------------

&REAC FUEL       = 'METHANE',
      SOOT_YIELD = 0.022 /

&SURF ID         = 'BURNER',
      HRRPUA     = 160.0,
      COLOR      = 'RASPBERRY' /


&VENT ID      = 'BurnerOutlet',
      SURF_ID = 'BURNER',
      XB      = 0.80,1.20, 0.80,1.20, 0.00,0.00 /



# ---------------------------------------
# Analytics
# ---------------------------------------

&DEVC ID = 'TEMP_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'TEMPERATURE' /
&DEVC ID = 'TEMP_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'TEMPERATURE' /

&DEVC ID = 'THER_z00', XYZ = 0.0, 0.0, 0.05, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z01', XYZ = 0.0, 0.0, 0.15, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z02', XYZ = 0.0, 0.0, 0.25, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z03', XYZ = 0.0, 0.0, 0.35, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z04', XYZ = 0.0, 0.0, 0.45, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z05', XYZ = 0.0, 0.0, 0.55, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z06', XYZ = 0.0, 0.0, 0.65, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z07', XYZ = 0.0, 0.0, 0.75, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z08', XYZ = 0.0, 0.0, 0.85, QUANTITY = 'THERMOCOUPLE' /
&DEVC ID = 'THER_z09', XYZ = 0.0, 0.0, 0.95, QUANTITY = 'THERMOCOUPLE' /





&TAIL /

After the new information is added, the string can be written to disk as a new text file. For it we use the write() method. The 'w' parameter will create the file if it does not exist, and overwrite it if it does.

# Set path to file to read.
file_name = "NewInput.fds"
file_path = os.path.join("data", "simple_fds_template", file_name)

# Write text file.
with open(file_path, "w") as new_fds_file:
    new_fds_file.write(new_content)

Regular Expressions#

Regular expressions (regex) are another powerful tool to search for patterns in a text. They are implemented for many programming languages. The webpage regex101 can help you design a specific search pattern.