Skip to content

Commit 183aecf

Browse files
authored
Merge pull request #16423 from cxp484/FireX
FireX: Merge with firemodels/master
2 parents b8c3b99 + 89225f2 commit 183aecf

7 files changed

Lines changed: 703 additions & 15 deletions

File tree

Binary file not shown.

Manuals/FDS_Technical_Reference_Guide/Radiation_Chapter.tex

Lines changed: 222 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,13 @@ \subsection{Spatial Discretization}
500500
where the $D$-terms on the LHS are integrals (\ref{cellfaceintegral}) evaluated on upwind and downwind sides of the cell. In the rectilinear mesh,
501501
$D_{xu}^l = -D_{xd}^l$ and the equation can be simplified further. In addition, the integrals over the solid angle can be calculated analytically
502502
\begin{align}
503+
\label{eq:directional_weights1}
503504
D_x^l & = \int_{\Omega^l}(\bs^l\cdot {\bf i}) \, \d\Omega = \int_{\dph}\int_{\dth} (\bs^l\cdot{\bf i}) \sin\theta \; \d \theta \d\phi = \int_{\dph}\int_{\dth} \cos\phi\sin\theta\sin\theta\; \d\theta \d\phi \notag \\
504505
& = \frac{1}{2}\left( \sin\phi^+ - \sin\phi^- \right) \left[\Delta\theta - \left(\cos\theta^+\sin\theta^+ - \cos\theta^-\sin\theta^-\right)\right] \\
506+
\label{eq:directional_weights2}
505507
D_y^l & = \int_{\Omega^l}(\bs^l\cdot {\bf j}) \, \d\Omega = \int_{\dph}\int_{\dth} \sin\phi\sin\theta\sin\theta\; \d\theta \d\phi \notag \\
506508
& = \frac{1}{2}\left( \cos\phi^- - \cos\phi^+ \right) \left[\Delta\theta - \left(\cos\theta^+\sin\theta^+ - \cos\theta^-\sin\theta^-\right)\right] \\
509+
\label{eq:directional_weights3}
507510
D_z^l & = \int_{\Omega^l}(\bs^l\cdot {\bf k}) \d \Omega = \int_{\dph}\int_{\dth} \cos\theta\sin\theta\; \d\theta \d\phi \notag \\
508511
& = \frac{1}{2}\Delta\phi \left[\left(\sin\theta^+\right)^2 - \left(\sin\theta^-\right)^2\right]
509512
\\
@@ -535,7 +538,225 @@ \subsection{Spatial Discretization}
535538
all the upwind intensities are determined from solid or gas phase boundaries. In theory, iterations are needed if the reflections
536539
or scattering are important, or if the scenario is optically very thick. Currently, no iterations are made.
537540

538-
To mitigate ray effects in the wall radiative heat flux, an option is implemented to randomly rotate the local meridian ($x$), azimuthal ($y$), and axial ($z$) coordinate system at each selected time step with respect to which the angular coordinates, $\theta$ and $\phi$ are evaluated. When random rotation is enabled, the discrete angular integration coefficients (e.g., $D_{xu}^l$) are no longer exactly symmetric with respect to the mesh faces. Consequently, the sum of $D_{xu}^l$ (and the corresponding coefficients for the other coordinate directions) over a face may deviate slightly from the theoretical value of $\pi$.
541+
\textbf{Random rotation of angular directions:}
542+
To mitigate ray effects in the predicted wall radiative heat flux, the local meridional ($x'$), azimuthal ($y'$), and axial ($z'$) coordinate system may be randomly rotated at selected time steps. The angular coordinates, $\theta$ and $\phi$, are then evaluated with respect to the rotated coordinate system. Figure~\ref{fig:FVMAngularRotation} illustrates the angular discretization for several orientations of the local coordinate system. In this example, the azimuthal ($y'$) and axial ($z'$) axes are rotated in the $YZ$ plane, while the meridional ($x'$) axis remains unchanged. When random rotation is enabled, a new random orientation of the local coordinate system is assigned at each selected time step. Consequently, rays originating from a source intersect distant target surfaces along different discrete directions over time. This variation improves the effective angular coverage and substantially reduces ray effects in the time-averaged wall radiative heat flux. Figure~\ref{fig:rad_rand_rot_cube_hit} demonstrates this effect for an angular discretization containing 24 discrete directions. After 100 random rotations, the ray-intersection locations provide nearly uniform coverage of the enclosure surfaces. Increasing the number of rotations to 500 produces still more uniform surface coverage.
543+
544+
With this rotation, the angular coordinates $\theta$ and $\phi$ are defined with respect to the rotated coordinate system $(x',y',z')$. Let the unit vectors of the rotated meridional, azimuthal, and axial axes be denoted by $\mathbf{v}_{\mathrm{mer}}$, $\mathbf{v}_{\mathrm{azi}}$, and $\mathbf{v}_{\mathrm{axi}}$, respectively. Then, for the $l$th discrete direction with local direction cosines $(\mu_{\mathrm{mer},l},\mu_{\mathrm{azi},l},\mu_{\mathrm{axi},l})$, the corresponding direction vector in the global Cartesian coordinate system is given by
545+
546+
\begin{equation}
547+
\begin{aligned}
548+
\begin{bmatrix}
549+
D_x^l\\
550+
D_y^l\\
551+
D_z^l
552+
\end{bmatrix}
553+
&=
554+
\mu_{\mathrm{mer},l}\,\mathbf{v}_{\mathrm{mer}}
555+
+\mu_{\mathrm{azi},l}\,\mathbf{v}_{\mathrm{azi}}
556+
+\mu_{\mathrm{axi},l}\,\mathbf{v}_{\mathrm{axi}} \\
557+
&=
558+
\begin{bmatrix}
559+
v_{\mathrm{mer},x} & v_{\mathrm{azi},x} & v_{\mathrm{axi},x}\\
560+
v_{\mathrm{mer},y} & v_{\mathrm{azi},y} & v_{\mathrm{axi},y}\\
561+
v_{\mathrm{mer},z} & v_{\mathrm{azi},z} & v_{\mathrm{axi},z}
562+
\end{bmatrix}
563+
\begin{bmatrix}
564+
\mu_{\mathrm{mer},l}\\
565+
\mu_{\mathrm{azi},l}\\
566+
\mu_{\mathrm{axi},l}
567+
\end{bmatrix},
568+
\end{aligned}
569+
\label{eq:rotated_direction_cosines}
570+
\end{equation}
571+
572+
where the local direction cosines $(\mu_{\mathrm{mer},l},\mu_{\mathrm{azi},l},\mu_{\mathrm{axi},l})$ are obtained from Eqs.~\ref{eq:directional_weights1}--\ref{eq:directional_weights3}.
573+
574+
When rotation is enabled, the discrete angular integration coefficients (e.g., $D_{xu}^l$) are no longer exactly symmetric with respect to the mesh faces. Consequently, the summation of $D_{xu}^l$ (and the corresponding coefficients for the other coordinate directions) over a face deviates slightly from the theoretical value of $\pi$. For the default discretization of 100 solid angles, these deviations are typically very small and decrease further as the angular resolution is increased.
575+
576+
\newcommand{\DrawRADFVMSphere}[1]{%
577+
\pgfmathsetmacro{\ang}{#1}
578+
579+
% Sphere
580+
\shade[ball color=blue!10] (0,0) circle (1);
581+
\draw[thick] (0,0) circle (1);
582+
583+
% Latitude lines
584+
\foreach \i in {0,...,\NRT}
585+
{
586+
\pgfmathsetmacro{\theta}{180*\i/\NRT}
587+
588+
\draw[blue!60, thick]
589+
plot[
590+
variable=\phi,
591+
domain=0:360,
592+
samples=150,
593+
smooth
594+
]
595+
(
596+
{
597+
(sin(\theta)*sin(\phi))*cos(\ang)
598+
+(cos(\theta))*sin(\ang)
599+
},
600+
{
601+
-(sin(\theta)*sin(\phi))*sin(\ang)
602+
+(cos(\theta))*cos(\ang)
603+
}
604+
);
605+
}
606+
607+
% Longitude lines
608+
\foreach \i in {0,...,7}
609+
{
610+
\pgfmathsetmacro{\thetaLow}{180*\i/\NRT}
611+
\pgfmathsetmacro{\thetaHigh}{180*(\i+1)/\NRT}
612+
613+
\pgfmathsetmacro{\nphiRaw}{
614+
0.5*\NRA*(cos(\thetaLow)-cos(\thetaHigh))
615+
}
616+
617+
\pgfmathtruncatemacro{\nphi}{
618+
max(4,4*round(\nphiRaw/4))
619+
}
620+
621+
\foreach \j in {0,...,\numexpr\nphi-1\relax}
622+
{
623+
\pgfmathsetmacro{\phi}{360*\j/\nphi}
624+
625+
\draw[red!60, thick]
626+
plot[
627+
variable=\theta,
628+
domain=\thetaLow:\thetaHigh,
629+
samples=30,
630+
smooth
631+
]
632+
(
633+
{
634+
(sin(\theta)*sin(\phi))*cos(\ang)
635+
+(cos(\theta))*sin(\ang)
636+
},
637+
{
638+
-(sin(\theta)*sin(\phi))*sin(\ang)
639+
+(cos(\theta))*cos(\ang)
640+
}
641+
);
642+
}
643+
}
644+
645+
%-----------------------------------
646+
% Axes
647+
%-----------------------------------
648+
649+
\ifnum\ang=0
650+
651+
% No rotation: y=y', z=z'
652+
\draw[->, very thick, black]
653+
(0,0) -- (1.45,0)
654+
node[right] {$y,y'$};
655+
656+
\draw[->, very thick, black]
657+
(0,0) -- (0,1.45)
658+
node[above] {$z,z'$};
659+
660+
\else
661+
662+
%-----------------------------------
663+
% Default fixed axes
664+
%-----------------------------------
665+
666+
\draw[->, thick, black]
667+
(0,0) -- (1.45,0)
668+
node[right] {$y$};
669+
670+
\draw[->, thick, black]
671+
(0,0) -- (0,1.45)
672+
node[above] {$z$};
673+
674+
675+
%-----------------------------------
676+
% Rotated axes
677+
%-----------------------------------
678+
679+
\draw[->, very thick, red]
680+
(0,0) --
681+
({1.45*cos(\ang)},
682+
{-1.45*sin(\ang)})
683+
node[pos=1.05, right] {$y'$};
684+
685+
\draw[->, very thick, red]
686+
(0,0) --
687+
({1.45*sin(\ang)},
688+
{1.45*cos(\ang)})
689+
node[pos=1.05, above] {$z'$};
690+
691+
\fi
692+
693+
694+
%-----------------------------------
695+
% X-axis (out of page)
696+
%-----------------------------------
697+
698+
\draw[thick] (0,0) circle (0.05);
699+
\fill (0,0) circle (0.015);
700+
\node[below left] at (0,0) {$x,x'$};
701+
702+
}
703+
704+
\begin{figure}[htbp]
705+
\centering
706+
707+
\begin{tikzpicture}[scale=2.3]
708+
709+
\def\NRA{100}
710+
\pgfmathtruncatemacro{\NRT}{
711+
2*round(0.5*1.17*\NRA^(1/2.26))
712+
}
713+
714+
%-------------------------
715+
% Top left
716+
%-------------------------
717+
\begin{scope}[shift={(0,4)}]
718+
\DrawRADFVMSphere{0}
719+
\node at (0,-1.65) {(a) $0^\circ$};
720+
\end{scope}
721+
722+
%-------------------------
723+
% Top right
724+
%-------------------------
725+
\begin{scope}[shift={(3.5,4)}]
726+
\DrawRADFVMSphere{45}
727+
\node at (0,-1.65) {(b) $45^\circ$};
728+
\end{scope}
729+
730+
%-------------------------
731+
% Bottom left
732+
%-------------------------
733+
\begin{scope}[shift={(0,0)}]
734+
\DrawRADFVMSphere{135}
735+
\node at (0,-1.65) {(c) $135^\circ$};
736+
\end{scope}
737+
738+
%-------------------------
739+
% Bottom right
740+
%-------------------------
741+
\begin{scope}[shift={(3.5,0)}]
742+
\DrawRADFVMSphere{225}
743+
\node at (0,-1.65) {(d) $225^\circ$};
744+
\end{scope}
745+
746+
\end{tikzpicture}
747+
748+
\caption[Finite volume method angular discretization]{Finite volume method angular discretization with the default 100 discrete directions (which turned out to be 104). The angular discretization consists of 8 divisions in the polar angle $\theta$, with the corresponding number of divisions in the azimuthal angle $\phi$ given by [4, 12, 16, 20, 20, 16, 12, 4]. Top left: default angular discretization without rotation. Top right: $45^\circ$ clockwise rotation of the azimuthal ($y'$) and axial ($z'$) coordinate axes in the $YZ$ plane, relative to which the angular discretization is performed. Bottom left: $125^\circ$ clockwise rotation. Bottom right: $225^\circ$ clockwise rotation.}
749+
\label{fig:FVMAngularRotation}
750+
\end{figure}
751+
752+
\begin{figure}[ht]
753+
\begin{center}
754+
\includegraphics[height=5in]{FIGURES/Rad_rand_rot_cube_hit}
755+
\caption[Random rotation of radiation angles]{Distribution of radiation-ray hit locations on the surfaces of a $20 \times 20 \times 20 \, \mathrm{m}^3$ cubic enclosure for varying numbers of random rotations of the angular distribution, with the rays emitted from a spherical source (radius = 1 m) at the enclosure center. Each color represents the rays generated during one random rotation. Increasing the number of rotations improves the spatial uniformity of the surface coverage and reduces directional bias associated with a fixed angular discretization.}
756+
\label{fig:rad_rand_rot_cube_hit}
757+
\end{center}
758+
\end{figure}
759+
539760

540761
\subsection{Spatial Discretization in Cut Cells}
541762

Manuals/FDS_User_Guide/FDS_User_Guide.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10444,7 +10444,7 @@ \subsection{Flame Height and Flame Tilt}
1044410444
The ``flame height,'' and to some extent the ``flame tilt,'' are common quantities used to assess the hazard from a large fire. FDS does not output these quantities directly, but there is a combination of devices and controls that yield these values. This is all best explained with an example. Suppose you are simulating a fire in a domain that is 10~m high with a grid resolution of 0.2~m. The fire is centered about the point $(x,y)=(0,0)$ at $z=0$. The following input lines compute the flame height and tilt:
1044510445
\begin{lstlisting}
1044610446
&DEVC ID='HRR', Z_ID='z', XBP=0.0,0.0,0.0,0.0,0.1,9.9, QUANTITY='HRRPUV',
10447-
SPATIAL_STATISTIC='VOLUME INTEGRAL', DX=5, DY=5, DZ=0.1, POINTS=100,
10447+
SPATIAL_STATISTIC='VOLUME INTEGRAL', DX=5, DY=5, DZ=0.1, POINTS=50,
1044810448
STATISTICS_START=10. /
1044910449
&CTRL ID='H', FUNCTION_TYPE='PERCENTILE', INPUT_ID='HRR', PERCENTILE=0.97 /
1045010450
&DEVC ID='x_max', XB=-5,5,-5,5,9.0,9.2, QUANTITY='TEMPERATURE',

Source/dump.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3629,6 +3629,7 @@ SUBROUTINE INITIALIZE_DIAGNOSTIC_FILE(DT)
36293629
WRITE(LU_OUTPUT,'(A,ES10.3)') ' Packing Ratio ', SF%VEG_LSET_BETA
36303630
WRITE(LU_OUTPUT,'(A,ES10.3)') ' Surface Area/Volume (1/m) ', SF%VEG_LSET_SIGMA*100. ! Convert from 1/cm to 1/m
36313631
WRITE(LU_OUTPUT,'(A,ES10.3)') ' Fuel Depth (m) ', SF%VEG_LSET_HT
3632+
WRITE(LU_OUTPUT,'(A,ES10.3)') ' Fuel Load (kg/m2) ', SF%VEG_LSET_SURF_LOAD
36323633
ENDIF
36333634

36343635
ENDDO SURFLOOP

Source/read.f90

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8326,15 +8326,17 @@ SUBROUTINE READ_SURF(QUICK_READ)
83268326
ENDIF
83278327

83288328
! Level set vegetation fire spread specific
8329+
! VEG_LSET_FUEL_INDEX: -1=undefined, 0=custom Rothermel from layer props, 1-13=Albini fuel models
83298330

83308331
VEG_LSET_SPREAD = .FALSE.
8331-
IF (VEG_LSET_IGNITE_TIME < 1.E6_EB .OR. VEG_LSET_FUEL_INDEX>0 .OR. VEG_LSET_ROS_00>0._EB) VEG_LSET_SPREAD = .TRUE.
8332+
! SURF_LOAD without ROS_00/index implies custom fuel (index 0)
8333+
IF (VEG_LSET_FUEL_INDEX<0 .AND. VEG_LSET_ROS_00<=0._EB .AND. VEG_LSET_SURF_LOAD>0._EB) VEG_LSET_FUEL_INDEX = 0
8334+
IF (VEG_LSET_IGNITE_TIME < 1.E6_EB .OR. VEG_LSET_FUEL_INDEX>=0 .OR. VEG_LSET_ROS_00>0._EB) VEG_LSET_SPREAD = .TRUE.
83328335
IF (VEG_LSET_SPREAD .AND. LEVEL_SET_MODE==0) THEN
83338336
WRITE(MESSAGE,'(A,A,A)') 'ERROR(305): SURF ',TRIM(ID),' indicates a level set simulation, but LEVEL_SET_MODE not set on MISC.'
83348337
CALL SHUTDOWN(MESSAGE) ; RETURN
83358338
ENDIF
8336-
IF (VEG_LSET_FUEL_INDEX>0 .AND. LEVEL_SET_COUPLED_FIRE) HRRPUA = 1._EB ! HRRPUA to be set properly later
8337-
IF (VEG_LSET_ROS_00 >0 .AND. LEVEL_SET_COUPLED_FIRE) HRRPUA = 1._EB
8339+
IF ((VEG_LSET_FUEL_INDEX>=0 .OR. VEG_LSET_ROS_00>0._EB) .AND. LEVEL_SET_COUPLED_FIRE) HRRPUA = 1._EB ! set properly later
83388340

83398341
SF%VEG_LSET_SPREAD = VEG_LSET_SPREAD
83408342
SF%VEG_LSET_ROS_00 = VEG_LSET_ROS_00 ! no-wind, no-slope RoS (m/s), Rothermel model
@@ -8376,8 +8378,8 @@ SUBROUTINE READ_SURF(QUICK_READ)
83768378
END SELECT
83778379
ENDIF
83788380

8379-
! Set defaults for custom fuel model (for indexed fuels they are either user-specified or calculated later)
8380-
IF (SF%VEG_LSET_FUEL_INDEX==0._EB) THEN
8381+
! Defaults for undefined fuel properties (set elsewhere for indexed fuels)
8382+
IF (SF%VEG_LSET_FUEL_INDEX<=0) THEN
83818383
IF (SF%VEG_LSET_SIGMA<0._EB) SF%VEG_LSET_SIGMA = 50._EB
83828384
IF (SF%VEG_LSET_HT<0._EB) SF%VEG_LSET_HT = 0.2_EB
83838385
IF (SF%VEG_LSET_SURF_LOAD<0._EB) SF%VEG_LSET_SURF_LOAD = 1.0_EB
@@ -9306,7 +9308,7 @@ SUBROUTINE SET_SURF_DEFAULTS
93069308
VEG_LSET_M100 = 0.05_EB
93079309
VEG_LSET_MLW = 0.70_EB
93089310
VEG_LSET_MLH = 0.70_EB
9309-
VEG_LSET_FUEL_INDEX = 0
9311+
VEG_LSET_FUEL_INDEX = -1 ! -1=undefined, 0=custom Rothermel, 1-13=Albini models
93109312
VEG_LSET_SURF_LOAD = -1.0_EB !kg/m^2
93119313
VEG_LSET_FIREBASE_TIME = -1.0_EB
93129314
VEG_LSET_CHAR_FRACTION = 0.20_EB

Source/vege.f90

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,18 @@ SUBROUTINE INITIALIZE_LEVEL_SET_FIRESPREAD_1(NM)
4646

4747
M => MESHES(NM)
4848

49-
! Loop through all SURFace types and find level set cases that need a calculated RoS
49+
! Loop through all SURFace types and find level set cases that need a calculated RoS.
50+
! VEG_LSET_FUEL_INDEX: -1=undefined (user ROS_00), 0=custom Rothermel, 1-13=Albini models.
5051

5152
DO SURF_INDEX=0,N_SURF
5253
SF => SURFACE(SURF_INDEX)
53-
IF (SF%VEG_LSET_SPREAD .AND. SF%VEG_LSET_FUEL_INDEX>0) THEN
54+
IF (.NOT.SF%VEG_LSET_SPREAD) CYCLE
55+
IF (SF%VEG_LSET_FUEL_INDEX>=0) THEN
5456
SF%VEG_LSET_ROS_00 = ROS_NO_WIND_NO_SLOPE(SF%VEG_LSET_FUEL_INDEX,SURF_INDEX)
55-
ENDIF
56-
IF (SF%VEG_LSET_SPREAD .AND. SF%VEG_LSET_FUEL_INDEX==0) THEN
57-
SF%BURN_DURATION = SF%VEG_LSET_FIREBASE_TIME
58-
IF (LEVEL_SET_COUPLED_FIRE) SF%MASS_FLUX(REACTION(1)%FUEL_SMIX_INDEX) = &
59-
(1._EB-SF%VEG_LSET_CHAR_FRACTION)*SF%VEG_LSET_SURF_LOAD/SF%VEG_LSET_FIREBASE_TIME
57+
ELSE
58+
SF%BURN_DURATION = SF%VEG_LSET_FIREBASE_TIME
59+
IF (LEVEL_SET_COUPLED_FIRE) SF%MASS_FLUX(REACTION(1)%FUEL_SMIX_INDEX) = &
60+
(1._EB-SF%VEG_LSET_CHAR_FRACTION)*SF%VEG_LSET_SURF_LOAD/SF%VEG_LSET_FIREBASE_TIME
6061
ENDIF
6162
ENDDO
6263

@@ -1010,6 +1011,11 @@ REAL(EB) FUNCTION ROS_NO_WIND_NO_SLOPE(ROTHERMEL_FUEL_INDEX,SURF_INDEX)
10101011
mlh = SF%VEG_LSET_MLH
10111012

10121013
SELECT CASE(ROTHERMEL_FUEL_INDEX)
1014+
CASE(0) ! Custom homogeneous fuel from SURF layer properties
1015+
w0d1=SF%VEG_LSET_SURF_LOAD ; w0d2=0._EB ; w0d3=0._EB ; w0lh=0._EB ; w0lw=0._EB
1016+
svd1=SF%VEG_LSET_SIGMA*100._EB ! stored as 1/cm; Rothermel formulas use 1/m
1017+
svd2=358._EB ; svd3=98._EB ; svlh=4921._EB ; svlw=4921._EB
1018+
mx=0.15_EB ; depth=SF%VEG_LSET_HT ; rhop=512._EB ; heat=18607._EB ; st=0.0555_EB ; se=0.01_EB
10131019
CASE(1) ! 'Short Grass'
10141020
w0d1=0.1659 ; w0d2=0. ; w0d3=0. ; w0lh=0. ; w0lw=0. ! dry mass per unit area (kg/m2)
10151021
svd1=11483. ; svd2=358. ; svd3=98. ; svlh=4921. ; svlw=4921. ! surface area to volume (1/m)
@@ -1076,6 +1082,8 @@ REAL(EB) FUNCTION ROS_NO_WIND_NO_SLOPE(ROTHERMEL_FUEL_INDEX,SURF_INDEX)
10761082
w0lh = SF%VEG_LSET_SURF_LOAD/w0*w0lh
10771083
w0lw = SF%VEG_LSET_SURF_LOAD/w0*w0lw
10781084
w0 = (w0d1 + w0d2 + w0d3 + w0lh + w0lw)
1085+
ELSE
1086+
SF%VEG_LSET_SURF_LOAD = w0
10791087
ENDIF
10801088

10811089
! Auxiliary functions

0 commit comments

Comments
 (0)