-
Notifications
You must be signed in to change notification settings - Fork 2
Paraview: Vortex and Tendex
Here we describe the practical basics of creating vortex/tendex data from SpEC. To do this, you'll first need to set up your run such that it calculates and outputs data needed by the streamline integrator. Next, you'll have to run ApplyObservers from an appropriate input file controlling the streamline integrator options. After this is complete, it remains only to visualize the streamlines using your favourite visualization software; probably ParaView.
The detailed procedure outlined here worked prior to SpEC's support of AMR. The exact options required may now differ in small details.
First, create initial data per the usual procedure.
You'll now need to modify some of the .input files within the Ev directory (you could, of course, do this before generating ID). You'll need options to a) store volume data for the electric and magnetic components of the Weyl tensor and b) output that data.
Up to the exact positioning of commas, a working set of options for a) is
WeylMagnetic(Output=DistortedWeylB;
CdK=DistortedCdK;
g=Distortedg;
SqrtDetg=DistortedSqrtDetg;
),
WeylElectric(Output=DistortedWeylE;
Ricci = DistortedSpatialRicci;
ExCurv = DistortedK;
Invg = DistortedInvg;),
SpatialCoordMap::TransformTensorToDifferentFrame
(Input =DistortedWeylB;
Output=InertialWeylB;
IndexPositions=ll;
MapPrefixToInputFrame=DistortedToInertial;
),
SpatialCoordMap::TransformTensorToDifferentFrame
(Input =DistortedWeylE;
Output=InertialWeylE;
IndexPositions=ll;
MapPrefixToInputFrame=DistortedToInertial;
),
SpatialCoordMap::TransformTensorToDifferentFrame
(Input =Distortedg;
Output=Inertialg;
IndexPositions=ll;
MapPrefixToInputFrame=DistortedToInertial;
),
which should go in DataBoxItems.input. The first two options compute WeylElectric and WeylMagnetic from quantities which are present by default. These are in the 'distorted' frame, while visualization in the 'inertial' frame is preferable; the conversion is performed by the next three options. It is possible to specify a SpatialCoordMap within the streamline integrator itself, which is presumably more computationally efficient, so this step may be dropped if wall clock time of the evolution becomes an issue.
Next in Observers.input one might add
ObserveInSubdir(Subdir=Streamlines;Observers=
DumpTensors(
Input = InertialWeylE, InertialWeylB, Inertialg;
FileNames = VTWeylE, VTWeylB, Inertialg;
);
),
which instructs SpEC to create subdirectories Run/Streamlines and to dump the DataBox items InertialWeylE, InertialWeylB, and Inertialg - at each timestep and grid point - within, under the file names VTWeylE, VTWeylB, and Inertial g. The former were named in DataBoxItems.input above, and of course any name is possible so long as the two files are consistent.
This could conceivably become a rather large amount of data for a 'real' run, so a more efficient solution might be required in this case.
You will also need options to control how long the run goes on for, at which timestep data are dumped, etc, but control of these aspects is not affected by the vortex/tendex considerations.
In principle, the streamline integrator could be included within Observers.input and SpEC could presumably do everything at once. However, I've never tried this, and the streamline documentation heavily implies that noone else has either, so you're on your own if that is the plan.
The mainstream solution is to run ApplyObservers as a postprocessing step. The relevant class is Observers::ParallelEigenvectorStreamlines, which integrates the streamlines of eigenvector fields in parallel. Here's the class description:
static std::string Help() {
return ClassID()+"\n"
" \n"
"OPTIONS: \n"
" Initializer = string; \n"
" # EigenStreamInitialzer to be used to set initial conditions/\n"
" # names for the streamlines. \n"
" SymmetricCovariantTensor = string; \n"
" SpatialMetric = string; \n"
" # SymmetricCovariantTensor and SpatialMetric must be \n"
" # provided with components in the same frame 'StartPoint' \n"
" # is specified in. \n"
" MapPrefixFromGridFrame = string; \n"
" # If specified, then streamline integration will be \n"
" # performed in the mapped frame. To find streamlines in the\n"
" # inertial frame choose 'GridToInertial'. \n"
" AbsoluteErrorScale = double; \n"
" # error scale for OdeErrorMeasures::Absolute \n"
" MinimumDlambda = double; \n"
" # minimum step size \n"
" InitialDlambda = double; \n"
" # suggested initial step size \n"
" EigenvalueDegeneracyTol = double; \n"
" TerminateOnLambda = double; \n"
" # if this option is specified, streamline integration will \n"
" # terminate at this lambda value. \n"
" TerminateOnFullDegeneracy = bool; # default false \n"
" # Terminates streamline integration when all eigenvalues \n"
" # are degenerate according to EigenvalueDegeneracyTol. \n"
" DenseTriggerOnLambda = string; \n"
" # Standard syntax for DenseTrigger. Triggers on lambda, the\n"
" # streamline parameter. \n"
" Verbosity = int; \n"
;
}
Your ApplyObservers.input will need to 1. Set up a DataBox and read into it VTWeylE, VTWeylB, and Inertialg from disk. If you're planning to see from the apparent horizons, which you probably are, you'll need those as well. A working set of options is:
DataBoxItems =
Domain(
Items=
ReadSurfaceInfoFromFile(
FileName=ApparentHorizons/AhACoefs.dat;
Output = AhA;
Time = 0.0;
DeltaT=1.e-7;
),
ReadSurfaceInfoFromFile(
FileName=ApparentHorizons/AhBCoefs.dat;
Output = AhB;
Time = 0.0;
DeltaT=1.e-7;
),
),
Subdomain(
Items=
#volume WeylE
ReadTensorFromDisk(
Input = VTWeylE;
Time = 0.0;
DeltaT = 1.e-10;
Dim = 3;
Dir = Streamlines;
RankSymm = 11;
Output = VTWeylE;
),
#volume WeylB
ReadTensorFromDisk(
Input = VTWeylB;
Time = 0.0;
DeltaT = 1.e-10;
Dim = 3;
Dir = Streamlines;
RankSymm = 11;
Output = VTWeylB;
),
#spatial g
ReadTensorFromDisk(
Input = Inertialg;
Time = 0.0;
DeltaT = 1.e-10;
Dim = 3;
Dir = Streamlines;
RankSymm = 11;
Output = Inertialg;
),
);
2. Run ParallelEigenvectorStreamlines on those data. Each separate call will create one class of streamlines seeded from the intersection of a user-selected Strahlkorper with rays along a fan of N vectors from some central point to some VecA and some VecB. The streamlines will be integrated out to some coordinate distance lambda, also specified by the user. For example, to seed 10 length-50 lines from AhA radiating from (10.0,0.0,0.0) to a fan of vectors from (0,0,1) to (1,0,0), one might include
Observers =
ObserveInSubdir(Subdir=Streamlines;Observers=
#Tendex for AhA
#+z to +x
ParallelEigenvectorStreamlines
(SymmetricCovariantTensor=VTWeylE;
SpatialMetric=Inertialg;
MapPrefixFromGridFrame =;
AbsoluteErrorScale=1.e-6;
MinimumDlambda=1.e-10;
InitialDlambda=1.e-3;
EigenvalueDegeneracyTol=1.e-9;
DenseTriggerOnLambda=EveryDeltaT(DeltaT=0.1;);
Verbosity=2;
TerminateOnLambda=50;
Initializer=ApparentHorizon(
StrahlkorperBasename=AhA;
Center = (-10.0,0.0,0.0);
VecA = (0,0,1);
VecB = (1,0,0);
Basename = TendexAhAa;
NumStreams = 10.;
VecAposition=0.0;
VecBposition=1.0;
NumRefinements=5;
);
);
);
Thus, to create nice dipole-like fields, at least eight invocations are required; one for each of two half-circles about each apparent horizons for each class of streamline (tendex and vortex). To clarify the options:
- SymmetricCovariantTensor: the DataBox item (assumed symmetric) whose eigenvalues are to be integrated.
- SpatialMetric: the spatial metric in the same coordinate system as SymmetricCovariantTensor.
- MapPrefixFromGridFrame: optional; a SpatialCoordMap to be applied to the former two tensors.
- AbsoluteErrorScale, MinimumDlambda, InitialDLambda: the code will attempt to use the largest step size in lambda within some error threshold. These are that threshold, the minimum step size, and the initial step size guess, respectively.
-EigenvalueDegeneracyTol: If the Weyl fields have degenerate eigenvalues, a naiive integrator may spuriously hop from one streamline to another (since the two will be parallel at the point of degeneracy). This is a smoothing threshold which prevents this. If you are seeing lines veer off at right angles, you need to increase this value.
- DenseTriggerOnLambda: I'm not entirely clear on what this does.
- Verbosity: ditto, though the basic idea is fairly self-evident.
- Initializer: this controls where the streamlines get seeded from. Several initializers exist, but you are probably interested in ApparentHorizon, which does the aforementioned fan-of-rays thing. Its options are:
-StrahlkorperBasename: whatever you called the relevant AH.
-Center: the approximate location of the AH's centre in grid coordinates. As far as I can tell, this must be specified by hand, and needs to at least be inside the AH, but strict accuracy is not necessary.
- VecA, VecB: the vectors bounding the fan.
- Basename: added to the filename of the output.
- NumStreams: how many streamlines to integrate.
- VecAposition, VecBposition: presumably these control the positioning of the seed vectors, but I have not needed to change them from their defaults.
- NumRefinements: controls the accuracy of the integrator.
Integrating a streamline can take a while, and as far as I've been able to determine there is no way to resume an integration if your wallclock time runs out. You thus might find it advantageous to create more instances of ParallalEigenvectorStreamlines than strictly required, submitting each independently.
Now you just run ApplyObservers on your file (e.g. ApplyObservers -UseTimes=0.0 ApplyObservers.input, which you can qsub if you wish).
If everything worked, Streamlines should now contain a bunch of files named something like "Tendex*.dat, Vortex*.dat". There exists a SpEC exec to bundle these into nice ParaView friendly .vtk files, which you run from within the Streamlines directory using i.e.
ConvertStreamlinesToVtk -n Tendex Tendex*.dat
which will create a Tendex.vtk which can be simply loaded into ParaView.