User subroutines are written in JavaScript and consist only of
the body of the function that implements the field behavior. There is no
special function name; instead, the JavaScript function is associated
directly with the condition field being defined. The app can read user subroutines from
a file, or you can define a user subroutine in a text input area launched from
the dialog box for the feature.
Function Definition
User subroutines written in JavaScript are just the body of the
function that implements the field behavior. There is no function name assigned
because only the body of the function is required. The contents of the class
can be referenced using the JavaScript
this reference.
The Flow solver calls user subroutines once per iteration. This approach means that the user
subroutine could be called thousands of times during a simulation. This
frequency of calls, coupled with the potential to address large portions of the
model, requires that care be taken to implement user subroutines that are
computationally efficient.
Function Arguments
Two arguments are always passed to the JavaScript function defined
by the user. The first argument, support, describes the
object on which the user subroutine is defined (for example, boundary or volume). The
second argument, timeParameters, describes
the current simulation time and increment information.
JavaScript Classes
A number of classes are exposed as part of the user subroutine API,
and these classes have a number of properties and methods that are available
for use by the user subroutine implementer. The
Field class contains the code provided by the user
subroutine implementer.
Properties are data members of the class. Each class has a unique
set of properties. Only the
values property of the
Field class can be modified by the implementer.
All other properties are read only.
Some classes have methods that can be used for querying, which enables you to compute information related to field computation. The methods on the
support class instance passed in as an argument
can be used to query current Flow solver state information, such as current
temperature or velocity.
Flow solver state information is retrieved using two methods,
getScalar and
getVector. The method used depends on the
algebraic type of the data being queried. For example, the
getVector method would be used to query
coordinates because that data is stored as a vector. The following arguments are passed to
the two query methods: a string that corresponds to the quantity being
requested, and an enumeration that describes the location at which the
information is required.
Examples: Simple User Subroutines
The following example shows how you can define a heat source that
increases linearly as the simulation time progresses:
for (var i = 0; i < this.values.length; i++) {
this.values[i] = 100.0 * timeParameters.time/timeParameters.totalTime;
}
The following example defines a parabolic inlet velocity:
coord = support.getVector('COORD', CenteringType.FACE);
for (var i = 0; i < this.values.length; i++) {
this.values[i] = 1.0 - coord[i][1]*coord[i][1];
}
Global Variables
One global object,
cfd, is available for use within the user
subroutine implementation. This object is used to access system-related
resources such as parallel processing and file logging. There are no
console or
window global objects available (even though they
are common in JavaScript code) because this code is not being run in the
context of a web browser.
Parallel Execution
User subroutines are executed in parallel when the Flow solver is
run in parallel. The Flow solver uses MPI for its parallel execution model,
which means that each MPI process will call the user subroutine. The difference
between all of these calls is that each user subroutine in each process will
address a different part of the boundary or volume. The portion of the model
for which a user subroutine is responsible is determined by the solver domain
decomposition. It is possible that some processors may not be responsible for
any portion of the model.
In many workflows the user subroutine in each process can do its
work independently of the other MPI processes. This independence enables the
user subroutines to be used for both serial and parallel processing. If
coordination between processors is required, additional parallel API calls must
be used to ensure that the information is shared appropriately.
The parallel API toolkit provided for user subroutine developers is
loosely based on a subset of the MPI API. It is important that these functions
be called by all user subroutines in all processors. Failure to do so will
result in the program becoming deadlocked, and the simulation will not
progress. This is true even for processors that have not been allocated a
portion of the model to process.
The following parallel functions are available:
- Methods
for computing global sum, min and max.
- A method for broadcasting
information from one processor to all others.
Single values or
arrays of values can be passed to all of these functions. The type of return
values always matches the type of the values passed in as arguments. Parallel
functions are exposed to the user subroutine implementer via the
mpcomm property of the
cfd global object.
Example: User Subroutine Suitable for both Serial and Parallel
Execution Scenarios
This example illustrates a user subroutine that does a
global sum to compute a total heat source. This user subroutine is suitable for
both serial and parallel execution scenarios.
volume = support.getScalar('EVOL', CenteringType.ELEMENT);
var vtotal = 0.0;
for (var i = 0; i < volume.length; i++) {
vtotal += volume[i];
}
vtotal = cfd.mpcomm.globalSum(vtotal);
this.values[0] = vtotal*200.0;
Caching Information
Some of the information computed by a user subroutine is useful to
subsequent calls. JavaScript's ability to add information dynamically to the
Field class instance itself is useful in this
regard. New properties can be added using the
this reference that will persist from call to
call. Testing to see whether or not the property has been added can be done
using the method
hasOwnProperty with the custom property name
passed as the argument.
Example: Caching Technique
This caching technique can be used to reduce the overhead execution
time in the parabolic inlet velocity example:
if (!this.hasOwnProperty('coord')) {
this.coord = support.getVector('COORD', CenteringType.FACE);
}
for (var i = 0; i < this.values.length; i++) {
this.values[i] = 1.0 - this.coord[i][1]*this.coord[i][1];
}
API Details
Table 1. Properties and Methods Available in the API
Class Name
|
Property
|
Type
|
Description
|
SMACfd
|
mpcomm
|
Mpcomm
|
Multi-processor communication object
|
Mpcomm
|
rank
|
integer
|
MPI rank
|
size
|
integer
|
Number of MPI processes
|
Field
|
name
|
string
|
Name of the field type; for example, TEMP
|
values
|
vector of doubles
|
Field values. In general the length of
values equals to the number of elements in
the selected support. One exception is the total heat power, where the length
of
values is one; that is, the field value is
assigned to
this.values[0].
|
Boundary
|
surface
|
Surface
|
Surface used to define the boundary
|
conditions
|
vector of Condition objects
|
Condition objects applied at the boundary
|
Zone
|
elset
|
ElementSet
|
Element set object
|
conditions
|
vector of Condition objects
|
Condition object applied on the domain
|
Surface
|
name
|
string
|
Surface name
|
elements
|
vector of integers
|
User element labels
|
faces
|
vector of integers
|
Face identifiers
|
ElementSet
|
name
|
string
|
Element set name
|
elements
|
vector of integers
|
User element labels
|
Condition
|
field
|
Field
|
Field object applied by the condition
|
TimeParameters
|
dt
|
double
|
Current time increment
|
time
|
double
|
Current simulation time
|
totalTime
|
double
|
Total time for the simulation
|
increment
|
integer
|
Increment number
|
maxIncrement
|
integer
|
Maximum increment count
|
Expression
|
definition
|
string
|
Definition of the expression
|
Table 2. Methods on the Available Classes
Class Name
|
Method
|
Return Type
|
Description
|
Boundary
|
getScalar(string field, CenteringType
centeringType)
|
vector of doubles
|
Get scalar field values
|
getVector(string field, CenteringType
centeringType)
|
vector of double[3]
|
Get vector field values
|
getLocalCoordinates()
|
vector of double[3]
|
Get facet centroid coordinates in local
axis system (Cartesian: X, Y, Z. Cylindrical: ,
,
Z). Returns global coordinates if the field is defined in global axis system.
|
Zone
|
getScalar(string field, CenteringType
centeringType)
|
vector of doubles
|
Get scalar field values
|
getVector(string field, CenteringType
centeringType)
|
vector of double[3]
|
Get vector field values
|
getLocalCoordinates()
|
vector of double[3]
|
Get element centroid coordinates in local
axis system (Cartesian: X, Y, Z. Cylindrical: ,
,
Z). Returns global coordinates if the field is defined in global axis system.
|
Mpcomm
|
globalSum(integer | double | Array)
|
Same as input
|
Compute a global sum across all processors
|
globalMin(integer | double | Array)
|
Same as input
|
Compute a global minimum across all
processors
|
globalMax(integer | double | Array)
|
Same as input
|
Compute a global maximum across all
processors
|
broadcast(integer | double | Array, integer
rank)
|
Same as first argument
|
Broadcast information in first argument
from specified rank
|
Expression
|
evaluate(Boundary | Zone support)
|
vector of doubles or vector of double[3]
|
Compute the result of expression evaluated on support.
|
apply(Boundary | Zone support, Array output)
|
void
|
Fill output with result of expression evaluated on support. output must have the correct length when passed into method.
|
Table 3. Flow Solver State Information That Can Be Queried
Class Name
|
Method
|
Centering
|
Variable
|
Description
|
Boundary |
getScalar
|
FACE |
TEMP
|
Temperature at boundary facet
|
PRESSUREGAU
|
Gauge pressure at boundary facet
|
getVector |
COORD
|
Facet centroid coordinates in global axis
system
|
V
|
Velocity at boundary facet
|
Normal |
Face normal vector |
AREA |
Facet area |
Mesh Motion
Boundary |
getVector |
NODE |
Normal |
Face normal vector |
Zone
|
getScalar
|
ELEMENT
|
DENSITY
|
Density at element centroid
|
PRESSUREGAU
|
Gauge pressure at element centroid
|
TEMP
|
Temperature at element centroid
|
EVOL
|
Volume at element centroid
|
VISCOSITY
|
Viscosity at element centroid
|
getVector
|
COORD
|
Element centroid coordinates in global axis
system
|
V
|
Velocity at element centroid
|