Creating a new FACETS updater
In this tutorial we will learn how to create a new updater and use it from an input file. As an example, we will write an updater to solve the linear diffusion equation using an explicit scheme. This updater can be extended to develop an implicit solver and handle non-linear flux terms. The following steps were testes on iter.txcorp.com. If you want to try this on you machine, first build facetsall and then follow the given instructions. Note that you only need to build facets proper (i.e. build with -W nubeam,uedge,plasma_state,wallpsi,fmcfm,ga_transport,fluxgrid)
Step 1: Create a new branch
Create a new branch to add the new updater. It is generally a good practice to work in a branch while developing new code. After new regression tests are added and all existing tests pass, you can merge you branch into the trunk. For this tutorial we will not merge the branch into trunk.
Run the following command
svn cp https://ice.txcorp.com/svnrepos/code/facets/trunk https://ice.txcorp.com/svnrepos/code/facets/branches/ammar-difftut
Of course, you should use your own name instead of "ammar-difftut" for your branch.
Step 2: Configure and build your branch
Check out the branch:
svn co https://ice.txcorp.com/svnrepos/code/facets/branches/ammar-difftut cd ammar-difftut
Now build the branch, compiling it again already built libraries. You may have to change your supra-search-path to your install directory. To build, create "ser" directory to build out of place.
./config/cleanconf.sh mkdir ser cd ser ../configure --disable-parallel --with-optimization=debug --with-supra-search-path=/internal:/contrib make depend make -j 2
Step 3: Copy the FcDummyUpdater? and add the new files to svn. Register updater and rebuild.
We will base this updater on the dummy updater in the fccmpnt directory. First copy the dummy updater header and source file to fcexamples:
cd .. cp fccmpnt/FcDummyUpdater.h fcexamples/FcExplicitDiffusionUpdater.h cp fccmpnt/FcDummyUpdater.cpp fcexamples/FcExplicitDiffusionUpdater.cpp
Add these to svn
svn add fcexamples/FcExplicitDiffusionUpdater.h svn add fcexamples/FcExplicitDiffusionUpdater.cpp svn ci -m "Adding new example updater"
Open the FcExplicitDiffusionUpdater?.h and FcExplicitDiffusionUpdater?.cpp files and rename FcDummyUpdater? to FcExplicitDiffusionUpdater?. Clean up the class (delete all "privates", extra includes and delete all method bodies) and add these files to the Makefile.am in the fcexamples directory.
To register the updater into facets open the file fcexamples/FcExampleUpdaterRegistery.cpp. Add the new updater to the header list and register it:
new TxMaker<FcExplicitDiffusionUpdater, FcUpdater>("explicitDiffusionUpdater1d");
This will allow using the new updater using the kind field "explicitDiffusionUpdater1d" from an input file. Rebuild the branch.
cd ser make
Step 4: Create a new input file to test updater
At this point we have enough code written to test the updater. Of course, as we do not have any update code written yet, nothing will happen when we run the input file. To start off, check out a copy of fctests, cd to fctests/tests/infrastructure and copy the input file
svn co https://ice.txcorp.com/svnrepos/code/fctests/trunk fctests cd fctests/tests/infrastructure cp simplediffusion.pre explicitdiffusion.pre
We wil not go over the input file in detail, but you should refer to the Tutorial on FACETS input files.
Now, open the file and replace the kind=simpleDiffusionUpdater line (line 58) with explicitDiffusionUpdater1d. Also, clean up the body of that updater block and add a new array TempNew? for the updater temperature. We need to do this as will have our new updater take a temperature array and update the new solution to a new array. Of course, we need to copy the new array to the old one before we take the next time-step. For this, add a linear-combiner updater and add the updater to the "advanceStep" UpdateStep?.
Now we can preprocess the input file and run it
/scr_iter/ammar/ammar-difftut/ser/txutils/txpp.py explicitdiffusion.pre /scr_iter/ammar/ammar-difftut/ser/facets/facetser -i explicitdiffusion.in
When we run the input file through facets we will get an error that looks like
Type checking updater 'simpleDiff' in UpdateStep 'advanceStep' ... FAIL ** ERROR: Updater simpleDiff expects 0 input dataStructs but got 1
The reason for this error is that we put "Temp" as an input array and "TempNew?" as the output array. However, we did not instruct facets about this. We can fix this by editing the FcExplicitDiffusionUpdater?.cpp and adding this information to the declareTypes() method:
void
FcExplicitDiffusionUpdater::declareTypes() {
this->setInType(0, typeid(FcDistArray<1, double>));
this->setOutType(0, typeid(FcDistArray<1, double>));
}
This tells facets that we expect exactly one input array and one output array. We can now recompile and run the simulation. The simulation should now run without any error.
Step 5: Read in input variables needed in updater
As this is an explicit updater for constant diffusion problems, we will read in the diffusion coefficient and a CFL number from the input file. Note that for stability, the CFL number must be less than 1.0. The updater block we will want to parse will look like
<Updater simpleDiff>
kind = explicitDiffusionUpdater1d
onGrid = domain
in = [Temp]
out = [TempNew]
diffusionCoefficient = 1.0
cfl = 0.95
</Updater>
To parse this data out from the input file, facets will call our updater's setAttib() method. Inside this method we can read these variables and store them in our class's privates (i.e. add diffusionCoefficient and cfl to private section of FcExplicitDiffusionUpdater? header file). Now, setAttrib() should look like
void
FcExplicitDiffusionUpdater::setAttrib(const TxHierAttribSet& tas) {
// setup base class
FcUpdater::setAttrib(tas);
diffCoeff = tas.getParam("diffusionCoefficient");
cfl = tas.getParam("cfl");
}
Note that the method to extract a floating point number from the input file is "getParam()". There are other methods to fetch integers, strings and vectors of these available. We can now recompile the code, add the parameters to the updater block in the input file and run the simulation again. Note that if we forget these parameters, facets will throw a rather cryptic exception. You can check if these parameters exist and throw a more meaningful exception or set some default values if you wish.
Step 6: Completing the updater
At this point we are ready to implement the actual algorithm. The steps to get here may seem quite complicated, but are really quite simple and once you do it a couple of times it takes only a few minutes to do Steps 1-5. The actual algorithm
We intend to solve the linear diffusion equation with an explicit scheme. Hence, the first thing we need to do is to ensure that the time-step that facets tells us to take is small enough to satisfy the stability limit. For this, we first fetch the time-step and the grid spacing and check if the requested time-step lies inside the CFL limit. If it does not, we return immediately with a "failed" status, advising facets what time-step we really should be called with.
To do this, we add the following code to the update() method of the class
FcUpdaterState
FcExplicitDiffusionUpdater::update(double t) {
// immediately bail out if timestep is too large
double dt = this->getDt();
const FcCartGrid<1>& grid = this->getConstGrid<FcCartGrid<1> >();
double dx = grid.getDx(0);
// ensure dt is small enough (we compare with 4*epsilon to avoid comparsions close to 0.0)
if (dt - cfl*dx*dx/diffCoeff > 4*cfl*dx*dx/diffCoeff*std::numeric_limits<double>::epsilon())
return FcUpdaterState(false, cfl*dx*dx/diffCoeff);
return FcUpdaterState(true, dt);
}
Once we are sure the time-step is small enough, we can implement the actual update formula. To do this, we first get a hold of the input array (solution at t) and output array (solution at t+dt):
// get hold of arrays (solution at t) const FcDistArray<1, double>& Temp = this->getIn<FcDistArray<1, double> >(0); // get hold of arrays (solution at t+dt) FcDistArray<1, double>& Temp = this->getOut<FcDistArray<1, double> >(0);
Now, all we need to do is implement the actual update formula using central differences:
// compute coefficient that appears in update formula
double s = dt*diffCoeff/(2*dx*dx);
// loop, updating solution using finite-differences
for (int i=Temp.lower(0); i<Temp.upper(0); ++i) {
TempNew(i) = Temp(i) + s*(Temp(i+1) - 2*Temp(i) + Temp(i-1));
}
That is essentially it. With this, we now have an explicit solver for diffusion equation with constant coefficients.
