programmer's documentation
For transported scalar: ustssc subroutine

The routine is called for each scalar, user or specific physisc. It is therefore necessary to test the value of the scalar number iscal to separate the treatments of the different scalars (if (iscal.eq.p) then ....).

The additional source term is decomposed into an explicit part $ (crvexp) $ and an implicit part $ (crvimp) $ that must be provided here. The resulting equation solved by the code for a scalar $f$ is:

\[ \rho \norm{\vol{\celli}}\frac{df}{dt} + .... = crvimp \cdot f + crvexp \]

Note
that $ crvexp $ and $ crvimp$ are defined after the Finite Volume integration over the cells, so they include the $\norm{\vol{\celli}}$ term. More precisely:
  • $ crvexp$ is expressed in $ kg\cdot [scal] \cdot s^{-1} $ , where $ [scal] $ is the unit of the scalar
  • $ crvimp$ is expressed in $ kg \cdot s^{-1} $

The $ crvexp $ and $ crvimp$ arrays are already initialized to 0 before entering the the routine. It is not needed to do it in the routine (waste of CPU time).

For stability reasons, Code_Saturne will not add -crvimp directly to the diagonal of the matrix, but Max(-crvimp,0). This way, the crvimp term is treated implicitely only if it strengthens the diagonal of the matrix. However, when using the second-order in time scheme, this limitation cannot be done anymore and -crvimp is added directly. The user should therefore test the negativity of crvimp by himself.

When using the second-order in time scheme, one should supply:

The selection of cells where to apply the source terms is based on a getcel command. For more info on the syntax of the getcel command, refer to the user manual or to the comments on the similar command getfbr in the routine cs_user_boundary_conditions.

Warning
If the scalar is the temperature, the resulting equation solved by the code is:

\[ \rho C_p \norm{\vol{\celli}} \frac{dT}{dt} + .... = crvimp \cdot T + crvexp \]

Note
$crvexp$ and $ crvimp$ are defined after the Finite Volume integration over the cells, so they include the $\norm{\vol{\celli}}$ term. More precisely:
  • $crvexp$ is expressed in $ W $
  • $crvimp$ is expressed in $ W \cdot K^{-1} $

Steep source terms

In case of a complex, non-linear source term, say $ F(f) $, for scalar $ f $, the easiest method is to implement the source term explicitely.

\[ \frac{df}{dt} = .... + F(f^{(n)}) \]

where $ f^{(n)} $ is the value of $f$ at time $t^n$, the beginning of the time step.

This yields :

However, if the source term is potentially steep, this fully explicit method will probably generate instabilities. It is therefore wiser to partially implicit the term by writing:

\[ \frac{df}{dt} = .... + \frac{dF}{df} f^{(n+1)} - \frac{dF}{df} f^{(n)} + F(f^{(n)}) \]

This yields:

Local variables

! Local variables
character(len=80) :: chaine
integer ivar, iiscvr, iel
integer ilelt, nlelt
double precision tauf, prodf, voltf, pwatt
integer, allocatable, dimension(:) :: lstelt
double precision, dimension(:), pointer :: cpro_rom
type(var_cal_opt) :: vcopt

Initialization and finalization

The following initialization block needs to be added for the following examples:

! Allocate a temporary array for cells selection
allocate(lstelt(ncel))

At the end of the subroutine, it is recommended to deallocate the work array:

deallocate(lstelt)

In theory Fortran 95 deallocates locally-allocated arrays automatically, but deallocating arrays in a symmetric manner to their alloacation is good pratice, and avoids using a different logic between C and Fortran.

Remaining initialization

Index number of the variable associated to scalar iscal

ivar = isca(iscal)

Name of the variable associated to scalar iscal

call field_get_label(ivarfl(ivar), chaine)

Indicator of variance scalars


iiscvr = iscavr(iscal)

Density

call field_get_val_s(icrom, cpro_rom)
call field_get_key_struct_var_cal_opt(ivarfl(ivar), vcopt)
if (vcopt%iwarni.ge.1) then
write(nfecra,1000) chaine(1:8)
endif

Example 1

Example of arbitrary source term for the scalar f, 2nd scalar in the calculation.

$ S=A \cdot f+B $

appearing in the equation under the form

$ \rho \dfrac{df}{dt}=S \: \text{(+ regular other terms in the equation)} $ In the following example:

\[A=-\frac{\rho}{\tau_f} \]

\[B=\rho \cdot prod_f \]

with:

which yields:

Body

Source term applied to second scalar

if (iscal.eq.2) then
tauf = 10.d0
prodf = 100.d0
do iel = 1, ncel
crvimp(iel) = - cell_f_vol(iel)*cpro_rom(iel)/tauf
enddo
do iel = 1, ncel
crvexp(iel) = cell_f_vol(iel)*cpro_rom(iel)*prodf
enddo
endif

Example 2

Example of arbitrary volumic heat term in the equation for enthalpy h.

In the considered example, a uniform volumic source of heating is imposed in the cells with coordinate X in [0;1.2] and Y in [3.1;4].

The global heating power if Pwatt (in $W$) and the total volume of the selected cells is volf (in $m^3$).

This yields:

Body

Warning
It is assumed here that the thermal scalar is an enthalpy. If the scalar is a temperature. PWatt does not need to be divided by $ C_p $ because $C_p$ is put outside thediffusion term and multiplied in the temperature equation as follows:

\[ \rho C_p \norm{\vol{\celli}} \frac{dT}{dt} + ... = \norm{\vol{\celli}(iel)} \frac{pwatt}{voltf} \]

with pwatt = 100.d0

Calculation of voltf

voltf = 0.d0
call getcel('x > 0.0 and x < 1.2 and y > 3.1 and '// &
'y < 4.0', nlelt, lstelt)
do ilelt = 1, nlelt
iel = lstelt(ilelt)
voltf = voltf + cell_f_vol(iel)
enddo
if (irangp.ge.0) then
call parsom(voltf)
endif

Apply source term

do ilelt = 1, nlelt
iel = lstelt(ilelt)
! No implicit source term
crvimp(iel) = 0.d0
! Explicit source term
crvexp(iel) = cell_f_vol(iel)*pwatt/voltf
enddo