Skip to content

Commit f8ca013

Browse files
committed
Merge remote-tracking branch 'firemodels/master'
2 parents 811db54 + bdb434f commit f8ca013

File tree

2 files changed

+177
-0
lines changed

2 files changed

+177
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from scipy.integrate import solve_ivp
4+
import os
5+
import sys
6+
7+
# Add path to Verification/Pyrolysis
8+
sys.path.append('../../Verification/Pyrolysis')
9+
10+
# Define global variables
11+
dTdt = None
12+
R0 = None
13+
E = None
14+
A = None
15+
residue = None
16+
17+
def plot_style():
18+
# Define plot style settings (placeholder)
19+
plt.style.use('seaborn-darkgrid')
20+
global Plot_Units, Plot_X, Plot_Y, Plot_Width, Plot_Height
21+
global Font_Name, Label_Font_Size, Font_Interpreter
22+
global Figure_Visibility, Paper_Units, Paper_Width, Paper_Height
23+
Plot_Units = 'normalized'
24+
Plot_X = 0.1
25+
Plot_Y = 0.1
26+
Plot_Width = 0.8
27+
Plot_Height = 0.8
28+
Font_Name = 'Arial'
29+
Label_Font_Size = 12
30+
Font_Interpreter = 'latex'
31+
Figure_Visibility = 'on'
32+
Paper_Units = 'inches'
33+
Paper_Width = 6
34+
Paper_Height = 4
35+
36+
def addverstr(ax, filename, mode):
37+
# Placeholder for addverstr function
38+
# In Python, you might read the version string from the file and add text to the plot
39+
if os.path.exists(filename):
40+
with open(filename, 'r') as file:
41+
ver_str = file.read().strip()
42+
ax.text(0.05, 0.95, ver_str, transform=ax.transAxes, fontsize=8,
43+
verticalalignment='top')
44+
45+
def reaction_rate(T, Y):
46+
"""
47+
Defines the ODE system dY/dt = -A * Y * exp(-E / (R * T))
48+
"""
49+
global A, E, R0
50+
return -A * Y * np.exp(-E / (R0 * T))
51+
52+
def main():
53+
global dTdt, R0, E, A, residue
54+
55+
plt.close('all')
56+
plt.figure()
57+
58+
plot_style()
59+
60+
show_fds = True
61+
62+
for i_plot in range(1, 3):
63+
fig, ax1 = plt.subplots()
64+
fig.set_size_inches(Paper_Width, Paper_Height)
65+
ax1.set_position([Plot_X, Plot_Y, Plot_Width, Plot_Height])
66+
67+
dTdt = 5.0 / 60.0 # degrees C per second
68+
R0 = 8314.3 # J/(kmol*K)
69+
70+
if i_plot == 1:
71+
n_components = 3
72+
T_p = np.array([100.0 + 273.0, 300.0 + 273.0])
73+
Y_0 = np.array([0.00, 1.00, 0.00])
74+
delta_T = np.array([10.0, -80.0])
75+
r_p = np.zeros(n_components - 1)
76+
residue = np.array([0.0, 0.0])
77+
else:
78+
n_components = 3
79+
T_p = np.array([100.0 + 273.0, 300.0 + 273.0])
80+
Y_0 = np.array([0.1, 0.9, 0.0])
81+
delta_T = np.array([10.0, 80.0])
82+
residue = np.array([0.0, 0.2])
83+
r_p = np.zeros(n_components - 1)
84+
85+
# Calculate A and E
86+
E = np.zeros(n_components - 1)
87+
A = np.zeros(n_components - 1)
88+
for i in range(n_components - 1):
89+
if delta_T[i] > 0:
90+
r_p[i] = 2.0 * dTdt * (1.0 - residue[i]) / delta_T[i]
91+
E[i] = np.exp(1.0) * r_p[i] * R0 * T_p[i] ** 2 / dTdt
92+
A[i] = np.exp(1.0) * r_p[i] * np.exp(E[i] / (R0 * T_p[i]))
93+
94+
# Solve the ODE dY/dT = f(T, Y)
95+
# Since dY/dt = f(T, Y) and dT/dt is constant, dY/dT = f(T, Y) / dTdt
96+
def dydT(T, Y):
97+
return reaction_rate(T, Y) / dTdt
98+
99+
sol = solve_ivp(dydT, [273, 673], Y_0, method='BDF',
100+
rtol=1e-10, atol=1e-8)
101+
T = sol.t
102+
Y = sol.y.T
103+
104+
# Compute dm/dT
105+
m = np.sum(Y, axis=1)
106+
dmdT = np.gradient(m, T)
107+
dmdT[0] = 0.0
108+
dmdT[-1] = 0.0
109+
110+
# Plot the solution
111+
TC = T - 273.0
112+
ax2 = ax1.twinx()
113+
line1, = ax1.plot(TC, m, label='Normalized Mass')
114+
line2, = ax2.plot(TC, -dmdT * dTdt * 1000.0, label='Normalized Mass Loss Rate × 10³ (s⁻¹)', color='orange')
115+
ax1.set_xlabel('Temperature (°C)', fontsize=Label_Font_Size, fontname=Font_Name,
116+
fontfamily=Font_Interpreter)
117+
ax1.set_ylabel('Normalized Mass', fontsize=Label_Font_Size, fontname=Font_Name,
118+
fontfamily=Font_Interpreter)
119+
ax2.set_ylabel('Normalized Mass Loss Rate × 10³ (s⁻¹)', fontsize=Label_Font_Size, fontname=Font_Name,
120+
fontfamily='LaTeX')
121+
ax1.set_ylim([0, 1.1])
122+
ax2.set_ylim([0, 2.2])
123+
ax1.set_yticks([0, 0.2, 0.4, 0.6, 0.8, 1.0])
124+
ax2.set_yticks([0, 0.4, 0.8, 1.2, 1.6, 2.0])
125+
line1.set_linestyle('-')
126+
line2.set_linestyle('-')
127+
128+
# Add the FDS solution
129+
if show_fds:
130+
filename = f'pyrolysis_{i_plot}_devc.csv'
131+
if not os.path.exists(filename):
132+
print(f'Error: File {filename} does not exist. Skipping case.')
133+
continue
134+
FDS = np.genfromtxt(filename, delimiter=',', skip_header=2)
135+
ax1.plot(FDS[:, 3], FDS[:, 1], 'b--', label='FDS Mass')
136+
ax2.plot(FDS[:, 3], FDS[:, 2] * 500.0, 'r--', label='FDS Mass Loss Rate × 500')
137+
138+
# Add vertical lines to indicate the temperature peaks
139+
for i in range(n_components - 1):
140+
ax2.axvline(x=T_p[i] - 273.0, color='black', linewidth=1)
141+
142+
# Add version string if file is available
143+
chid = f'pyrolysis_{i_plot}'
144+
git_filename = f'{chid}_git.txt'
145+
addverstr(ax1, git_filename, 'linear')
146+
147+
# Create the PDF files
148+
fig.set_visible(True if Figure_Visibility == 'on' else False)
149+
fig.set_size_inches(Paper_Width * 1.1, Paper_Height)
150+
pdf_filename = f'../../Manuals/FDS_User_Guide/SCRIPT_FIGURES/{chid}.pdf'
151+
plt.savefig(pdf_filename, format='pdf')
152+
153+
plt.show()
154+
155+
if __name__ == "__main__":
156+
main()
157+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
Functions plot_style and addverstr: In the MATLAB code, these are likely custom functions for setting plot styles and adding version strings. In the Python translation, plot_style initializes plot parameters using matplotlib, and addverstr is a placeholder that reads a version string from a file and adds it to the plot. You may need to implement the actual logic based on your specific requirements.
3+
4+
Global Variables: MATLAB's global variables are translated to module-level variables in Python. However, it's generally recommended to pass variables explicitly to functions to avoid potential issues with state management.
5+
6+
ODE Solver: MATLAB's ode15s is translated to scipy.integrate.solve_ivp with the BDF method, which is suitable for stiff ODEs. The ODE function reaction_rate is adjusted to account for the difference in defining dY/dT based on the temperature rate dTdt.
7+
8+
File Reading: MATLAB's csvread is replaced with numpy.genfromtxt, which is more flexible and robust for reading CSV files in Python.
9+
10+
Plotting: MATLAB's plotyy is handled using Matplotlib's twinx to create a secondary y-axis. Labels, legends, and styles are set using Matplotlib's API.
11+
12+
Indexing: MATLAB indices start at 1, whereas Python indices start at 0. Adjustments have been made to account for this difference, especially in loop ranges and array indexing.
13+
14+
Error Handling: The MATLAB display and return statements are translated to Python's print and continue statements to handle missing files gracefully without terminating the entire script.
15+
16+
Units and Calculations: Ensure that the units (e.g., temperature in Kelvin vs. Celsius) are consistently handled throughout the translation to maintain the integrity of the computations.
17+
18+
Placeholder Implementations: Certain functions like plot_style and addverstr are provided as placeholders. You'll need to implement their specific functionalities based on your original MATLAB definitions or requirements.
19+
20+
File Paths: The addpath in MATLAB is translated to modifying sys.path in Python. Ensure that the relative paths are correct based on your project's directory structure.

0 commit comments

Comments
 (0)