next up previous
Next: Assembling the matrix Up: Applications Previous: Applications

Writing output detached to disk

The output classes, i.e. basically the classes DataOut and DataOutStack and their base classes, follow a strictly hierarchical model of data flow. The two terminal classes know about such things as triangulations, degrees of freedom, or finite elements, but they translate this structured information into a rather simple intermediate format. This conversion is done in the build_patches functions of these classes. The actual output routines only convert this intermediate format into one of the supported graphics formats, which is then a relatively simple task.

This separation of processing of structured data and actual output of the intermediate format was chosen since the actual output routines became rather complex with growing scope of the whole library. For example, we had to update all output functions when vector-valued finite elements were supported, and we had to do so again when discontinuous elements were developed. This became an unmanageable burden with the growing number of output formats, and we decided that an intermediate format would be more appropriate, which is created by only one function, but can be written to output formats by a number of different functions.

In the present context, this has the following implications: once the intermediate data is created by the build_patches function, we need no more preserve the data from which it was made (i.e. the grid which it was computed on, or the vector holding the actual solution values) and we can go on with computing on the next finer grid, or the next time step, while the intermediate data is converted to a graphics format file detached from the main process. The only thing which we must make sure is that the program only terminates after all detached output threads are finished. This can be done in the following way:

    // somewhere define a thread manager that keeps track of all
    // detached (`global') threads
    ACE_Thread_Manager global_thread_manager;

    // This is the class which does the computations:
    class MainClass {
        ...

        // now two functions, the first is called from the main program
        // for output, the second will manage detached output
        void write_solution ();
        void write_detached (DataOut<dim> *data_out);
    };


    void MainClass::write_solution () {
      DataOut<dim> *data_out = new DataOut<dim>();

      // attach DoFHandler, add data vectors, ...
     
      data_out->build_patches ();

      // now everything is in place, and we can write the data detached
      // Note that we transfer ownership of `data_out' to the other thread
      Threads::spawn (global_thread_manager,
                      Threads::encapsulate(&MainClass<dim>::write_detached)
                          .collect_args(this, data_out));
    };

    
    void MainClass::write_detached (DataOut<dim> *data_out) {
      ofstream output_file ("abc");
      data_out->write_gnuplot (output_file); 

      // now delete the object which we got from the starting thread
      delete data_out;
    };


    int main () {
      ...  // do all the work
   
      // now wait for all detached threads to finish
      global_thread_manager.wait ();
    };

Note that the functions spawn and encapsulate are prefixed by Threads:: since in the actual implementation in deal.II they are declared within a namespace of that name.

It should be noted that if you want to write output detached from the main thread, and from the main thread at the same time, you need a version of the C++ standard library delivered with your compiler that supports parallel output. For the GCC compiler, this can be obtained by configuring it with the flag -enable-threads at build time, or by using GCC version 3.0 or later.


next up previous
Next: Assembling the matrix Up: Applications Previous: Applications
Wolfgang Bangerth
2000-04-20