trixi-framework / Trixi.jl

Trixi.jl: Adaptive high-order numerical simulations of conservation laws in Julia
https://trixi-framework.github.io/Trixi.jl
MIT License
539 stars 109 forks source link

Allow passing arbitrary point-wise and integral functionals to `TimeSeriesCallback` #589

Open sloede opened 3 years ago

sloede commented 3 years ago

What about adding the option of using arbitrary functionals of the solution, which could also be integrals like we have in the analysis callback? Then, we could have something like PointwiseFunctionals and IntegralFunctionals, say, that could use the same infrastructure of the TimeSeriesCallback. In the long run, this could get us lift and drag etc.

Originally posted by @ranocha in https://github.com/trixi-framework/Trixi.jl/issues/556#issuecomment-823338637

jlchan commented 3 years ago

Following up on https://github.com/trixi-framework/Trixi.jl/pull/695#discussion_r670548704. @ranocha - did you have some ideas on an interface?

ranocha commented 3 years ago

Our current interfaces are

help?> TimeSeriesCallback
search: TimeSeriesCallback

  TimeSeriesCallback(semi, point_coordinates;
                     interval=1, solution_variables=cons2cons,
                     output_directory="out", filename="time_series.h5",
                     RealT=real(solver), uEltype=eltype(cache.elements))

  Create a callback that records point-wise data at points given in point_coordinates every interval time
  steps. The point coordinates are to be specified either as a vector of coordinate tuples or as a
  two-dimensional array where the first dimension is the point number and the second dimension is the
  coordinate dimension. By default, the conservative variables are recorded, but this can be controlled by
  passing a different conversion function to solution_variables.

  After the last time step, the results are stored in an HDF5 file filename in directory output_directory.

  The real data type RealT and data type for solution variables uEltype default to the respective types used in
  the solver and the cache.

  │ Experimental implementation
  │
  │  This is an experimental feature and may change in future releases.

help?> AnalysisCallback
search: AnalysisCallback

  AnalysisCallback(semi; interval=0,
                         save_analysis=false,
                         output_directory="out",
                         analysis_filename="analysis.dat",
                         extra_analysis_errors=Symbol[],
                         extra_analysis_integrals=())

  Analyze a numerical solution every interval time steps and print the results to the screen. If save_analysis,
  the results are also saved in joinpath(output_directory, analysis_filename).

  Additional errors can be computed, e.g. by passing extra_analysis_errors = [:primitive].

  Further scalar functions func in extra_analysis_integrals are applied to the numerical solution and
  integrated over the computational domain. See Trixi.analyze, Trixi.pretty_form_utf, Trixi.pretty_form_ascii
  for further information on how to create custom analysis quantities.

What I have in mind is something like the following. We create additional structs such as

For all of these functionals, quantities can also be a tuple such as quantities=(entropy, energy_kinetic). Then, the TimeSeriesCallback is constructed by passing an arbitrary number of such functionals. The implementation of PointwiseFunctionals would follow our current approach of the TimeSeriesCallback in 2D; IntegralFunctional would be based on our current setup of the AnalysisCallback. Thus, the interface would look like

  TimeSeriesCallback(functionals;
                     interval=1,
                     output_directory="out", filename="time_series.h5", # can also be omitted/empty/nothing if data should not be written to a file
  )

I would prefer an interface such as get_data(time_series_callback) to be able to retrieve the in-memory data without needing to write to a file; writing to a file should be optional and not the only way to get the data. Optionally, we could consider implementing a check such as needs_du(functional) to detect whether we need to compute the current RHS as in https://github.com/trixi-framework/Trixi.jl/blob/50ba5ffb0dd9f8933b916c2271c7bcbc22122723/src/callbacks_step/analysis.jl#L213-L217, but that's an optimization that can also be done later.

sloede commented 3 years ago

I like everything being said in https://github.com/trixi-framework/Trixi.jl/issues/589#issuecomment-883903672. Further, I propose to at least keep in mind the future need (and I am sure it will come) for an ArbitraryIntegralQuantityFunctional. I am thinking about a functional that could, e.g., evaluate lift and drag for a given airfoil geometry. It might not affect the implementation questions discussed here at all; I just wanted to make sure that something like this is on the feature map when laying out our approach.