-
Notifications
You must be signed in to change notification settings - Fork 0
MOOSE FAQ
- Supported Finite Element Types
- String-Derived Types
- MooseEnum
- Moose Setup
- Coupling to an Arbitrary Number of Variables
- AuxKernel Restrictions
- Boundary Conditions
- Functions
- UserObjects
- Using Hypre
- Restart
- Misc
- Warnings
- Petsc 3.1 to 3.3
The following is a list of finite element families and their valid orders within the MOOSE framework.
Any families with a * next to their name haven't been used yet by anyone using MOOSE (as far as the authors know). "ANY" means that you can use any order from FIRST through FORTYTHIRD.
-
BERNSTEIN
*: ANY -
LAGRANGE
: FIRST, SECOND, THIRD (THIRD only in 1D) -
L2_LAGRANGE
*: FIRST, SECOND, THIRD (THIRD only in 1D) -
CLOUGH
: SECOND, THIRD -
HERMITE
: THIRD -
HIERARCHIC
: ANY -
L2_HIERARCHIC
*: ANY -
MONOMIAL
: ANY -
SCALAR
: ANY -
SZABAB
*: FIRST through SEVENTH -
XYZ
: ANY
MOOSE has a number of string-derived types that are provided to improve type safety when writing Custom Actions, and for use within the Peacock GUI. When writing Custom Actions, you will encounter compile-time (rather than run-time) errors about missing Parameters if you fail to provide parameters of the correct types.
Some of the more common types are:
-
NonlinearVariableName
: This is the type that represents one of the nonlinear variable names in your system. Usually these are defined in the[Variables]
block. An example of using this type with a params object is given below:
params.set<NonlinearVariableName>("variable") = "density";
-
AuxVariableName
: This is the type that represents one of the auxiliary variable names in your system. Usually these are defined in the[AuxVariables]
block. -
VariableName
: This is a type for a generic variable name. This applies mainly toElementPostprocessors
,SidesetPostprocessors
, andNodalPostprocessors
when a variable name is expected.
MooseEnum
is a class designed to be a "smart" replacement for the
C++ enum
type. MooseEnums
are intended for use anywhere in a input
file where your parameter has some fixed number of options. By using a
MooseEnum
in your MOOSE object, your options will be propertly read
from the input file and checked against a master list of valid
options. In addition, pick-lists will be automatically displayed when
using Peacock. Note: MooseEnum is case-preserving, but not
case-sensitive.
An example of declaring and immediately using a MooseEnum
in a
validParams()
function is given below:
#include "MooseEnum.h"
template<>
InputParameters validParams<MyObject>()
{
// Create a list of options (comma separated list of options
// in a single string), and optionally supply a default option
// as the second argument in the constructor
MooseEnum myOptions("Option1, Option2, Option3, ...OptionN", "Option1");
InputParameters params = validParams<ParentObject>();
params.addParam<MooseEnum>("myProperty", myOptions, "A property used in my calculation");
return params;
}
You can extract a MooseEnum parameter like any other type, and it can be reassigned. The assignment will throw an error if the assigned string is not valid for the MooseEnum (as defined in its constructor).
MooseEnum foo = getParam<MooseEnum>("myProperty");
foo = "Option2";
The MooseEnum can be compared to string literals:
if (foo == "Option1")
{
// Do something with "Option1"
}
Or used in a switch statement (by default, the enumeration numbering starts at 1):
MooseEnum test("first, second, third", "first");
switch (test)
{
case 1:
// do something with "first"
break;
case 2:
// do something with "second"
break;
case 3:
// do something with "third"
break;
default:
mooseError("Unhandled enumeration!");
}
In this section, commands which are to be typed into the shell are
preceded by a dollar sign $
, to represent the shell prompt.
Building MOOSE
First, make sure you have the latest version of the repository. MOOSE is constantly being updated. Update your local repository:
$ cd ~/projects/trunk
$ svn up
If you are receiving errors during the building of MOOSE or your application, there is a possibility that libMesh is not compiled correctly. To verify libMesh is not the issue, first make sure you are up to date, and then try rebuilding it:
$ cd ~/projects/trunk
$ svn up
$ cd ~/projects/trunk/libmesh
$ ./build_libmesh_moose.sh
During the configure stage, verify you can find status messages similar to the following somewhere in the output:
---------------------------------------------
----- Configuring for optional packages -----
---------------------------------------------
checking for /opt/packages/petsc/petsc-3.1-p8/gnu-opt/include/petsc.h... yes
<<< Configuring library with MPI from PETSC config >>>
<<< Configuring library with PETSc version 3.1.0 support >>>
<<< Configuring library with Hypre support >>>
If you don't see this text, or there are errors about not finding PETSc, then please scroll down to the Environment section below, and verify that your environment is correct. Building libMesh may take a few minutes. Once it is complete, navigate to your application and run make there:
$ cd ~/projects/trunk/<your_application>
$ make -j4
If there is an error during the building of your application, running
the run_tests
script in each directory your application depends on
is a good way to isolate issues. For MOOSE, for example, there is a
separate location where tests are run from:
$ cd ~/projects/moose_test
$ make -j4
$ ./run_tests -j4
Environment
If something in your environment is not set correctly, check that the following environment variables are set.
- Verify that PETSc is indeed installed in $PETSC_DIR:
$ echo $PETSC_DIR
/opt/packages/petsc/petsc-3.1-p8/gnu-opt
- Verify that MPICH is installed:
$ echo $MPI_HOME
/opt/packages/mpich2/mpich2-1.4.1p1/gnu-opt
- Verify that we will actually be using said MPI installation:
$ which mpicc
/opt/packages/mpich2/mpich2-1.4.1p1/gnu-opt/bin/mpicc
- Verify libMesh will be using mpicc during the build:
$ echo $CC
mpicc
- Verify that gfortran is in your PATH:
$ which gfortran
/usr/bin/gfortran
If each of these tests produces reasonable output, but libMesh still fails to configure/build, then you should contact Jason Miller for further assistance.
If you're in a hurry, a simple last-step cop-out is to try reinstalling the MOOSE package. The MOOSE package may be re-installed safely multiple times, and this procedure may solve a possible environment issue.
If you are not using the MOOSE redistributable package, you are responsible for building the following pieces of software yourself:
- C, C++, and Fortran compilers. We recommend Clang with GNU Fortran or the GNU Compiler Collection.
- An MPI library. We recommend either mpich or openmpi.
- HYPRE
- PETSc
- libMesh
The trick here is to do "vector coupling". What this means is coupling to an arbitrary number of variables simultaneously.
How do you do such a thing? The input file syntax is as follows:
[AuxKernels]
[./]
type = SummingAux
variable = the_sum
coupled_vars = 'var1 some_other_var var25'
[../]
[]
That will couple var1
, some_other_var
and var25
into SummingAux
as
coupled_vars
. Note: coupled_vars
is the name of the coupling
parameter that you added using params.addCoupledVar()
like usual.
Now... how do you get out the value of each one? This requires some slightly more advanced C++ code involving pointers. In your class you do this:
In your header file:
class SummingAux : public AuxKernel
{
private:
std::vector<VariableValue *> _vals;
std::vector<VariableGradient *> _grad_vals;
...
};
In your source file:
SummingAux::SummingAux(const std::string & name, InputParameters parameters)
: AuxKernel(name, parameters)
{
int n = coupledComponents("coupled_vars");
_vals.resize(n);
_grad_vals.resize(n);
for (unsigned int i=0; i<_vals.size(); ++i)
{
_vals[i] = &coupledValue("coupled_vars", i);
_grad_vals[i] = &coupledGradient("coupled_vars", i);
}
}
What this is doing is filling up vectors of VariableValue
and
VariableGradient
pointers. You can then loop over them as follows:
for (unsigned int i=0; i<_vals.size(); ++i)
the_sum += (*_vals[i])[_qp]
Or whatever your particular application requires. The trick is that
you have to dereference the pointers in the vectors (that's the
(*_vals[i])
part) and then index by _qp
like you normally do for a
coupled variable.
- Nodal
AuxKernels
can't useMaterialProperties
. - This is not an arbitrary restriction. If a Node falls on a block boundary... which Material object would you use for the material property value at that point?
What are the names of the boundaries for 3D Generated Mesh?
X faces: left, right Y faces: front, back Z faces: top, bottom
Is there a difference between sideset "n" and nodeset "n"?
It is the "boundary" n... we don't distinguish between sidesets and nodesets as far as numbering goes... that has the side effect that sidesets and nodesets MUST have unique numbers... i.e. you cannot have a sideset with ID 1 and a nodeset with ID 1. (Technically you can, but it probably won't mean what you think it will mean... "boundary 1" will be the union of the sideset 1 and nodeset 1.)
Come up with a scheme and stick to it. I usually recommend numbering sidesets from 1 (like 1, 2, 3, 4, 5, etc.) and nodesets from 100 (like 100, 101, 102, etc.) so that you won't have any collisions. We use a similar scheme to this in BISON (although I think nodesets start at 1,000 or 10,000 to give them more room).
Further, I highly recommend using boundary "naming". In that case
you can say "left_nodes" or "right_side". To keep things straight.
If you have a limited number of boundaries this is a good idea. You
can either name them in Cubit when you generate the mesh, use the
automatic names from GeneratedMesh
or assign names to numbers in the
Mesh block (look at the input file syntax dump or the MOOSE manual to
see how to do that).
Can we specify DirichletBC for a nodeset which might not be on the boundary?
Yes. Just give it a nodeset ID (like 105 or something) and then just say "boundary = 105" in your DirichletBC block in your input file. You can do this for "internal" NeumanBCs as well but it's trickier. In Cubit you have to assign one side of the boundary that the sideset is "with respect to"... which you can do right in the Sideset section of Cubit.
What's the difference between PresetBC and DirichletBC?
PresetBC
is a special type of DirichletBC
. It is useful in the
case where you know what the value of the variable should be at the
beginning of the timestep. It forces the value of the variable at
that boundary to be the value it is supposed to be before the solve
even starts. In other words, it sets the initial guess for the value of
the variable on that boundary to be exactly the right value.
DirichletBC on the other hand just uses the value from the last timestep (or the initial condition for the first timestep) as the initial guess. This might not be a good guess (especially for the first timestep)... but on the other hand you might not know at all what the value is supposed to be (like in the case of nonlinear coupled DirichletBCs).
So think of it like this:
PresetBC
: Set the value of the variable to what it's supposed to be and hold it there.
DirichletBC
: Use a guess... but really solve for the value of the variable
A couple more notes on this:
-
DirichletBC
is older... so it is just used more often (even in cases where a PresetBC could be used instead). -
If your problem is boundary condition driven (like a prescribed displacement) you will really want to use PresetBC because that will set the "forces" (i.e. residuals) in your boundary elements correctly at the beginning of each solve (instead of having to "solve" for the movement of the boundary nodes like with
DirichletBC
).
What functions are available for the MooseParsedFunction class?
The function parsing capability in MOOSE comes from an external library called FunctionParser that is distributed with libMesh. For more information, see the FunctionParser website.
This is still a new system - and we haven't done documentation for it yet because it's been in quite a bit of flux. I still expect at least one significant API change for this system (having to do with restart)... BUT it has stabilized enough that I think it's useful.
For now, the best documentation is in the tests and in the UserObjects
that are in MOOSE. But here's a quick synopsis:
You could think of a UserObject
as a generalization of Postprocessors.
Indeed... if you look at Doxygen you will see that Postprocessors
are UserObjects
and UserObjects
can be used
as Postprocessors!
What's the difference then?
Postprocessors compute one scalar value. UserObjects
, on the
other hand, can compute whatever they want and provide whatever kind
of interface they want to provide so other MOOSE objects can get
access to what they computed.
Just like Postprocessors, there are 4 "kinds" of UserObjects
: Nodal, Elemental,
Side and General (which correspond to the 4 base classes for
UserObjects
). When you create a UserObject
you must override some
functions:
virtual void initialize() = 0
virtual void execute() = 0
virtual void threadJoin(const UserObject &uo) = 0
virtual void finalize() = 0
virtual void destroy() = 0
Here's a short synopsis of each:
-
initialize()
: Called before looping begins (i.e., before looping over nodes for a NodalUserObject). Usually where you want to initialize some values. -
execute()
: Called on each geometric object (i.e., each Node for a NodalUserObject). This is where you want to do your main computation. -
threadJoin()
: Called during threaded loop execution. You must take the data from "uo" and "merge" it into the data for "this" object. -
finalize()
: Called after the geometric loop. Usually do some final computation here. -
destroy()
: Called when you need to release any dynamically created memory (i.e., when the object is being destroyed). Basically, a destructor.
So a UserObject
will do some computation on a bunch of geometric
objects (like on all Elements, or all Sides in a Sideset) and store
the results of that computation internally. At that point, it is up
to you to provide an interface (functions on your UserObject
) to
expose that data.
When another MOOSE object wants to use a UserObject
you do that by
calling getUserObject<UserObjectType>("name")
in your initialization
list... similar to getting a Function object. Note that you have to
provide the UserObjectType and the reference that comes back to you
from that call is of type UserObjectType. What this means is that you
can call functions on that object that were defined in that object.
Let's look at an example. Let's say that I need the average
temperature on the current Block in the mesh to compute a material
property. To implement this without a UserObject
, you would have
had to create an ElementAverageValue Postprocessor for every Block
in your mesh, then create a Material that would get ALL of those
Postprocessor values and handle them differently depending on what
subdomain the Material was currently being evaluated on. It would
have worked... but it would have been very messy.
You can now solve this problem by creating a new UserObject
that
inherits from ElementAverageValue, and call it, say,
BlockAverageValue. What BlockAverageValue is going to do is
accumulate separate integrals of the value and volume in each block
and store them in the std::maps
std::map<unsigned int, Real> _block_to_integral;
std::map<unsigned int, Real> _block_to_volume;
Then, inside BlockAverageValue you would create a function like this:
Real blockAverage(unsigned int subdomain_id);
Then, in your Material, you would get this object by name by taking the name in from the input file (just like you get a Function):
MyMaterial::MyMaterial(name, params) :
...
_block_average_value(getUserObject<BlockAverageValue>("block_average_value_user_object")),
...
{}
And then in computeQpProperties()
you would be able to do this:
_my_prop[_qp] = 5.9 * _block_average_value.blockAverage(_current_elem->subdomain_id());
To make this work in the input file you would have:
[UserObjects]
[./abav]
type = BlockAverageValue
variable = u
other_param = stuff
[../]
[]
[Materials]
[./my_mat]
type = MyMaterial
...
block_average_value_user_object = abav
[../]
[]
Basically, a UserObject
can do whatever it wants and provide whatever
data it wants. It's a completely open-ended system with unlimited
possibilities. But, with great power comes great responsibility! do
not overuse this sytem. If your computation fits into one of the
other MOOSE systems, do it there instead.
Also, don't get sloppy with the UserObject
system! You can actually
create your own object-oriented hierarchy with a base class that
inherits from ElementUserObject and then have multiple implementations
of that base class and your other MOOSE objects retrieve your
UserObjects
by calling:
getUserObject<MyBaseClass>()
Then you can use the interface defined in MyBaseClass. This will give you
the ability to swap in and out your own UserObjects
... providing
tons of flexibility.
For more explanation look at LayeredIntegral
in MOOSE and the
corresponding layered_integral_test in moose_test.
Hypre is an Algebraic Multigrid Preconditioner suitable for preconditioning a wide range of MOOSE-based simulations.
These basic options work for several 2D problems:
petsc_options = '-snes_mf_operator -ksp_monitor'
petsc_options_iname = '-pc_type -pc_hypre_type'
petsc_options_value = 'hypre boomeramg'
When running in 3D, you need to set -pc_hypre_boomeramg_strong_threshold
to 0.7 manually for best performance:
petsc_options = '-snes_mf_operator -ksp_monitor'
petsc_options_iname = '-pc_type -pc_hypre_type -pc_hypre_boomeramg_strong_threshold'
petsc_options_value = 'hypre boomeramg 0.7'