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

Generated by: LCOV version 1.14