D3PLOT JavaScript API reference manual |
The following pages describe the functions ("methods") available in the D3PLOT JavaScript interface.
Functions in the global scope are presented first, followed by Classes shared with other Oasys LS-DYNA Environment programs.
For information about how to run JavaScript files see the JavaScript Interface section of the D3PLOT manual.
All scripts and script runs are independent
JavaScript variables and "current" settings are not "remembered" in any way across successive executions of scripts. Each script, including a second and subsequent execution of the same compiled script, is wholly independent; and default current model, window and other values are reset every time a script is executed.
Argument types
JavaScript is a very weakly typed language in which data can be thought of as "numbers", "strings", "arrays", "objects" and so on. However when describing a function it is useful to be more precise about the sort of argument expected or returned, and the following descriptions are used below.
Integer A "whole number" which would qualify as an integer in languages such as Fortran or C. Real A "floating point number". JavaScript does all floating point arithmetic in double precision, so there is no concept of a single precision "float". Boolean Either JS_TRUE or JS_FALSE. Typically this is the success/failure status outcome of function calls that do not return a data value. It does not translate to a "number", and should only be used for logical tests. String A string of one or more characters in "..". For example "this is a string". <type> Array An array of values of <type>. JavaScript does not assign types to arrays, and their subscripts may be of mixed type, however in the context of this API arrays will be of a single type, eg integer, which will be specified. Object JavaScript objects may be of any type, and members may be added at will. In the context of this API they will be classes, and the class members will be specified. Constant Will be a capitalised constant value from a defined list (eg DX, SOLID, etc), effectively an integer. A common error is to pass an argument that is a String to a function below that expects a "number" of some sort, typically when data has been read from an external file and processed using string manipulation functions. This will generate a "wrong type of argument" error when the function executes.
The solution is to use one of the JavaScript conversion functions Number(xxx), ParseInt(xxx) or ParseFloat(xxx) to convert the string to a number.
Compulsory and optional arguments
Many functions have optional arguments. These will always be the trailing arguments and they will be written have '(optional)' written after them in the function description. For example:
DemoFunction(arg_1, arg_2, arg_3 (optional), arg_4 (optional))
<arg_1> and <arg_2> are compulsory.
<arg_3> and <arg_4> are optional and may be omitted.However if an optional argument is to be specified then any other optional arguments that precede must also be supplied. For example in the example above if <arg_4> is to be specified then <arg_3> must also be supplied even if it has the default value of zero.
Unless specified otherwise below a value of zero can be used for any optional argument that has to be supplied but is to be ignored.
Return values
All functions in this API return a value, although you are free to ignore this. As a general rule those that "GetXxxx" something return an integer, double, array or object as a result, and others return the boolean value JS_TRUE or JS_FALSE to denote success or failure. Each function's return type is documented below, and will be one of:
Boolean JS_TRUE or JS_FALSE Integer An integer value Real A floating point value Array An array of values, usually all of type Integer or Real Object A JavaScript "object" that is a structure with members defined by name. Execution errors and warnings
Errors and warnings from the JavaScript "engine" itself are sent to <stdout>. If a script fails to compile, or generates errors during execution, you should examine the controlling terminal window, or the log file if output has been piped to that.
Errors and warnings during execution of the functions described below will result in messages being sent both to the D3PLOT dialogue window and to <stdout>. Most such errors will result in termination of the script execution with error status, although there are a few cases where "harmless" errors, meaning unlikely to corrupt anything, will generate warnings and execution will continue.
Adapting programming style to improve memory efficiency
It is perfectly possible to import all the data from one or more states into the JavaScript arena and to process it there, however you should be aware that data storage in JavaScript is quite "bloated" (for example all scalar values are stored as 8 byte words) and that you may hit memory problems if you try to import too much data. It is possible to increase the size of the JavaScript arena, but this still has to be allocated from the machine's storage heap and you may ultimately hit the memory limits of your machine.
JavaScript also allows you to create and extend arrays and objects at will, making it very tempting to write scripts that exploit this flexibility. This is fine so long as you don't attempt to store too much in this way, since it is a very wasteful of memory, but if you start using "create and extend" for large quantities of data you will find that you run out of memory quite quickly.
With this problem in mind this API has been written in a way that will - hopefully - encourage you to keep your bulk data storage inside D3PLOT proper, and only to import data as and when it is required for processing. This will result in faster execution and fewer memory-related problems.
User Defined Binary Components
As an incentive to use memory efficiently an unlimited number of "User Defined Binary Components" (referred to as UBIN) may be created. These are very similar to the existing D3PLOT user-defined components, and are processed in much the same way. They have the following attributes:
- UBIN components are created from the JavaScript by CreateUbinComponent().
- Each component must be assigned to one of the categories: SOSH (Solid and Shell), BEAM or NODE.
- Each component must be one of the types SCALAR, VECTOR or TENSOR.
- Each UBIN component has its values supplied via JavaScript "PutUbinData()" functions.
- Similarly the data may be re-imported into the JavaScript via "GetUbinData()" calls.
If the D3PLOT session contains more than one model remember that UBIN components are "programme wide". This means that if you create a UBIN component in a single model that data component "slot" will exist in all models, but only models (and nodes or elements within them) for which values have been "put" will have values defined, and a subsequent "get" on anything else will return a value of zero. Therefore if you wish to populate the UBIN component for multiple models it will be necessary for your JavaScript to loop over them. Expressed as pseudo-code you will need to write something like this:
Create UBIN component
For each model to be considered
{
Make this model current
Create and "put" data for nodes and/or elements
}D3PLOT manages UBIN data in such a way that memory consumption is minimised, writing it out to (binary ".ubd") disk files if necessary and re-importing it from these files if required. This process is transparent to both the interactive user and the JavaScript programmer, and means that the amount of data that is created and stored in this way is limited only by the disk space available on the machine.
UBIN data is also saved to file when no longer needed, or when D3PLOT exits, meaning that it is saved as an additional dataset and thus is automatically available during subsequent processing of a model.
The "current" model and state for data manipulation routines
It is usually the case that you will be processing data for a single model and state at a time, and to save the need to specify these arguments to every data processing routine the data "put" and "get" routines in this interface operate by default on a "current state" in a "current model". The data "put" and "get" routines have an optional argument to specify a state different to the current one if required. Each model has its own, independent current state, and setting this will only affect the current model.
(Functions that process windows, or "by window" (eg cut-sections) use the window's model and state rather than this "current" one. Be careful of this distinction.)
Ordering data processing for efficiency: changing state number is expensive...
When you wish to process data over a range of states, for example to find a data envelope over time, you should bear in mind that because of the way storage is managed changing states is an expensive operation. Therefore it is better to perform all the processing in state A, then repeat for state B, and so on; rather that looping over all states separately for each item. For example:
GOOD: state loop is the outer one
BAD: state loop is the inner one. for(istate=1; istate<=max_states; istate++)
{for(i=1; i<=n_items; i++)
{
for(i=1; i<=n_items; i++)
{
for(istate=1; istate<=max_states; istate++)
{<get data>
<process it><get data>
<process it>Clearly there is also a trade-off to be made between local storage in the JavaScript in the "good" example on the left versus slower speed in the "bad" one on the right.
... but the Direct Disk Access (dda) flag can be used if necessary
Routine GetData() normally works on the assumption that you will want to read data for many items from the current state, therefore if the requested data is not currently in core it will read the complete data vector for all items of that type from disk into memory for that state, most likely re-using the memory used for the same data vector in a previous state. This is efficient since all subsequent data reads for items in that state can be processed directly from memory without any further disk access, and it explains why changing states is a potentially expensive operation.
However the situation may arise where you want to read data for only a few items over a wide range of states, possibly in a random order, and the overhead of reading the complete data block for all items is prohibitively costly. In addition hopping back and forth between states might result in "churning" as data is read, discarded and reread repeatedly, making a bad situation worse.
The <dda> argument to GetData() will, if set to ON, change this behaviour so that complete data vectors are not read into memory, and instead data for the requested item only is read directly from disk, and then forgotten. This is an efficient solution when only a few items are being read from a large model over a range of states, but it will become progressively slower as more and more items are read, since each will require an explicit read from disk.
Clearly if data for enough items is read directly there will come a point where it is better to revert to the default behaviour. It is not possible to give guidance about where this point will lie since it will be a function of model size, number of states, computer memory capacity and speed of disk access; if you are writing a script that "hops about" states in a model you will have to experiment in order to find the best solution for your application. It is recommended that you try the default behaviour first (<dda> undefined or OFF), and only try setting it if the speed of your script is unacceptably slow.
Locking and unlocking data in states against reuse ("scavenging")
When dealing with large models it is almost always the case that the amount of data to be processed far exceeds the amount of memory available in the computer, making it impossible to store everything of interest in memory at the same time; it is also the case that the larger a process becomes the more slowly it tends to run.
Therefore D3PLOT has a strategy for minimising internal memory consumption, and it reuses memory allocated previously when it believes that it is no longer needed in a process called "scavenging". As a general rule it assumes that data in the current state is "wanted", but that data in any other states is fair game for scavenging, meaning that Javascripts which wish to perform repeated "gets" and/or "puts"of data in more than one state within a loop may suffer from memory "churning" in which data vectors are repeatedly allocated, reused and reread.
The current state, or indeed the state in which data is being "put" or "got" if the <state_id> argument is used in these functions, is known to be "wanted" so it is implicitly "locked" against memory scavenging. However it is possible to "lock" any state explicitly using the LockState() function, which tells the memory manager that such states are not to be considered when scavenging data.
The result of locking states is that when a request for memory to store data is made the memory manager may find that there is no memory eligible for reuse, in which case it will allocate more from the the operating system. This will increase the size of the D3PLOT process, and if taken to extremes will eventually exhaust the memory available on the computer and cause the JavaScript to fail or indeed D3PLOT to crash. Therefore you should only lock states when necessary, and you should unlock them again once the data they contain no longer needs to be available for immediate use.
States remain locked until:
- You unlock them explicitly with UnlockState()
- You use SetCurrentState() to make a new state current. This automatically unlocks all states except the new current one.
- You exit the JavaScript and return to normal (interactive or batch) D3PLOT usage.
Internal "item" numbers and external labels
Inside D3PLOT all nodes, elements, parts, etc are dealt with by their internal "item number" which is a sequence starting from 1 with no gaps, and external labels are used only when displaying data for the user; likewise element topology lists and lists of elements at nodes all refer to internal item numbers. The reason for this is obvious: finding data for an item number is a direct lookup, whereas an external label may be non-sequential and require a search to find its internal equivalent.
All the functions below which process data for explicit items take a pair of arguments (written as <type/item> for convenience):
Type code One of the item type constants NODE, SOLID, etc Item number If +ve this is treated as an internal item number starting at 1
If -ve this is treated as an external label id.Clearly it is far more efficient to use internal indices rather than labels, since the latter require a search to resolve them, however the option is available if required. When presenting data to the user, or writing it to file, the "GetLabel()" function can be used to return the external label of an internal <type/item> pair.
Ordering of data in vector and tensor arrays
Where data is transferred in arrays the following data order is used.
Data type Array length Data order Data vector (eg force vector, direction vector)
Coordinate (eg origin, centroid)array[3] [X, Y, Z] Data tensor (eg element stresses) array[6] [XX, YY, ZZ, XY, YZ, ZX] To make this easier, and especially to avoid any ordering errors for tensor data, this API has the following constants defined:
- X, Y, Z for vectors
- XX, YY, ZZ, XY, YZ, ZX for tensors.
(Tensors are symmetric, so constants YX (== XY), ZY (== YZ), XZ (== ZX) are also defined, it doesn't matter which are used.)These are intended to be used for array subscripts to make coding clearer. For example in the following table both columns mean the same thing and are equally valid, but it is recommended that you use the left hand column's syntax as it is both clearer and less error-prone.
Using constants ... is much
clearer than ...Using numbers fx = a[X];
fy = a[Y];
fz = a[Z];fx = a[0];
fy = a[1];
fz = a[2];sxx = b[XX];
sxy = b[XY];
szx = b[ZX];sxx = b[0];
sxy = b[3];
szx = b[5];
Testing for the presence of a given data component
One problem when post-processing data is that you cannot assume that a given data component will be present in a model database, as most output is switchable. The GetData() function will return values of zero for components that are not present, but will not issue any warning messages in the process. So in order to write robust scripts that will work with databases of unknown origin it is wise to use QueryDataPresent() to interrogate the database before attempting to extract data from components known to be optional.
Special considerations when working with adaptively remeshed analyses
When working with adaptively remeshed analyses you should bear in mind that each file family will almost certainly have a different number of nodes and elements, and that it is therefore extremely important to ensure that the item indices you are extracting are valid for the current state. To be on the safe side it is best to obtain the "number of" items every time you change state numbers.
In addition there is no guarantee that node or element <i> in family #1 will be the same in family #2. Exercise great care when extracting data from multiple families!
The "current frame" in windows
Windows in D3PLOT display results at a particular time in an analysis, each such time being a "frame", and animation displays the sequence of "frames". By default the times shown are those of each results state, meaning that "frame id" is equal to "state id". However this is not always the case, consider the following:
- The user has chosen to interpolate results by time, with the result that "frames" lie at fixed time intervals which no longer equate to states.
- The user is post-processing an eigenvalue analysis in which each "state" is in fact a given modeshape. In this case animation cycles a modeshape through the phase angles from -180 to +180 degrees in steps, with each phase angle step being a "frame".
Admittedly the two cases above are only rarely used, but the distinction between "frames" and "states" is important in these situations.
This API provides the following routines to deal with frames in window:
GetWindowMaxFrame(window_id) Returns the highest frame in <window_id> SetWindowFrame(window_id, frame_number) Displays frame <frame_number> in the specified window(s) GetWindowFrame(window_id) Returns the current frame of <window_id> Note that the "current frame" of a given window is purely an attribute of that window, and has no connection with the current model and state of this API as described above.
Models in Windows
D3PLOT requires an active window to contain at least one model, although it is possible to display any number of further models in the window. The following routines process and provide information about models in windows.
GetWindowModels(window_id) Returns an object containing information about the model(s) in <window_id> Processing windows containing multiple models can become difficult since each model's data at a given "frame" may contain results at a different time, and the attributes of each model may be different - for example not all models may contain a given data component.
Functions in this API which manipulate model data in windows may have an optional <model_id> argument to specify which model in a window is being processed. If this is omitted then the first model, as returned by function GetWindowModels(), will be used.
They may also have an optional <state_id> argument to specify the state number to be used. If this is omitted the model's state at the current "frame" in that window will be used.
It will be clear that processing data in windows containing multiple models is best avoided - please see "Recommended window setup when using this API" for suggestions about how to avoid these problems.
It will be clear from the above that departing from defaults of "one model per window" and "one frame per state" makes JavaScript programming more difficult since the programmer has to add extra arguments to function calls to make sure that the correct data is being processed. Therefore it is strongly recommended that when scripts manipulate windows, or process data on a "per-window" basis (eg cut-sections), the following limitations should be adhered to: