SpECTRE Documentation Coverage Report
Current view: top level - __w/spectre/spectre/docs/Tutorials - BeginnersTutorial.md Hit Total Coverage
Commit: 4d43624d64e749c1edac1bf2b22ce14141d2836f Lines: 0 1 0.0 %
Date: 2026-08-12 22:55:12
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : \cond NEVER
       2             : Distributed under the MIT License.
       3             : See LICENSE.txt for details.
       4             : \endcond
       5             : # A Hitchhiker's Guide to Running SpECTRE {#beginners_guide}
       6             : 
       7             : \tableofcontents
       8             : 
       9             : SpECTRE can be a bit complicated to get started with, especially if you aren't
      10             : familiar with our core concepts of task-based parallelism and Template
      11             : Meta-Programming (TMP). However, <a
      12             : href="https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Don't_Panic">
      13             : Don't Panic</a>. This guide aims to get you introduced to running,
      14             : visualizing, editing, and then rebuilding SpECTRE to give you a feel for what
      15             : SpECTRE is all about, all on your own laptop! Hopefully by the end of this guide
      16             : you'll feel comfortable enough to look at other executables and maybe even
      17             : venture into the code itself!
      18             : 
      19             : ## Prerequisites
      20             : 
      21             : To start off, you'll need to obtain an environment to build and run SpECTRE in.
      22             : You could try and install all the dependencies yourself, but that is very
      23             : tedious and very error prone. Instead, we provide a
      24             : [Docker](https://docs.docker.com/get-docker/) container with all the
      25             : dependencies pre-installed for you to use. The container also has the SpECTRE
      26             : repository cloned in it already so you don't have to worry about getting it
      27             : yourself. To obtain the docker image, run
      28             : 
      29             : ```
      30             : docker pull sxscollaboration/spectre:demo
      31             : ```
      32             : 
      33             : Another program you will need for this tutorial is
      34             : [Paraview](https://www.paraview.org/download/) for visualizing the output. You
      35             : specifically will need version 5.10.1 for this tutorial.
      36             : 
      37             : If you'd like to use VSCode, the tutorial also has instructions for how to start
      38             : in VSCode as well.
      39             : 
      40             : ## Into the Container
      41             : 
      42             : For both a terminal and VSCode, create the container in a terminal and start it.
      43             : 
      44             : ```
      45             : docker create --entrypoint "/bin/bash" --rm --name spectre_demo -p 11111:11111 \
      46             :     -i -t sxscollaboration/spectre:demo
      47             : ```
      48             : ```
      49             : docker start spectre_demo
      50             : ```
      51             : 
      52             : We connect port `11111` on your local machine to port `11111` of the container
      53             : so we can use Paraview. The `--rm` will delete the container when you stop it.
      54             : This won't put you into the container, only start it in the background.
      55             : 
      56             : \note The `--entrypoint "/bin/bash"` is important because the default entrypoint
      57             : of the container is the SpECTRE CLI. You can try out the default entrypoint by
      58             : running `docker run sxscollaboration/spectre:demo -h` and it'll print the help
      59             : string for the CLI. See the [Python documentation](py/cli.html) for more.
      60             : 
      61             : You can also run a [Jupyter](https://jupyter.org/index.html) server for
      62             : accessing the Python bindings (see \ref spectre_using_python) or running Jupyter
      63             : notebooks. To do so, append another `-p` option with your specified port, e.g.
      64             : `-p 8000:8000`. You can chain as many `-p` options as you want to expose more
      65             : ports.
      66             : 
      67             : The SpECTRE repository is located at `/work/spectre` inside the container.
      68             : 
      69             : 
      70             : ### With a Terminal {#with_terminal}
      71             : 
      72             : To hop in the container from a terminal, simply type
      73             : 
      74             : ```
      75             : docker attach spectre_demo
      76             : ```
      77             : 
      78             : and now you're in the container!
      79             : 
      80             : ### With VSCode
      81             : 
      82             : If you're using VSCode, you'll need the `Remote-Containers` extension to be
      83             : able to access the container. Once you have it, open the
      84             : [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette)
      85             : and run the following commands.
      86             : 
      87             : 1. `Remote-Containers: Attach to Running Container` - you should see the
      88             :    container `spectre_demo` that's currently running. Select that.
      89             : 2. `File: Open Folder` - select `/work/spectre` which is where the repo is.
      90             : 
      91             : Now you're in the container within VSCode! The terminal in VSCode will look
      92             : identical to the one if you hadn't used VSCode.
      93             : 
      94             : \note Any changes you make inside `/work/spectre` will be lost once you stop the
      95             : container. If you'd like your changes to persist, get rid of the `--rm` flag in
      96             : the `docker create` command.
      97             : 
      98             : ## Compiling the code
      99             : 
     100             : \note From here on out, all paths are assumed to be inside the container unless
     101             : specified otherwise.
     102             : 
     103             : The container already has a SpECTRE build pre-configured. Go to the build
     104             : directory and compile the executables that we will use in this tutorial:
     105             : 
     106             : ```sh
     107             : cd /work/spectre/build
     108             : make -j2 ExportCoordinates3D EvolveScalarAdvection2D all-pybindings
     109             : ```
     110             : 
     111             : This will compile the code on two cores. If you'd like to use more cores, use
     112             : the `-j N` option where `N` is the number of cores.
     113             : 
     114             : Once the executables are compiled they will be available in the
     115             : `/work/spectre/build/bin` directory. The container already has this directory
     116             : added to the `PATH` environment variable, so you can run executables from the
     117             : command line right away:
     118             : 
     119             : ```sh
     120             : spectre --help
     121             : ```
     122             : 
     123             : If you are not in the container but instead on a desktop or cluster you can add
     124             : the bin directory to your path by `cd`ing to the build directory and running
     125             : ```
     126             : export PATH=$PATH:`pwd`/bin
     127             : ```
     128             : If you log out and log back in you will need to set this again, unless you add
     129             : it to your shell's init file.
     130             : 
     131             : ## Running ExportCoordinates3D
     132             : 
     133             : First we will run the `ExportCoordinates3D` executable to visualize
     134             : the coordinates of a binary black hole domain.
     135             : Make a directory where you will run everything:
     136             : 
     137             : ```
     138             : mkdir /work/runs
     139             : cd /work/runs
     140             : ```
     141             : 
     142             : Copy over the input file
     143             : `/work/spectre/tests/InputFiles/ExportCoordinates/InputTimeDependent3D.yaml`
     144             : into your `/work/runs` directory. To run the executable, do
     145             : 
     146             : ```
     147             : spectre run --no-schedule -j 4 InputTimeDependent3D.yaml
     148             : ```
     149             : 
     150             : This will run it on 4 cores (the `--no-schedule` means it will run on the
     151             : login/head node if you are using an HPC system). After this finishes you should
     152             : see two `H5` files in your run directory:
     153             : 
     154             : 1. ExportCoordinates3DVolume0
     155             : 2. ExportCoordinates3DReductions
     156             : 
     157             : The `Volume` file is where we store data from every element in our domain, like
     158             : the coordinates or the metric. The `Reductions` file is for more global
     159             : quantities like the minimum grid spacing over all the elements in our domain.
     160             : 
     161             : \note Next time you run the executable, you will have to either move or delete
     162             : the existing `H5` files as SpECTRE will error if it detects that an output file
     163             : already exists. This is to prevent you from accidentally overwriting data.
     164             : You can also use the `--force / -f` and `--clean-output / -C` flags to have
     165             : `spectre run` delete the existing files before running the executable.
     166             : 
     167             : ## Visualizing our BBH Coordinates
     168             : 
     169             : Now it's time to use Paraview to visualize the coordinates we use for our BBH
     170             : evolutions! SpECTRE will actually export the physical frame coordinates for
     171             : every executable we have because they are a really useful diagnostic to have. We
     172             : are just using the ExportCoordinates executable here so that you don't have to
     173             : run a BBH evolution on your laptop which probably wouldn't work because of
     174             : memory requirements.
     175             : 
     176             : Before we get to Paraview, we have to tell paraview how to actually use the
     177             : coordinates in the `Volume` `H5` file. To do this we have a tool called
     178             : `generate-xdmf` in our Python command-line interface. Inside the `runs`
     179             : directory where you have the `H5` files, run
     180             : 
     181             : ```
     182             : spectre generate-xdmf \
     183             :   --subfile-name element_data --output BBH_Coords \
     184             :   ExportCoordinates3DVolume*h5
     185             : ```
     186             : 
     187             : We output volume data per node so we append the
     188             : node number to each volume file we have. Since you're most likely running on a
     189             : laptop, you'll only be running on one node so you should only get one output
     190             : file for the volume. The `--subfile-name` argument is the group name inside the
     191             : `H5` file where the data is stored (groups can be checked by
     192             : `h5ls -r FILE_NAME`). `generate-xdmf` will generate a file called
     193             : `BBH_Coords.xmf`. Make sure to keep this `.xmf` file next to the volume file it
     194             : was generated from. It uses relative paths to find the volume file which means
     195             : if you move it, you won't be able to visualize anything.
     196             : 
     197             : ### Attaching Paraview
     198             : 
     199             : This is where we actually need Paraview. We have a headless (no GUI) vesion of
     200             : paraview inside the container which we will refer to as the "server". To start
     201             : the Paraview server, run
     202             : 
     203             : ```
     204             : pvserver &
     205             : ```
     206             : 
     207             : \note For cluster users, check cluster-specific instructions for ParaView.
     208             : You may require MPI and specify which port to use.
     209             : You can run `mpirun -np 1 pvserver -p PARAVIEW_REMOTE_PORT` on the remote
     210             : cluster. Here, `PARAVIEW_REMOTE_PORT` is the port that the ParaView server
     211             : will use on the cluster. You may choose it to be, for example, 11112.
     212             : With 'pvserver' running on the remote machine, open a new local terminal.
     213             : Locally, run
     214             : `ssh -L11111:YOUR_CLUSTER:PARAVIEW_REMOTE_PORT USERNAME@YOUR_CLUSTER`.
     215             : 
     216             : The `&` is so that the server runs in the background. If you hit `Enter`
     217             : a couple times you'll get back to being able to type commands. You should see
     218             : some output similar to
     219             : 
     220             : ```
     221             : Waiting for client...
     222             : Connection URL: cs://92bbb69f2af2:11111
     223             : Accepting connection(s): 92bbb69f2af2:11111
     224             : ```
     225             : 
     226             : This means it's waiting for you to connect some external Paraview session (the
     227             : "client") to the server. Now, ***outside*** the container, start a session of
     228             : Paraview 5.10.1. (Again, you must use this version otherwise it won't work
     229             : properly.)
     230             : 
     231             : \note For cluster users, run `mpirun -n 1 pvserver --version` to check ParaView
     232             : version. On your computer, download ***exactly the same*** version of Paraview.
     233             : 
     234             : Go to `File > Connect`. Click `Add Server`. Name it whatever you
     235             : want, but keep the Host as `localhost`, the Server Type as `Client/Server`, the
     236             : Port as `11111` (remember the `-p 11111:11111` flag from the docker command?).
     237             : Here's a snapshot of what it should look like before you configure.
     238             : 
     239             : \image html paraview_server.png "Paraview server settings"
     240             : 
     241             : Hit `Configure`, then hit `Save` (we don't care about the launch configuration).
     242             : Now you should see a list of your configured servers. Select the one you just
     243             : created and hit `Connect`. It may take a minute or two to connect to the server,
     244             : but once you do on the left you'll see something like
     245             : 
     246             : \image html paraview_connect.png "Successfully connected Paraview to a server"
     247             : 
     248             : \note If you close your client, the server will stop and you won't be able to
     249             : reconnect. You'll have to restart the server in the container.
     250             : 
     251             : ### Open the XMF File in Paraview Client {#open_xmf}
     252             : 
     253             : Now that you have Paraview connected to the container, open the `BBH_Coords.xmf`
     254             : file you just generated inside Paraview (the paths you'll see are the ones in
     255             : the container, not your filesystem). You may be prompted to choose which XDMF
     256             : reader to use. Choose the `XDMF Reader` option. The `Xdmf3` options won't work.
     257             : Once you choose a reader, on the left, you'll see
     258             : 
     259             : \image html beginners_paraview_left.png "Paraview side-bar"
     260             : 
     261             : You can uncheck all the boxes in the `Point Arrays` section as they aren't
     262             : necessary for visualizing the coordinates. Then hit `Apply`. Now you should see
     263             : a solid sphere. This isn't super helpful. In the top bar you should see a
     264             : dropdown to change the style that the points are plotted in. Select `Surface
     265             : With Edges` like so. (Note: Your top bar may look slightly different from this
     266             : depending on what version of `Paraview` you have.)
     267             : 
     268             : \image html beginners_paraview_top.png "Paraview top-bar"
     269             : 
     270             : Now you'll have a solid sphere with highlighted lines. To view the interior of
     271             : the domain, you'll need to add a filter. To access the filters, navigate to
     272             : `Filters` on the top menu bar, hover over `Alphabetical`, and search for your
     273             : filter of choice.  Probably the two most helpful filters
     274             : for viewing the domain are the `Slice` and `Clip` filters. (Note that you'll
     275             : have to choose the `Surface With Edges` option for each filter separately.)
     276             : 
     277             : `Slice` is fairly self explanatory in that it will show you a single plane
     278             : through the domain. Experiment with different planes to see our whole domain
     279             : structure!
     280             : 
     281             : The `Clip` filter will remove all points "above" a certain plane, where "above"
     282             : is in the direction of the normal of that plane. If you combine two orthogonal
     283             : `Clip`s, you can actually view a 3D wedge of our domain. Try moving the centers
     284             : of the planes to view the domain around our excision surfaces! They have a lot
     285             : of cool structure.
     286             : 
     287             : If you'd like to read more about our BBH domain, you can look at the
     288             : documentation for `domain::creators::BinaryCompactObject`.
     289             : 
     290             : ## Evolution of BBH Coordinates
     291             : 
     292             : Now that you are able to export and visualize our BBH domain coordinates at a
     293             : single time, let's make a small movie of the coordinates as they evolve! To do
     294             : this, we'll need to edit the input file `InputTimeDependent3D.yaml`. If you
     295             : aren't familiar with YAML, it's a file type that uses key-value pairs to create
     296             : actual objects in our C++ code. Feel free to experiment with keys and values in
     297             : our input files. If you're unsure about what a key or value should be, we offer
     298             : an easy way to check the options in the input file without running a whole
     299             : simulation. In your `/work/runs` directory, if you run
     300             : 
     301             : ```
     302             : spectre validate InputTimeDependent3D.yaml
     303             : ```
     304             : 
     305             : the executable will parse and check the input file. If you made a typo, or added
     306             : an incorrect key/value, a list of the available keys and their associated values
     307             : will be printed.
     308             : 
     309             : To change the number of times we output the coordinates, we'll need to go to the
     310             : `%EventsAndTriggers:` block of the input file. This block is mainly where we
     311             : specify which quantities we want to observe in a simulation or where we
     312             : "Trigger" a specific "Event" to happen. (For more info on `%EventsAndTriggers`,
     313             : see the \ref tutorial_events_and_triggers tutorial.) Currently in this input
     314             : file we only have one Trigger/Event pair. The %Trigger is `TimeCompares:` and
     315             : the %Event is `Completion`. To have the simulation run longer, change the
     316             : `Value:` under `TimeCompares:` to something larger. If you look at the
     317             : `Evolution:` block above the `%EventsAndTriggers:` block, you'll see that the
     318             : initial time step is `0.5`. The way this executable is set up, the coordinates
     319             : will be exported every time step. So set the final time `Value:` under
     320             : `TimeCompares:` to some larger multiple of `0.5` so that you'll have the
     321             : coordinates at a bunch of different times (a final time of `20` is reasonable.
     322             : Depending on how many cores you run on this should take a couple minutes).
     323             : 
     324             : Then, run the executable just like you did above (remember to move or delete the
     325             : existing `H5` files), run `generate-xdmf`, and open it in Paraview and apply
     326             : some filters of your choice. We recommend using a `Slice` filter with the normal
     327             : pointing in the `-z` direction. This is because our BBH domain rotates about the
     328             : `z` axis. Now, in the top bar of Paraview, you should see a "Play" button that
     329             : looks like a sideways triangle (see the second image in the \ref open_xmf
     330             : section). If you click this, Paraview will step through all the timesteps in the
     331             : output files and you'll be able to see the domain rotate a bit!
     332             : 
     333             : Next, we encourage you to play with the other inputs that control how the domain
     334             : evolves over time. These options are housed in the
     335             : 
     336             : ```yaml
     337             : DomainCreator:
     338             :   BinaryCompactObject:
     339             :     ...
     340             :     TimeDependentMaps:
     341             :       ExpansionMap:
     342             :         ...
     343             :       RotationMap:
     344             :         ...
     345             :       SizeMapA:
     346             :         ...
     347             :       SizeMapB:
     348             :         ...
     349             : ```
     350             : 
     351             : block of the input file. Since this tutorial is more about running the code, we
     352             : won't go into too much detail about each option. However, in general:
     353             : 
     354             : 1. `ExpansionMap` is a global map (all parts of the domain) that controls the
     355             :    separation between the excision surfaces
     356             : 2. `RotationMap` is a global map that controls how the excision spheres rotate
     357             :    about each other
     358             : 3. `SizeMap` is a local map only around the excision spheres (not in the wave
     359             :    zone) that control the compression of grid points.
     360             : 
     361             : Play around with these values! You may get an error if you put something that's
     362             : too unphysical, but this is a fairly consequence-free playground for you to
     363             : explore so just try a different value.
     364             : 
     365             : Now you have a movie of how BBH coordinates evolve in a SpECTRE simulation!
     366             : 
     367             : ## Exploring DG+FD
     368             : 
     369             : Now that you are able to run, and visualize SpECTRE, let's explore a feature
     370             : that is fairly unique to SpECTRE and is really powerful for handling
     371             : discontinuities and shocks in our simulations. We call this feature `DG+FD`
     372             : (it's also sometimes referred to as just `subcell`).
     373             : 
     374             : ### Description of DG+FD
     375             : 
     376             : `FD` is the usual finite difference you are used to. All of the BSSN codes use
     377             : finite difference for solving Einstein's equations. FD is very good at capturing
     378             : shocks and discontinuities and is a very robust method, making it well suited
     379             : for hydro problems and other systems that have shocks and discontinuities.
     380             : 
     381             : `DG` stands for Discontinuous Galerkin. DG is a spectral method for representing
     382             : a solution on a grid, meaning that instead of taking the difference between the
     383             : function value at two points to get the derivative, it uses known basis
     384             : functions to represent the solution. Then the derivative can be known
     385             : analytically and you only need to supply the coefficients for the basis. DG
     386             : works best for representing smooth solutions; ones with very few shocks and
     387             : discontinuities (like GR in vacuum). This makes DG much faster than FD for
     388             : smooth solutions.
     389             : 
     390             : In SpECTRE, we combine these two different methods into one system to take
     391             : advantage of the strengths of each. When we have a solution that is smooth in
     392             : some parts of the domain, but has shocks in other parts, using only one of these
     393             : methods has disadvantages. If we only used DG, we wouldn't be able to resolve
     394             : the shocks very well driving the errors up a lot. If we only used FD, we'd be
     395             : able to represent the solution well, but it would be computationally
     396             : inefficient. So we combine DG+FD so that we only do DG in the parts of the
     397             : domain where the solution is smooth, and switch to FD in parts where there may
     398             : be a shock or discontinuity. The algorithm for switching between DG and FD is
     399             : explained in this image.
     400             : 
     401             : \image html dg_fd_schematic.png "Scheme for switching between DG and FD (credit: Nils Deppe)"
     402             : 
     403             : If you'd like to learn more about how SpECTRE implements its DG+FD scheme, you
     404             : can read [the paper](https://arxiv.org/abs/2109.11645) on the ArXiv.
     405             : 
     406             : ### Running the Kuzmin Problem
     407             : 
     408             : To demonstrate DG+FD, we will be evolving the \link
     409             : ScalarAdvection::Solutions::Kuzmin Kuzmin \endlink problem using the
     410             : `EvolveScalarAdvection2D` executable. This is a simple test problem that
     411             : rotates a set of geometric shapes with uniform angular velocity, which can be
     412             : used to evaluate how well a numerical code can handle discontinuities stably
     413             : over time. Inside the container make a new directory `/work/runs2` where you
     414             : will run it. Also copy the default input file in
     415             : `/work/spectre/tests/InputFiles/ScalarAdvection/Kuzmin2D.yaml` to this new
     416             : `/work/runs2` directory.
     417             : 
     418             : ### Changing the Default Input File
     419             : 
     420             : The default input file has very low resolution so we'll need to crank that up a
     421             : bit. The way to do this is to change the initial refinement levels and initial
     422             : number of grid points which are located in
     423             : 
     424             : ```yaml
     425             : DomainCreator:
     426             :   Rectangle:
     427             :     ...
     428             :     InitialRefinement: [x, y]
     429             :     InitialGridPoints: [x, y]
     430             : ```
     431             : 
     432             : `InitialRefinement:` represents how many times we split a `Block` in half in
     433             : order to create `Element`s, which are the fundamental units of our domain. So an
     434             : initial refinement of `[1, 1]` means we split a single Block into 4 elements
     435             : (split in half once in each direction). For an initial refinement of `[2, 2]` we
     436             : first do 1 refinement like before, and then split each of the resulting 4
     437             : elements in half again in each direction, resulting in 16 total Elements. To
     438             : determine the total number of Elements for a given refinement (same in all
     439             : directions), just do $2^{\mathrm{Dim * Refinement}}$. If you're confused by
     440             : the terminology we use to describe the domain, we have a \ref domain_concepts
     441             : guide that explains all terms related to our domain.
     442             : 
     443             : `InitialGridPoints` represents the number of grid points per dimension in each
     444             : Element after the final refinement has been applied. So if we had an initial
     445             : refinement of `[2, 2]` like above and then initial grid points `[3, 3]` in each
     446             : Element, we'd have a total of 9x16=144 grid points.
     447             : 
     448             : As for actual numbers to use, you can experiment to see what gives good,
     449             : well-resolved results. You'll definitely need more refinement than the default
     450             : input file, but since refinement scales exponentially, this can become very
     451             : expensive very quickly. On a laptop, you probably shouldn't go higher than
     452             : refinement `[6, 6]`. As for grid points, this will depend on how much refinement
     453             : you have. If you have a ton of small elements, you won't need too many grid
     454             : points to resolve the solution; something like `[4, 4]` would work. If you don't
     455             : have a lot of refinement, you may want more grid points if you still want to
     456             : resolve your solution. For a DG scheme, increasing the number of grid points (p
     457             : refinement) reduces the numerical error exponentially where the solution is
     458             : smooth, so computational resources are used more effectively. However, to
     459             : resolve shocks and discontinuities we have to refine the domain into more and
     460             : smaller elements instead (h refinement). Striking the most effective balance
     461             : between h and p refinement in different parts of the domain is the job of an
     462             : adaptive mesh refinement (AMR) algorithm.
     463             : 
     464             : The default input file only runs for a few time steps so we'll want to make this
     465             : run longer so we can actually see some evolution. From the documentation of the
     466             : \link ScalarAdvection::Solutions::Kuzmin Kuzmin \endlink system, the solution
     467             : will rotate with an angular velocity of `1.0` (in code units). Thus, to do a
     468             : full orbit, it will take `6.28` code units of time. In the `%EventsAndTriggers:`
     469             : block of the input file, we see that the `Completion` event is triggered by the
     470             : `Slabs` trigger. We could, in theory, calculate out how many slabs `6.28` code
     471             : units is using the time step, but that's super tedious. Instead let's trigger
     472             : completion using the `TimeCompares` trigger instead. We used this before when
     473             : exporting the BBH coordinates, so just copy over the yaml block and change the
     474             : `Value:`.
     475             : 
     476             : Your final `%EventsAndTriggers:` block should look something like this:
     477             : 
     478             : ```yaml
     479             : EventsAndTriggers:
     480             :   - Trigger:
     481             :       TimeCompares:
     482             :         Comparison: GreaterThanOrEqualTo
     483             :         Value: 6.28
     484             :     Events:
     485             :       - Completion
     486             :   ...
     487             : ```
     488             : 
     489             : Now you should be ready to run the executable and get some output. Here, you
     490             : will almost definitely benefit by running this on many cores by adding the
     491             : `-j N` flag to the command you use to run the executable. Since we use lots
     492             : of smaller elements, we distribute these over the available resources via a
     493             : \link domain::BlockZCurveProcDistribution space filling curve \endlink to speed
     494             : things up.
     495             : 
     496             : ```
     497             : spectre run --no-schedule -j 4 Kuzmin2D.yaml
     498             : ```
     499             : 
     500             : ### Visualizing the Kuzmin Problem
     501             : 
     502             : Once your run finishes, extract the volume data with `generate-xdmf` using
     503             : 
     504             : ```
     505             : spectre generate-xdmf \
     506             :   --subfile-name VolumeData --output kuzmin_problem \
     507             :   ScalarAdvectionKuzmin2DVolume*h5
     508             : ```
     509             : 
     510             : (Note that the `subfile-name` is different than before because it was different
     511             : in the input file) and load it into Paraview once again. We are only interested
     512             : in the quantity `U` which is the scalar field we were evolving. You can uncheck
     513             : any other boxes. So now, instead of coordinates on your screen, you should see a
     514             : large square colored by the solution profile described in the \link
     515             : ScalarAdvection::Solutions::Kuzmin Kuzmin \endlink system. You should also
     516             : notice that there are smaller squares that don't touch each other in the middle
     517             : of the domain and on the edges there are large sections that are continuous.
     518             : These are the FD and DG grids, respectively. If you go to the top bar in
     519             : Paraview and change how you view the grid to `Surface With Edges`, this will
     520             : become even more apparent.
     521             : 
     522             : You will notice that the FD grid is mostly around where the interesting features
     523             : are in the solution profile; the cylinder with a wedge cut out, the cone, and
     524             : the hump. And then the DG grid is mostly where the solution should be zero
     525             : towards the boundary of the domain (i.e. the very smooth part). So right from
     526             : the start, you can see that we are saving computational effort by only doing the
     527             : expensive, yet robust, method (FD) where it is necessary and the efficient
     528             : method (DG) everywhere else where the solution is smooth.
     529             : 
     530             : Now hit the "Play" button in the top bar of Paraview and watch the solution
     531             : evolve. You'll notice that the elements in the domain switch back and forth
     532             : between FD and DG. They do so in such a way that the elements will switch to FD
     533             : when an interesting feature enters the element and then switch back to DG once
     534             : the feature leaves. In this way, we are able to actually track shocks and
     535             : discontinuities in real time in our solution by where the code switches to using
     536             : FD instead of DG. This is extremely useful for expensive GRMHD simulations where
     537             : we only want to do FD at a shock boundary, yet that shock boundary is moving
     538             : through the domain. We are able to dynamically track this shock and resolve it
     539             : well with FD, then switch back to DG after the shock passes through and the
     540             : solution has settled down again.
     541             : 
     542             : A pretty cool filter you can add is `Warp By Scalar`. In the left panel, choose
     543             : the solution variable `U` as the scalar to use and hit `Apply`. In the viewing
     544             : panel there should be a `2D` or `3D` button that you can toggle to make the view
     545             : 3D. Once you do that you should be able to see that the height of the feature is
     546             : your solution `U`. If you change the grid to `Surface With Edges` you can see
     547             : the FD or DG grids warp with the solution. And if you hit "Play" it'll rotate
     548             : around and you'll see the features moving in 3D! (Don't worry if you can't find
     549             : this filter. Not all versions of Paraview may have it.)
     550             : 
     551             : We encourage you to play around with the refinement and grid points before the
     552             : next section to get a feel for how each changes the runtime and accuracy of
     553             : solution.
     554             : 
     555             : ## Editing the Kuzmin System
     556             : 
     557             : Hopefully now you feel comfortable enough running SpECTRE that you
     558             : can get the default input file for the pre-built executables, edit it, and run
     559             : it. Now we are going to try our hand at actually editing some code in SpECTRE
     560             : and then building SpECTRE. We're going to stick with the \link
     561             : ScalarAdvection::Solutions::Kuzmin Kuzmin \endlink system and add a new feature
     562             : to the solution profile!
     563             : 
     564             : You can find the files for the Kuzmin system at
     565             : `/work/spectre/src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/
     566             : Kuzmin.?pp`.
     567             : In the `hpp` file, you'll see a lot of Doxygen documentation and then the actual
     568             : Kuzmin class. The only function that you will need to care about is
     569             : 
     570             : ```cpp
     571             : template <typename DataType>
     572             : tuples::TaggedTuple<ScalarAdvection::Tags::U> variables(
     573             :     const tnsr::I<DataType, 2>& x, double t,
     574             :     tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;
     575             : ```
     576             : 
     577             : All of our analytic solutions have a function similar to this that will set the
     578             : value corresponding to the tag in the return type. If you're unfamiliar with
     579             : tags in SpECTRE, you can look at these sections for an explanation, \ref
     580             : databox_a_taggedtuple_databox and \ref databox_a_proper_databox. However, it's
     581             : basically just a fancy way of doing a compile-time key/value pair. The tag is
     582             : the key, and the value is whatever you want it to be. In our case, the value is
     583             : a tensor, representing the solution.
     584             : 
     585             : The definition of this function in the `cpp` file is where you will be editing
     586             : the actual Kuzmin solution. Towards the bottom of this function, there is a
     587             : `for` loop that sets the solution at every grid point. This is where you will
     588             : add in a new feature to the solution.
     589             : 
     590             : You can pick any feature you want to add, so long as it's inside the domain
     591             : bounds of `[0,1]x[0,1]` and centered around `(0.75, 0.5)`. This is because of
     592             : how the kuzmin solution is set up with existing features at `(0.25, 0.5); (0.5,
     593             : 0.25); (0.5, 0.75)`. If you're having trouble thinking of a feature to add try
     594             : one of the following features:
     595             : 
     596             : - Square centered at `(0.75, 0.5)` with solution value `1.0`
     597             :   - Side length `0.1` (any larger and it might interfere with the other
     598             :     features)
     599             :   - Circle of radius `0.045` centered on the square with value `0.0`
     600             : - Triangle centered at `(0.75, 0.5)` with one corner facing in the `+x`
     601             :   direction with solution value `1.0`
     602             : - Square centered at `(0.75, 0.5)`
     603             :   - Side length `0.1` (any larger and it might interfere with the other
     604             :     features)
     605             :   - Left half of the square has value `1.0` and right half of the square has
     606             :     value `0.5`
     607             : 
     608             : \note The more detailed you make your feature, the more resolution you will need
     609             : to resolve it.
     610             : 
     611             : ### Re-building SpECTRE
     612             : 
     613             : Once you have your feature coded up, go ahead and save your changes. Now we will
     614             : build SpECTRE! Go to the `/work/spectre/build` directory. This is where you have
     615             : to be in order to build SpECTRE. We use [CMake](https://cmake.org/) to configure
     616             : our build directory. However, since the executables are already pre-built, this
     617             : means the build directory is already configured! So you don't have to worry
     618             : about `CMake` for now. If you wanted to reconfigure, for example using a
     619             : different compiler, then you'd have to run `CMake`. If you want to learn more
     620             : about how we use `CMake`, take a look at the \ref common_cmake_flags developers
     621             : guide.
     622             : 
     623             : To build the Kuzmin executable, run
     624             : 
     625             : ```
     626             : make EvolveScalarAdvection2D
     627             : ```
     628             : 
     629             : This should be very fast because you only edited a `cpp` file. Congrats! You've
     630             : just built SpECTRE!
     631             : 
     632             : Now re-run the executable in your `/work/runs2` directory. Hopefully everything
     633             : works and you get some output. When you plot it in Paraview, it should look
     634             : almost the same as before except your feature will be there too rotating with
     635             : the others! How cool! You can also see if your feature needs FD or DG more by
     636             : how much it switches back and forth.
     637             : 
     638             : Experiment some more with either different features or different resolution!
     639             : 
     640             : ## Conclusions
     641             : 
     642             : Congrats! You've made it through the tutorial! If you only want to run our
     643             : pre-built executables, you have all the tools necessary to run, visualize, and
     644             : re-build them. If you want a full list of our executables, do
     645             : `make list` in the build directory. This will also include our `Test_`
     646             : executables which you can just ignore.
     647             : 
     648             : In an already configured build directory, all you have to do to build a new
     649             : executable is
     650             : 
     651             : ```
     652             : make ExecutableName
     653             : ```
     654             : 
     655             : and then you can copy the default input file from
     656             : `/work/spectre/tests/InputFiles` and run it. Running an executable with the
     657             : `--help` flag will give a description of what system is being evolved and the
     658             : input options necessary.

Generated by: LCOV version 1.14