MULTIPLIER_FUNCTION

Specifies a scalar multiplier function to scale values of other commands and parameters at run time.

Type

AcuSolve Command

Syntax

MULTIPLIER_FUNCTION ("name") {parameters...}

Qualifier

User-given name.

Parameters

type (enumerated) [=none]
Type of the multiplier function.
constant or const
Fixed value. Requires constant_value.
sine_series or sine
Summation of a series of sine functions. Requires sine_coefficients.
piecewise_linear or linear
Piecewise linear curve fit. Requires curve_fit_values and curve_fit_variable.
cubic_spline or spline
Cubic spline curve fit. Requires curve_fit_values and curve_fit_variable.
piecewise_log_linear or log_linear
Piecewise log/linear curve fit. Requires curve_fit_values and curve_fit_variable.
design_variable
User-defined name of the design optimization variable.
response_variable
The responses to be used for optimization.
user_function or user
User-defined function. Requires user_function, user_values and user_strings.
user_global_data or ugd
User-defined global variable. Requires user_global_data.
modifiable
Used like constant type, except value may be modified within a user-defined function of another multiplier function.
constant_value or value (real) (=1)
The constant value of the multiplier function. Used with constant and modifiable types.
sine_coefficients (array) [={1,0,0}]
A three-column array of amplitude/frequency/phase data values. Used with sine_series type.
curve_fit_values or curve_values (array) [={0,1}]
A two-column array of independent-variable/multiplier-function data values. Used with piecewise_linear, cubic_spline, and piecewise_log_linear types.
curve_fit_variable or curve_var (enumerated) [=time]
Independent variable of the curve fit. Used with piecewise_linear, cubic_spline, and piecewise_log_linear types.
time
Current run time.
cyclic_time
Current run time, except cycled with period curve_fit_variable_cyclic_period.
time_step or step
Current time step.
time_increment or dt
Current time increment.
Piecewise linear curve fit. Requires curve_fit_values and curve_fit_variable.
multiplier_function
Multiplier function. Requires curve_fit_variable_multiplier_function.
user_global_data
User-defined global variable. Requires curve_fit_variable_user_global_data.
curve_fit_variable_multiplier_function (string) [=none]
Name of the multiplier function. Used with multiplier_function curve fit variable.
curve_fit_variable_user_global_data (string) [=none]
Name of the user global data. Used with user_global_data curve fit variable.
curve_fit_variable_cyclic_period (real) >=0 [=1]
The period of cyclic time. Used with cyclic_time curve fit variable.
design_variable (list) [={}]
Design_variable list to be used with OPTIMIZATION.
response_variable (string) [=""]
User-defined name of the response variable that is computed from the simulation.
user_function or user (string) [no default]
Name of the user-defined function. Used with user_function type.
user_values (array) [={}]
Array of values to be passed to the user-defined function. Used with user_function type.
user_strings (list) [={}]
Array of strings to be passed to the user-defined function. Used with user_function type.
user_history_variables (list) [={}]
Array of history values passed to the user-defined function. Used with user_function type. For each entry in this array, AcuSolve allocates storage such that you can store and access values across time steps within a UDF.
user_global_data or ugd (string) [no default]
Name of the user global data variable. Used with user_function type or user_global_data curve fit variable.
evaluation (enumerated) [=once_per_solution_update]
Frequency of evaluating the multiplier function.
once_per_solution_update or often
Every solution update.
once_per_time_step or step
Every time step.
filter (enumerated) [=none]
Type of filter to smooth the multiplier function. May not be used with modifiable type.
none
No filter.
relaxation or damp
Smooth the value of the multiplier function. Requires filter_relaxation_factor.
iir
Infinite impulse response digital filter. Requires iir_input_coefficients and iir_output_coefficients.
mic
Multi-Iterative Coupling.
filter_relaxation_factor (real) >=0 <=1 [=0.5]
The value of the filter relaxation factor. Used with relaxation filter.
iir_input_coefficients (array) [={1}]
Infinite impulse response digital filter coefficients applied to computed values. Used with iir filter.
iir_output_coefficients (array) [={}]
Infinite impulse response digital filter coefficients applied to previously filtered values. Used with iir filter.
dependencies (list) [={}]
List of other multiplier functions this one depends on. Required when this multiplier function depends on another indirectly through a user global data.

Description

This command specifies a scalar multiplier function to be used to dynamically scale various problem parameters at run time.

The commands that accommodate multiplier functions are:
  • MULTIPLIER_FUNCTION
  • SOLAR_RADIATION
  • EXTERNAL_CODE
  • REFERENCE_FRAME
  • MESH_MOTION
  • FLEXIBLE_BODY
  • TIME_INCREMENT
  • GRAVITY
  • MASS_HEAT_SOURCE
  • VOLUME_HEAT_SOURCE
  • MASS_SPECIES_SOURCE
  • VOLUME_SPECIES_SOURCE
  • VISCOSITY_MODEL
  • CONDUCTIVITY_MODEL
  • DIFFUSIVITY_MODEL
  • POROSITY_MODEL
  • SURFACE_TENSION_MODEL
  • CONTACT_ANGLE_MODEL
  • EMISSIVITY_MODEL
  • NODAL_BOUNDARY_CONDITION
  • PERIODIC_BOUNDARY_CONDITION
  • LINE_SOURCE
  • FAN_COMPONENT
  • HEAT_EXCHANGER_COMPONENT
  • ELEMENT_BOUNDARY_CONDITION
  • SIMPLE_BOUNDARY_CONDITION
  • SURFACE_INTEGRATED_CONDITION
  • RADIATION_SURFACE
For example, to set a gravity vector to zero for a run time up to 10, linearly change the gravity to -9.81 in the y direction by a run time of 20 and subsequently keep it at that level, you may issue the following commands:
GRAVITY( "pulse gravity" ) {
	type                           = constant
	gravity                        = { 0, -9.81, 0 }
	multiplier_function            = "pulse function"
   }
MULTIPLIER_FUNCTION( "pulse function" ) {
	type                           = piecewise_linear
	curve_fit_values               = {  0., 0 ;
	                                   10., 0 ;
	                                   20., 1 ;
	                                  100., 1 ; }
	curve_fit_variable             = time
}

The physical gravity values are stored in the gravity parameter. The MULTIPLIER_FUNCTION command simply scales these values to produce the values used in AcuSolve.

The scaling process described above may be applied to gravity of all types. For example, in
GRAVITY( "pulse gravity" ) {
type                          = user_function
user_function                 = "usrSomeGravity"
multiplier_function}          = "pulse function"

the resulting values from the user-defined function "usrSomeGravity" are scaled by the multiplier function in a similar fashion as above.

A constant type provides a fixed scaling factor for all times.

A sine_series type defines a time-dependent scaling factor of the form:(1)
M = i = 1 N α i sin ( Ω i t + φ i )
where α i , Ω i , and φ i are the amplitude, frequency and phase of the i -th mode of a sine series given by the i -throw of sine_coefficients; N is the number of rows in sine_coefficients; t is time; and M is the value of the multiplier function. The phase is in radians and the frequency is in radians per time unit, for example, rads/sec. For example, a two-term sine series multiplier function may be given by:
MULTIPLIER_FUNCTION( "sine series" ) {
     type                              = sine_series
     sine_coefficients                 = { 1., 10., 1.57 ;2., 20., 3.14 ; }
}

A multiplier function of type piecewise_linear, cubic_spline, or piecewise_log_linear computes an interpolated value from the curve fit data of curve_fit_values as a function of curve_fit_variable. curve_fit_values is a two-column array of real values. The first column specifies the independent variable values while the second column specifies the corresponding multiplier function values. The independent variable values must be in ascending order. The limit point values of the curve fit are used when curve_fit_variable falls outside of the curve fit limits.

The curve_fit_values data may be read from a file. For the above example, the curve fit values may be placed in a file, say pulse_function.fit:
0.     0
10.    0
20.    1
100.   1
and read as:
MULTIPLIER_FUNCTION( "pulse function" ) {
     type = piecewise_linear
     curve_fit_values = Read( "pulse_function.fit" )
     curve_fit_variable = time
}
For the piecewise_log_linear curve fit, the interpolation is performed on the log of the dependent values, which must be strictly positive. This interpolation provides a geometric interpolation. For example,
MULTIPLIER_FUNCTION( "growth" ) {
     type = piecewise_log_linear
     curve_fit_values = { 10, 1 ; 13, 1000 }
     curve_fit_variable = time_step
}
would yield the following values as a function of time step:
1       1
...
10      1
11      10
12      100
13      1000
14      1000
...
If curve_fit_variable is set to cyclic_time then the current time is looped in the interval, 0, curve_fit_variable_cyclic_time, and values of the independent variable in curve_fit_values should fall in this range. Setting curve_fit_variable_cyclic_time to zero is the same as an infinite period, that is, regular time is used. If curve_fit_variable is set to multiplier_function then another multiplier function, given by curve_fit_variable_multiplier_function, is evaluated to produce the independent variable. Multiplier functions may be chained to any depth in this way, but a non-terminating recursive loop is flagged as an error. Similarly, if curve_fit_variable is set to user_global_data, then the variable is set to the current value of the user global data given by curve_fit_variable_user_global_data. The following example returns a value of 50 for the multiplier function:
MULTIPLIER_FUNCTION( "curve fit UGD" ) {
     type                                  = piecewise_linear
     curve_fit_values                      = { 0, 0 ;100, 200 }
     curve_fit_variable                    = user_global_data
     curve_fit_variable_user_global_data   = "var_1"
}
USER_GLOBAL_DATA( "var_1" ) {
     value                                 = 25 
}

A multiplier function of type user_function executes a user-defined function to obtain its scaling factor; see the AcuSolve User-Defined Functions Manual for a detailed description of user-defined functions.

As an example, consider a transient problem with inflow x-velocity oscillating at 10 Hz. Assume that the file inflow.nbc contains the list of inflow nodes and the file inflow.xvel contains the x-velocity values; see the NODAL_BOUNDARY_CONDITION command for details. Then the input commands may be given as:
NODAL_BOUNDARY_CONDITION( "inflow x_velocity" ) {
     variable                  = x_velocity
     type                      = nodal
     nodes                     = Read( "inflow.nbc" )
     nodal_values              = Read( "inflow.xvel" )
     multiplier_function       = "sine wave at 10 Hz"
   }
MULTIPLIER_FUNCTION( "sine wave at 10 Hz" ) {
     type                      = user_function
     user_function             = "usrSinWave"
     user_values               = { 10. } # frequency
}
where the user-defined function "usrSinWave" may be implemented as:
#include "acusim.h"
#include "udf.h"
UDF_PROTOTYPE( usrSinWave ) ;                   /* function prototype                */
Void usrSinWave (
     UdfHd udfHd,                               /* Opaque handle for accessing data  */
     Real* outVec,                              /* Output vector                     */
     Integer nItems,                            /* Number of items in outVec         */
     Integer vecDim                             /* Vector dimension of outVec        */
   ) {
     Real freq ;                                /* frequency                         */
     Real time ;                                /* run time                          */
     Real* usrVals ;                            /* user values                       */
     udfCheckNumUsrVals( udfHd, 1 ) ;           /* check for error                   */
     usrVals = udfGetUsrVals( udfHd ) ;         /* get the user vals                 */
     freq = usrVals[0] ;                        /* get the frequency                 */
     time = udfGetTime( udfHd ) ;               /* get the run time                  */
     *outVec = sin( time * freq * 6.2832 ) ;    /* compute mult.func.*/
} /* end of usrSinWave() */
The next example of a user-defined function illustrates the client/server functionality of AcuSolve. There are four associated UDF functions; these are described in the AcuSolve User-Defined Functions Manual. Here it is desired that the average pressure over the "inflow" surface be equal to -4, but it is necessary to impose a nodal boundary condition on the x-velocity for stability and control over the velocity profile. This is possible by adjusting the nodal boundary condition multiplier function until the measured average pressure converges to the target pressure. The perl script usrPres.pl running on the remote machine "pelican" waits for an input line containing the measured pressure and then returns the multiplier function based on the measured pressure, target pressure, and the previous value of the multiplier function. An initial value is supplied by the MULTIPLIER_FUNCTION command, in this case, one. "inflow" refers to a SURFACE_OUTPUT command that computes the average pressure over the appropriate surface. The input commands may be given as:
NODAL_BOUNDARY_CONDITION( "inflow x-vel" ) {
     variable                                      = x_velocity
     ...
     multiplier_function                           = "inflow pressure control"
}
MULTIPLIER_FUNCTION( "inflow pressure control" ) {
     type                                          = user_function
     user_function                                 = "usrClientServer"
     user_values                                   = { -4, 1, 0 }
     user_strings                                  = { "rsh pelican usrPres.pl","inflow" }
} 
where the user-defined function "usrClientServer" may be implemented as:
#include "acusim.h"
#include "udf.h"
UDF_PROTOTYPE(usrClientServer) ;            /* function prototype */
Void usrClientServer(
     UdfHd udfHd,                           /* Opaque handle for accessing data */
     Real* outVec,                          /* Output vector */
     Integer nItems,                        /* Number of items in outVec */
     Integer vecDim                         /* Vector dimension of outVec */
) {
     Real mfValue ;                         /* MF return value */
     Real targPres ;                        /* target pressure */
     Real verbose ;                         /* verbose level */
     Real* trac ;                           /* pointer to surface traction */
     Real* usrVals ;                        /* user supplied values */
     String command ;                       /* execution command */
     String inflowOsi ;                     /* inflow surface output */
     String* usrStrs ;                      /* user supplied strings */
     char buffer[1024] ;                    /* a string buffer */
     char* name ;                           /* name of the UDF */
   udfCheckNumUsrVals( udfHd, 3 ) ;
   udfCheckNumUsrStrs( udfHd, 2 ) ;
   usrVals = udfGetUsrVals( udfHd ) ;
   usrStrs = udfGetUsrStrs( udfHd ) ;
   targPres = usrVals[0] ;
   mfValue = usrVals[1] ;
   verbose = usrVals[2] ;
   command = usrStrs[0] ;
   inflowOsi = usrStrs[1] ;
   /* Start the client */
   if ( udfFirstCall(udfHd) ) {
      sprintf( buffer, "%s %g %g %g", command,
               targPres, mfValue, verbose ) ;
      udfOpenPipePrim( udfHd, buffer ) ;
   }
   if  ( !udfFirstStep(udfHd) ) {
       /* Get the average pressure at the inflow surface */
       trac = udfGetOsiData( udfHd,inflowOsi, UDF_OSI_TRACTION ) ;
       /* Send the data to the client program */
       udfWritePipe( udfHd, "pressure= %.16g", -trac[0] ) ;
       /* Receive the data from the client program */
       udfReadPipe( udfHd, buffer, 1024 ) ;
       sscanf( buffer, "%*s %le", &mfValue ) ;
       if ( fabs(-trac[0]-targPres) > 1.e-4*fabs(targPres) ) {
             udfSetSig( udfHd, UDF_SIG_NO_CONV, 0 ) ;
  }
}
outVec[0] = mfValue ;
name = udfGetName( udfHd ) ;
udfCheckUgd( udfHd, name ) ;
udfSetUgdData( udfHd, name, mfValue ) ;
} /* end of usrClientServer() */
The perl script usrPres.pl may be implemented as:
#!/usr/bin/env perl
$| = 1 ;
if ( scalar(@ARGV) != 3 ) {
   die "Usage: $0 target_pressure initValue verbose" ;
}
$targPres = shift @ARGV ;
$mf       = shift @ARGV ;
$verbose  = shift @ARGV ;
$atFct    = 0.8 ;
while( <> ) {
  chomp ;
  $line = $_ ;
  if ( $line !~ /\Apressure= (.*)\Z/ ) {
   die "Invalid line: $_\n" ;
}
$mfPrev = $mf ;
$p1     = $1 ;
$p2     = $p1 * $p1 ;
$lhs11  = $atFct * $lhs11 + $p1 * $p1 ;
$lhs12  = $atFct * $lhs12 + $p1 * $p2 ;
$lhs22  = $atFct * $lhs22 + $p2 * $p2 ;
$res1   = $atFct * $res1 + $p1 * $mf ;
$res2   = $atFct * $res2 + $p2 * $mf ;
$d      = $lhs11 * $lhs22 - $lhs12 * $lhs12 ;
if ( $d > 1.e-8 * ($lhs11 * $lhs22) ) {
$a1     = (+$lhs22 * $res1 - $lhs12 * $res2) / $d ;
$a2     = (-$lhs12 * $res1 + $lhs11 * $res2) / $d ;
$mf     = ($a2 * $targPres + $a1) * $targPres ;
} elsif ( $lhs11 > 0 ) {
$a1     = $res1 / $lhs11 ;
$mf     = $a1 * $targPres ;
}
$mf     = 2.0 * $mfPrev if $mf > 2.0 * $mfPrev ;
$mf     = 0.5 * $mfPrev if $mf < 0.5 * $mfPrev ;
printf "multiplier_function= %.16g\n", $mf ;
print STDERR "pressure = $p1 mf = $mf\n" if $verbose ;
}

A user_global_data type uses the current value of the global data variable given by the user_global_data parameter as the value for the multiplier function.

A modifiable type acts exactly the same as a constant type, except its value may be changed within a user-defined function of another multiplier function as follows:
MULTIPLIER_FUNCTION( "Mod MF" ) {
   type                  = modifiable
   constant_value        = 2 #in case not modified
   dependencies          = { "main" }
}
MULTIPLIER_FUNCTION( "main" ) {
   type                  = user_function
   user_function         = "usrSomeMf"
}
Note: dependencies must be specified since the dependence on the second mulitiplier function cannot be detected automatically. See below.
Within the UDF "usrSomeMf" you can now have:
udfSetMfData( udfHd, "Mod MF", 12. );

See the AcuSolve User-Defined Functions Manual for a detailed description of user-defined functions.

The result of MULTIPLIER_FUNCTION may be modified in two ways. The evaluation parameter controls when the multiplier function is updated. For example, setting this to once_per_time_step freezes the value for the rest of the time step.

The filter parameter controls how much smoothing is applied to the multiplier function. If relaxation is specified then the multiplier function is given by the following:(2)
M n = α M ˜ n + ( 1 α ) M n 1

where α is given by filter_relaxation_factor, Mn is the final value of the multiplier function at time step n, and M ˜ n is the value of the multiplier function without filtering at time step n.

For an iir filter, an infinite impulse response (IIR) digital filter is applied. This is a generalization of the relaxation filter and is given by:(3)
M n = i = 1 N a a i M ˜ n + 1 i + i = 1 N b b i M n i
with the constraint that(4)
i = 1 N a a i + i = 1 N b b i = 1
where a i , i = 1 , , N a is given by iir_input_coefficients and b i , i = 1 , , N b is given by iir_output_coefficients. N a and N b are determined by the number of elements of the corresponding arrays. If the constraint is not satisfied, AcuSolve automatically scales the coefficients appropriately. For example,
MULTIPLIER_FUNCTION( "MF with digital filter" ) {
   ...
   filter                      = iir
   iir_input_coefficients      = { 0.8, 0.8 }
   iir_output_coefficients     = { -0.6 }
}
The mic filter is designed to control error amplifications when two fields are coupled together through a time multiplier function. This is generally used for DC-FSI problems. See EXTERNAL_CODE. For example,
MULTIPLIER_FUNCTION( "MIC_TMF" ) {
   ...
   evaluation       = often 
   filter           = mic 
}

The dependencies parameter does not modify the value of the multiplier function but ensures that it is evaluated correctly. If the current multiplier function is a modifiable type or depends on a user global data variable which in turn is set within a user-defined function of a second multiplier function, then it is necessary for the second multiplier function to be evaluated before the current one. However, AcuSolve has no way to detect this indirect dependency, hence you must list all such dependencies explicitly. Dependencies may be chained indefinitely, but any non-terminating recursive loop is flagged as an error.

Note: Direct dependencies, such as those that occur with the multiplier_function type, are automatically detected and do not need to be listed in dependencies.