Application Programming Guide
Application Programming Guide
Contact Information
Visit the Contact COMSOL page at www.comsol.com/contact to submit general inquiries or
search for an address and phone number. You can also visit the Worldwide Sales Offices page at
www.comsol.com/contact/offices for address and contact information.
If you need to contact Support, an online request form is located on the COMSOL Access page
at www.comsol.com/support/case. Other useful links include:
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Syntax Primer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Data Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Declarations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Built-in Elementary Math Functions. . . . . . . . . . . . . . . . . . . . . 15
Control Flow Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Important Programming Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Ctrl+Space for Code Completion . . . . . . . . . . . . . . . . . . . . . 19
Recording Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Methods Called from the Model Builder . . . . . . . . . . . . . . . . 24
Global Methods, Form Methods, and Local Methods . . . . . 24
Method Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Introduction to the Model Object . . . . . . . . . . . . . . . . . . . . . . . . 26
Model Object Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Creating a Model Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Creating Model Components and Model Object Nodes . . 29
Get and Set Methods for Accessing Properties . . . . . . . . . . 29
Parameters and Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Unary and Binary Operators in the Model Object . . . . . . . . 37
Geometry. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Mesh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Physics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Material. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Study. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
|3
Multiphysics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Working with Model Objects . . . . . . . . . . . . . . . . . . . . . . . . . 51
The Model Object Class Structure. . . . . . . . . . . . . . . . . . . . . 53
The Application Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Shortcuts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Accessing the Application Object. . . . . . . . . . . . . . . . . . . . . . 57
The Name of User Interface Components . . . . . . . . . . . . . . 57
Important Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Get and Set Methods for the Color of a Form Object . . . . 58
General Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
The Main Application Methods. . . . . . . . . . . . . . . . . . . . . . . . 61
Main Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Form Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Item . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Data Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
AppEvent Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Method Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Form, Form Object, and Item List Methods . . . . . . . . . . . . . 94
The Built-in Method Library for the Application Builder. . . . . . 96
Model Utility Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
License Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
File Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Operating System Methods. . . . . . . . . . . . . . . . . . . . . . . . . . 108
Email Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Email Class Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
GUI-Related Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
GUI Command Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
4|
Debug Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Methods for External C Libraries . . . . . . . . . . . . . . . . . . . . . 129
Progress Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .131
Date and Time Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . .137
Conversion Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .140
Array Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
String Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Collection Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Model Builder Methods for Use in Add-Ins. . . . . . . . . . . . . 154
Programming Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Running the Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Visualization Without Solution Data: Grid Datasets. . . . . . 156
Visualization of Points, Curves, and Surfaces . . . . . . . . . . . .158
Reading and Writing Data to File . . . . . . . . . . . . . . . . . . . . . 168
Converting Interpolation Curve Data. . . . . . . . . . . . . . . . . . 191
Plotting Points on a Parametric Surface . . . . . . . . . . . . . . . . 193
Using Selections for Editing Geometry Objects . . . . . . . . .194
Recursion and Recursively Defined Geometry Objects. . . 199
Mesh Information and Statistics. . . . . . . . . . . . . . . . . . . . . . . 203
Accessing Higher-Order Finite Element Nodes . . . . . . . . .204
Accessing System Matrices and Vectors. . . . . . . . . . . . . . . . 206
Data Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Using Selections in Add-Ins . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Using Built-In Methods from an External Java Library. . . . . 229
Measuring the Java Heap Space Memory. . . . . . . . . . . . . . .230
Time-Limited and Hardware-Locked Applications . . . . . . . 230
|5
6|
Introduction
This book is a guide to writing code for COMSOL® models and applications
using the Method Editor. The Method Editor is an important part of the
Application Builder and is available in the COMSOL Desktop® environment in
the Windows® version of COMSOL Multiphysics. For an introduction to using
the Application Builder and its Form editor and Method Editor, see the book
Introduction to Application Builder.
Writing a method is needed when an action is not already available in the standard
run commands associated with functionality in the model tree nodes of the Model
Builder. A method may, for example, contain loops, process inputs and outputs,
and send messages and alerts to the user of the application.
In the Model Builder, the model tree is a graphical representation of the data
structure that represents a model. This data structure is called the model object
and stores the state of the underlying COMSOL Multiphysics model that is
embedded in an application.
The contents of the application tree in the Application Builder is accessed through
the application object, which is an important part of the model object. You can
write code using the Method Editor to directly access and change the user
interface of a running application, for example, to update button text, icons,
colors, and fonts.
In the COMSOL Multiphysics environment, you use the Java® programming
language to write methods, which means that you can utilize the extensive
collection of Java® libraries. In addition to the Java® libraries, the Application
Builder includes a built-in library for building applications and modifying the
model object. A number of tools and resources are available to help you
automatically create code for methods. For more information on autogeneration
of code, see the book Introduction to Application Builder.
This book assumes no prior knowledge of the Java® programming language.
However, some familiarity with a programming language is helpful.
|7
Syntax Primer
If you are not familiar with the Java® programming language, read this section to
quickly get up to speed with its syntax. When creating applications, it is useful to
know the basics of Java such as how to use the if, for, and while control
statements. The more advanced aspects of Java will not be covered in this book.
For more detail, see any dedicated book on Java programming or one of the many
online resources. You can also learn a lot by reviewing the methods in the example
applications available in the Application Libraries.
Data Types
Other data types such as strings are classes, which are also referred to as composite
data types.
In methods, you can use any5 of the primitive or composite data types available in
Java and the Java libraries. Many of the Application Builder built-in methods make
use of primitive or composite data types. For example, the timeStamp() method
provides a long integer as its output.
8|
int i=5; // initialize i and assign the value 5
double d=5.0; // initialize d and assign the value 5.0
boolean b=true; // initialize b and assign the value true
The constants 5, 5.0, and true are literals. Java distinguishes between the literals
5 and 5.0, where 5 is an integer and 5.0 is a double (or float).
However, the opposite will not work automatically (you will get a compilation
error). Instead you can use explicit type casting as follows:
|9
int i; // initialize i
double d; //initialize d
d=41.0;
i=(int) d; // the double d is assigned to the integer i and i is 41
You can convert between integers and doubles within arithmetic statements in
various ways, however you will need to keep track of when the automatic type
conversions are made. For example:
int i; // initialize i
double d; //initialize d
i=41;
d=14/i; // d is 0
In the last line, 14 is seen as an integer literal and the automatic conversion to a
double is happening after the integer division 14/41, which results in 0.
Compare with:
int i; // initialize i
double d; //initialize d
i=41;
d=14.0/i; // d is 0.3414...
In the last line, 14.0 is seen as a double literal and the automatic conversion to a
double is happening before the division and is equivalent to 14.0/41.0.
You can take charge over the type conversions with explicit casting by using the
syntax (int) or (double):
int i; // initialize i
double d,e; //initialize d and e
i=41;
d=((int) 14.0)/i; // d is 0
e=14/((double) i); // e is 0.3414...
When declaring a string variable, the first letter of the data type is capitalized. This
is a convention for composite data types (or object-oriented classes).
After you have declared a string variable, a number of methods are automatically
made available that can operate on the string in various ways. Two such methods
are concat and equals as described below, but there are many more methods
available in the String class. See the online Java documentation for more
information.
Concatenating Strings
To concatenate strings, you can use the method concat as follows:
10 |
String a = "string A";
String b = " and string B";
a.concat(b);
Comparing Strings
Comparing string values in Java is done with the equals method and not with the
== operator. This is due to the fact that the == operator compares whether the
strings are the same when viewed as class objects and does not consider their
values. The code below demonstrates string comparisons:
boolean streq = false;
String a = "string A";
String b = "string B";
streq = a.equals(b);
// In this case streq == false
streq = (a == b);
// In this case streq == false
b = "string A";
streq = a.equals(b);
// In this case streq == true
Special Characters
If you would like to store, for example, a double quotation mark or a new line
character in a string you need to use special character syntax preceded by a
backslash (\). The table below summarizes some of the most important special
characters.
| 11
SPECIAL CHARACTER DESCRIPTION
\\ Backslash
\t Tab
\b Backspace
\r Carriage return
\f Form feed
\n Newline
Note that in Windows the new line character is the composite \r\n whereas in
Linux and macOS \n is used.
The example below shows how to create a string in Windows that you later on
intend to write to file and that consists of several lines.
String contents = "# Created by me\r\n"
+"# Version 1.0 of this file format \r\n"
+"# Body follows\r\n"
+"0 1 \r\n"
+"2 3\r\n"
+"4 5\r\n";
The string is here broken up into several lines in the code for readability. However,
the above is equivalent to the following:
String contents = "# Created by me\r\n# Version 1.0 of this file format \r\n#
Body follows\r\n0 1 \r\n2 3\r\n4 5\r\n";
A RRAYS
In the application tree, the Declarations node directly supports 1D and 2D arrays
of type string (String), integer (int), Boolean (boolean), or double (double). A
1D array may be referred to as a vector and a 2D array referred to as a matrix,
provided that the array is rectangular. A nonrectangular array is called jagged or
ragged. In methods, you can define higher-dimensional arrays as well as arrays of
data types other than string, integer, Boolean, or double.
1D Arrays
If you choose not to use the Declarations node to declare an array, then you can
use the following syntax in a method:
double dv[] = new double[12];
12 |
When a double vector has been declared in this way, the value of each element in
the array will be zero.
To access elements in an array you use the following syntax:
double e;
e = dv[3]; // e is 0.0
Arrays are indexed starting from 0. This means that dv[0] is the first element of
the array in the examples above, and dv[11] is the last element.
You can simultaneously declare and initialize the values of an array by using curly
braces:
double dv[] = {4.1, 3.2, 2.93, 1.3, 1.52};
2D Arrays
2D rectangular arrays can be declared as follows:
double dm[][] = new double[2][3];
This corresponds to a matrix of doubles with 2 rows and 3 columns. The row
index comes first.
You can simultaneously declare and initialize a 2D array as follows:
double dm[][] = {{1.32, 2.11, 3.43},{4.14, 5.16, 6.12}};
where the value of, for example, dm[1][0] is 4.14. This array is a matrix since it is
rectangular (it has same number of columns for each row). You can declare a
ragged array as follows:
double dm[][] = {{1.32, 2.11}, {4.14, 5.16, 6.12, 3.43}};
Copying Arrays
For copying arrays, the following code:
for (int i1 = 0; i1 <= 11; i1++) {
for (int i2 = 0; i2 <= 2; i2++) {
input_array[i1][i2] = init_input_array[i1][i2];
}
}
| 13
D e c l a r a ti o n s
Variables defined in the Declarations node in the application tree are directly
available as global variables in a method and need no further declarations.
Variables declared in methods will have local scope unless you specify otherwise.
The Declarations node directly supports integers (int), doubles (double), and
Booleans (boolean). In addition, strings are supported (see “Strings and Java
Objects” on page 10). In the Declarations node, variables can be scalars, 1D arrays,
and 2D arrays.
To simplify referencing form objects as well as menu, ribbon, and toolbar items by
name, you can create shortcuts with a custom name. These names are available in
the Declarations node under Shortcuts. They are directly available in methods along
with the other global variables defined under Declarations. For more information
on shortcuts, see “Shortcuts” on page 55.
14 |
F ORM D ECLARATIONS
Variables can also be defined as Form Declarations under each respective form node
in the application tree.
Form declarations can be of the types Scalar, Array 1D, Array 2D and Choice List.
Global declarations are exposed to all user-interface components of the application
whereas form declarations are only exposed to the form that they are defined in
and the form objects within that form. Form declarations are used to limit the
scope of variables and thereby logically separate the different parts of an
application.
Elementary math functions for use in methods are available in the Java math
library. Some examples:
double a = Math.PI; // the mathematical constant pi
double b = Math.sin(3*a); // trigonometric sine function
double c = Math.cos(4*a); // trigonometric cosine function
double d = Math.random(); // random number uniformly distributed in [0,1)
double e = Math.exp(2*a); // exponential function
double f = Math.log(1+e); // natural base e logarithm
double g = Math.pow(10,3); // power function
double h = Math.log10(2.5); // base 10 logarithm
double k = Math.sqrt(81.0); // square root
There are several more math functions available in the Java math library. For
additional information, see any Java book or online resource.
| 15
Control Flow Statements
Java supports the usual control flow statements if-else, for, and while. You can
use the Language Elements tool to insert template code corresponding to a number
of control flow, of or block, statements.
The following examples illustrate some of the most common uses of control flow
statements.
T HE IF-ELSE S TATEMENT
This is an example of a general if-else statement:
if (a < b) {
alert("Value too small.");
} else {
alert("Value is just right.");
}
Between curly braces {} you can include multiple lines of code, each terminated
with a semicolon. If you only need one line of code, such as in the example above,
this shortened syntax is available:
if (a < b)
alert("Value too small.");
else
alert("Value is just right.");
16 |
T HE F OR S TATEMENT
Java supports several different types of for statements. This example uses the
perhaps most conventional syntax:
// Iterate i from 1 to N:
int N = 10;
for (int i = 1; i <= N; i++) {
// Do something
}
An alternative syntax is shown in the example on page 65 where the loop is over
all form objects in a list of form objects:
for (FormObject formObject : app.form("form1").formObject()) {
if ("Button".equals(formObject.getType())) {
formObject.set("enabled", false);
}
}
where the local iteration variable looped over is formObject of the type, or class,
FormObject. The collection of objects, in this case
app.form("form1").formObject(), can be an array or other types of lists of
objects. Using this syntax, the iteration variable loops over all entries in the
collection, from start to finish. Another example can be found on page 95.
T HE W HILE S TATEMENT
This example shows a while statement.
double t = 0, h = 0.1, tend = 10;
while(t < tend) {
// do something with t
t = t + h;
}
For a more advanced example of a while statement, see “Creating and Removing
Model Tree Nodes” on page 42.
Note that Java also supports do-while statements.
T HE W ITH S TATEMENT
When writing methods in the Method Editor, in addition to the standard Java
control flow statement, there is also an optional with statement that can be used
to make Application Builder code more compact and easier to read (you enable
this in File>Preferences). A simple example is shown below:
// Set the global parameter L to a fixed value
with(model.param());
set("L", "10[cm]");
endwith();
| 17
model.param().set("L", "10[cm]");
In this case using the with statement has limited value since just one parameter is
assigned but for multiple assignments readability increases. See “Parameters and
Variables” on page 35 for an example with multiple assignments.
Note that the with statement is only available when writing code in the Method
Editor. It is not available when using the COMSOL API for use with Java®. You
can turn off the use of with statements in the section for Methods in Preferences.
The method descr returns the variable description for the last parameter or
variable in a with statement:
with(model.param());
set("L", "10[cm]");
String ds = descr("L");
endwith();
Assuming that the parameter description of the parameter L is Length. The string
ds will have the value Length.
E XCEPTION H ANDLING
An exception is an error that occurs at runtime. The Java® programming language
has a sophisticated machinery for handling exceptions and each exception
generates an object of an exception class. The most common way to handle
exceptions is by using try and catch, as in the example below.
double d[][] = new double[2][15];
try {
d = readMatrixFromFile("common:///my_file.txt");
} catch (Exception e) {
error("Cannot find the file my_file.txt.");
}
where an error dialog box is shown in case the file my_file.txt is not found in
the application file folder common. See the Java® documentation for more
information about using try and catch.
18 |
Important Programming Tools
The Application Builder includes several tools for automatically generating code
as well as debugging. These tools include code completion, Record Method, Record
Code, Convert to New Method, Editor Tools, Language Elements, Copy as Code to
Clipboard, Call Stack, and Variables, and are described in the book Introduction to
Application Builder. These utilities allow you to quickly get up and running with
programming tasks even if you are not familiar with the syntax.
The following sections describes two of the most important tools: code
completion using Ctrl+Space and Record Code. Using these tools will make you
more productive, for example, by allowing you to copy-paste or autogenerate
blocks of code.
While typing code in the Method Editor, the Application Builder can provide
suggestions for code completions. The list of possible completions are shown in a
separate completion list that opens while typing. In some situations, detailed
information appears in a separate window when an entry is selected in the list.
Code completion can always be requested with the keyboard shortcut Ctrl+Space.
Alternatively Ctrl+/ can be used to request code completion, which is useful if
Ctrl+Space is in use by the Windows operating system such as for certain
languages. When accessing parts of the model object, you will get a list of possible
completions, as shown in the figure below:
Select a completion by using the arrow keys to choose an entry in the list and
double-click, or press the Tab or Enter key, to confirm the selection.
If the list is long, you can filter by typing the first few characters of the completion
you are looking for.
| 19
For example, if you enter the first few characters of a variable or method name,
and press Ctrl+Space, the possible completions are shown:
In the example above, only variables that match the string iv are shown. This
example shows that variables local to the method also appear in the completion
suggestions.
You can also use Ctrl+Space to learn about the syntax for the built-in methods that
are not directly related to the model object. Type the name of the command and
use Ctrl+Space to open a window with information on the various calling
signatures available.
Additional information is also available in the form of tool tips that are displayed
when hovering over the different parts of the code.
The Method Editor also supports code completion for properties, including listing
the properties that are available for a given model object feature node, and
providing a list of allowed values that are available for a given property.
The figure below shows an example of code completion for the mesh element size
property, where a list of the allowed values for the predefined element sizes is
presented.
20 |
COMSOL Multiphysics and its add-on modules contain thousands of physics
features that you can learn about by using, for example, Record Code,
Save as>Model File for Java, and code completion. The figure below shows code
completion for a particular feature in the Electric Currents interface.
Recording Code
Click the Record Code button in the Code section of the Method Editor ribbon to
record a sequence of operations that you perform using the model tree, as shown
in the figure below.
Certain operations in the application tree can also be recorded, for example, code
that changes the color of a text label in a running application may be generated.
To record a new method, click the Record Method button in the Main section of
the Home tab.
| 21
While recording code, the COMSOL Desktop windows are surrounded by a red
frame:
22 |
To stop recording code, click one of the Stop Recording buttons in the ribbon of
either the Model Builder or the Application Builder.
By using Data Access, you can set the values of the Heat transfer coefficient and the
External temperature properties of the busbar tutorial model used in the books
Introduction to COMSOL Multiphysics and Introduction to Application
Builder.
To generate similar code using Record Code (Data Access is not used when
recording code), follow these steps:
• Create a simple application based on the busbar model (MPH file).
• In the Model Builder window, in the Developer tab, click Record Method, or
with the Method Editor open, click Record Code.
• Change the value of the Heat transfer coefficient to 5.
• Change the value of the External temperature to 300[K].
• Click Stop Recording.
• If it is not already open, open the method with the recorded code.
| 23
To generate code corresponding to changes to the application object, use Record
Code or Record Method, then go to the Form editor, and, for example, change the
appearance of a form object. The following code corresponds to changing the
color of a text label from the default Inherit to Blue:
app.form("form1").formObject("textlabel1").set("foreground", "blue");
Built-in methods that changes the application object are only available when
running applications and not when running methods from the Model Builder.
Use the tools for recording code to quickly learn how to interact with the model
object or the application object. The autogenerated code shows you the names of
properties, parameters, and variables. Use strings and string-number conversions
to assign new parameter values in model properties. By using Data Access while
recording, you can, for example, extract a parameter value using get, process its
value in a method, and save it back into the model object using set. For more
information on Data Access, see the Introduction to Application Builder.
Methods called from the Model Builder directly modify the model object
represented by the Model Builder in the current session. Using methods in this
way can be used to automate modeling tasks that consist of several manual steps.
For example, in a model with multiple studies, you can record code for the process
of first computing Study 1; then computing Study 2, which may be based on the
solution from Study 1; and so on.
To customize the workflow in the Model Builder you can create an add-in based
on methods by using a Method Call or a Settings Form. For an introductory example
of using methods from the Model Builder and for information on how to create
add-ins, see the Introduction to Application Builder.
There are global methods, form methods, and local methods. Global methods are
displayed in the application tree and are accessible from all methods and form
objects. Form methods are displayed in the application tree as child nodes to the
form it belongs to. A local method is associated with a form object or event and
can be opened from the corresponding Settings window.
Global methods are exposed to all components of the application whereas form
methods are only exposed to the form that they are defined in and the form objects
24 |
within that form. You can use form methods to provide a logical separation of the
different parts of an application.
Me thod Names
A method name has to be a text string without spaces. The string can contain
letters, numbers, and underscores. Java® programming language keywords
cannot be used. The name must not begin by a number (this is also true for the
name of a form object, variable, and method).
A global method cannot have the same name as a form method and vice versa. In
addition, the following names are reserved:
• onActivate
• onClick
• onClose
• onDataChange
• onEvent
• onFocusGained
• onFocusLost
• onLoad
• onPickingChanged
• onStartup
• onShutdown
| 25
Introduction to the Model Object
The model object is the data structure that stores the state of the COMSOL
Multiphysics model. The model object contents are reflected in the COMSOL
Desktop user interface by the structure of the Model Builder and its model tree.
The model object is associated with a large number of methods for setting up and
running sequences of operations such as geometry sequences, mesh sequences,
and study steps. As an alternative to using the Model Builder, you can write
programs in the Method Editor that directly access and change the contents of the
model object.
The model object methods are structured in a tree-like way, similar to the nodes
in the model tree. The top-level methods just return references that support
further methods. At a certain level the methods perform actions, such as adding
data to the model object, performing computations, or returning data.
For a complete list of methods used to edit the model object, see the
Programming Reference Manual. For an introduction to using the Model
Builder, see the book Introduction to COMSOL Multiphysics.
The contents of the application tree in the Application Builder are accessed
through the application object, which is an important part of the model object.
You can write code using the Method Editor to alter, for example, button text,
icons, colors, and fonts in the user interface of a running application.
This section gives an overview of the model object. The section “The Application
Object” on page 55 gives an overview of the application object.
In the model tree and when working with the model object from methods, tags
are used as handles to different parts of the model object. These tags can also be
made visible in the Model Builder by first clicking the Model Builder toolbar
menu Model Tree Node Text and then choosing Tag, as shown in the figure below.
26 |
The figures below show an example of a model tree without tags shown in the left
figure and with tags shown in the right figure.
In code, the tags are referenced using double quotes. For example, in the
following line
model.geom("geom1").create("r1", "Rectangle");
geom1 is a tag for a geometry object and r1 is a tag for a rectangle object. The
following sections contain multiple examples of using tags to create and edit parts
of a model object.
The option Name, available in the Model Tree Node Text menu in the Model Builder
toolbar, represents the name used for scoping. The scope names are used to access
the different parts of the model object. This is important, for example, when
working with global variables for defining the constraints and objective functions
| 27
for an optimization study. In the figure below, the variables mass, freq, and
maxStress are referenced by scope names: comp1.mass, comp1.solid.freq, and
comp1.maxStress.
Using scope names avoids name collisions in cases where there are multiple model
components or multiple physics interfaces with identical variable names.
If you create an application using the Model Builder and the Application Builder,
then a model object model is automatically created the first time you enter the
Model Builder. This is then available as a reserved variable name. When using the
Model Wizard, the creation of the model tag is automatically handled.
You can create additional model objects with calls to the createModel method.
Such additional model objects will not be visible in the Model Builder (only model
is).
For more information on working with several model objects, see the section
“Working with Model Objects” on page 51.
28 |
Creating Model Components and Model Object Nodes
The component is given a definite spatial dimension when you create a geometry
node:
model.geom().create("geom1", 2);
G e t a n d S e t M e t h o d s f o r A c c e s s i n g P r o p e r ti e s
The get and set methods are used to access and assign, respectively, property
values in the different parts of the model object. To assign individual elements of
a vector or matrix, the setIndex method is used. The property values can be of
the basic data types: String, int, double, and boolean, as well as vectors or
matrices of these types (1D or 2D arrays).
| 29
The get, set, and create methods (described in the previous section) are also
accessible from the model tree by right-clicking and selecting Copy as Code to
Clipboard.
T HE GET M ETHODS
The family of get methods is used to retrieve the values of properties. For
example, the getDouble method can be used to retrieve the value of the
predefined element size property hauto for a mesh and store it in a variable hv:
double hv = model.mesh("mesh1").feature("size").getDouble("hauto");
See the section “Example Code” on page 33 below for more information on the
property hauto.
The syntax for the family of get methods for the basic data types is summarized in
the following table:
TYPE SYNTAX
30 |
All arrays are returned as copies of the data; writing to a retrieved array does not
change the data in the model object. To change the contents of an array in the
model object, use one of the methods set or setIndex.
Automatic type conversion is attempted from the property type to the requested
return type.
T HE SET M ETHOD
The syntax for assignment using the set method is exemplified by this line of
code, which sets the title of a plot group pg1:
model.result("pg1").set("title", "Temperature T in Kelvin");
The first argument is a string with the name of the property, in the above example
"title". The second argument is the value and can be a basic type as indicated by
the table below.
TYPE SYNTAX
Using the set method for an object returns the object itself. This allows you to
append multiple calls to set as follows:
model.result("pg1").set("edgecolor", "black").set("edges", "on");
The previous line of code assigns values to both the edgecolor and edges
properties of the plot group pg1 and is equivalent to the two lines:
model.result("pg1").set("edgecolor", "black");
model.result("pg1").set("edges", "on");
| 31
Automatic type conversion is attempted from the input value type to the property
type. For example, consider a model parameter a that is just a decimal number
with no unit. Its value can be set with the statement:
model.param().set("a", "7.54");
where the value "7" is a string. In this case, the following syntax is also valid:
model.param().set("a",7.54);
For the setIndex method in general, use one of these alternatives to set the value
of a single element:
setIndex(String name,String value,int index);
setIndex(String name,String value,int index1,int index2);
The name argument is a string with the name of the property. The value argument
is a string representation of the value. The indices start at 0, for example:
setIndex(name,value,2);
sets the first and second row of the array and are equivalent to the single statement:
set("name",new String[][]{{"1","2","3"},{"4","5"}});
32 |
// String[] properties();
String[] props =
model.component("comp1").physics("ht").feature("solid1").properties();
E XAMPLE C ODE
The following code block can be used to warn an application’s user of excessive
simulation times based on the element size:
if (model.mesh("mesh1").feature("size").getDouble("hauto") <= 3) {
exp_time = "Solution times may be more than 10 minutes for finer element
sizes.";
}
In the above example, getDouble is used to retrieve the value of the property
hauto, which corresponds to the Element Size parameter Predefined in the Settings
window of the Size node under the Mesh node. This setting is available when the
Sequence type is set to User-controlled mesh, in the Settings window of the Mesh
node.
The following line of code retrieves an array of strings corresponding to the
legends of a 1D point graph.
String[] legends =
model.results("pg3").feature("ptgr1").getStringArray("legends");
The figure below shows an example of a vector of legends in the Settings window
of the corresponding Point Graph.
| 33
The following line of code sets the Dataset dset1 for the Plot Group pg1:
model.result("pg1").set("data", "dset1");
The following lines of code set the anisotropic diffusion coefficient for a Poisson’s
equation problem on a block geometry.
model.geom("geom1").create("blk1", "Block");
with(model.geom("geom1").feature("blk1"));
set("size", new String[]{"10", "1", "1"});
endwith();
model.geom("geom1").run();
with(model.physics("c").feature("cfeq1"));
setIndex("c", "-0.1", 0, 1);
setIndex("c", "-0.2", 0, 6);
setIndex("f", "2.5", 0);
endwith();
The code below sets the material link index to the string variable alloy, defined
under the Declarations node.
model.material("matlnk1").set("link", alloy);
The code below sets the coordinates of a cut point dataset cpt1 to the values of
the 1D array samplecoords[].
with(model.result().dataset("cpt1"));
set("pointx", samplecoords[0]);
set("pointy", samplecoords[1]);
set("pointz", samplecoords[2]);
endwith();
The code below sets the title and color legend of a plot group pg2 and then
regenerates the plot.
with(model.result("pg2"));
set("titletype", "auto");
endwith();
with(model.result("pg2").feature("surf1"));
set("colorlegend", "on");
endwith();
model.result("pg2").run();
34 |
Parameters and Variables
This code defines a global parameter L with Expression 0.5[m] and Description
Length:
model.param().set("L", "0.5[m]");
model.param().descr("L", "Length");
You can also use the with syntax to set the Expression and Description for several
parameters, for example:
with(model.param());
set("L", "0.5[m]");
descr("L", "Length");
set("wd", "10[cm]");
descr("wd", "Width");
set("T0", "500[K]");
descr("T0", "Temperature");
endwith();
The evaluation is in this case with respect to the base Unit System defined in the
model tree root node.
| 35
To return the unit of the parameter L, if any, use:
String Lunit=model.param().evaluateUnit("L");
To write the value of a model expression to a global parameter, you typically need
to convert it to a string. The reason is that model expressions may contain units.
Multiply the value of the variable Length with 2 and write the result to the
parameter L including the unit of cm.
Length = 2*Length;
model.param().set("L", toString(Length) + "[cm]");
To return the value of a parameter in a different unit than the base Unit System,
use:
double Length_real = model.param().evaluate("L","cm");
For the case where the parameter is complex valued, the real and imaginary parts
can be returned as a double vector of length 2:
double[] realImag = model.param().evaluateComplex("Ex", "V/m");
For parameters that are numbers without units, you can use a version of the set
method that accepts a double instead of a string. For example, the lines
double a_double = 7.65;
model.param().set("a_param", a_double);
V ARIABLES
The syntax for accessing and assigning variables is similar to that of parameters.
For example, the code:
with(model.variable("var1"));
set("F", "150[N]");
descr("F", "Force");
endwith();
and the following code stores the model expression for the same variable in a string
fs.
String fs = model.variable("var1").get("f");
36 |
Unary and Binary Operators in the Model Object
The table below describes the unary and binary operators that can be used when
accessing a model object, such as the model expressions used when defining
parameters, variables, material properties, and boundary conditions, as well as in
expressions used in results for postprocessing and visualization.
The following example code creates a variable to indicate whether the effective von
Mises stress exceeds 200 MPa by using the inequality solid.mises>200[MPa]:
model.variable().create("var1");
model.variable("var1").model("comp1");
model.variable("var1").set("hi_stress", "solid.mises>200[MPa]");
The same plot can be created by directly using the inequality expression in the
surface plot expression as follows:
with(model.result("pg3").feature("surf1"));
set("expr", "solid.mises > 200[MPa]");
endwith();
model.result("pg3").run();
| 37
Geometry
Once the Geometry node is created (see “Creating Model Components and Model
Object Nodes” on page 29) you can add geometric features to the node. For
example, add a square using default position (0, 0) and default size 1:
model.geom("geom1").create("sq1", "Square");
The first input argument "sq1" to the create method is a tag, a handle, to the
square. The second argument "Square" is the type of geometry object.
Add another square with a different position and size:
model.geom("geom1").create("sq2", "Square");
with(model.geom("geom1").feature("sq2"));
set("pos", new String[]{"0.5", "0.5"});
set("size", "0.9");
endwith();
The with statement in the above example is used to make the code more compact
and, without using with, the code statements above are equivalent to:
model.geom("geom1").feature("sq2").set("pos", new String[]{"0.5", "0.5"});
model.geom("geom1").feature("sq2").set("size", "0.9");
Take the set difference between the first and second square:
model.geom("geom1").create("dif1", "Difference");
with(model.geom("geom1").feature("dif1").selection("input"));
set(new String[]{"sq1"});
endwith();
with(model.geom("geom1").feature("dif1").selection("input2"));
set(new String[]{"sq2"});
endwith();
To build the entire geometry, you call the method run for the Geometry node:
model.geom("geom1").run();
In this way, you have access to the functionality that is available in the geometry
node of the model tree. Use Record Code or any of the other tools for automatic
generation of code to learn more about the syntax and methods for other
geometry operations.
38 |
R EMOVING M ODEL T REE N ODES
You can remove geometry objects using the remove method:
model.geom("geom1").feature().remove("sq2");
Remove a series of geometry objects (circles) with tags c1, c2, ..., c10:
for (int n = 1; n <= 10; n = n+1) {
model.geom("geom1").feature().remove("c"+n);
}
Me sh
The following line adds a Mesh node, with tag mesh1, linked to the geometry with
tag geom1:
model.mesh().create("mesh1", "geom1");
You can control the mesh element size either by a preconfigured set of sizes or by
giving low-level input arguments to the meshing algorithm.
The following line:
model.mesh("mesh1").autoMeshSize(6);
corresponds to a mesh with Element size set to Coarse. The argument to the
method autoMeshSize ranges from 1-9, where 1 is Extremely fine and 9 is
Extremely coarse.
To generate the mesh, you call the run method for the mesh node:
model.mesh("mesh1").run();
| 39
set("hmax", "0.09"); // Maximum element size
set("hmin", "3.0E-3"); // Minimum element size
set("hgrad", "1.2"); // Maximum element growth rate
set("hcurve", "0.35"); // Curvature factor
set("hnarrow", "1.5"); // Resolution of narrow regions
endwith();
model.mesh("mesh1").run();
Note that you can also set local element size properties for individual points,
edges, faces, and domains. Use Record Code or any of the other tools for automatic
generation of code to learn more about the syntax and methods for other mesh
operations.
Physics
40 |
The first input argument to the create method is a physics interface tag that is
used as a handle to this physics interface. The second input argument is the type
of physics interface. The third input argument is the tag of the geometry to which
the physics interface is assigned.
To set a fixed temperature boundary condition on a boundary, you first create a
TemperatureBoundary feature using the following syntax:
model.physics("ht").create("temp1", "TemperatureBoundary", 1);
The first input argument to create is a feature tag that is used as a handle to this
boundary condition. The second input argument is the type of boundary
condition. The third input argument is the spatial dimension for the geometric
entity that this boundary condition should be assigned to. Building on the
previous example of creating a 2D rectangle, the input argument being 1 means
that the dimension of this boundary is 1 (that is, an edge boundary in 2D).
The next step is to define which selection of boundaries this boundary condition
should be assigned to. To assign it to boundary 1 use:
model.physics("ht").feature("temp1").selection().set(new int[]{1});
The following lines of code show how to define a second boundary condition for
a spatially varying temperature, varying linearly with the coordinate y:
model.physics("ht").create("temp2", "TemperatureBoundary", 1);
model.physics("ht").feature("temp2").selection().set(new int[]{4});
model.physics("ht").feature("temp2").set("T0", "(300 + 10[1/m]*y)[K]");
Use Record Code or any of the other tools for automatic generation of code to learn
more about the syntax and methods for other physics interface features and other
physics interfaces.
| 41
C REATING AND R EMOVING M ODEL T REE N ODES
Below is a larger block of code that removes, creates, and accesses physics interface
feature nodes. It uses the Iterator class and methods available in the java.util
package. For more information, see the Java® documentation.
String[] flowrate = column1;
String[] Mw = column2;
java.util.Iterator<PhysicsFeature> iterator =
model.physics("pfl").feature().iterator();
while (iterator.hasNext()) {
if (iterator.next().getType().equals("Inlet"))
iterator.remove();
}
if (flowrate != null) {
for (int i = 0; i < flowrate.length; i++) {
if (flowrate[i].length() > 0) {
if (Mw[i].length() > 0) {
int d = 1 + i;
model.physics("pfl").create("inl" + d, "Inlet");
model.physics("pfl").feature("inl" + d).setIndex("spec", "3", 0);
model.physics("pfl").feature("inl" + d).set("qsccm0", flowrate[i]);
model.physics("pfl").feature("inl" + d).set("Mn", Mw[i]);
model.physics("pfl").feature("inl" + d).selection().set(new int[]{d});
}
}
}
}
The need to remove and create model tree nodes is fundamental when writing
methods because the state of the model object is changing each time a model tree
node is run. In the method above, the number of physics feature nodes are
dynamically changing depending on user inputs. Each time the simulation is run,
old nodes are removed first and then new nodes are added.
M a t e r ia l
You can give the material a name, for example, Aluminum, as follows:
model.material("mat1").label("Aluminum");
42 |
The following lines of code shows how to create a basic material property group
for heat transfer:
with(model.material("mat1").propertyGroup("def"));
set("thermalconductivity", new String[]{"238[W/(m*K)]"});
set("density", new String[]{"2700[kg/m^3]"});
set("heatcapacity", new String[]{"900[J/(kg*K)]"});
endwith();
The built-in property groups have a read-only tag. In the above example, the tag
def represents the property group Basic in the model tree.
The resulting model tree and Material node settings are shown in the figure below.
Note that some physics interfaces do not require a material to be defined. Instead,
the corresponding properties can be accessed directly in the physics interface. This
is also the case if the physics model settings are changed from From material to User
defined. For example, for the Heat Transfer in Solids interface, this setting can be
| 43
found in the Settings window of the subnode Solid, in the sections Heat Conduction,
Solid and Thermodynamics, Solid, as shown in the figure below.
Use Record Code or any of the other tools for automatic generation of code to learn
more about the syntax and methods for materials.
Study
The Study node in the model tree contains one or more study steps, instructions
that are used to set up solvers and solve for the dependent variables. The settings
for the Study and the Solver Configurations nodes can be quite complicated.
Consider the simplest case for which you just need to create a study, add a study
step, and run it.
Building on the example from the previous sections regarding stationary heat
transfer, let’s add a Stationary study step.
model.study().create("std1"); // Study with tag std1
model.study("std1").create("stat", "Stationary");
model.study("std1").run();
The call to the method run automatically generates a solver sequence in a data
structure model.sol and then runs the corresponding solver. The settings for the
solver are automatically configured by the combination of physics interfaces you
have chosen. You can manually change these settings, as shown later in this
section. The data structure model.sol roughly corresponds to the contents of the
Solver Configurations node under the Study node in the model tree.
All low-level solver settings are available in model.sol. The structure
model.study is used as a high-level instruction indicating which settings should
be created in model.sol when a new solver sequence is created.
44 |
For backward compatibility, some of the low-level settings in model.sol are
automatically generated when using Record Code.
The example below shows a somewhat more elaborate case of programming the
study that would be applicable for the stationary heat transfer example shown
earlier. The instructions below more closely resemble the output autogenerated by
using the Record Code option.
First create instances of the Study node (with tag std1) and a Stationary study step
subnode:
model.study().create("std1");
model.study("std1").create("stat", "Stationary");
The actual settings that determine how the study is run are contained in a
sequence of operations in the Solution data structure, with tag sol1, which is
linked to the study:
model.sol().create("sol1");
model.sol("sol1").study("std1");
Next, create a Dependent Variables node, which controls the scaling and initial
values of the dependent variables and determines how to handle variables that are
not solved for:
model.sol("sol1").create("v1", "Variables");
Now create a Stationary Solver node. The Stationary Solver contains the
instructions that are used to solve the system of equations and compute the values
of the dependent variables.
model.sol("sol1").create("s1", "Stationary");
Add subnodes to the Stationary Solver node to choose specific solver types. In this
example, use an Iterative solver:
model.sol("sol1").feature("s1").create("i1", "Iterative");
You can have multiple Solution data structures in a study node (such as sol1, sol2,
and so on) defining different sequences of operations. The process of notifying the
study of which one to use is done by “attaching” the Solution data structure sol1
with study std1:
model.sol("sol1").attach("std1");
| 45
The attachment step determines which Solution data structure sequence of
operations should be run when selecting Compute in the COMSOL Desktop user
interface.
Finally, run the study, which is equivalent to running the Solution data structure
sol1:
model.sol("sol1").runAll();
The resulting Study node structure is shown in the figure below. Note that there
are several additional nodes added automatically. These are default nodes and you
can edit each of these nodes by explicit method calls. You can edit any of the nodes
while using Record Code to see the corresponding methods and syntax used.
46 |
The first line below may not be needed depending on whether the Fully Coupled
subnode has already been generated or not (it could have been automatically
generated by code similar to what was shown above).
model.sol("sol1").feature("s1").create("fc1", "FullyCoupled");
with(model.sol("sol1").feature("s1").feature("fc1"));
set("dtech", "auto"); // The Nonlinear method (Newton solver)
set("initstep", "0.01"); // Initial damping factor
set("minstep", "1.0E-6"); // Minimum damping factor
set("rstep", "10"); // Restriction for step-sized update
set("useminsteprecovery", "auto"); // Use recovery damping factor
set("minsteprecovery", "0.75"); // Recovery damping factor
set("ntermauto", "tol"); // Termination technique
set("maxiter", "50"); // Maximum number of iterations
set("ntolfact", "1"); // Tolerance factor
set("termonres", "auto"); // Termination criterion
set("reserrfact", "1000"); // Residual factor
endwith();
For more information on the meaning of these and other low-level solver settings,
see the Solver section of the Programming Reference Manual.
Changing the low-level solver settings requires that model.sol has first been
created. It is always created the first time you compute a study, however, you can
trigger the automatic generation of model.sol as follows:
model.study().create("std1");
model.study("std1").create("stat", "Stationary");
model.study("std1").showAutoSequences("sol");
| 47
else {
solutionState = "solutionexists";
}
Almost all of the example applications in the Application Libraries use this
technique.
Results
The Results node contains nodes for Datasets, Derived Values, Tables, Plot Groups,
Export, and Reports. As soon as a solution is obtained, a set of Plot Group nodes
are automatically created. In the example of Heat Transfer in Solids, when setting
up such an analysis in the Model Builder, two Plot Group nodes are added
automatically. The first one is a Surface plot of the Temperature and the second one
is a Contour plot showing the isothermal contours. Below you will see how to set
up the corresponding plots manually.
First create a 2D plot group with tag pg1:
model.result().create("pg1", "PlotGroup2D");
Create a Surface plot for pg1 with settings for the color table used, the
intra-element interpolation scheme, and the dataset referring to the parent of the
Surface plot node, which is the pg1 node:
model.result("pg1").feature().create("surf1", "Surface");
model.result("pg1").feature("surf1").label("Surface");
with(model.result("pg1").feature("surf1"));
set("colortable", "ThermalLight");
set("smooth", "internal");
set("data", "parent");
endwith();
Now create a second 2D plot group with contours for the isotherms:
model.result().create("pg2", "PlotGroup2D");
model.result("pg2").label("Isothermal Contours (ht)");
with(model.result("pg2"));
48 |
set("data", "dset1");
endwith();
model.result("pg2").feature().create("con1", "Contour");
model.result("pg2").feature("con1").label("Contour");
with(model.result("pg2").feature("con1"));
set("colortable", "ThermalLight");
set("smooth", "internal");
set("data", "parent");
endwith();
To find the maximum temperature, add a Surface Maximum subnode to the Derived
Values node as follows:
First create the Surface Maximum node with tag max1:
model.result().numerical().create("max1", "MaxSurface");
Note that in this context the method corresponding to the Derived Values node is
called numerical.
Next, specify the selection. In this case there is only one domain 1:
model.result().numerical("max1").selection().set(new int[]{1});
Create a Table node to hold the numerical result and write the output from max1
to the Table:
model.result().table().create("tbl1", "Table");
model.result().table("tbl1").comments("Surface Maximum 1 {max1} (T)");
model.result().numerical("max1").set("table", "tbl1");
model.result().numerical("max1").setResult();
Use Record Code or any of the other tools for automatic generation of code to learn
more about the syntax and methods for Results.
| 49
Loading Data to Tables
By using the loadFile method you can import data into a table and then display
it using a results table form object or a table surface plot. The following example
demonstrates loading data from an Excel file into a table and visualizing the
contents using a table surface plot. The file in this example is assumed to be
imported, in an application, using a file import form object with a file declaration
file1 as the File Destination.
model.result().table("tbl1").loadFile("upload:///file1", "", cells);
/*
The string variable cells contains the spreadsheet selection to be
imported, for example A1:J7.
The following code creates a plot group pg1 with a table surface plot.
This code is not needed if the embedded model already contains a table
and a table surface plot.
*/
model.result().create("pg1", 2);
model.result("pg1").create("tbls1", "TableSurface");
with(model.result("pg1").feature("tbls1"));
set("table", "tbl1");
endwith();
with(model.result("pg1").feature("tbls1"));
set("dataformat", "cells");
endwith();
model.result("pg1").feature("tbls1").create("hght1", "TableHeight");
with(model.result("pg1").feature("tbls1").feature("hght1"));
set("view", "view3");
endwith();
with(model.view("view3").camera());
set("viewscaletype", "manual");
set("xscale", "1");
set("yscale", "1");
set("zscale", "1");
endwith();
// The following line is needed to update the plot
model.result("pg1").run();
Multiphysics
50 |
Currents interface and a Heat Transfer in Solids interface with an additional
Electromagnetic Heat Source node under the Multiphysics node. The following lines
of code illustrate the corresponding method calls.
model.physics().create("ec", "ConductiveMedia", "geom1");
model.physics().create("ht", "HeatTransfer", "geom1");
model.multiphysics().create("emh1", "ElectromagneticHeatSource", "geom1",
2);
model.multiphysics("emh1").selection().all();
with(model.multiphysics("emh1"));
set("EMHeat_physics", "ec");
set("Heat_physics", "ht");
endwith();
When using the Model Builder to set up a Joule Heating analysis, nodes in addition
to those shown above will be created corresponding to Joule heating in thin shells,
should they exist in the model, and temperature couplings if there are multiple
field variables for electric potential and temperature.
When using the Model Builder in the COMSOL Desktop interface, an embedded
model with variable name model is automatically created. The embedded model
has a special status. For example, the automatic code generation tools only
consider the embedded model. In addition, when you save to or load from an
MPH file, only the embedded model is saved or loaded. General tools include the
Save Application As command in the Application Builder and File>Save As, from the
File menu of the COMSOL Desktop environment.
However, in an application you are allowed to create and edit multiple models.
Saving and loading such models is done by using the built-in methods saveModel
and loadModel. An MPH file can only contain a single model object.
If you need to create model objects, in addition to the embedded model, use the
built-in method createModel.
To create a new model you use:
Model extmodel = createModel();
A unique model tag is created automatically and assigned to the model. If you
want to explicitly control the model tag, use:
Model extmodel = createModel("My_model_1");
where My_model_1 is a unique tag. It is recommended that you do not use the
names Model1, Model2, Model3, and so on, since these names are used by the
mechanism that automatically generates model tags for the embedded model
when loading and saving MPH files.
| 51
The following example retrieves the model tag of the embedded model:
String my_modeltag = model.tag();
however, you rarely need to use the model tag of the embedded model object.
Instead of creating and building up the contents of a model from scratch, you can
load an existing model and edit it.
For example in the Windows operating system, load a model my_model.mph from
the folder C:\COMSOL_Work, by using the built-in method loadModel:
Model extmodel = loadModel("C:\\COMSOL_Work\\my_model.mph");
A unique model tag is created automatically and is assigned to the model upon
load. Note the double-backslash syntax in the file name. Backslash (\) is a special
character in Java and the double backslash is needed in this case.
To make your application portable, you can use the file scheme syntax available in
the Application Builder. Assuming you stored the MPH file in the common folder,
the call to loadModel should be:
Model extmodel = loadModel("My_Model_1", "common:///my_model.mph");
In this example, the tag My_Model_1 is important since it is used to retrieve the
model from other methods. Once loaded, the model extmodel exists in the work
space of the current COMSOL Multiphysics or COMSOL Server session. Note
that an MPH file can only contain one model object, so there is no ambiguity on
which model you refer to when loading an MPH file.
Assume that you, in one method, have loaded the model extmodel with the tag
My_Model_1, such as in the example above. The model variable extmodel is not
available in other methods. In order to retrieve the model from another method
use:
Model mymodel = getModel("MyModel_1");
The contents of mymodel and extmodel are the same, but these variables exist in
the variable space of two different methods.
The tag My_Model_1 uniquely identified and retrieved the model object from the
current COMSOL Multiphysics or COMSOL Server session.
To clear the contents of a model object, use the built-in method clearModel.
For a list of model utility methods, see “Model Utility Methods” on page 96.
52 |
may need to temporarily turn off model history recording as illustrated by the
following example:
model.hist().disable();
// some code
model.hist().enable();
For a full description of the class structure and method signatures, see the HTML
document Java Documentation available in the COMSOL Documentation. You
find the Java documentation under COMSOL API for use with Java® at the bottom
of the Documentation window.
| 53
The figure below shows the document as displayed in a the COMSOL
documentation viewer.
If you encounter a class that you are unfamiliar with you will get help by a tooltip
as in the figure below for the output of the getView method.
By using the keyboard shortcut Ctrl+1 the correct type declaration is assigned to
the variable as shown in the figure below.
This way you can avoid having to consult the Java Documentation.
54 |
The Application Object
The application object is a part of the model object and is the data structure that
allows access to the user interface features of an application from within a method.
The state of the application object is reflected in the COMSOL Desktop user
interface by the contents of the Application Builder and its application tree.
You can write code using the Method Editor to directly access and change the
features presented in a running application, including button text, icons, colors,
and fonts.
The application object gives you access to a subset of the features and settings
available in the Application Builder. You can use the application object methods
for runtime modifications to the user interface, but not for building a complete
user interface. For building the user interface of an application, you need to use
the Form editor as described in the book Introduction to Application Builder.
This section gives an overview of the application object.
Shortcuts
Form objects and other user interface components are referenced in methods by
using a certain syntax. For example, using the default naming scheme
form3/button5 refers to a button with name button5 in form3 and
form2/graphics3 refers to a graphics object with name graphics3 in form2. You
can also change the default names of forms and form objects. For example, if
form1 is your main form then you can change its name to main.
To simplify referencing form objects as well as menu, ribbon, and toolbar items by
name, you can create shortcuts with a custom name. In the Settings window of an
object or item, click the button to the right of the Name field and type the name
of your choice.
| 55
To create or edit a shortcut, you can also use the keyboard shortcut Ctrl+K.
All shortcuts that you create are made available in a Shortcuts node under
Declarations in the application tree.
In the Settings window for Shortcuts shown below, a number of shortcuts were
created for a various form objects.
The shortcuts can be referenced in other form objects or in code in the Method
Editor. The example below shows a shortcut tempVis used as an input argument
to a temperature plot.
56 |
Shortcuts are automatically updated when objects are renamed, moved, copied,
and duplicated. They are available in application methods as read-only Java®
variables, similar to string, integer, double, and Boolean declarations.
Using shortcuts is recommended because it avoids the need to update methods
when the structure of the application user interface changes.
Shortcuts can also be created for most objects in the model builder tree.
E XAMPLE C ODE
If the application contains a button named button1 in a form named form1, and
the button has a shortcut named b1, the following two ways to change the button
text to red are equivalent:
b1.set("foreground", "red");
app.form("form1").formObject("button1").set("foreground", "red");
In the Method Editor you can directly access the application object part of the
model object by using the app variable. This variable is a shorthand for
model.app().
Access the various parts of the application object by using the name of a form
object, form, item, and so on. A name in the application object has the same
function as the tag in the model object omitting the model.app part.
For example, in the line of code
app.form("form1").formObject("button1").set("enabled", false);
the string form1 is the name of a form and button1 is the name of a button.
I m p o r t a n t C l a ss e s
| 57
D ECLARATION C LASSES
In addition to the basic data types and shortcut declarations, the Declaration node
may include Choice List and Unit List declarations. The corresponding classes are
ChoiceList and UnitSet. The parent class to ChoiceList and UnitSet is called
DataSource. In addition, Scalar, Array 1D, and Array 2D data types are handled
by the DataSource class. For more information, see “Data Source” on page 88.
M ETHOD C LASS
The Method class is used to represent methods. For more information, see
“Method Class” on page 94.
Each class has a set of associated methods that are used to edit the corresponding
user interface component at runtime. These are described in the following
sections.
In addition to the main user interface component classes, there are also list
versions of the Form, FormObject, and Item classes. These are: FormList,
FormObjectList, and ItemList.
The get and set methods described in the section “Get and Set Methods for
Accessing Properties” on page 29 are applicable to the model object as well as the
58 |
model.app part of the model object. In addition, the following methods are
available for changing the color of a form object:
Not all methods are applicable to all properties. Use Ctrl+Space to use code
completion to find out what methods are applicable for a certain object, and what
property names and property values are applicable for a certain method.
General Properties
The following table lists properties that are available for several different user
interface components, including form objects. In the table below, a user interface
component is referred to as an object.
| 59
PROPERTY VALUE DEFAULT DESCRIPTION
fontsize String -1 The font size in points. The special
value -1 represents the default size,
which means that the size is taken
from the parent object (the Forms
node) or from the system default size
if no parent object defines the size.
foreground String default The foreground color for the
corresponding user interface element.
visible true | false true If the value is true, the corresponding
object is visible in the user interface.
A foreground or background color property is represented by a string value. The
available colors are: black, blue, cyan, gray, green, magenta, red, white, and
yellow, or a custom color may also be defined. The special value default means
that the color is taken from the parent object. Depending on the parent type, this
could mean that default is Inherit or Transparent, referring to the corresponding
setting in the Settings window in the Form editor. An arbitrary RGB color can be
represented by a string of the form rgb(red,green,blue) where red, green, and
blue are integers between 0 and 255. Color properties can also be manipulated
using the getColor and setColor methods to directly access the red, green, and
blue color components. If a color property has the value default, it does not have
red, green, and blue values. In this case, the getColor method returns the array
[0,0,0].
E XAMPLE C ODE
The following example reads the current background color for a form, makes the
color darker, and applies the modified color to the same form.
int[] rgb = app.form("form1").getColor("background");
for (int i = 0; i < 3; i++)
rgb[i] /= 2;
app.form("form1").setColor("background", rgb[0], rgb[1], rgb[2]);
The following line of code sets the background color to default which in the case
of the background color property corresponds to the Form editor setting
Transparent.
app.form("form1").set("background", "default");
The following line of code sets the background color to the RGB values 125, 45,
and 43.
app.form("form1").set("background", "rgb(125,45,43)");
60 |
The M a in A pplica t i o n Met h o d s
E XAMPLE C ODE
app.set("asktosave", true);
The following code appends a text string to the application window title.
String oldTitle = app.mainWindow().getString("title");
app.mainWindow().set("title", oldTitle + " modified");
| 61
// Get the index within the list of the DataSource with the name "svar".
int index = list.index("svar");
Main Window
The menuBar and toolBar items are visible in the application user interface if the
menu type is set to Menu bar in the Settings window of the Main Window. The
ribbon and fileMenu items are visible in the user interface if the menu type is set
to Ribbon. It is possible to access and modify items that are not visible based on
the menu type setting, but doing so will not have any visible effect in the user
interface.
62 |
The MainWindow class has the following properties:
E XAMPLE C ODE
// Don’t show the filename in the application user interface window bar.
app.mainWindow().set("showfilename", false);
// Set dark application theme.
app.mainWindow().set("theme", "$dark");
// Set light image export theme.
app.mainWindow().set("imagetheme", "$light");
Form
| 63
NAME SYNTAX DESCRIPTION
hasProperty boolean hasProperty(String name) Returns true if there is a
modifiable property with the
specified name.
method MethodList method() Returns the list of methods.
method Method method(String name) Gets a method with the
specified name.
The Form class has the following properties:
E XAMPLE C ODE
app.form("form1").set("icon", "compute.png");
app.form("form1").formObject("button1").set("enabled", false);
DataSource ds = app.form("form1").declaration("var");
For examples of how to use the declaration method, see “The Main Application
Methods” on page 61.
Form Object
64 |
NAME SYNTAX DESCRIPTION
form FormList form() For a CardStack form object, returns the list of
local cards.
form Form form(String name) For a CardStack form object, returns the local card
with the specified name.
getName String getName() Returns the name of this form object.
getParentForm Form getParentForm() Returns the parent form that contains this form
object.
getType String getType() Returns the form object type name, as defined in
the following sections.
getValue Value getValue() Returns the current value of a FormObject when
applicable. The method is supported by the
following types of FormObjects: input field, toggle
button, check box, combo box, file import, array
input, radio button, selection input, text, list box,
table, slider, knob.
hasProperty boolean Returns true if there is a modifiable property with
hasProperty(String
name)
the specified name.
item ItemList item() For a Toolbar, Graphics or Table form object,
returns the list of user-defined buttons.
item Item item(String name) For a Toolbar, Graphics or Table form object,
return the user-defined button with the specified
name.
visible void visible(String Sets the visible state of the form.
form, boolean visible)
Most form objects have one or more of the properties listed in “General
Properties” on page 59. A form object has a certain property if the corresponding
setting is available in the Form editor. Additional properties are supported for
several types of form objects. The general properties that are supported and any
additional properties for form objects are listed in the following sections.
E XAMPLE C ODE
The following code loops over all buttons and disables them:
for (FormObject formObject : app.form("form1").formObject()) {
if ("Button".equals(formObject.getType())) {
formObject.set("enabled", false);
}
}
The getType method retrieves the type of form object. In the above example the
type of form object is Button and the statement
| 65
"Button".equals(formObject.getType()) represents a string comparison
between the output of the getType method and the string "Button".
For an example of using getValue, see “Data Validation” on page 211.
The following table lists all form object types that can be returned by getType:
A RRAY I NPUT
Example Code
app.form("form1").formObject("arrayinput1").set("enabled", false);
66 |
B UTTON
In the Form editor, if a button has its Size setting set to Large, it always displays its
text property. If the button is Small, it either displays the icon or the text
according to the following rule: if the icon property is empty, the text is
displayed, if the icon property is not empty, the icon is displayed.
Example Code
app.form("form1").formObject("button1").set("enabled", false);
C ARD S TACK
Example Code
app.form("form1").formObject("cardstack1").set("visible", false);
To access objects in a local card, either use shortcuts or use the form method:
app.form("form1").formObject("cardstack1").form("card1")
.formObject("button1").set("enabled", false);
| 67
C HECK B OX
Example Code
app.form("form1").formObject("checkbox1").set("tooltip", "tooltip text");
C OMBO B OX
Example Code
app.form("form1").formObject("combobox1").set("foreground", "blue");
68 |
D ATA D ISPLAY
Example Code
app.form("form1").formObject("datadisplay1")
.setColor("background", 192, 192, 192);
| 69
E QUATION
Example Code
app.form("form1").formObject("equation1").set("visible", false);
F ILE I MPORT
Example Code
app.form("form1").formObject("fileimport1")
.set("filetypes", new String[]{"ALL2DCAD"});
F ORM
A form used as a subform does not have any modifiable properties.
70 |
F ORM C OLLECTION
Example Code
This line of code changes the font:
app.form("form1").formObject("collection1").set("font", "Arial");
The expanded method is only supported by form collections which use sections.
The first argument is the tag of the form which is represented by the section. The
second argument determines if the sections should be expanded or collapsed.
| 71
G RAPHICS
Example Code
This line of code displays plot group 5 (pg5) in the graphics object graphics1 in
the form with the name Temperature:
app.form("Temperature").formObject("graphics1")
.set("source", model.result("pg5"));
The following line of code using useGraphics is equivalent to the above example:
useGraphics(model.result("pg5"), "Temperature/graphics1");
Either method changes the source of the graphics form object and then plots the
contents.
To clear the contents of a graphics object, use:
app.form("Temperature").formObject("graphics1")
.set("source", (ModelEntity) null);
or equivalently
useGraphics(null, "Temperature/graphics1");
The code below displays the mesh in the model tree node mesh1 in the graphics
object graphics1 contained in the card of a card stack:
app.form("mesh").formObject("cardstack1").form("card1")
.formObject("graphics1").set("source", model.mesh("mesh1"));
The code below enables data picking for a graphics object and sets the data picking
target to a domain point probe:
app.form("form1").formObject("graphics1").set("datapick", true);
app.form("form1").formObject("graphics1").set("datapicktarget",
model.component("comp1").probe("pdom1"));
If a shortcut graphics1 has been created for the graphics object and a shortcut
pdom1 has been created for the probe object, the above can be shortened to:
72 |
graphics1.set("datapick", true);
graphics1.set("datapicktarget", pdom1);
H YPERLINK
Example Code
with (app.form("form1").formObject("hyperlink1"));
set("text", "COMSOL");
set("url", "www.comsol.com");
endwith();
I MAGE
Example Code
app.form("form1").formObject("image1").set("icon", "compute.png");
| 73
I NFORMATION C ARD S TACK
Example Code
app.form("form1").formObject("infocard1").set("fontunderline", true);
74 |
I NPUT F IELD
| 75
PROPERTY VALUE DEFAULT DESCRIPTION
maxinteger Integer 1000 The maximum allowed integer
value. This property is only
accessible when the Filter setting
is set to Integer and the
corresponding check box is
enabled in the Data Validation
section.
mininteger Integer 0 The minimum allowed integer
value. This property is only
accessible when the Filter setting
is set to Integer and the
corresponding check box is
enabled in the Data Validation
section.
notation auto | scientific auto When the value is scientific,
| decimal numbers are always displayed
using scientific notation. When
the value is decimal, numbers
are never displayed using
scientific notation. When the
value is auto, the notation
depends on the size of the
number.
precision Integer 4 The number of significant digits
displayed.
tooltip String "" The tooltip displayed when the
mouse pointer is located over
the input field.
Example Code
app.form("form1").formObject("inputfield1").set("precision", 6);
76 |
L INE
Example Code
app.form("form1").formObject("line1").set("text", "divider text");
app.form("form1").formObject("line1").set("thickness", 10);
app.form("form1").formObject("line1").set("linecolor", blue);
L IST B OX
Example Code
app.form("form1").formObject("listbox1").set("foreground", "red");
To change the list box contents, modify the corresponding choice list:
app.declaration("choicelist1").appendListRow("new value", "new name");
| 77
L OG
Example Code
app.form("form1").formObject("log1").set("fontsize", "20");
M ESSAGE L OG
Example Code
app.form("form1").formObject("messages1").set("background", "gray");
P ROGRESS B AR
To create and update progress information see “Progress Methods” on page 131.
Example Code
app.form("form1").formObject("progressbar1").set("visible", false);
78 |
R ADIO B UTTON
To change the display name for a radio button, modify the value in the
corresponding choice list.
For a choice list that is used by a radio button, it is not possible to change the value
of any row, or to add or remove rows. Only the display name can be changed.
Example Code
app.form("form1").formObject("radiobutton1").set("fontitalic", true);
app.declaration("choicelist1").setDisplayName("new name", 0);
R ESULTS T ABLE
To change the contents of the results table use the method useResultsTable or
evaluateToResultsTable. See also “GUI-Related Methods” on page 115.
Example Code
app.form("form1").formObject("resultstable1").set("visible", true);
app.form("form1").formObject("resultstable1").set("source",
model.result().table("tbl2"));
useResultsTable(model.result().table("tbl2"), "/form1/resultstable1");
| 79
S ELECTION I NPUT
Example Code
app.form("form1").formObject("selectioninput1").set("graphics",
"graphics1");
To change the model selection, assuming sel1 is a shortcut to the selection input
form object:
sel1.set("source", model.selection("sel2"));
80 |
S LIDER
The min value is allowed to be larger than the max value, in which case the slider
behaves as if the values were swapped. The smallest value always corresponds to
the left side of the slider.
Example Code
app.form("form1").formObject("slider1").set("min", 1);
app.form("form1").formObject("slider1").set("max", 12);
app.form("form1").formObject("slider1").set("steps", 11);
| 81
K NOB
82 |
PROPERTY VALUE DEFAULT DESCRIPTION
notation auto | auto When the value is scientific,
scientific | numbers are always displayed using
decimal
scientific notation. When the value is
decimal, numbers are never
displayed using scientific notation.
When the value is auto, the notation
depends on the size of the number.
precision Integer 4 The number of significant digits that
are displayed.
scale none | marks | marks To control whether the scale should
markslabels |
marksalllabels
have tick marks or labels. When set
to none, the knob has no tick marks
or labels. When set to marks, the
knob has tick marks but no labels.
When set to markslabels, the knob
has tick marks and labels for the
minimum and maximum values.
When set to marksalllabels, the
knob has ticks marks and all major
ticks has a label.
steps Integer 5 The number of steps between the
min and max values. The number of
tick marks is one more than the
number of steps.
tooltip String "" The tooltip text.
Example Code
app.form("form1").formObject("knob1").set("min", 1);
app.form("form1").formObject("knob1").set("max", 12);
app.form("form1").formObject("knob1").set("steps", 11);
S PACER
A spacer object does not have any modifiable properties.
| 83
T ABLE
To change the contents of the table, change the declaration variables or model
entities the table is displaying.
Example Code
app.form("form1").formObject("table1").set("enabled", false);
T EXT
Example Code
app.form("form1").formObject("text1").set("textalign", "center");
84 |
T EXT L ABEL
Example Code
app.form("form1").formObject("textlabel1").set("text", "custom text");
T OGGLE B UTTON
A button with size large always displays the text, a button with size small displays
either the icon or the text. If the icon property is empty, the text is displayed. If
the icon property is not empty, the icon is displayed.
Example Code
app.form("form1").formObject("togglebutton1").set("icon",
"about_information.png");
| 85
T OOLBAR
Example Code
app.form("form1").formObject("toolbar1").set("background", "gray");
U NIT
Example Code
app.form("form1").formObject("unit1").set("visible", false);
V IDEO
Example Code
app.form("form1").formObject("video1").set("visible", false);
86 |
W EB P AGE
Example Code
app.form("form1").formObject("webpage1").set("type", "report");
app.form("form1").formObject("webpage1").set("report", "rpt1");
model.result().report("rpt1");
model.result().report("rpt1").run();
Item
Item objects represent items, toggle items, user defined buttons in Toolbar,
Graphics and Table form objects, and submenus in the menu bar, toolbar, ribbon
and file menu. The following methods are available:
| 87
The Item class contains the following properties:
In order for an item to be enabled, the enabled property needs to have the value
on for the item itself as well as for all of its parents. In other words, disabling an
item also disables all of its subitems.
Item objects also include separators. However, separators do not have any
accessible properties.
Item objects for user-defined buttons do not have the title and tooltip
properties. For Table form objects, predefined items such as “move up” and
“move down” do not have any modifiable properties.
E XAMPLE C ODE
app.mainWindow().menuBar("menu1").set("title", "new title");
app.mainWindow().menuBar("menu1").item("toggle_item1").set("text",
"test");
Data Source
88 |
S CALAR , A RRAY 1D, AND A RRAY 2D M ETHODS
The methods described in the following table are applicable for Scalar, Array 1D,
and Array 2D objects (of types String, Boolean, Integer, Double). These
methods are used to manipulate such variable objects during runtime.
| 89
E XAMPLE C ODE
// Get a scalar double declaration.
DataSource ds = app.declaration("var");
// The 'var' declaration is a scalar double so we use the getDouble method
// to read its value.
double cur = ds.getDouble();
// Modifying the local field 'cur' does not affect the value stored in the
// data source 'ds'.
cur = cur + 1;
// Set the value of the data source.
ds.set(cur);
90 |
NAME SYNTAX DESCRIPTION
setValue setValue(String value, int row) Sets the value for the given row
(0-based). If the row is equal to
the length of the list, a new row is
added with the value and an
empty display name.
setDisplayName setDisplayName(String displayName, int Sets the display name for the
row)
given row (0-based). If the row is
equal to the length of the list, a
new row is added with the
display name and an empty value.
Example Code
The code below adds the string Aluminum 3004 to a choice list. Note that the
choice list index starts at 0, whereas the material tags start at 1 (mat1, mat2, mat3,
and mat4).
ChoiceList choiceList = getChoiceList("choicelist1");
choiceList.setListRow("mat4", "Aluminum 3004", 3);
For more information on using choice lists for changing materials, see the book
Introduction to Application Builder.
U NIT S ET M ETHODS
When the object is a UnitSet the following additional methods are also available:
| 91
NAME SYNTAX DESCRIPTION
getDoubleArray double[] getDoubleArray(String Returns the value of the specified
name)
property as a double array value.
set set(String name, String value) Sets a new value for the property
set(String name, double value)
set(String name, double[] value)
with the specified name.
Example code
The following code enables data picking for the graphics1 object, connects it to
the graphicsdata1 object and sets some properties on the graphicsdata1 object:
app.form("form1").formObject("graphics1").set("datapick", true);
app.form("form1").formObject("graphics1").set("datapicktarget",
app.declaration("graphicsdata1"));
app.declaration("graphicsdata1").set("edim", "boundary");
app.declaration("graphicsdata1").set("method", "pointdir");
AppEvent Class
92 |
instance of this class when a FormObject event is being triggered. The
getUserControl method retrieves the FormObject where the event originated.
V ALUE C LASS
The methods getNewValue and getOldValue returns an object of type Value
which has methods according to the table below. The Value class represents a
value which can be retrieved in different formats depending on the form object.
For example, an event triggered from an input field is a scalar variable (boolean,
int, double, or String). This means that only a subset of the methods in the table
below is valid for a particular form object event.
| 93
NAME SYNTAX DESCRIPTION
getStringArray String[] Gets the string array value.
getStringArray()
getStringMatrix String[][] Gets the string matrix value.
getStringMatrix
The following code exemplifies using the event field, for the class AppEvent, and
the methods getNewValue and getOldValue:
String value = event.getNewValue().getString();
String oldValue = event.getOldValue().getString();
For an example of using the event field and its associated methods, see “Data
Validation” on page 211.
Me thod Class
The Method class is used to represent a method. The following method is available
for a Method object:
94 |
NAME SYNTAX PURPOSE
index int index(String name) Returns the 0-based position of the object with a
given name in the list.
names String[] names() Returns an array of names for all objects in the
list.
size int size() Returns the number of objects in the list.
Additionally the FormList class has the following methods:
It is also possible to use a list in an enhanced for loop to operate on all objects in
the list.
In the following example, the background color is set to red in all forms, by
looping over all forms:
for (Form f : app.form()) { // app.form() is of type FormList
f.set("background", "red");
}
| 95
The Built-in Method Library for the Application Builder
This section lists built-in methods available in the Method Editor in addition to
the methods that operate on the model and application objects. For more
information on the model object and its methods, refer to earlier sections of this
book and the Programming Reference Manual. For more information on the
application object, see “The Application Object” on page 55. Some of the listings
have associated example code but for more extensive programming examples, see
“Programming Examples” on page 156.
The syntax rules are those of the Java® programming language. Note that each line
of code needs to end with a semicolon (;), but the semicolon is omitted in the
listings below.
The following table summarizes the model utility methods for querying, creating,
loading, and saving model objects. The model object is stored on the MPH file
format.
96 |
NAME SYNTAX DESCRIPTION
loadProtectedModel Model loadProtectedModel(String Loads a password protected
filename, String password)
model from a file. The model is
given a unique tag.
loadRecoveryModel Model loadRecoveryModel(String tag, Loads a model from a recovery
String foldername)
directory/folder structure.
loadRecoveryModel Model loadRecoveryModel(String Loads a model from a recovery
foldername)
directory/folder structure. The
model is given a unique tag.
modelTags String[] modelTags() Returns an array of model tags
for all loaded models, including
the embedded model.
removeModel removeModel(String tag) Removes a model. The
removeModel(Model model)
embedded model cannot be
removed.
saveModel saveModel(Model model, String Saves a model to a file. The
filename)
filename can be a file scheme
path or (if allowed by security
settings) a server file path.
uniqueModeltag String uniqueModeltag(String prefix) Returns a model tag that is not in
use.
getComsolVersion getComsolVersion() Returns the current software
version as a string.
Example Code
The code below loads a model using loadModel, presented in the table above. It
extracts the x-, y-, and z-coordinates of all mesh nodes and stores them in a 2D
double array coords[3][N], where N is the number of mesh nodes. The individual
x-,y-, and z- coordinates are available as the length-N 1D arrays coords[0],
coords[1], coords[2], respectively. (The node locations can be plotted by using
the Cut Point 3D dataset in combination with a 3D Point Trajectories plot.)
Model extmodel = loadModel("C:\\Paul\\pacemaker_electrode.mph");
SolverFeature step = extmodel.sol("sol1").feature("v1");
XmeshInfo xmi = step.xmeshInfo();
XmeshInfoNodes nodes = xmi.nodes();
double[][] coords = nodes.coords();
For more information on methods operating on the model object, see the
Programming Reference Manual.
Note that to make the code above platform independent for use in an application
you can instead use the common application file folder:
Model extmodel = loadModel("common:///pacemaker_electrode.mph");
| 97
License Methods
The license methods read the license number and check out or test the licenses of
the current session or for an MPH file. This functionality can be used, for example,
to limit the use of an application to one or a few license numbers or to dynamically
adapt functionality of an application depending on which product licenses are
available.
98 |
E XAMPLE CODE
The following code tries to check out a license for the AC/DC Module, and
displays an error message if it fails:
if (!checkoutLicense("ACDC"))
alert("There seems to be a problem. Please contact Alice and Bob at
123-456-7890.");
You can use this to customize license error messages by calling a method
containing this code before any add-on product specific features are used by the
application.
In the Application Builder root node you can further select the Ignore license errors
during launch check box. This will make it possible for users to start an application
regardless of which licenses are available. However, this will not work if the
application, at startup, uses features required by an add-on product. The
application has to be created in such a way that the add-on product specific
features are not used in the startup phase of the application but instead are
deferred to later in the workflow.
To emulate a scenario where there are not enough available licenses you can, for
example, disable one or more products in the Licensed and Used Products in Session
dialog box, available from the File menu in the COMSOL Desktop environment.
The following code tests if the COMSOL installation has the capability to use an
MPH file:
boolean ok = hasProductForFile(“model_file.mph”);
| 99
PRODUCT/FEATURE FEATURE NAME
100 |
PRODUCT/FEATURE FEATURE NAME
File Methods
File methods are used to read and write data to a file or portions of a file. Note
that higher-level techniques for reading and writing to files are available from
within the Application Builder user interface. For more information, see the book
Introduction to Application Builder and “GUI Command Methods” on page
127.
| 101
NAME SYNTAX DESCRIPTION
readMatrixFromFile double[][] Reads the contents of the given
readMatrixFromFile(String name)
file into a double matrix. The file
has the same spreadsheet type
format as available in the model
tree Export node.
readStringMatrixFromFile String[][] Reads the contents of the given
readStringMatrixFromFile(String
name)
file into a string matrix. The file
has the same spreadsheet type
format as available in the model
tree Export node.
readCSVFile String[][] readCSVFile(String Reads a file with
name)
comma-separated values (CSV
file) into a string matrix. Expects
file to use the RFC 4180 format
for CSV.
writeFile writeFile(String name, String Writes the given string contents
contents)
to the given file name.
writeFile writeFile(String name, String Writes the given string contents
contents, boolean append)
to the given file name. If append is
true, then the contents are
appended instead of overwritten.
writeFile writeFile(String name, Writes the array data to the
double[][] data)
given file. The spreadsheet format
is used, which means it can be
read by readMatrixFromFile.
writeFile writeFile(String name, Writes the array data to the
double[][] data, boolean
append)
given file. The spreadsheet format
is used, which means it can be
read by readMatrixFromFile. If
append is true, then the contents
are appended instead of
overwritten.
writeFile writeFile(String name, Writes the array data to the
String[][] data)
given file. The spreadsheet format
is used, which means it can be
read by
readStringMatrixFromFile.
102 |
NAME SYNTAX DESCRIPTION
writeFile writeFile(String name, Writes the array data to the
String[][] data, boolean
append)
given file. The spreadsheet format
is used, which means it can be
read by
readStringMatrixFromFile. If
append is true, then the contents
are appended instead of
overwritten.
openFileStreamWriter CsWriter Returns a CsWriter that can
openFileStreamWriter(String
name)
write to the given file.
openFileStreamWriter CsWriter Returns a CsWriter that can
openFileStreamWriter(String
name, boolean append)
write to the given file. If append is
true, then the contents are
appended instead of overwritten.
openBinaryFileStreamWriter CsBinaryWriter Returns a CsBinaryWriter that
openBinaryFileStreamWriter(Stri
ng name)
can be used to write to the given
file byte-by-byte.
openBinaryFileStreamWriter CsBinaryWriter Returns a CsBinaryWriter that
openBinaryFileStreamWriter(Stri
ng name, boolean append)
can be used to write to the given
file byte by byte. If append is true,
then the contents are appended
instead of overwritten.
writeCSVFile writeCSVFile(String name, Writes the given string array
String[][] data)
data to a CSV file. The RFC 4180
format is used for the CSV.
writeCSVFile writeCSVFile(String name, Writes the given string array
String[][] data, boolean
data to a CSV file. The RFC 4180
append)
format is used for the CSV. If
append is true, then the contents
are appended instead of
overwritten.
writeCSVFile writeCSVFile(String name, Writes the given double array
double[][] data)
data to a CSV file. The RFC 4180
format is used for the CSV.
writeCSVFile writeCSVFile(String name, Writes the given double array
double[][] data, boolean
data to a CSV file. The RFC 4180
append)
format is used for the CSV. If
append is true, then the contents
are appended instead of
overwritten.
| 103
NAME SYNTAX DESCRIPTION
exists boolean exists(String name) Tests whether a file with the given
name exists.
If the name is not a file scheme
path name or an absolute path,
then the method first finds out
whether a file with file scheme
path embedded:/// + argument
exists. If such a file does not exist,
then it tests whether there is a
file with a matching name in the
current working directory.
deleteFile deleteFile(String file) Delete a file with the given name
if it exists. The file is deleted on
the server,. The name can use a
file scheme path.
copyFile copyFile(String sourceFile, Copies a file on the server. Both
String destFile)
the source and target names can
use file scheme paths.
importFile importFile(String name) Displays a file browser dialog box
importFile(String name,
String[] fileTypes)
and uploads the selected file to
the file declaration with the given
name. After this, the uploaded file
can be accessed with
upload:///<name>. The optional
fileTypes argument can be used
to filter which file types are
available for selection in the file
browser. The easiest way to get a
list of valid file types is to use
code completion in the Method
Editor.
104 |
NAME SYNTAX DESCRIPTION
importFile importFile(ModelEntity entity, Displays a file browser dialog box
String name)
and uploads the selected file to
the Filename text field in the
given model object entity. This
defines an input file that the
application will need at a later
stage. For example, the Filename
of an interpolation function
accessed with
model.func(’<tag>’)). The
uploaded file can be accessed
with
upload:///<tag>/filename.
writeExcelFile writeExcelFile(String name, Writes the given string array
String[][] data)
data starting from the first cell in
the first sheet of an Excel file.
This method requires LiveLink™
®
for Excel .
writeExcelFile writeExcelFile(String name, Writes the given string array
String sheet, String cell,
data starting from the specified
String[][] data)
cell in the specified sheet of an
Excel file. This method requires
LiveLink™ for Excel®.
readExcelFile String[][] readExcelFile(String Reads the first sheet of an Excel
name)
file, starting from the first cell,
into a String[][]. This method
requires LiveLink™ for Excel®.
readExcelFile String[][] readExcelFile(String Reads the specified sheet of an
name, String sheet, String
cell)
Excel file, starting from the
specified cell, into a String[][].
This method requires LiveLink™
®
for Excel .
| 105
NAME SYNTAX DESCRIPTION
getFilePath String getFilePath(String name) Returns the absolute server file
path of the server proxy file
corresponding to a certain file
scheme path, or null if the server
proxy file for the given path does
not exist.
This method can be used to pass
the path to, for example, a file
using the temp:/// scheme to
external code or an application.
In addition, this method is used to
retrieve the file name of an
uploaded file when using the file
scheme upload:///inputFile,
for example by using a File
declaration together with a File
Import form object.
getClientFileName String getClientFileName(String Returns the original name of an
name)
uploaded file on the client file
system (or null if there is no
uploaded file matching the given
file scheme path).
This method is only useful for
providing user interface feedback.
For example, to get information
on which uploaded file is being
used. There is no guarantee that
the original file would still exist on
the client or even that the
current client would be the same
as the original client.
106 |
NAME SYNTAX DESCRIPTION
getClientFilePath String getClientFilePath(String Returns the original path of an
name)
uploaded file on the client file
system (or null if there is no
uploaded file matching the given
file scheme path). Returns only
the file name part of the path
when called from an application
running in a web browser.
This method is only useful for
providing user interface feedback.
For example, to get information
on which uploaded file is being
used. There is no guarantee that
the original file would still exist on
the client or even that the
current client would be the same
as the original client.
createDirectory createDirectory(String name) Creates a file directory name, see
examples below in the Example
Code section.
E XAMPLE C ODE
This line of code copies the uploaded file file1 to the temp folder with new file
name file2.mphbin and then prompts the user to save the file to any location.
copyFile("upload:///file1", "temp:///file2.mphbin");
fileSaveAs("temp:///file2.mphbin");
This line of code deletes the file file2.mphbin from the temp folder.
deleteFile("temp:///file2.mphbin");
This line of code creates a directory in the user folder, as specified in Preferences.
createDirectory("user:///a/b");
This line of code creates a directory in the Temp folder under C:.
createDirectory("C:\\Temp\\a\\b");
Note that the method fileSaveAs returns a boolean that indicates if saving the
file was successful or not. Saving a file will fail if, for example, the user does not
have access to the target folder.
| 107
Op e ra ti n g S y st e m Met h o d s
Operating system methods are used for accessing operating system information
and commands from an application.
108 |
NAME SYNTAX DESCRIPTION
playSound playSound(String name) Plays the sounds in the given file
on the client. Only .wav files are
supported; no external libraries
are required.
playSound playSound(double hz, int millis) Plays a signal at a given frequency
hz and with given duration
millis in milliseconds on the
client.
E XAMPLE C ODE
The line of code below plays one of the sounds available in the data/sounds folder
of the COMSOL installation and has been embedded in the application and stored
in the Sounds library.
playSound("embedded:///success_1.wav");
| 109
In the command sequence of a form object, this is equivalent to selecting an Open
File node under GUI Commands>File Commands and clicking Run, as shown in the
figure below.
This line of code opens the COMSOL home page in the default browser:
openURL("https://www.comsol.com");
110 |
Email Methods
Email methods are used for sending emails from an application, typically with
attachments containing results from a simulation.
| 111
NAME SYNTAX DESCRIPTION
EmailMessage.setUse mail.setUser(String name, String Sets the username and password
r password)
to use for email (SMTP) server
authentication. This method must
be called after the setServer
method.
EmailMessage.setSec mail.setSecurity(String security) Sets the connection security type
urity
for email (SMTP) server
communication. Valid values are
’none’, ’starttls’ and ’tls’.
This method must be called after
the setServer method.
EmailMessage.setFro mail.setFrom(String fromAddress) Sets the from address.
m
EmailMessage.setTo mail.setTo(String... to) Sets the to addresses.
EmailMessage.setCc mail.setCc(String... cc) Sets the cc addresses.
EmailMessage.setBcc mail.setBcc(String... bcc) Sets the bcc addresses.
EmailMessage.setSub mail.setSubject(String subject) Sets the email subject line. Note
ject
that newline characters are not
allowed.
EmailMessage.setBod mail.setBodyText(String body) Sets the email body as plain text.
yText
An email can contain both a text
and an HTML body.
EmailMessage.setBod mail.setBodyHtml(String body) Sets the email body as HTML
yHtml
text. An email can contain both a
text and an HTML body.
EmailMessage.attach mail.attachFile(String filename) Adds an attachment from a file.
File
The attachment MIME type is
determined by the file name
extension.
EmailMessage.attach mail.attachFile(String filename, Adds an attachment from a file
File String mimeType)
with the specified MIME type.
EmailMessage.attach mail.attachFromModel(ModelEntity Adds an attachment created
FromModel modelEntity)
from a report, export, or table
feature in the model.
EmailMessage.attach mail.attachText(String text, String Adds a text attachment with a
Text mimeSubType)
specified sub-MIME type, such as
plain or HTML.
112 |
NAME SYNTAX DESCRIPTION
EmailMessage.attach mail.attachBinary(byte[] binary, Adds an attachment from a byte
Binary String mimeType)
array with the specified MIME
type.
EmailMessage.send mail.send() Sends the email to the email
(SMTP) server. An email object
can only be sent once.
Each to, cc, and bcc address string can contain multiple email addresses separated
by a comma or a semicolon character. Whitespace is allowed before and after the
separator character.
E MAIL P REFERENCES
To set preferences for an outgoing email (SMTP) server, open the Email page of
the Preferences dialog box, as shown in the figure below.
E XAMPLE C ODE
The following code sends an email and attaches a report:
EmailMessage mail = new EmailMessage();
mail.setTo(emailTo);
| 113
mail.setSubject("Tubular Reactor Simulation");
mail.setBodyText("The computation has finished. Please find the report
attached.");
mail.attachFromModel(model.result().report("rpt1"));
mail.send();
The figure below shows the corresponding form object and Settings window.
The following code is similar but also configures the email server settings.
EmailMessage mail = new EmailMessage();
mail.setServer("smtp.myemail.com", 587);
mail.setUser("[email protected]", "password");
mail.setSecurity("starttls");
mail.setFrom("[email protected]");
mail.setTo("[email protected]");
mail.setSubject("Tubular reaction simulation");
114 |
mail.setBodyText("The computation has finished");
mail.send();
GUI-Related Methods
The graphical user interface (GUI) related methods are used for displaying dialog
boxes with messages, editing form objects and user interface content, getting
run-time properties of the application user interface, and running methods.
| 115
NAME SYNTAX DESCRIPTION
closeForm closeForm(String name) Closes the form with the
given name. Closing forms is
only possible in applications
using subwindows. This
method is not applicable for
single window applications.
closeDialog closeDialog(String name) Closes the form, shown as a
dialog box, with the given
name.
dialog dialog(String name) Shows the form with the
given name as a dialog box.
Equivalent to the dialog
method of a Form object; see
below.
alert alert(String text) Stops execution and displays
an alert message with the
given text.
alert alert(String text, String title) Stops execution and displays
an alert message with the
given text and title.
confirm String confirm(String text) Stops execution and displays
a confirmation dialog box
with the given text. It also
displays two buttons, "Yes"
and "No". The method
returns "Yes" or "No"
depending on what the user
clicks.
confirm String confirm(String text, String Stops execution and displays
title)
a confirmation dialog box
with the given text and title.
It also displays two buttons,
"Yes" and "No". The method
returns "Yes" or "No"
depending on what the user
clicks.
116 |
NAME SYNTAX DESCRIPTION
confirm String confirm(String text, String Stops execution and displays
title, String yes, String no)
a confirmation dialog box
with the given text and title.
It also displays two buttons
with the given strings as
labels. The method returns
the label of the button that
the user clicks.
confirm String confirm(String text, String Stops execution and displays
title, String yes, String no,
String cancel)
a confirmation dialog box
with the given text and title.
It also displays three buttons
with the given strings as
labels. The method returns
the label of the button that
the user clicks.
error error(String message) Stops execution and opens
an error dialog box with the
given message.
error error(String message, Throwable Stops execution and opens
cause)
an error dialog box with the
given message including the
underlying cause of class
Throwable, the general Java
exception class, or one of its
subclasses, such as
RuntimeException. This can
be used to “wrap” native
COMSOL Multiphysics error
messages with custom error
messages.
request String request(String text) Stops execution and displays
a dialog box with a text field,
requesting input from the
user. The given text is the
label of the text field. The
method returns the entered
text or null if the cancel
button is clicked.
| 117
NAME SYNTAX DESCRIPTION
request String request(String text, String Stops execution and displays
defaultString)
a dialog box with a text field,
requesting input from the
user. The given text is the
label of the text field and the
default string is the text
initially shown in the text
field. The method returns the
entered text or null if the
cancel button is clicked.
request String request(String text, String Stops execution and displays
title, String defaultString)
a dialog box with a text field,
requesting input from the
user. The given text is the
label of the text field, the
default string is the text
initially shown in the text
field, and the title is the title
of the dialog box. The
method returns the entered
text or null if the cancel
button is clicked.
message message(String message) Sends a message to the
message log if available in the
application.
message message(arg) Sends a message arg to the
message log. For an
application this requires that
a message log is added to the
application user interface.
The input argument arg can
be a scalar, 1D array, or 2D
array of the types string,
double, int or Boolean.
clearLog clearLog() Clears the log window.
clearMessageLog clearMessageLog() Clears the message log
window.
118 |
NAME SYNTAX DESCRIPTION
evaluateToResultsTable evaluateToResultsTable(NumericalFe Evaluates the given entity, a
ature entity, String name, boolean
clear)
Derived Value, in the table
evaluateToResultsTable(NumericalFe object given by the name,
ature entity, FormObject graphics, name path, or graphics form
boolean clear)
object in the second
argument, which will then be
the default target for the
evaluations of the Derived
Value. If the third argument is
true, the table is cleared
before adding the new data,
otherwise the data is
appended.
evaluateToDoubleArray2D double[][] Evaluates the given entity, a
evaluateToDoubleArray2D(NumericalF
eature entity)
Derived Value, and returns
the non-parameter column
part of the real table that is
produced as a double matrix.
All settings in the numerical
feature are respected, but
those in the current table
connected to the numerical
feature are ignored.
evaluateToIntegerArray2D int[][] Evaluates the given entity, a
evaluateToIntegerArray2D(Numerical
Feature entity)
Derived Value, and returns
the non-parameter column
part of the real table that is
produced as an integer
matrix. All settings in the
numerical feature are
respected, but those in the
current table connected to
the numerical feature are
ignored.
| 119
NAME SYNTAX DESCRIPTION
evaluateToStringArray2D String[][] Evaluates the given entity, a
evaluateToStringArray2D(NumericalF
eature entity)
Derived Value, and returns
the non-parameter column
part of the, potentially
complex, table that is
produced as a string matrix.
All settings in the numerical
feature are respected but
those in the current table
connected to the numerical
feature are ignored.
useResultsTable useResultsTable(TableFeature Shows the values from the
tableFeature, String resultsTable)
tableFeature in the
useResultsTable(TableFeature
tableFeature, FormObject resultsTable form object.
resultsTable)
getChoiceList ChoiceList getChoiceList(String Returns an object of the type
name)
ChoiceList, representing a
choice list node under the
declarations branch. The type
ChoiceList has methods
that make it easier to change
the matrix value with respect
to changing and accessing
values and display names
individually.
setFormObjectEnabled setFormObjectEnabled(String name, Sets the enable state for the
boolean enabled)
form object specified by the
name or name path.
setFormObjectVisible setFormObjectVisible(String name, Sets the visible state for the
boolean visible)
form object specified by the
name or name path.
setFormObjectText setFormObjectText(String name, Sets the text for the form
String text)
object specified by the name
or name path in the second
argument. This method
throws an error if it is
impossible to set a text for
the specified form object.
120 |
NAME SYNTAX DESCRIPTION
setFormObjectEditable setFormObjectEditable(String name, Sets the editable state for the
boolean editable)
form object specified by the
name or name path. This
functionality is only available
for text field objects.
setMenuBarItemEnabled setMenuBarItemEnabled(String name, Sets the enable state for the
boolean enabled)
menu bar item specified by
the name or name path (from
menu bar) in the first
argument.
setMainToolbarItemEnabled setMainToolbarItemEnabled(String Sets the enable state for the
name, boolean enabled)
main toolbar item specified
by the name or name path
(from main toolbar) in the
first argument.
setFileMenuItemEnabled setFileMenuItemEnabled(String name, Sets the enable state for the
boolean enabled)
file menu item specified by
the name or name path
(from file menu) in the first
argument.
setRibbonItemEnabled setRibbonItemEnabled(String name, Sets the enable state for the
boolean enabled)
ribbon item specified by the
name or name path (from
main window) in the first
argument.
setToolbarItemEnabled setToolbarItemEnabled(String name, Sets the enable state for the
boolean enabled)
toolbar form object item
specified by the name or
name path in the first
argument.
useView useView(View view, String name) Applies a view to the
useView(View view, FormObject
graphics)
graphics contents given by
the name, name path, or
graphics form object in the
second argument.
resetView resetView(String name) Resets the view to its initial
resetView(FormObject graphics)
state in the graphics contents
given by the name, name
path, or graphics form object
in the second argument.
| 121
NAME SYNTAX DESCRIPTION
getView ViewBase getView(String name) Returns the view currently
ViewBase getView(FormObject
graphics)
used by the graphics contents
given by the name, name
path, or graphics form object
in the second argument.
goToView goToView(String name); Goes to a standard view in
main graphics window. The
parameter name is one of the
view orientation strings in the
following list:
"xy","xz","yx","yz","zx"
,"zy".
goToView goToView(String name, String Goes to a standard view in
graphicsname);
the given graphics form
object graphicsname.
goToView goToView(String name, FormObject Goes to standard view in the
graphics);
given graphics form object
graphics.
setWebPageSource setWebPageSource(String name, Sets the source for the form
String source)
object specified by the name
or name path in the first
argument. This method
throws an error if the name
does not refer to a Web Page
form object.
getScreenHeight int getScreenHeight() Returns the height in pixels of
the primary screen on client
system, or of the browser
window if Web Client is
used.
getScreenWidth int getScreenWidth() Returns the width in pixels of
the primary screen on client
system, or of the browser
window if Web Client is
used.
storeChanges storeChanges(String form) Commits the values entered
in a dialog box having On
request enabled. The method
takes the name of the form
as its argument.
122 |
A LERTS AND M ESSAGES
The methods alert, confirm, and request display a dialog box with a text string
and optional user input. The following example uses confirm to ask the user if a
direct or an iterative solver should be used in an application. Based on the answer,
the alert function is then used to show the estimated memory requirement for
the selected solver type in a message dialog box:
String answer = confirm("Which solver do you want to use?",
"Solver Selection","Direct", "Iterative");
if (answer.equals("Direct")) {
alert("Using the direct solver will require about 4GB of memory when solving.");
} else {
alert("Using the iterative solver will require about 2GB of memory when
solving.");
}
E XAMPLE C ODE
The following code changes the camera zoom angle and updates the graphics for
each change.
useView(model.view("view1"), "/form1/graphics1");
for (int i = 0; i < 25; i++) {
sleep(2000);
model.view("view1").camera().set("zoomanglefull", 12-i*5.0/25);
useGraphics(model.geom("geom1"), "/form1/graphics1");
}
This line of code displays plot group 5 (pg5) in the graphics object graphics1 in
the form with the name Temperature:
useGraphics(model.result("pg5"), "/Temperature/graphics1");
The code below displays the mesh in the model tree node mesh1 in the graphics
object graphics1 contained in the card of a card stack. The second line runs a
zoom extents command to ensure proper visualization of the mesh.
useGraphics(model.mesh("mesh1"), "/mesh/cardstack1/card1/graphics1");
zoomExtents("/mesh/cardstack1/card1/graphics1");
The code below displays a request dialog box that lets the user type in a file name
for an HTML report. If the user has typed a file name, then a report is generated.
String answerh = request("Enter file name","File Name", "Untitled.html");
if (answerh != null) {
model.result().report("rpt1").set("format","html");
model.result().report("rpt1").set("filename","user:///"+answerh);
model.result().report("rpt1").run();
}
| 123
The code below is similar to the code above, but in this case the report is saved in
Microsoft® Word® format (.docx).
String answerw = request("Enter file name","File Name", "Untitled.docx");
if (answerw != null) {
model.result().report("rpt2").set("format","docx");
model.result().report("rpt2").set("filename","user:///"+answerw);
model.result().report("rpt2").run();
}
This line of code sets the view of the graphics object form1/graphics1 to View 5,
as defined in the model tree:
useView(model.view("view5"), "form1/graphics1");
124 |
You can use Data Access in combination with Editor Tools to create a slider or an
input field that sets the transparency level (alpha) of a plot group. The figure
below shows a Settings window of a slider with the transparency level as Source.
In this case you need to create a method for updating the view that is called to
handle an event from the slider or form object. In the example above, the slider
uses a Local method defined in the Events section. This method contains one line
of code that updates the view:
useView(getView("/form1/graphics1"), "/form1/graphics1");
Note that different transparency levels are not supported when accessing an
application from a browser using COMSOL Server.
| 125
Note that you can also set a view from the command sequence of, for example, a
button: select a view subnode under the Views node in the editor tree and click the
Plot button under the tree.
To go to one of the standard views in the main Graphics window, for example in
an add-in, you can use:
goToView("xy");
This line of code forms a string containing the screen width and height:
screenSize = toString(getScreenWidth()) + "-by-" +
toString(getScreenHeight());
You can present the string with an input field or a data display object using this
string as a source (the string screenSize needs to be declared first).
126 |
GUI Command Methods
The GUI command methods correspond to the GUI Commands node in the editor
tree. The editor tree is displayed in, for example, the Choose Commands to Run
section in the Settings window for a button object in the Application Builder.
| 127
NAME SYNTAX DESCRIPTION
scenelight sceneLight(String graphicsName) Toggles scene light in the given
sceneLight(FormObject graphics)
graphics object.
selectAll selectAll(String graphics) Sets the selection to all entities in
selectAll(FormObject graphics)
the given graphics object.
transparency transparency(String graphicsName) Toggles transparency in the given
transparency(FormObject graphics)
graphics object.
zoomExtents zoomExtents(String graphicsName) Makes the entire model visible
zoomExtents(FormObject graphics)
within the extent of the given
graphics object.
E XAMPLE C ODE
For examples of how to use fileSaveAs, see the Introduction to Application
Builder. This method is frequently needed for saving files in general.
You can create an application that saves and exits automatically by running the
following lines of code, for example, after solving:
saveModel(model,"C:\\COMSOL\\file.mph");
exit();
or
saveModel(model, "common:///file.mph");
exit();
This is useful in a COMSOL Server setting since using exit() in this way will free
up any licenses that are checked out.
Debu g Methods
The debug method is used to display variable contents in the Debug Log window.
E XAMPLE C ODE
The code below prints strings and doubles to the Debug Log window.
128 |
xcoords[i] = Math.cos(2.0*Math.PI*divid);
ycoords[i] = Math.sin(2.0*Math.PI*divid);
debugLog("These are component values for case 1:");
debugLog("x:");
debugLog(xcoords[i]);
debugLog("y:");
debugLog(ycoords[i]);
The methods for external C libraries are used for linking Application Builder
methods with compiled C-code.
E XTERNAL M ETHOD
| 129
NAME SYNTAX DESCRIPTION
invokeWideString long invokeWideString(String method, Invokes the named native
Object... arguments)
method in the library with the
supplied arguments. Strings are
converted to wchar_t *. Returns
the value returned by the
method.
close void close() Releases the library and frees
resources. If you do not call this
method, it is automatically
invoked when the external library
is no longer needed.
130 |
Progress Methods
Progress methods are used to create and update progress information in the Status
bar, in a progress form object, and in a dialog box.
| 131
NAME SYNTAX DESCRIPTION
resetProgress resetProgress() Removes all progress levels
and resets progress to 0 and
the message to an empty
string.
showIndeterminateProgr showIndeterminateProgress(String Shows a progress dialog box
ess message)
with an indeterminate progress
bar, given message and a cancel
button.
showIndeterminateProgr showIndeterminateProgress(String Shows a progress dialog box
ess message, boolean cancelButton)
with an indeterminate progress
bar, given message and an
optional cancel button.
showProgress showProgress() Shows a progress dialog box
with a cancel button. No
model progress is included.
showProgress showProgress(boolean modelProgress) Shows a progress dialog box
with a cancel button and an
optional model progress.
showProgress showProgress(boolean modelProgress, Shows a progress dialog box
boolean addSecondLevel)
with a cancel button, optional
model progress, and one or
two levels of progress
information. Two levels can
only be used if modelProgress
is true.
showProgress showProgress(boolean modelProgress, Shows a progress dialog box
boolean addSecondLevel, boolean
cancelButton)
with optional model progress,
one or two levels, and possibly
a cancel button. Two levels can
only be used if modelProgress
is true.
closeProgress closeProgress() Closes the currently shown
progress dialog box.
132 |
NAME SYNTAX DESCRIPTION
startProgress startProgress(String name) Resets the value of the given
startProgress(FormObject progressBar)
progress bar form object name
to 0. The progress bar to
control can be specified with
an absolute path, such as
form1/progressbar1, or a
name relative to the context
from which the method was
called.
Nothing is done if no progress
bar corresponding to the given
name is found.
setProgressBar setProgressBar(String name, int Sets the value of the given
workDone, String message)
setProgressBar(FormObject progressBar,
progress bar form object name
int workDone, String message) in the range 0-100 and the
associated progress message.
Values out of range are
converted to 0 or 100. The
progress bar to control can be
specified with an absolute path,
such as form1/progressbar1,
or a name relative to the
context from which the
method was called.
Nothing is done if no progress
bar corresponding to the given
name is found, or if the
progress bar is used for
showing model progress.
setProgressBar setProgressBar(String name, int Same as above, but does not
workDone)
setProgressBar(FormObject progressBar,
update the progress message.
int workDone)
E XAMPLE C ODE
showProgress(true, true, true);
/* Opens a progress dialog box with cancel button showing two levels of
progress. The values shown in progress dialog box will be updated to match
the two levels of progress. */
| 133
setProgress(0, "Init step 1");
/* Sets the second-level progress to 0 and the second-level progress message
to "Init step 1". */
// do some work
setProgress(40);
/* Sets the second-level progress to 40, this causes the top-level progress
to be updated to 8 (40 % of 0-20). */
// do some work
// do some work
<call-solver>
/* Similar to meshing, the progress messages and values from the solver are
shown in the second-level progress bar and the top-level progress value goes
from 40 to 100 while the solver progress goes from 0 to 100. */
closeProgress();
134 |
This will keep Computing data as the progress message.
Use the setProgress method by itself if you want to display custom progress in
the task and status bar. Once you have done this, that progress bar will no longer
be updated by progress information from the COMSOL model, but will be
completely dependent on further calls to setProgress for changes in its value.
Precede it with a call to showProgress to also display the built-in progress dialog
box, see below.
Note that progress information from the COMSOL model will not be shown in
between calls to setProgress. Progress is reset between method calls. If you want
to combine custom steps of progress in methods with built-in model progress,
then use setProgressInterval instead.
With setProgressInterval, you can control the top two levels of progress
information. The second level can be displayed in a progress dialog box and a
progress bar form object, see the code segment below. The second progress level,
controlled by your own custom progress calculation, is connected to the first level
such that one interval at the top level corresponds to the entire second level. Thus
if the interval is 0–50, when the second level progress reaches 40, for example, the
first level will be set to 20 (=(40/100)*50).
Important uses of the method setProgressInterval are listed below:
• Combining calls to the COMSOL model so that you get continuous
progress going from 0–100.
• Computing several studies as well as evaluating several plots. Call
setProgressInterval before each call to the built-in methods with an
interval that approximates how much time each model computation takes.
For example:
setProgressInterval("Computing solution", 0, 80);
model.study("std1").run();
setProgressInterval("Plotting", 80, 100);
useGraphics(model.result("pg3"), "energy_response_plot/graphics1");
| 135
If you, in a running application, wish to no longer use progress intervals, call
resetProgress to return to the original state. This will also reset progress to 0.
136 |
Date and Time Methods
The date and time methods are used to retrieve the current date and time as well
as information on computation times.
| 137
NAME SYNTAX DESCRIPTION
getExpectedComputa model.setExpectedComputationTime(String Returns a string describing
tionTime format)
the approximate
computation time of the
application. The string can be
altered by the method
setExpectedComputationTi
me.
setLastComputation model.setLastComputationTime(long time) Set the last computation
Time
time, overwriting the
automatically generated time.
You can use the timeStamp
method to record time
differences and set the
measured time in ms (a long
integer).
getLastComputation String model.getLastComputationTime Returns the last computation
Time (String format)
time in the given format. The
format can either be a time
unit or text describing a
longer format. Currently
supported formats are:
hr:min:sec Returns the time
in hours, minutes, and
seconds in the format X h Y
min Z sec.
h:min:s Returns the time in
hours, minutes, and seconds
in the format X h Y min Z s.
detailed Returns the time in
seconds and also includes
more readable units for
longer times. This format is
localized and the output is
translated to the current
language setting.
For example, you can
retrieve the time in ms by
using
getLastComputationTime("m
s").
138 |
E XAMPLE C ODE
The following code overrides the built-in computation time that is available in the
information nodes in the model tree.
long t0 = timeStamp(); // initialize record of computation time
Sleep
The code below makes the application idle for 1000 ms.
long delay = 1000;
sleep(delay);
| 139
Co n v e rs io n Me th o d s
Conversion methods are used to convert between the different data types
Booleans, integers, doubles, strings, and arrays. These methods are shorthand
versions of conversion methods in the standard Java libraries.
140 |
NAME SYNTAX DESCRIPTION
toInt int[][] toInt(String[]... strs) Converts all the strings in the
given matrix to integers and
returns an integer matrix.
toString String toString(int value) Converts the given integer to a
string.
toString String toString(double value) Converts the given double to a
string.
toString String toString(boolean value) Converts the given Boolean to a
string.
toString String toString(double value, int digits) Converts the given double to a
string with the given number of
significant digits.
toString String toString(double value, int Converts the given double to a
digits,boolean remove)
string with the given number of
significant digits with trailing zeros
removed if the Boolean remove is
true. For example, 10.0000001
with number of digits set to 3 will
return 10 rather than 10.000.
toString String toString(double value, String format) Converts the given double to a
string using the given format
specifier, which is the same as
java.util.Formatter. See the
corresponding Java format string
documentation for more
information.
toString String[] toString(double[] darray) Converts all the doubles in the
given array to strings and returns
a string array.
toString String[][] toString(double[][] dmatrix) Converts all the doubles in the
given matrix to strings and
returns a string matrix.
toString String[] toString(int[] iarray) Converts all the integers in the
given array to strings and returns
a string array.
toString String[][] toString(int[][] imatrix) Converts all the integers in the
given matrix to strings and
returns a string matrix.
| 141
NAME SYNTAX DESCRIPTION
toString String[] toString(boolean[] barray) Converts all the Booleans in the
given array to strings and returns
a string array.
toString String[][] toString(boolean[][] bmatrix) Converts all the Booleans in the
given matrix to strings and
returns a string matrix.
A r r ay M et h o d s
Array methods are used to add, remove, insert, and extract subsets of 1D and 2D
arrays.
142 |
NAME SYNTAX DESCRIPTION
getSubMatrix int[][] getSubMatrix(int[][] matrix, int Returns a rectangular submatrix
startCol, int endCol, int startRow, int
endRow)
of the input matrix spanning
columns from startCol to
endCol, and rows from startRow
to endRow.
getSubMatrix boolean[][] getSubMatrix(boolean[][] matrix, Returns a rectangular submatrix
int startCol, int endCol, int startRow, int
endRow)
of the input matrix spanning
columns from startCol to
endCol, and rows from startRow
to endRow.
insert String[] insert(String[] array, String value, Inserts an element at position
int index)
index in an array and returns the
expanded array.
insert double[] insert(double[] array, double value, Inserts an element at position
int index)
index in an array and returns the
expanded array.
insert int[] insert(int[] array, int value, int Inserts an element at position
index)
index in an array and returns the
expanded array.
insert boolean[] insert(boolean[] array, boolean Inserts an element at position
value, int index)
index in an array and returns the
expanded array.
insert String[] insert(String[] array, String[] Inserts elements in an array at
value, int[] index)
positions given by the index array
and returns the expanded array.
insert double[] insert(double[] array, double[] Inserts elements in an array at
value, int[] index)
positions given by the index array
and returns the expanded array.
insert int[] insert(int[] array, int[] value, int[] Inserts elements in an array at
index)
positions given by the index array
and returns the expanded array.
insert boolean[] insert(boolean[] array, boolean[] Inserts elements in an array at
value, int[] index)
positions given by the index array
and returns the expanded array.
append String[] append(String[] array, String value) Adds an element to the end of an
array and returns the expanded
array.
append double[] append(double[] array, double value) Adds an element to the end of an
array and returns the expanded
array.
| 143
NAME SYNTAX DESCRIPTION
append int[] append(int[] array, int value) Adds an element to the end of an
array and returns the expanded
array.
append boolean[] append(boolean[] array, boolean Adds an element to the end of an
value)
array and returns the expanded
array.
append String[] append(String[] array, String[] Adds elements to the end of an
value)
array and returns the expanded
array.
append double[] append(double[] array, double[] Adds elements to the end of an
value)
array and returns the expanded
array.
append int[] append(int[] array, int[] value) Adds elements to the end of an
array and returns the expanded
array.
append boolean[] append(boolean[] array, boolean[] Adds elements to the end of an
value)
array and returns the expanded
array.
remove String[] remove(String[] array, int index) Removes an element from an
array and returns the shortened
array.
remove double[] remove(double[] array, int index) Removes an element from an
array and returns the shortened
array.
remove int[] remove(int[] array, int index) Removes an element from an
array and returns the shortened
array.
remove boolean[] remove(boolean[] array, int index) Removes an element from an
array and returns the shortened
array.
remove String[] remove(String[] array, int[] index) Removes elements from an array
and returns the shortened array.
remove double[] remove(double[] array, int[] index) Removes elements from an array
and returns the shortened array.
remove int[] remove(int[] array, int[] index) Removes elements from an array
and returns the shortened array.
remove boolean[] remove(boolean[] array, int[] Removes elements from an array
index)
and returns the shortened array.
144 |
NAME SYNTAX DESCRIPTION
insertRow String[][] insertRow(String[][] matrix, Inserts a row into a rectangular
String[] value, int rowIndex)
2D array and returns the
expanded array.
insertRow double[][] insertRow(double[][] matrix, Inserts a row into a rectangular
double[] value, int rowIndex)
2D array and returns the
expanded array.
insertRow int[][] insertRow(int[][] matrix, int[] Inserts a row into a rectangular
value, int rowIndex)
2D array and returns the
expanded array.
insertRow boolean[][] insertRow(boolean[][] matrix, Inserts a row into a rectangular
boolean[] value, int rowIndex)
2D array and returns the
expanded array.
insertRow String[][] insertRow(String[][] matrix, Adds rows to a rectangular 2D
String[][] value, int[] rowIndex)
array and returns the expanded
array.
insertRow double[][] insertRow(double[][] matrix, Adds rows to a rectangular 2D
double[][] value, int[] rowIndex)
array and returns the expanded
array.
insertRow int[][] insertRow(int[][] matrix, int[][] Adds rows to a rectangular 2D
value, int[] rowIndex)
array and returns the expanded
array.
insertRow boolean[][] insertRow(boolean[][] matrix, Adds rows to a rectangular 2D
boolean[][] value, int[] rowIndex)
array and returns the expanded
array.
replaceRow String[][] replaceRow(String[][] matrix, Replaces a row in a rectangular
String[] value, int rowIndex)
2D array and returns the array.
replaceRow double[][] replaceRow(double[][] matrix, Replaces a row in a rectangular
double[] value, int rowIndex)
2D array and returns the array.
replaceRow int[][] replaceRow(int[][] matrix, int[] Replaces a row in a rectangular
value, int rowIndex)
2D array and returns the array.
replaceRow boolean[][] replaceRow(boolean[][] matrix, Replaces a row in a rectangular
boolean[] value, int rowIndex)
2D array and returns the array.
replaceRow String[][] replaceRow(String[][] matrix, Replaces rows in a rectangular
String[][] value, int[] rowIndex)
2D array and returns the array.
replaceRow double[][] replaceRow(double[][] matrix, Replaces rows in a rectangular
double[][] value, int[] rowIndex)
2D array and returns the array.
replaceRow int[][] replaceRow(int[][] matrix, int[][] Replaces rows in a rectangular
value, int[] rowIndex)
2D array and returns the array.
| 145
NAME SYNTAX DESCRIPTION
replaceRow boolean[][] replaceRow(boolean[][] matrix, Replaces rows in a rectangular
boolean[][] value, int[] rowIndex)
2D array and returns the array.
appendRow String[][] appendRow(String[][] matrix, Adds a row to the end of a
String[] value)
rectangular 2D array and returns
the expanded array.
appendRow double[][] appendRow(double[][] matrix, Adds a row to the end of a
double[] value)
rectangular 2D array and returns
the expanded array.
appendRow int[][] appendRow(int[][] matrix, int[] Adds a row to the end of a
value)
rectangular 2D array and returns
the expanded array.
appendRow boolean[][] appendRow(boolean[][] matrix, Adds a row to the end of a
boolean[] value)
rectangular 2D array and returns
the expanded array.
appendRow String[][] appendRow(String[][] matrix, Adds rows to the end of a
String[][] value)
rectangular 2D array and returns
the expanded array.
appendRow double[][] appendRow(double[][] matrix, Adds rows to the end of a
double[][] value)
rectangular 2D array and returns
the expanded array.
appendRow int[][] appendRow(int[][] matrix, int[][] Adds rows to the end of a
value)
rectangular 2D array and returns
the expanded array.
appendRow boolean[][] appendRow(boolean[][] matrix, Adds rows to the end of a
boolean[][] value)
rectangular 2D array and returns
the expanded array.
removeRow String[][] removeRow(String[][] matrix, int Removes a row from a 2D array
rowIndex)
and returns the smaller array.
removeRow double[][] removeRow(double[][] matrix, int Removes a row from a 2D array
rowIndex)
and returns the smaller array.
removeRow int[][] removeRow(int[][] matrix, int Removes a row from a 2D array
rowIndex)
and returns the smaller array.
removeRow boolean[][] removeRow(boolean[][] matrix, int Removes a row from a 2D array
rowIndex)
and returns the smaller array.
removeRow String[][] removeRow(String[][] matrix, int[] Removes rows from a 2D array
rowIndex)
and returns the reduced array.
removeRow double[][] removeRow(double[][] matrix, int[] Removes rows from a 2D array
rowIndex)
and returns the reduced array.
146 |
NAME SYNTAX DESCRIPTION
removeRow int[][] removeRow(int[][] matrix, int[] Removes rows from a 2D array
rowIndex)
and returns the reduced array.
removeRow boolean[][] removeRow(boolean[][] matrix, Removes rows from a 2D array
int[] rowIndex)
and returns the reduced array.
insertColumn String[][] insertColumn(String[][] matrix, Adds a column into a rectangular
String[] value, int columnIndex)
2D array and returns the
expanded array.
insertColumn double[][] insertColumn(double[][] matrix, Adds a column into a rectangular
double[] value, int columnIndex)
2D array and returns the
expanded array.
insertColumn int[][] insertColumn(int[][] matrix, int[] Adds a column into a rectangular
value, int columnIndex)
2D array and returns the
expanded array.
insertColumn boolean[][] insertColumn(boolean[][] matrix, Adds a column into a rectangular
boolean[] value, int columnIndex)
2D array and returns the
expanded array.
insertColumn String[][] insertColumn(String[][] matrix, Adds columns to a rectangular
String[][] value, int[] columnIndex)
2D array and returns the
expanded array.
insertColumn double[][] insertColumn(double[][] matrix, Adds columns to a rectangular
double[][] value, int[] columnIndex)
2D array and returns the
expanded array.
insertColumn int[][] insertColumn(int[][] matrix, int[][] Adds columns to a rectangular
value, int[] columnIndex)
2D array and returns the
expanded array.
insertColumn boolean[][] insertColumn(boolean[][] matrix, Adds columns to a rectangular
boolean[][] value, int[] columnIndex)
2D array and returns the
expanded array.
replaceColumn String[][] replaceColumn(String[][] matrix, Replaces a column in a
String[] value, int columnIndex)
rectangular 2D array and returns
the array.
replaceColumn double[][] replaceColumn(double[][] matrix, Replaces a column in a
double[] value, int columnIndex)
rectangular 2D array and returns
the array.
replaceColumn int[][] replaceColumn(int[][] matrix, int[] Replaces a column in a
value, int columnIndex)
rectangular 2D array and returns
the array.
| 147
NAME SYNTAX DESCRIPTION
replaceColumn boolean[][] replaceColumn(boolean[][] matrix, Replaces a column in a
boolean[] value, int columnIndex)
rectangular 2D array and returns
the array.
replaceColumn String[][] replaceColumn(String[][] matrix, Replaces columns in a rectangular
String[][] value, int[] columnIndex)
2D array and returns the array.
replaceColumn double[][] replaceColumn(double[][] matrix, Replaces columns in a rectangular
double[][] value, int[] columnIndex)
2D array and returns the array.
replaceColumn int[][] replaceColumn(int[][] matrix, int[][] Replaces columns in a rectangular
value, int[] columnIndex)
2D array and returns the array.
replaceColumn boolean[][] replaceColumn(boolean[][] matrix, Replaces columns in a rectangular
boolean[][] value, int[] columnIndex)
2D array and returns the array.
appendColumn String[][] appendColumn(String[][] matrix, Adds a column at the end of a
String[] value)
rectangular 2D array and returns
the expanded array.
appendColumn double[][] appendColumn(double[][] matrix, Adds a column at the end of a
double[] value)
rectangular 2D array and returns
the expanded array.
appendColumn int[][] appendColumn(int[][] matrix, int[] Adds a column at the end of a
value)
rectangular 2D array and returns
the expanded array.
appendColumn boolean[][] appendColumn(boolean[][] matrix, Adds a column at the end of a
boolean[] value)
rectangular 2D array and returns
the expanded array.
appendColumn String[][] appendColumn(String[][] matrix, Adds columns to the end of a
String[][] value)
rectangular 2D array and returns
the expanded array.
appendColumn double[][] appendColumn(double[][] matrix, Adds columns to the end of a
double[][] value)
rectangular 2D array and returns
the expanded array.
appendColumn int[][] appendColumn(int[][] matrix, int[][] Adds columns to the end of a
value)
rectangular 2D array and returns
the expanded array.
appendColumn boolean[][] appendColumn(boolean[][] matrix, Adds columns to the end of a
boolean[][] value)
rectangular 2D array and returns
the expanded array.
removeColumn String[][] removeColumn(String[][] matrix, Removes a column from a
int columnIndex)
rectangular 2D array and returns
the smaller array.
148 |
NAME SYNTAX DESCRIPTION
removeColumn double[][] removeColumn(double[][] matrix, Removes a column from a
int columnIndex)
rectangular 2D array and returns
the smaller array.
removeColumn int[][] removeColumn(int[][] matrix, int Removes a column from a
columnIndex)
rectangular 2D array and returns
the smaller array.
removeColumn boolean[][] removeColumn(boolean[][] matrix, Removes a column from a
int columnIndex)
rectangular 2D array and returns
the smaller array.
removeColumn String[][] removeColumn(String[][] matrix, Removes columns from a
int[] columnIndex)
rectangular 2D array and returns
the reduced array.
removeColumn double[][] removeColumn(double[][] matrix, Removes columns from a
int[] columnIndex)
rectangular 2D array and returns
the reduced array.
removeColumn int[][] removeColumn(int[][] matrix, int[] Removes columns from a
columnIndex)
rectangular 2D array and returns
the reduced array.
removeColumn boolean[][] removeColumn(boolean[][] matrix, Removes columns from a
int[] columnIndex)
rectangular 2D array and returns
the reduced array.
matrixSize int[] matrixSize(String[][] matrix) Returns the number of rows and
columns of a matrix as an integer
array of length 2.
matrixSize int[] matrixSize(double[][] matrix) Returns the number of rows and
columns of a matrix as an integer
array of length 2.
matrixSize int[] matrixSize(int[][] matrix) Returns the number of rows and
columns of a matrix as an integer
array of length 2.
matrixSize int[] matrixSize(boolean[][] matrix) Returns the number of rows and
columns of a matrix as an integer
array of length 2.
transpose String[][] transpose(String[][] matrix) Returns the transpose of a
matrix.
transpose double[][] transpose(double[][] matrix) Returns the transpose of a
matrix.
| 149
NAME SYNTAX DESCRIPTION
transpose int[][] transpose(int[][] matrix) Returns the transpose of a
matrix.
transpose boolean[][] transpose(boolean[][] matrix) Returns the transpose of a
matrix.
String Methods
String methods are used to process string variables and string arrays.
150 |
NAME SYNTAX DESCRIPTION
split String[] split(String str) Returns an array of strings by
splitting the given string at spaces.
split String[] split(String str, String separator) Returns an array of strings by
splitting the given string at the
given separator.
substring String substring(String str, int start, int Returns a substring with the given
length)
length starting at the given
position.
unique String[] unique(String[] strs) Returns an array of strings with
the unique values in the given
array of strings.
C olle ct ion M e t ho ds
Collection methods are used to copy, compare, sort, and merge variables and
arrays.
| 151
NAME SYNTAX DESCRIPTION
copy boolean[] copy(boolean... toCopy) Returns a copy of the given array
of booleans, which can also be
specified as a varargs of booleans.
copy boolean[][] copy(boolean[]... toCopy) Returns a copy of the given
boolean matrix, which can also
be specified as a varargs of rows
(boolean arrays).
equals boolean equals(String[] str1, String[] str2) Returns true if all strings in the
given array are equal and they
have the same number of
elements.
equals boolean equals(String[][] matr1, String[][] Returns true if all strings in the
matr2)
given matrix are equal and they
have the same number of
elements.
equals boolean equals(int[] ints1, int[] ints2) Returns true if all integers in the
given array are equal and they
have the same number of
elements.
equals boolean equals(int[][] ints1, int[][] ints2) Returns true if all integers in the
given matrix are equal and they
have the same number of
elements.
equals boolean equals(double dl1, double dl2, double Compares whether the relative
relErrorTolerance)
error of two doubles is within
allowed tolerance using abs(( a -
b ) / b ), where b is the larger of
the doubles (by absolute value).
equals boolean equals(double dl1, double dl2) Same as above, but uses a default
relErrorTolerance of 0.0001.
equals boolean equals(double[] dbls1, double[] Compares the relative errors ( ~
dbls2, double relErrorTolerance)
abs(( a - b) / b ) of elements in
the arrays pairwise and returns
true if all relative errors are
below relErrorTolerance and
the arrays have the same number
of elements.
equals boolean equals(double[] dbls1, double[] Same as above, but uses a default
dbls2)
relErrorTolerance of 0.0001.
152 |
NAME SYNTAX DESCRIPTION
equals boolean equals(double[][] dbls1, double[][] Compares the relative errors ( ~
dbls2, double relErrorTolerance)
abs(( a - b ) / b ) of elements in
the matrices pairwise and returns
true if all relative errors are
below relErrorTolerance and
the matrices have the same
number of elements.
equals boolean equals(double[][] dbls1, double[][] Same as above, but uses a default
dbls2)
relErrorTolerance of 0.0001.
sort sort(String[] strs) Sorts the given array of strings.
NOTE: The array is sorted in
place.
sort sort(int[] ints) Sorts the given array of integers.
NOTE: The array is sorted in
place.
sort sort(double[] doubles) Sorts the given array of doubles.
NOTE: The array is sorted in
place.
sort sort(String[][] strs) Sorts the given 2D array of
strings. The columns are sorted
by their row values from top to
bottom. NOTE: The array is
sorted in place.
sort sort(int[][] ints) Sorts the given 2D array of
integers. The columns are sorted
by their row values from top to
bottom. NOTE: The array is
sorted in place.
sort sort(double[][] doubles) Sorts the given 2D array of
doubles. The columns are sorted
by their row values from top to
bottom. NOTE: The array is
sorted in place.
merge merge(String[]... toMerge) Returns an array of strings with all
strings merged from the given
arrays.
| 153
NAME SYNTAX DESCRIPTION
merge merge(int[]... toMerge) Returns an array of integers with
all integers merged from the two
given arrays.
merge merge(double[]... toMerge) Returns an array of doubles with
all doubles merged from the two
given arrays.
For writing add-in method code that operates on the current component, current
mesh, current physics, and so on. use the methods in the table below.
These methods return the corresponding entity such that the method code in an
add-in can operate on it. When called from an application a method in this
154 |
category returns null. Also, null is returned if no entity of the corresponding
type exists such that nothing is current.
To learn more about using these methods you can review the Application Builder
settings for one of the built-in add-ins by opening the corresponding MPH file.
In a typical Windows® installation the built-in add-in library is located at
C:\Program Files\COMSOL\COMSOL60\Multiphysics\addins
| 155
Programming Examples
This section contains examples that illustrate solving practical tasks by accessing
and manipulating the model object and using the built-in methods. Note that
additional examples of user-defined methods are provided in the example
applications of the Application Libraries accessible from the File menu.
To run the code in the examples below, you can create a method for use in a model
or application. You can, for example, choose the option Blank Model in the Model
Wizard. To create a new method, go to the Developer tab in the Model Builder
and click the New Method button in the ribbon. In the Application Builder, paste
the code into the new method. Finally, you can run the code from the Developer
tab in the Model Builder by choosing the method you just created from the Run
Method toolbar menu.
The section “Results” on page 48 shows how to write code for various parts of the
Results node in the model tree, including Datasets, Tables, and Plot Groups. These
examples assume that you have solution data available from solving, for example,
a heat transfer, CFD, or structural mechanics problem.
You can also create visualizations without having associated solution data by either
using grid datasets or using low-level functionality only available through
methods. You can, for example, write code for plotting points and triangles
without any associated solution data. These techniques are useful when creating
applications where customized plot functionality is needed. This section shows
how to use grid datasets, and the next section shows how to use low-level
functionality.
156 |
model.func().create("an1", "Analytic");
model.result().dataset().create("grid1", "Grid3D");
with(model.result().dataset("grid1"));
set("source", "data");
set("parmin1", -1);
set("parmax1", +1);
set("parmin2", -1);
set("parmax2", +1);
set("parmin3", -1);
set("parmax3", +1);
set("source", "function");
set("function", "an1");
endwith();
model.result().create("pg1", "PlotGroup3D");
model.result("pg1").create("iso1", "Isosurface");
with(model.result("pg1").feature("iso1"));
set("expr", "x^2+y^2+z^2-1");
set("levelmethod", "levels");
set("levels", 0.0);
endwith();
model.result("pg1").feature("iso1").create("col1", "Color");
with(model.result("pg1").feature("iso1").feature("col1"));
set("expr", "z");
endwith();
model.result("pg1").run();
Comments
If there is no solution-based Dataset available, then the Grid3D dataset needs to
have a Function as its Source. In the example above, a default Analytic function is
created with tag an1. A default Analytic function corresponds to f(x) = x, and its
only purpose is to give the grid dataset an evaluation context.
Note: The alternative is to solve a physics problem on a mesh and reference the
corresponding solution dataset. The method of referencing a Function makes it
possible to create visualizations without solution data.
The source for the Grid3D dataset with tag grid1 is set to function, and finally,
the function property of grid1 is set to an1.
The Grid3D dataset has options for max and min parameter bounds, shown in the
example code above. An additional grid resolution option is not shown in this
example. However, you can learn about its syntax by using Record Code from the
Model Builder.
| 157
Visualization of Points, Curves, and Surfaces
Once created, the plot type names are visible in the Settings window of each plot.
In addition to the properties modified by the examples below, in the Settings
window of these plot types, you can see the number of geometric entities created,
such as number of points, line segments, and triangles. Just as for other types of
plots, you can also change the Range of color and data, as well as Coloring and Style.
P OINTS IN 2D
The following code plots a circle of points using the Point Data plot type.
// A circle of points
Comments
The first line
String pgTag = model.result().uniquetag("pg");
158 |
creates a unique tag for the plot group to be created. This is useful if you intend
to add a varying number of plot groups in your model or application.
The line
ResultFeature pg = model.result().create(pgTag, 2);
creates a 2D Plot Group using the newly created unique tag. The second argument
to create defines the dimension of the plot group (2 for 2D, 3 for 3D, and so on).
The line
ResultFeature plot = pg.create("pt1", "PointData");
creates a plot of the type PointData. This plot type is only available through
methods.
The middle part of the example code generates the points making up the circle.
The point coordinates are stored in the 2-by-N array p, along with color data in the
array color of length N. The color data is, in this example, simply based on the
index of the points and is used to control the coloring of each point based on a
color table.
The last few lines populate the fields of the Point Data plot.
plot.set("pointdata", p)
.set("colordata", color)
.set("coloring", "colortable");
The property pointdata takes the 2-by-N array p as its input. The options for the
coloring property are colortable or uniform.
To learn about the syntax for the additional properties available for a Point Data
plot, you can run the above code in a blank model, browse to the Settings window
for the Point Data plot, click Record Code, and change the corresponding plot
properties. Note that the name of the plot type in the Settings window of the plot
in the model tree is Point Data.
P OINTS IN 3D
The following code plots points in an undulating pattern in 3D using the Point
Data plot type.
// Undulating points in 3D
| 159
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("pt1", "PointData");
int N = 37;
double[][] p = new double[3][N];
double[] color = new double[N];
double R = 1000;
for (int i = 0; i < N; i++) {
double angle = i*2*Math.PI/N;
p[0][i] = R*Math.cos(angle);
p[1][i] = R*Math.sin(angle);
p[2][i] = R*Math.cos(3*angle);
color[i] = p[1][i];
}
plot.set("pointdata", p)
.set("colordata", color)
.set("coloring", "colortable")
.set("sphereradiusscale", 1);
plot.run();
selectNode(pg);
Comments
When plotting 3D points the line
model.result().create(pgTag, 3);
has the second argument set to 3 in order to create a 3D Plot Group. In 3D, the
point coordinates, p is a 3-by-N array.
The line
.set("sphereradiusscale", 1);
C URVE IN 3D
The following code plots line segments in the shape of a 3D helix using the Line
Data plot type.
// A 3D helix from line segments
160 |
p[0][i] = s/5;
p[1][i] = Math.sin(s);
p[2][i] = Math.cos(s);
if (i > 0) {
t[0][i-1] = i-1;
t[1][i-1] = i;
}
}
plot.set("pointdata", p)
.set("elementdata", t);
plot.run();
selectNode(pg);
Comments
The line
ResultFeature plot = pg.create("line1", "LineData");
creates a plot of the type LineData. This plot type is only available through
methods. Just as for Point Data plots, the point coordinates p is a 3-by-N array. In
addition to pointdata, the LineData plot type takes elementdata as its input. In
the example, this is represented by the 2-by-N array t and contains indexes to the
columns of p, corresponding to the start and end points of the lines.
In a similar way, line segments can be plotted in 2D by creating a 2D plot group
and by letting the point coordinates be a 2-by-N array. See also “Points in 2D” on
page 158.
T RIANGULATED S HAPE IN 2D
The following code plots triangles in the shape of a 2D pentagon by using the
Surface Data plot type.
// A 2D pentagon from triangles
| 161
selectNode(pg);
Comments
The line
ResultFeature plot = pg.create("surf1", "SurfaceData");
creates a plot of the type SurfaceData. This plot type is only available through
methods. Just as for 2D Point Data plots, the point coordinates p is a 2-by-N array.
In addition to pointdata, and similar to the LineData plot type, the SurfData
plot type takes elementdata as its input. In the example, this is represented by the
3-by-N array t and contains indexes to the columns of p, corresponding to the
vertexes of the triangles. The ordering of the point indexes in the array t is not
important for 2D Surface Data plots.
F UNCTION S URFACE IN 3D
The following code plots triangles in the shape of a 3D rotationally symmetric
sinc-function surface by using the Surface Data plot type.
// A 3D sinc(r) function surface
162 |
pos++;
t[0][pos] = p00;
t[1][pos] = p11;
t[2][pos] = p10;
pos++;
}
}
plot.set("pointdata", p)
.set("elementdata", t)
.set("colordata", color)
.set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
This example is similar to “Triangulated Shape in 2D” on page 161, but with the
point array being a 3-by-N array for 3D surfaces. For Surface Data plots in 3D, the
ordering of the indexes in the elementdata array t matters. It determines the
direction of the surface normal, which is used for the lighting effect when using
Scene Light in the Graphics window. The surface normal of a triangle is determined
according to the “right-hand rule”. In mathematical terms, the surface normal is
defined as the vector product:
n= (p[ t[ 1]] – p[ t[ 0]] ) × (p[ t[ 2]] – p[t[0] ])
where the indexes into t represent the rows in one of the columns of t and p
represents a column in the array of points p.
To ensure that the lighting effect produces expected results, the triangle surface
normal directions need to consistently point in the same direction as the intended
overall surface normal direction. As an alternative to making sure that the indexes
come in the correct order, the normal direction may be given as an additional
input to a Surface Data plot. This is shown in the next example section, Sphere in
3D.
S PHERE IN 3D
The following code plots triangles in the shape of a 3D sphere by using the Surface
Data plot type.
// A coarse sphere with user-supplied normals
int Nx = 20;
int Ny = 10;
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("surf1", "SurfaceData");
double[][] p = new double[3][Nx*Ny];
double[][] normals = new double[3][Nx*Ny];
int[][] t = new int[3][2*(Nx-1)*(Ny-1)];
double[] color = new double[Nx*Ny];
| 163
int pos = 0;
double R = 10;
for (int i = 0; i < Ny; i++) {
for (int j = 0; j < Nx; j++) {
double theta = Math.PI*i/(Ny-1);
double phi = 2*Math.PI*j/(Nx-1);
double x = R*Math.sin(theta)*Math.cos(phi);
double y = R*Math.sin(theta)*Math.sin(phi);
double z = R*Math.cos(theta);
p[0][pos] = x;
p[1][pos] = y;
p[2][pos] = z;
normals[0][pos] = x;
normals[1][pos] = y;
normals[2][pos] = z;
color[pos] = z;
pos++;
}
}
pos = 0;
for (int i = 0; i < Ny-1; i++) {
for (int j = 0; j < Nx-1; j++) {
int p00 = Nx*i+j;
int p01 = Nx*i+j+1;
int p10 = Nx*(i+1)+j;
int p11 = Nx*(i+1)+j+1;
t[0][pos] = p00;
t[1][pos] = p01;
t[2][pos] = p11;
pos++;
t[0][pos] = p00;
t[1][pos] = p11;
t[2][pos] = p10;
pos++;
}
}
plot.set("pointdata", p)
.set("elementdata", t)
.set("colordata", color)
.set("normaldata", normals)
.set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
In this example, information about the surface normal direction is not given
implicitly by the triangle orientation, but instead explicitly by the parameter
normaldata by means of the 3-by-Nx*Ny array normals containing surface normal
vectors at each point. The normal vectors do not need to be normalized; only the
direction is used. The coloring of the sphere is based on the z-coordinate of each
triangle point and is stored for each point in the 3-by-Nx*Ny array color.
164 |
The sphere is constructed from a discrete grid defined in terms of spherical
coordinate angles, where each grid cell is divided into two triangles. The number
of triangles t is then given by 2*(Nx-1)*(Ny-1).
Comments
A Tube Data plot is similar to a Point Data, plot but with an absolute radius array
given as an argument to radiusdata. For the Point Data plot type, there is a similar
sphereradiusscale.
A RROWS IN 2D
The following code plots arrows in a circular pattern by using the Arrow Data plot
type.
// Arrows in a circular pattern in 2D
| 165
double angle = 2*Math.PI*i/N;
p[0][i] = Math.cos(angle);
p[1][i] = Math.sin(angle);
vec[0][i] = -len*p[0][i];
vec[1][i] = -len*p[1][i];
}
plot.set("pointdata", p)
.set("vectordata", vec);
plot.run();
selectNode(pg);
Comments
An Arrow Data plot associates an array of vectors, in the example vec, to each point
p.
A RROWS IN 3D
The following code plots arrows in a logarithmic spiral pattern by using the Arrow
Data plot type.
// Arrows in a logarithmic spiral pattern in 3D
Comments
In this example, in addition to the example in the section “Arrows in 2D”, color
data is used based on the point index.
166 |
A NNOTATIONS IN 2D
The following code renders text strings in a circular pattern by using the
Annotation Data plot type.
// Letters in a circular pattern in 2D
Comments
The property pos takes as its input an array of length 2 representing 2D
coordinates for the position of the string to be rendered. The property text takes
as its input the string to be rendered. The Boolean property showpoint determines
if a point, at the 2D coordinate position, should be rendered or not.
pg.run();
selectNode(pg);
| 167
Comments
The Boolean property latexmarkup determines if the text should be interpreted
using LaTeX syntax or not.
The Application Builder provides several built-in methods for reading and writing
different types of files: text files, CSV-files, Excel® files (requires LiveLink™ for
Excel®), and binary files. These file methods are listed in the table “File Methods”
on page 101.
Note that easy-to-use user-interface-based techniques for reading and writing to
file are available in the Form editor of the Application Builder. It is recommended
that you consider those techniques first before using the programming-based ways
described in this section. For more information, see the book Introduction to
Application Builder and “GUI Command Methods” on page 127. There, you
can also find information on the various file schemes used in the Application
Builder for reading and writing files when running applications in a web browser.
In addition, you can use the low-level methods available in the class CsReader to
read text files line by line or character by character. See the next section,
“Processing Text Files using the CsReader and CsWriter Classes” on page 182, for
more information.
If you have a LiveLink™ for Excel® license, then the following methods are
available for reading and writing Microsoft Excel Workbook files:
• readExcelFile
• writeExcelFile
168 |
I NTRODUCTION TO R EADING F ILES WITH A CSV-F ILE E XAMPLE
Assume that you want to automate a certain thermal analysis of a circuit board by
creating an application that reads in data from a spreadsheet. Further assume that
information about the circuit board components is given by a proprietary format
in a spreadsheet with columns for component type, heat dissipation, locations, and
sizes. Assume that such a file looks like:
B,0,0,0,-1.57,350,200,1.57
B,3,30,10,0,40,10,2
C,1,100,30,0,3,10,
C,1,110,30,0,3,10,
B,4,30,30,0,40,10,2
...
C,1,200,30,0,3,10,
B,10,100,150,0,10,20,30
B,10,130,150,0,10,20,30
B,10,160,150,0,10,20,30
Each row of the spreadsheet represents a different component. The first column
can contain a letter, either B or C, denoting that the component can be modeled
as either a Block or a Cylinder primitive. The next column is the total heat
dissipation within the component (measured in watts). The next three columns
represent the location of the component in the global Cartesian coordinate system
(measured in millimeters). Lastly, if the row contains a block component, there are
three more columns that denote the width, depth, and height of the block. If the
row contains a cylinder component, then there are two more columns that contain
the radius and height information, respectively.
For the example shown above, the first row of the spreadsheet represents the
circuit board itself, which is 1.57 mm thick and 350 mm-by-200 mm. It is offset
from the origin by −1.57 mm in the z direction and does not dissipate any heat.
| 169
You can write the data in the spreadsheet out to a comma-delimited text file, also
known as a CSV-file. The user interface of the application used to read the data is
shown in the figure below.
170 |
The Settings window for the File Import form object is shown in the figure below.
In the Settings window, CSV File (*.csv) is added to the File types list. When
browsing for the file, this setting will filter out any file that is not a CSV-file.
There is also a File Declaration called File 1, which is referenced by the file scheme
syntax upload:///inputFile in the method populateBoard, which is used to
read and process the data. The method is called as an event shown at the bottom
of the Settings window of the File Import form object in the Events section.
Note that if you would like to open a file browser from a button or a menu item,
instead of using a File Import object, you can create a method that calls the built-in
method importFile; for example
importFile("file1");
| 171
model.geom("geom1").create("P"+k, "Cylinder").set("pos", new
String[]{D[k][2], D[k][3], D[k][4]});
model.geom("geom1").feature("P"+k).set("r", D[k][5]);
model.geom("geom1").feature("P"+k).set("h", D[k][6]);
}
model.geom("geom1").feature("P"+k).set("selresult", "on");
model.variable().remove("var"+k);
model.variable().create("var"+k).model("comp1");
model.variable("var"+k).selection().named("geom1_P"+k+"_dom");
model.variable("var"+k).set("Q", D[k][1]);
}
model.geom("geom1").run();
zoomExtents("/form1/graphics1");
Comments
In the first line, the data read from the CSV-file is stored in the 2D array D. The
rest of the code parses this array and populates the various parts of a model object.
The application allows you to save the result as an MPH-file with variables defined
for the heat sources and geometry objects defined for the components, as shown
in the figures below.
172 |
You can download the MPH-file for this app from:
https://www.comsol.com/model/using-text-files-to-automate-model-preprocessing-46721
| 173
Workbook (*.xlsx) and Microsoft Excel Workbook (*.xls) to the File types section, as
shown in the figure below.
The next step is to add a few lines of code in the beginning of the method
populateBoard, as shown below.
String file_name = getFilePath("upload:///inputFile");
if (file_name.endsWith(".xls") || file_name.endsWith(".xlsx"))
D = readExcelFile("upload:///inputFile");
else if (file_name.endsWith(".csv"))
D = readCSVFile("upload:///inputFile");
else
error("Unknown file type.");
Comments
The 2D array D can be defined as a global array in the Declarations node in the
application tree. Alternatively, it can be declared as an array that is local to the
method by adding the line
String[][] D = null;
before the if statement. Which option to choose depends on how you would like
to use the 2D array data after having read the file.
The method getFilePath returns the full path and name of the uploaded file. The
if statements control which method is used to read the file based on its file
extension. The file extension is retrieved with the Java® method endsWith(),
174 |
which belongs to the String class. Note that you can see which methods are
available for a string by typing the name of the string followed by a period and
Ctrl+Space, as shown in the figure below.
to
B,10,130,140,0,10,20,30
This corresponds to a change in the y-coordinate of one of the blocks from 150 to
140.
The following code shows how to make this change and then write data on this
format, assuming that the array D has been declared as a global variable in the
Declarations node, as described above.
int[] sz = matrixSize(D);
D[sz[0]-2][3] = "140.0";
writeCSVFile("temp:///my_layout.csv", D);
fileSaveAs("temp:///my_layout.csv");
| 175
Comments
The first line stores the size of the 2D array (or matrix) D in a 1-by-2 array (or
vector) sz. The second line sets the string value of the y-coordinate of the block
of the second-to-last row in D.
The line
writeCSVFile("temp:///my_layout.csv", D);
176 |
R EADING M ATRIX F ILES
Reading files with numerical data in matrix format is easiest when using the
readMatrixFromFile method. This method assumes that the file has the
spreadsheet format, as available in the model tree Export node. The example below
shows a file on the spreadsheet format.
% Model: my_model.mph
% Version: COMSOL 6.0.0.278
% Date: Nov 1 2020, 8:00
% Dimension: 1
% Nodes: 5
% Expressions: 1
% Description: Line graph
% x y
1.2 -0.45
1.11 -0.3
1.0440468877558806 -0.38655264416650392
1.041666666666667 -0.49166666666666667
1.02 -0.15
The first few lines with comments start with the character % and are ignored by the
readMatrixFromFile method. You can optionally omit such lines and just have
the numerical part of a file read by readMatrixFromFile. Assume that this file is
uploaded to an application using a File Import form object and a File declaration
file1. The following code can then be used to read the data into a double array p.
double p[][] = readMatrixFromFile("upload:///file1”);
The code below shows how to both import and visualize these points in an
application that, in addition to a File Import form object and a File declaration
file, has a form form1 and a graphics object graphics1.
double p[][] = readMatrixFromFile("upload:///file1");
double pt[][] = transpose(p);
plot.set("pointdata", pt);
plot.run();
useGraphics(model.result(pgTag), "form1/graphics1");
Reading files on the spreadsheet format as a string array can be done with the
method readStringMatrixFromFile. Also, in this case, the comment lines will be
ignored. The code below shows how you can replace the first few lines in the
above example using readStringMatrixFromFile instead of
readMatrixFromFile.
| 177
String p[][] = readStringMatrixFromFile("upload:///file1");
double pt[][] = transpose(toDouble(p));
The resulting file can now be read back in and plotted by using the code of the
previous example. The result, in an application, may look like the figure below.
178 |
Note that you can append data to an already existing file by providing an
additional Boolean input argument; for example:
writeFile("temp:///my_data.txt", p,true);
fileSaveAs("temp:///my_data.txt");
If you would like to export a matrix with a mix of numeric and text data, you can
use the writeFile method with a string array instead of a double array. The syntax
for this case is otherwise identical to that of the double array shown in the example
above.
| 179
This application has two form objects: a File Import form object referencing a File
declaration file1 and a Text form object referencing a string str declared in the
Declarations node as a global variable.
The File Import form object has an Event that calls the method read_string upon
data change.
This method has one line of code, as shown below.
str = readFile("upload:///file1");
Since the Text object is referencing the global string str, the contents of the file
are displayed in the Text object immediately after import.
Parsing of smaller text files can be done with readFile in combination with the
many text processing methods available in the String class. However, it is often
more efficient to use methods in the CsReader class, as described in the section
“Processing Text Files using the CsReader and CsWriter Classes” on page 182,
especially for larger text files. The reason is that when using the readFile method,
the entire file is read into a string with all its contents kept in memory; whereas
when using the CsReader class methods, only small portions of the file are kept in
memory at any given time.
If you would like to parse smaller text files using readFile, then the built-in
“String Methods” on page 150 are useful. The example code below illustrates
using the built-in methods findIn, substring, split, as well as the regular Java®
methods System.getProperty and String.startsWith. The example parses the
header of a text file containing polygon information to retrieve information about
the number of points of each polygon in the main body of the file (not shown) as
180 |
well as the number of properties (for example, color or material property). The
header portion of the file may look like the example below.
Demo file for string parsing
Created on May 1st 2018
begin_header
number_of_points 4
number_of_properties 4
end_header
The code for parsing the header is listed below. It stores the number of points and
properties in the variables n_of_points and n_of_properties, respectively. To
keep things simple, no error handling is done. For example, the code assumes that
there is exactly one instance of begin_header and end_header.
int n_of_points = 0;
int n_of_properties = 0;
if (headerRowArr.length == 2) {
if (headerRowArr[0].trim().equalsIgnoreCase("number_of_points"))
n_of_points = toInt(headerRowArr[1]);
if (headerRowArr[0].trim().equalsIgnoreCase("number_of_properties"))
n_of_properties = toInt(headerRowArr[1]);
}
ix++;
} while (ix < headerContentsArr.length);
The Java® String class has many methods for text processing. See the online
Java® documentation for more information.
| 181
+"2 3\r\n"
+"4 5\r\n";
writeFile("temp:///my_data.txt", contents);
fileSaveAs("temp:///my_data.txt");
The use of the file scheme syntax temp:/// is described above in the earlier
examples of this section. The end-of-line characters of this example are for
Windows®; see also “Special Characters” on page 11.
To append additional data to the same file, for example:
String contents_2 = "6 7\r\n"
+"8 9\r\n"
+"10 11\r\n";
use an additional Boolean input argument, which appends data when set to true:
writeFile("temp:///my_data.txt", contents_2, true);
fileSaveAs("temp:///my_data.txt");
and the corresponding code is listed below (compare with the example “Reading
a Text File to a String” on page 179).
int n_of_points = 0;
int n_of_properties = 0;
182 |
int max_header_length = 100;
Comments
The line
CsReader reader = openFileStreamReader("upload:///file1");
opens a Java® character stream and assigns it to the object reader belonging to
the class CsReader.
The while loop condition contains the statement
(line = reader.readLine()) != null)
which is reading a line from the character stream and storing the result in the string
line. A line is considered to be terminated by one of the characters carriage return
\r, line feed \n, or the composite \r\n. If there are no more lines to read, then
null is returned.
For more information on the string methods used in this and earlier examples,
including findIn, substring, and split, see the section “Writing a String to a
Text File” on page 181.
| 183
Note that you can see which additional methods are available for the reader
object by using Ctrl+Space, as shown in the figure below.
By using Ctrl+Space following a string, you can see the many additional methods
available for strings, including the trim method used in the example above:
184 |
-1.1 -0.8
1.2 -0.9
0.9 1.3
-0.8 1.05
% Elements (segments)
1 2
2 3
3 4
The first few lines with comments start with the character % and are ignored when
imported as an Interpolation Curve. The first section containing data starts on the
line after % Coordinates. The second section containing data starts on the line
after % Elements (segments). Note that the strings Coordinates and Elements
(segments) are not necessary but each section containing data will be assumed to
start after each block of comments, regardless of what comes after the character %.
There may be additional blocks of data when, for example, exporting Contour plot
data.
The following example code uses a CsWriter stream to write interpolation curve
data to a text file. A template point set p is copied in a circular pattern for a given
radius R and number of copies n_of_copies.
CsWriter writer = openFileStreamWriter("temp:///my_curve.txt");
writer.append(header);
| 185
for (int i = 0; i < template_length; i++) {
i1 = i+1;
i2 = (i+1)%template_length+1;
i1 = i1+j*template_length;
i2 = i2+j*template_length;
line = toString(i1)+"\t"+toString(i2)+"\r\n";
writer.append(line);
}
}
writer.flush();
writer.close();
fileSaveAs("temp:///my_curve.txt");
Comments
The line
CsWriter writer = openFileStreamWriter("temp:///my_curve.txt");
opens a Java® character stream and assigns it to the object writer belonging to
the class CsWriter.
The line
writer.append(header);
appends the contents of the string header to the (empty) file my_curve.txt.
The line
writer.flush();
writes the contents of the character stream buffer to file and empties the buffer but
does not close the stream permanently. At this point, you can still write more data
to the stream.
The line
writer.close();
closes the stream permanently. If you wish to write additional data to the file, you
have to open the stream again and append additional data.
186 |
Just as described above for the reader object, you can see which additional
methods are available for the writer object by using Ctrl+Space, as shown in the
figure below.
| 187
W RITING B INARY F ILES
You write data to a binary file by using the methods of the class CsBinaryWriter
in a way that is somewhat similar to that of writing text using CsWriter. However,
instead of writing strings and characters, you are writing bytes. To see how many
bytes each data type requires, see the table in the section “Primitive Data Types”
on page 8.
The example code below writes random 3D point data to a binary file. Each point
coordinate is stored as a double and takes 8 bytes to store. The first 4 bytes of the
file stores the number of points in the file as an int.
To conveniently convert between the regular data types, such as double, int, and
byte arrays, the Java® library method java.nio.ByteBuffer is needed. This
method is not part of the standard methods available in the Method Editor and
you need to use the fully qualified Java® class name java.nio.ByteBuffer, as
shown in the example code below.
byte[] bytes8 = new byte[8];
byte[] bytes4 = new byte[4];
CsBinaryWriter bwriter =
openBinaryFileStreamWriter("temp:///my_binary_file.dat");
int N = 1000;
java.nio.ByteBuffer.wrap(bytes4).putInt(N);
bwriter.write(bytes4);
p[k][1] = Math.random();
java.nio.ByteBuffer.wrap(bytes8).putDouble(p[k][1]);
bwriter.write(bytes8);
p[k][2] = Math.random();
java.nio.ByteBuffer.wrap(bytes8).putDouble(p[k][2]);
bwriter.write(bytes8);
bwriter.flush();
}
bwriter.close();
fileSaveAs("temp:///my_binary_file.dat");
Comments
The first two lines declare byte arrays of size 8 and 4, respectively
The line
CsBinaryWriter bwriter =
openBinaryFileStreamWriter("temp:///my_binary_file.dat");
188 |
int N = 1000; // The number of points
uses the imported ByteBuffer method to convert the integer N to a byte array
bytes4 of length 4.
The line
bwriter.write(bytes4);
close the byte stream and display a file browser to the user to select a location to
save the binary file.
breader.read(bytes4);
int N = java.nio.ByteBuffer.wrap(bytes4).getInt();
breader.read(bytes8);
p[k][1] = java.nio.ByteBuffer.wrap(bytes8).getDouble();
breader.read(bytes8);
p[k][2] = java.nio.ByteBuffer.wrap(bytes8).getDouble();
}
breader.close();
| 189
double pt[][] = transpose(p);
plot.set("pointdata", pt);
plot.run();
useGraphics(model.result(pgTag), "form1/graphics1");
Comments
The line
CsBinaryReader breader = openBinaryFileStreamReader("upload:///file1");
opens a Java® byte stream based on a File declaration file1, typically referenced
in a File Browser form object, as in the earlier examples on reading text files.
The two lines
breader.read(bytes4);
int N = ByteBuffer.wrap(bytes4).getInt();
is the same as
p[k][2] = ByteBuffer.wrap(bytes8).order(ByteOrder.BIG_ENDIAN).getDouble();
In case the format you are reading is on the little endian format, the corresponding
line should be
p[k][2] = ByteBuffer.wrap(bytes8).order(ByteOrder.LITTLE_ENDIAN).getDouble();
190 |
Converting Interpolation Curve Data
The curve is checked for being monotonous, which is required in order to be able
to convert to an interpolation function. Note that since an interpolation curve is
represented using splines, even though the interpolation points form a
monotonous sequence, the resulting curve may not; hence, the second consistency
check (the first check can potentially be skipped).
// Convert using N points
int N = 100;
double monoTol = 1e-6;
int edgeNum=1;
| 191
for (int i = 1; i < len; i++) {
if ((curvePoints[i][0]-curvePoints[i-1][0]) < scaledTol) {
error("Curve needs to be a function curve with monotonously growing x
coordinates.");
}
}
model.func("int1").set("table", toString(XY));
Comments
The method assumes that there is a geometry sequence geom1 with an
interpolation curve ic1. It further assumes that there are no other geometry
features and that the geometry object has a single edge. The integer N determines
how granular the interpolation table should be. It is assumed that there is only one
edge in the geometry sequence (edgeNum). Note that the curve parameter range
may not be the unit interval (minS does not have to be 0.0 and maxS does not have
to be 1.0). To run the method more than once, you can create a cleanup method
that contains the lines:
model.func().remove("int1");
model.result().remove("pg1");
192 |
P l o t t i n g P o i n t s o n a P a r a me t r i c S u r f a c e
| 193
double s12List[][] = new double[N*N][2];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
s12List[i+N*j][0] = s1List[i];
s12List[i+N*j][1] = s2List[j];
}
}
double[][] XY = geom1.faceX(faceNum, s12List);
// Plot points
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("pt1", "PointData");
double[][] p = new double[3][N*N];
double[] color = new double[N*N];
for (int i = 0; i < N*N; i++) {
p[0][i] = XY[i][0];
p[1][i] = XY[i][1];
p[2][i] = XY[i][2];
color[i] = p[1][i];
}
plot.set("pointdata", p)
.set("colordata", color)
.set("coloring", "colortable")
.set("sphereradiusscale", 1);
plot.run();
selectNode(pg);
U s i n g S e le c t i o n s f o r E d i t i n g G e o m e t r y O b j e c t s
The following method generates a plate with an array of cylinders. The cylinders
may be used, for example, in a difference operation to create an array of holes in
the plate.
GeomSequence geom = model.component("comp1").geom("geom1");
geom.create("blk1", "Block");
geom.feature("blk1").set("size", new int[]{10, 10, 1});
geom.create("start_cyl1", "Cylinder");
geom.feature("start_cyl1").set("pos", new double[]{2.5, 2.5, 0});
geom.create("arr1", "Array");
geom.feature("arr1").selection("input").set("start_cyl1");
geom.feature("arr1").set("fullsize", new int[]{2, 2, 1});
geom.feature("arr1").set("displ", new int[]{5, 5, 0});
geom.run("arr1");
194 |
The resulting geometry is shown in the figure below.
Assume now that the resulting geometry, from the previous step, corresponds to
an imported geometry object and that you would like to replace the cylinders with
larger cylinders before subtracting and generating the holes. Furthermore, assume
that the cylinder objects are generated by an external software in such a way that,
although you know these objects are cylinders, they are represented as generic
geometry objects with no information on radius, height, or position. The
following method finds the array of cylinders, extracts coordinate information for
each cylinder, deletes the cylinders, creates a new array of wider cylinders, and
| 195
finally subtracts the cylinders from the plate. The resulting geometry is shown in
the figure below.
196 |
double scale = Math.max(Math.max(MaxX-MinX, MaxY-MinY), MaxZ-MinZ);
double scaleSelTol = scale*selTol;
double ry;
double tol = scale*1e-6; // Tolerance check for skew cylinders, optional
consistency check
for (int i = 0; i < nso; i++) {
xc[i] = (MaxXC[i]+MinXC[i])/2;
yc[i] = (MaxYC[i]+MinYC[i])/2;
zc[i] = (MaxZC[i]+MinZC[i])/2;
hc[i] = MaxZC[i]-MinZC[i];
radius[i] = (MaxXC[i]-MinXC[i])/2;
ry = (MaxYC[i]-MinYC[i])/2;
| 197
if (Math.abs(radius[i]-ry) > tol)
error("Object is not a circular cylinder.");
}
Comments
The method assumes that the input geometry objects have the same structure as
the plate with cylinders example above. Note that to clear the geometry sequence,
you can create a method with the line
model.component("comp1").geom("geom1").feature().clear();
This can be useful if you are running the main method from above repeatedly,
since you need to clear the geometry sequence before each run.
The first of the lines
geom.feature("del1").selection("input").init();
geom.feature("del1").selection("input").set(so);
initializes the selection to be empty of type object. The second line then selects all
objects with names in the array so. In general, a call to init() without input
argument means that the selection is for objects and init(n), where n=0,1,2, or
3, means that the selection is for points, edges, faces, and domains, respectively.
198 |
Note that in some cases n=-1 is used to denote the object level (instead of an
empty input argument); see the Programming Reference Manual.
The figure below shows an example based on a larger array of cylinders.
R e c u r s i o n a n d R e c u r s i v e l y D e f i n ed G eo m e t r y O b j e c t s
Sierpinski Carpet
The following method, create_carpet, initiates the recursion to a certain
recursive level according to a user-defined Parameter mslevel, defined under
Global Definitions in the Model Builder. It sets a limit at 5 levels in order to avoid
creating an exceedingly large geometry. The method assumes that you have
created a 2D Component and that you have declared an integer scalar variable
counter.
int level = (int) model.param().evaluate("mslevel");
if (level < 1)
error("Carpet level needs to be at least 1.");
if (level > 5)
error("Carpet level needs to be at most 5.");
counter = 0;
model.component("comp1").geom("geom1").feature().clear();
model.component("comp1").geom("geom1").autoRebuild("off");
| 199
double cx0 = 0, cy0 = 0;
double si0 = 1;
carpet(level, cx0, cy0, si0);
model.component("comp1").geom("geom1").runPre("fin");
The method create_carpet in turn calls the main recursive function carpet,
listed below, using four input arguments for the recursion level, the center x- and
y-coordinates, and the current side length.
The input arguments are defined in the method’s Settings window, as shown
below.
200 |
model.component("comp1").geom("geom1").feature("sq"+strix+"C"+
toString(counter)).label("Square"+strix+"C"+toString(counter));
} else {
l1 = l-1;
si1 = si/3;
carpet(l1, posx, posy, si1);
}
}
}
}
The figure below shows the resulting geometry for a level-3 carpet.
Note that the number of square geometry objects defined for the level 3 carpet is
512. The number of geometry objects for a level-N carpet is 8N. This means that
for the maximum level 5 set by the method create_carpet, the resulting
geometry has 32,768 geometry objects.
Menger Sponge
The methods for the Sierpinski carpet readily generalize to 3D. However, in 3D,
you need to be careful not to generate more objects than your computer can
handle. The method assumes that you have created a 3D Component and that you
have declared an integer scalar variable counter.
The following method, create_sponge, initiates the recursion.
int level = (int) model.param().evaluate("mslevel");
if (level < 1)
error("Sponge level needs to be at least 1.");
if (level > 3)
error("Sponge level needs to be at most 3.");
counter = 0;
| 201
model.component("comp1").geom("geom1").feature().clear();
model.component("comp1").geom("geom1").autoRebuild("off");
double cx0 = 0, cy0 = 0, cz0 = 0;
double si0 = 1;
sponge(level, cx0, cy0, cz0, si0);
model.component("comp1").geom("geom1").runPre("fin");
The method sponge, shown below, is called by the above method and recursively
creates a Menger sponge.
int l = level;
double posx, posy, posz, si1;
String strix;
int l1;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
posx = cx+i*si-si;
posy = cy+j*si-si;
posz = cz+k*si-si;
strix = toString(i)+toString(j)+toString(k);
if ((Math.abs((i-1))+Math.abs((j-1))+Math.abs((k-1))) > 1) {
if (l == 1) {
counter = counter+1;
model.component("comp1").geom("geom1").create("blk"+strix+"C"+
toString(counter), "Block");
with(model.component("comp1").geom("geom1").feature("blk"+strix+"C"+
toString(counter)));
set("base", "center");
set("size", new String[]{toString(si), toString(si),
toString(si)});
set("pos", new double[]{posx, posy, posz});
endwith();
model.component("comp1").geom("geom1").feature("blk"+strix+"C"+
toString(counter)).label("Block"+strix+"C"+toString(counter));
} else {
l1 = l-1;
si1 = si/3;
sponge(l1, posx, posy, posz, si1);
}
}
}
}
}
202 |
The figure below shows the resulting geometry for a level-2 sponge.
In this case, the number of geometry objects grows with the level N as 20N and
the level-2 sponge shown above has 400 block geometry objects.
Note that if you have any of the add-on products for additional CAD
functionality, you can export these geometry objects on standard CAD formats.
Without add-on products, you can export the meshed geometry to any of the
supported mesh formats.
You can retrieve a variety of mesh information and statistical quantities, such as
element quality, the total number of elements, the total mesh volume, and so on.
The figure below shows part of an application displaying such information.
Assuming that you would like to link the various mesh quantities to variables
declared under the Declarations node, the corresponding method code would
include the following lines of code:
| 203
a = model.component("comp1").mesh("mesh1").getNumElem();
b = model.component("comp1").mesh("mesh1").getMinQuality("tet");
c = model.component("comp1").mesh("mesh1").getMeanQuality("tet");
d = model.component("comp1").mesh("mesh1").getMinVolume("tet");
e = model.component("comp1").mesh("mesh1").getVolume("tet");
A c c e s s i n g H i g h e r - Or d e r F i n i t e E l e m e n t N o d e s
The extended mesh data structure contains information related to the finite
element method including, for example, the placement of higher-order element
nodes. The extended mesh information is contained in the class XmeshInfo and
provides information about the numbering of elements, nodes, and degrees of
freedom (DOFs) in the extended mesh and in the matrices returned by the
Assemble feature and the solvers. For detailed information on XmeshInfo, see the
Programming Reference Manual.
The following example method illustrates how to use the extended mesh
information to plot higher-order nodes in a few important special cases. Note that
general functionality for this is built-in when creating a Mesh Plot under Results.
The example below covers cases with one model component, one geometry, and
a subset of physics combinations. If you apply it to other cases, you will get an
error message.
204 |
// Note that this method is only implemented for one component and one geometry
and does not work for all physics combinations.
int sdim = 0;
if (coordsize[0] == 3) {
sdim = 3;
} else if (coordsize[0] == 2) {
sdim = 2;
} else
error("The geometry of the first component is not 2D or 3D.");
with(model.result(nodes).feature("mesh"));
set("elemcolor", "none");
| 205
set("wireframecolor", "gray");
set("elemscale", 0.999);
endwith();
with(model.result(nodes));
set("edges", true);
set("data", mesh);
endwith();
selectNode(pg);
Comments
The first few lines of the method set up a solver step object step, which is used to
extract the extended mesh information. The extended mesh information, which
contains information on the higher-order nodes, is extracted in the line
XmeshInfo xmi = step.xmeshInfo();
The lines
XmeshInfoNodes mynodes = xmi.nodes();
double[][] coords = mynodes.gCoords();
int[] coordsize = matrixSize(coords);
access and store the finite element node coordinates in a 2-by-coordsize (2D) or
3-by-coordsize (3D) array.
The following code segments set up a mesh dataset and an associated mesh plot.
The last section uses the low-level PointData plot type to visualize the finite
element nodes. For more information on this plot type, see “Points in 3D” on
page 159.
A c c e s s i n g S y s t e m M at r i c e s a n d V e c t o rs
You can gain low-level access to the finite element system matrices and vectors by
adding nodes of the types Assemble and Input Matrix under a Study node.
The example below shows how to set up and solve a 2D electrostatics problem on
the unit square [0,1]-by-[0,1]. After the original problem is solved, the load
vector is modified at a user-defined coordinate. The code searches for the degree
of freedom closest to the target user-defined coordinate and modifies the load
vector to a user-defined value. The physical interpretation of the modified load is
that of an added volume charge.
206 |
To run the example code below, first use the Model Wizard to create a blank
model. Then, add a new method and paste the example code below. Finally, run
the method. You can try changing the variable values in the Initializations section
at the beginning of the code and run again.
// Initializations
double x_load = 0.2; // Target x-coordinate for load
double y_load = 0.2; // Target y-coordinate for load
double load = 1e-9; // Load, volume charge
double dist = 10.0; // Distance to (x_load,y_load) from degree of freedom
int index = 0; // Index of the degree of freedom closest to (x_load,y_load)
| 207
model.physics("es").feature().create("sfcd1", "SurfaceChargeDensity", 1);
model.physics("es").feature("sfcd1").selection().set(new int[]{4});
// Add a varying distributed charge density along the rightmost boundary.
model.physics("es").feature("sfcd1").set("rhoqs", "1e-9*y");
model.component("comp1").physics("es").feature("ccn1").set("epsilonr_mat",
"userdef");
model.component("comp1").physics("es").feature("ccn1").set("epsilonr", "1");
// Change to 1st order shape functions, to keep things simple.
model.component("comp1").physics("es").prop("ShapeProperty").
set("order_electricpotential", 1);
// Create a 2D plot group with a surface plot for the original problem.
model.result().create("pg1", 2);
model.result("pg1").set("data", "dset1");
model.result("pg1").feature().create("surf1", "Surface");
model.sol("sol2").attach("std2");
208 |
// Define which system matrices should be input.
solft.set("L", "on");
solft.set("K", "on");
solft.set("M", "on");
solft.set("N", "on");
// K
int KM = solft.getM("K");
int KN = solft.getN("K");
int KNnz = solft.getNnz("K");
int[] Ki = solft.getSparseMatrixRow("K");
int[] Kj = solft.getSparseMatrixCol("K");
double[] Kv = solft.getSparseMatrixVal("K");
// For more information, see the Programming Reference Manual.
// L
double[] Lv = solft.getVector("L");
// N
int NM = solft.getM("N");
int NN = solft.getN("N");
int NNnz = solft.getNnz("N");
int[] Ni = solft.getSparseMatrixRow("N");
int[] Nj = solft.getSparseMatrixCol("N");
double[] Nv = solft.getSparseMatrixVal("N");
// M
double[] Mv = solft.getVector("M");
| 209
// K
solft.createSparseMatrix("K", KM, KN, KNnz, true);
solft.addSparseMatrixVal("K", Ki, Kj, Kv);
// L
solft.createVector("L", Lv.length, true);
solft.setVector("L", Lv);
// N
solft.createSparseMatrix("N", NM, NN, NNnz, true);
solft.addSparseMatrixVal("N", Ni, Nj, Nv);
// M
solft.createVector("M", Mv.length, true);
solft.setVector("M", Mv);
Comments
In the previous example, “Accessing Higher-Order Finite Element Nodes” on
page 204, the XmeshInfoNodes methods are used to access finite element nodes
that have the same length as the number of finite element nodes. In this example,
the XmeshInfoDofs methods are used to access the degrees of freedom vector,
which has the same length as the load vector.
Note that only the load vector is modified. The other matrices and vectors are
merely extracted and then put back into the system again.
210 |
D a t a V a l i d at i o n
In the Settings window for input fields, the section for Data Validation has a
subsection Numerical validation with settings that allow you to validate user inputs
with respect to values. The figure below shows an example of settings for entering
a radius.
The Filter options, the Lower bound and Upper bound settings are only visible if the
selection in the Unit dimension check combo box is set to one of None, Append unit
to number or Append unit from unit set.
The Value input fields for Lower bound and Upper bound allow you to use global
parameters or scalar declaration variables. Using scalar declaration variables in this
field supports the same type of data conversions as when using declaration names
as a method input argument in a command sequence. This means that scalar
Integer and Double declaration variable will always work and scalar String
declarations will work if they can be converted to a Double.
Using declaration variable names or global parameters in the Value input fields can
only be used when Unit dimension check is set to Append unit to number or Append
unit from unit set. In that case, the bounds are checked based on the numerical
value entered by the user in the input field. In a Value input field, you can not use
expressions in terms of declared variables, but only a single declaration name. If a
parameter exists with the same name as a declaration variable, then the value of the
global parameter will be used.
| 211
The scope of declaration names used in a Value field is limited to the parent form
of the input field.
Note that the bounds are updated dynamically as the value of the parameters or
declaration variable is changed. This means that several input fields, not actively
changed by the user, can fail numerical validation at once caused by a change in
another input field which causes the value of a parameter used in a validation
bound to change.
The Error message text field allows you to write a customized error message to be
shown when the input values are out of bounds. The error message text can
contain eval(variable), which is also allowed in the title for plot groups in the
Model Builder. In addition, the Tooltip of the input field allows use of
eval(variable). The variable can be the name of any global parameter or scalar
declaration. If a parameter and a declaration with the same name exists, then the
parameter is used. In addition to parameter and declaration names the special
strings MINVALUE and MAXVALUE can be passed to eval and will return the value
entered as lower bound or upper bound.
When using eval for a parameter, it will return the evaluated value of the
parameter using the same unit that is used to enter the bounds in the Value input
field. For a scalar declaration the string value of the declaration is returned which
will be true or false for a boolean declaration.
212 |
In such a case, the method will automatically get a scalar String input argument
with the name newValue, as shown in the figure below.
The variable newValue will get the new value entered in the input field by the user
of the app.
In addition to the new value, you may need to access to the old value entered in
the input field, for example, to be able to restore it if your custom data validation
fails. For this purpose, you can use the event object and its associated methods
which are accessible from methods that are called from events. The event object
has the following methods:
event.getOldValue()
event.getNewValue()
which return the old and new value, respectively, of the source associated with the
form object. The methods return an object called Value which is similar to the
DataSource object returned when you call:
app.declaration(<declarationName>)
| 213
A CCESS TO F ORMS AND F ORM O BJECTS IN E VENT M ETHODS
For methods that are called from events, you can access the properties formTag
and formObjectTag for the form object that triggered the event. This way, you
can access the form object from the event method using the syntax:
app.form(<formTag>).formObject(<formObjectTag>)
To make it more convenient to access the user control that triggered the event
there is also a method
event.getUserControl()
that returns the form object, menu item, or ribbon item that triggered the event.
The event object is additionally available for methods called from command
sequences in, for example, menu items and ribbon items. The interface
IPropertyAccess which has methods for getting and setting values on all user
interface controls such as form objects, menu items and so on can then be returned
by the getUserControl method.
Changes performed in the dialog box will then be stored to the associated source
variable or parameter immediately and it will work like a regular form in the main
application window.
214 |
To accomplish storing of the values when the dialog box is set to work on a copy,
corresponding to the first option above, and store the values on request, the
Settings window for a button object has a check box Store changes in the section
Dialog Actions, as shown in the figure below. When this check box is selected, the
values in the dialog box are stored from the copy used by the dialog box to the
actual values after the command sequence specified for the button has been run.
Part of this command sequence can include a method that performs validation of
the data that the user has entered in the dialog box. The figure below shows an
example of this scenario, with a method validation, for an OK button used in a
dialog box.
However, this validation approach will not work. This is due to the fact that when
the dialog box is set to Store changes on request, the values have not yet been
stored when the validation method runs since dialog actions such as Store
changes are performed after the command sequence for the button has been run.
This means that the validation code cannot access the new values to perform a
validation on them.
To remedy this, there is an access method that can be used together with form
objects such that the value currently entered in them can be used for data
| 215
validation before they have been stored in their associated source. The syntax for
calling this access method is:
app.form(<formTag>).formObject(<formObjectTag>).getValue();
This call will return a Value object, the same type of object described earlier and
used for calls to event.getOldValue() and event.getNewValue(). With this
type of access to the current value in the form object, a data validation can be
performed. The following form object types support the getValue method.
• Input field
• Toggle Button
• Check box
• Combo box
• File import
• Array input
• Radio button
• Selection input
• Text
• List box
• Table
• Slider
• Knob
• Gauge
If the user has clicked OK in a dialog box and the data validation fails, you typically
want to show an error dialog box and then leave the dialog box open to allow the
user to correct the input fields that failed validation. This not possible to achieve
using a direct approach since if the OK button is set to have Store changes as a Dialog
Action the storing of the data will always be performed after the command
sequence, defined in the Settings window of the OK button, has been run.
To remedy this, there is a method for programmatically store changes:
storeChanges(String name)
which will store the changes for a given form, used as a dialog box.
Note that you can call:
closeDialog(<dialogTag>)
216 |
call storeChanges and closeDialog as part of the flow in the validation method
if the validation passes.
Having a way to programmatically store changes also helps with the case where
the dialog box contains a button that also performs something when closing the
dialog box, for example, a Compute button. A validation may then be followed by,
for example, a compute method. In order to get this compute method to work on
the new values entered in the dialog box, storeChanges can be called after the
data validation has been performed but before the compute method is called.
If the user types a different string, then an alert window with a message Unknown
command is shown:
and the string value for the Command is reset to the default Type open dialog.
| 217
If the correct string open dialog is entered, then a dialog box opens, as shown in
the figures below.
The dialog box expects an integer between 1 and 10, in the first input field. In
addition, it expects the value in the second input field to be identical to the value
in first input field. If all these criteria is fulfilled, no error message is shown and the
app starts from the beginning showing the string Type open dialog in the main
form.
If the user enters different values, as shown in the figure below:
218 |
then an error message is shown with the message The value of y must be equal
to x.
When the user now closes the Error message dialog box, the user gets a new
opportunity to enter matching values in the dialog box.
The data validation functionality in this demonstration app is implemented using
an On data change event for the Command input field in the main form form1, as
shown in the figure below.
| 219
The code in method3 shows the user of event.getNewValue and
event.getOldValue, as shown below:
String value = event.getNewValue().getString();
String oldValue = event.getOldValue().getString();
if (value.toLowerCase().equals("open dialog")) {
dialog("/form2");
} else {
alert("Unknown command.");
command = oldValue;
}
The dialog box form2 has Store changes set to On request, as shown in the figure
below.
In the dialog box, the value of the variable x is validated in the On data Change
event of the input field with label Number x (1-10) and also when clicking the OK
button. The y value is only validated when clicking the OK button. The data
validations are implemented using the form object access method getValue. The
OK button makes use of the storeChanges method. The figure below shows the
220 |
method run for the On data change event for the input field with label Number x
(1-10).
| 221
The OK button calls method1 for the On data Change event, as shown in the figures
below.
222 |
int valueY =
app.form("form2").formObject("inputfield2").getValue().getInt();
method2();
if (valueX != valueY) {
error("The value of y must be equal to x.");
} else {
storeChanges("/form2");
closeDialog("/form2");
command = "Type open dialog";
}
In order to use selections in the Model Builder from an add-in, you leave the
source settings empty when using a selection input form object at the time the
add-in is created. Then you use a method to create an explicit selection in the
current model and link it to the selection input object of the add-in.
The figure below shows a simple add-in Boundary Selections used to demonstrate
this functionality. It contains a form with a selection input object and a button.
When the add-in is in focus, the user can click on one or more boundaries in the
| 223
graphics window to create the selection. Clicking the button triggers an Alert
with a dialog box where the selected boundaries are listed.
The figure below shows the form of the add-in as it appears when in use in a
model:
224 |
When the add-in Settings form is added to a model, an On load event is triggered
that runs a method createSelection. The Settings form is shown in the figure
below:
| 225
accidentally collide with the user’s selection features, used for other purposes, in
the Model Builder. The figure below shows the declaration of this String variable.
226 |
The figure below shows the Settings window for the Selection Input form object
selectioninput1. Note the empty selection of the Source and Graphics to Use
When Active. These settings are set by the method createSelection.
| 227
The figure below shows the Settings window for the Button form object button1.
When this button is clicked, a method displaySelection is run. The code for this
method is shown below.
SelectionFeature selection = model.selection(selectionTag);
alert("Selection boundaries are: "+concat(", ",
toString(selection.entities())));
For your own add-in, you can replace this code with any number of actions that
accepts an explicit selection as an input. For example, you can add the following
lines of code to the end of the method displaySelection in order to add a
variable a, local to this explicit selection, having the value 5.
model.component("comp1").variable().create("var1");
model.component("comp1").variable("var1").selection().geom("geom1", 2);
model.component("comp1").variable("var1").set("a", "5");
model.component("comp1").variable("var1").selection().named(selectionTag);
228 |
U s i n g B u i l t - I n M e t h o d s f r o m an E xte rna l Ja va Libr ary
4 Click Finish.
5 Right-click the src folder in your Eclipse project and select New... Package. Enter
demo as the package name and click Finish.
6 Right-click the demo package in your Eclipse project and select New... Class. Enter
Hello as the class name.
7 Click on Superclass>Browse and select ApplicationLanguageBase. Click OK
and Finish.
8 In the editor window for the Hello class, add the following method and save
the file:
public static void hello() {
alert("Hello!");
}
9 Right-click the JavaLibDemo project and select Export>Java>JAR file. Select the
export destination JAR-file and click Finish.
10Start COMSOL Multiphysics and create a Blank Model.
11In the Application Builder, under the application tree, right-click Libraries and
select External Java Library. In the Settings window, click Browse and select the
JAR-file previously exported from Eclipse®.
12In the Application Builder, add a form, button, and method for the button.
13In the Method Editor for the method, add the following code:
Hello.hello();
14Click Test Application and click the button in the application to verify that the
alert method is invoked.
| 229
M e as u r i n g t h e J av a H e a p S p a ce M e m o r y
Software components that are based on Java®, such as certain parts of COMSOL
Multiphysics, are predefined to use only a limited amount of memory. This limit,
the Java® heap space, is specified during startup. Note that the Java® heap space
only affects certain parts of the software and not, for example, meshing or solvers.
By default, COMSOL Multiphysics allocates 2 GB of Java® heap space memory.
See the Reference Manual for information about increasing the available heap
space memory.
In a method, you can measure the amount of heap space memory currently in use.
For example, while debugging, you can add the following code:
Runtime runtime = java.lang.Runtime.getRuntime();
runtime.gc();
debugLog("Used memory (MB): "+
(runtime.totalMemory()-runtime.freeMemory())/(1024*1024));
The first line adds a request to run the Java® garbage collector.
In order to monitor the memory usage in an application, you can replace
debugLog with message.
By writing a few lines of code, you can make your application expire after a set date
and lock the application to specific hardware.
The example MPH-file used in this section is available in the Application
Gallery at https://www.comsol.com/model/70151
P ASSWORD P ROTECTION
The settings of an application can in principle be read from the file system by a
user, including method code. By making your application password protected for
editing, the method code will no longer be readable. This setting is available from
the root node in either the model tree or the application tree, as shown in the
230 |
figure below.
Before implementing a time limit or hardware lock, as described below, make sure
your application is password protected. Password protection for running the
application is not required for this purpose.
ok = false;
try {
java.util.Date d = f.parse(timeoutDate);
long currentTime = timeStamp();
long timeoutTime = d.getTime()+24*60*60*1000; // To allow running until the end
of the day
if (currentTime < timeoutTime) {
ok = true;
}
}
catch (java.text.ParseException e) {
debugLog("Failed to parse timeout date "+timeoutDate);
debugLog(e.getMessage());
}
In this method, you need to decide on a date format. Two format examples are
shown and you can uncomment the line corresponding to the format you would
like to use. For more details on available formats, see the Java® documentation for
| 231
SimpleDateFormat. This method has one string input argument, timoutDate,
and one Boolean output argument, ok, as shown below.
232 |
if (contains(allowedAddresses, macString.toString())) {
ok = true;
break;
}
}
}
}
catch (java.net.SocketException e) {}
In order to check the MAC address when running an application, you need to
enable Allow access to network sockets under Security in Preferences. However, for
a compiled application, no security changes are needed.
This method has one array 1D string input argument, allowedAddresses, and
one Boolean output argument, ok, as shown below.
Note that you can provide a list of MAC addresses to allow use on a computer with
multiple network cards or multiple computers.
| 233
exit();
}
if (!check_date(trial_date)) {
alert("The trial for this application has expired "+trial_date, "COMSOL");
exit();
}
You can call this type of method at startup of the application, for example, as an
On load event for the main form of the application. In the Tuning Fork example
application, available in the Application Library of COMSOL Multiphysics, there
is a method p_init_application that is run as an On load event for the main
form. In this case, the method p_init_application can be edited as follows:
check_allowed_to_run();
if (model.sol("sol1").isEmpty()) {
solution_state = "nosolution";
}
else {
solution_state = "solutionexists";
}
zoomExtents("graphics1");
Notice the call to the method check_allowed_to_run in the first line. The figure
below shows this method in the Method Editor.
234 |
The method p_init_application is then called as an On load event. This is
specified in the Settings window of the main form, as shown in the figure below.
| 235
236 |
Index
129 button
B backslash 12 object 88
backspace 12 classes
| 237
color 60 old value 213
of user interface component 59 dataset 48, 157
combo box object 68 DataSource object 213
Compact History 53 date and time methods 137
Compile Equations node 45 debug
computation time 139 methods 128
last 138 Debug Log window 128
Compute 46 declaration 211
COMSOL Desktop 51 type 54
COMSOL Help Desk 53 Declarations 12, 14, 56
COMSOL Multiphysics 7, 26 deformation plot 34
COMSOL Multiphysics Programming degrees of freedom 204
Reference Manual 204 Dependent Variables node 45
confirm 11, 116, 123 description 18
contour plot 48 parameter 18, 35
contour plot data 185 variable 18
control flow statements 16 Developer tab 156
conversion Dialog Actions 215
between data types 9 dialog box 115, 116
methods 140 data validation 214, 217
coordinate information 195, 196 diffusion coefficient
Copy as Code to Clipboard 30 anisotropic 34
creating dimension
feature node 38, 42 spatial 29
model object 28, 51 disable form object 59, 64, 65, 120
CSV-file 168, 175 Display Name
curve for choice list 120
interpolation 184 double 9
parameterization 191 data type 8, 29
cut point variable conversion 140
dataset 34, 97 double quotation mark 11
238 |
email writing to 168
class 111 File Declaration 171
methods 111 file import 171
preferences 113 file import object 70
email attachment file open
export 111 system method 108
report 111 file scheme
table 111 syntax 101, 171
embedded model 51 Filter 211
enable form object 57, 59, 64, 65, 120 finite element
endian 190 accessing 204
equation system matrix 206
object 70 vector 206
error dialog box 18 floating point number 8
Error message 212 for loop 17, 65, 95
eval 212 form
events 92, 214 class 58, 63
example code 96, 156 declarations 15
Excel® file 50, 105, 168, 173, 176 list methods 94
exception form collection 71
handling 18 form feed 12
exit 127 form method 24
application 128 form object 70
expiration date for application 230 class 58, 64
explicit selection 223 list methods 94
export types 66
email attachment 111 Fully Coupled node 46
external C libraries 129
G general properties 59
external Java® library 229
generating code 19
F face Geometry node 38
parameterization 193 geometry object 38, 39
feature node names 196
creating 38, 42 get 29, 30
removing 39, 42 global method 24
file global parameter 35
methods 101 graphics
name 105 object 72, 115
open 108 view 121, 124
reading 168 grid dataset 156
| 239
GUI command toolbar 87
methods 127 iterative solver 45
GUI related methods 115 Iterator class and method 42
240 |
main application class 57, 61 turning off 159
main user interface component classes Model Java-file 52
58 Model M-file 52
Main Window model object 7, 26, 42, 55, 96
class 58, 62 class structure 53
node 58 tag 26
material model tree 26
link 34 node 41, 42
tag 91 model utility methods 52, 96
Materials node 42 Model Wizard 29, 51
math functions 15 models, working with multiple 51
maximum value 49 MPH file 51, 96, 127
menu multiphysics 50
item 87 Multiphysics node 50
mesh multiple models 51
information and statistics 203
N name
mesh element size 33, 39
form 55, 57
Mesh node 39
form object 55, 57
message log object 78, 118
in application object 57
message method 123
method 25
method 7, 96
scoping 27
form 24
shortcut 14, 55
get 30
user interface component 55, 57
global 24
network card 230
local 24
new value
name 25
data validation 93, 213
Method class 94
newline 11, 12
Method Editor 96
node
using 7, 19
finite element 204
method name 25
nonlinear solver 46
Microsoft® Word® format 124
normal
model 51
surface 163, 164
loading 51, 97
numerical
saving 51, 97
Derived Values 49
Model Builder 26
Numerical validation 211
model component 29
O old value
model data access 23
data validation 93, 213
model history 52, 159
On data change event 93, 212
recording 53
| 241
operating system Programming Reference Manual 26, 47,
methods 108 96, 97, 199, 204
operators 37 progress 131
Java 9 dialog box 132, 136
model object 37 methods 131
OS commands 108 progress bar object 78, 133, 135, 136
properties
P parameter 18, 34, 35, 49
general 59
method 17, 24
property and property values 29
real and imaginary part 36
parameterization R radio button object 79
edge 191 ragged arrays 12, 32
face 193 reading
parameterized solution 49 binary file 189
parsing CSV-file 169
text file 182 Excel® file 173
physics interface 41, 44 file 168
play sound 109 matrix file 177
plot Microsoft Excel® Workbook file 173
group 34, 123 spreadsheet 169
mesh element nodes 97 text file 182
point trajectories 97 text file to string 179
surface 37, 48 real and imaginary part
table surface 50 of parameter 36
useGraphics 115 Record Code 21, 46
Plot Group node 48 recursion 199
plot type recursive loop 199
Annotation Data 167 removing
Arrow Data 165, 166 feature node 39, 42
Line Data 160 report 113
Point Data 158, 159, 206 email attachment 111
Surface Data 161, 162, 163 HTML 123
Tube Data 165 Microsoft® Word® format 124
Point Data plot type 158, 159, 206 request 117, 123
point trajectories plot 97 reset
precedence, of operators 9, 37 model history 52
primitive data types 8 Results node 48
printing results table object 79, 120
graphics 127 RGB color 60
programming examples 96, 156 ribbon item 87
242 |
S save application 128 Stationary Solver node 45
save as 127 Stationary study step 44
saving status bar 131
model 51, 97 Store changes, in dialog box 214
scalar stream
object 88 character 183
scene light 128 String
sectionwise data type 10, 29
format 184, 187 methods 150
sectionwise format 184 string variable 49
selection conversion 141
explicit 223 methods 150
selection input strings
object 80, 223 comparing 11
selections 196 concatenating 10
add-in 223 Study node 44
set 29 subform object 70
setIndex 29 surface
shortcuts 14, 55 parameterization 193
Shortcuts node 14 Surface Data plot type 161, 162, 163
single quotation mark 11 surface normal 163, 164
sleep 139 surface plot 37, 48
slider object 81 system matrix
SMTP 113 accessing 207
solution system methods 108
data 156 OS commands 108
data structure 45 system vector 207
parameterized 49
T tab 12
Solution node 45
table 50
Solver Configurations node 44
email attachment 111
solver sequence 44
object 84, 119
spacer object 83
Table node 49
spatial dimension 29
table surface plot 50
special character 11
tag 57
Java 52
model object 26
special characters 182
physics interface 41
sphere 156
temporary folder
spreadsheet 169
location 176
format 102, 177, 178, 184
text file 168
| 243
reading 182 while loop 17
writing 182 with statement 17
text label object 85 writing
text object 84 CSV-file 175
time 137 Excel file 176
time-limited application 230 matrix file 178
title 116 string to text file 181
toggle button object 85 text file 182
toolbar to binary file 188
item 87 to file 168, 176
object 86
Z zoom extents 123, 128
Tooltip 212
transparency 125, 128
transparent
color 60
try and catch 18
Tube Data plot type 165
type declaration 54
U unit 36
object 86
Unit List 58
unit set
methods 90
object 88
unit sphere 156
Unit System 35
Upper bound 211
username 108
244 |