====================================================== ES: Elementary Supernova Spectrum Synthesis User Guide ====================================================== .. footer:: ###Page### .. sectnum:: .. contents:: Table of Contents .. raw:: pdf PageBreak Programs ======== - SYN++ --- Consider this a rewrite of the original SYNOW [1]_ code in modern C++. It has a few further enhancements, a new structured input control file format, and the atomic data files have been repackaged and are more complete than what SYNOW has. - SYNAPPS --- This code uses the same underlying library code used to build SYN++ to implement a spectrum synthesis calculation within the objective function of a parallel optimization framework. So, SYNAPPS works like an automated SYN++. The idea is for SYNAPPS to do the fitting work so a supernova spectroscopist can do thinking. Software Dependencies ===================== If you only want to use SYN++, then there is only one dependency. If you want to use SYNAPPS, there are three more, and you have to be able to compile and run parallel applications via MPI. Note that if you use Mac OS X, all of the packages listed below can be obtained quite easily using MacPorts (http://www.macports.org/). For Linux users, consult your package management software (like "aptitude" if you use Ubuntu). Sorry Windows users: You are on your own here. - CFITSIO (http://heasarc.gsfc.nasa.gov/fitsio/) FITS I/O library. Required for building SYN++ and SYNAPPS. The atomic line list files are stored in FITS format, eliminating a support issue SYNOW had with byte rotation. - BLAS (http://www.netlib.org/blas/) Basic Linear Algebra Subprograms. Required for building SYNAPPS, but not SYN++. - LAPACK (http://www.netlib.org/lapack/) Linear Algebra Package. Required for building SYNAPPS, but not SYN++. - APPSPACK-5.0.1-C3 (https://software.sandia.gov/appspack/downloads/appspack-5.0.1-C3.tar.gz) Asynchronous Parallel Pattern Search, special Computational Cosmology Center version for use with SYNAPPS. Required for building SYNAPPS, but not SYN++. If you visit the APPSPACK [2]_ website, be sure you get the version of APPSPACK from the "other versions" page that says "special version only for use with SYNAPPS". Otherwise you will have a harder time building it. Note also that BLAS/LAPACK have to be built first. - MPI (for example, http://www.open-mpi.org/) Message Passing Interface. You need to have MPI compilers and libraries installed in order to use APPSPACK. - OpenMP (http://openmp.org/wp/) Shared-memory parallel programming. This is an optional dependency for both SYN++ and SYNAPPS. Note that if you want to use SYNAPPS in hybrid parallel mode (with both MPI and OpenMP) that you may need to study your "mpirun" (or equivalent) command line to ensure you get partitioning right. Downloads ========= - Source code is hosted on GitHub [3]_ at http://github.com/rcthomas/es/ - The latest distribution version is available at https://github.com/rcthomas/es/releases - The atomic line data is available at http://c3.lbl.gov/es/es-data.tar.gz Installation ============ First, follow the instructions for compiling the other software dependencies, or install them using package manager software like MacPorts or aptitude, depending on your system. When you install APPSPACK, consult the "platforms" subdirectory there. You may find a platform installation script that is suitable to your situation. Importantly, if you specify a --prefix=$PREFIX argument to configure for APPSPACK, keep track of what value you used for $PREFIX, you'll need it later. Once the dependencies (including APPSPACK-5.0.1-C3) are installed, you can proceed with ES. It ought to look something like this on a Unix-like system. Suppose you downloaded version 1.00 of ES. Then:: $ tar zxvf es-1.00.tar.gz $ cd es-1.00 $ ./configure $ make $ make install You may need to pass options to configure (see "./configure -h" for the full list you can use). Most commonly one will need to use the --prefix flag. If you do not have super-user privileges, you could do something like:: $ ./configure --prefix=$HOME/local Thus, the "make install" command will deposit the ES executables and example control files into the installation path. No libraries or header files are installed. If you use the above example, then you get this structure in your file system when all is said and done:: $HOME/ local/ bin/ syn++ synapps ... share/ es/ syn++.yaml synapps.yaml An example. Most supernova researchers I know work on Mac OS X. I like to use MacPorts to install my Unix-like packages. This works alright for me on Mac OS X Snow Leopard with MacPorts:: ./configure CXX=g++-mp-4.4 CC=gcc-mp-4.4 F77=gfortran-mp-4.4 --prefix=$HOME/local --with-cfitsio-cpp="-I/opt/local/include" --with-cfitsio-libs="-L/opt/local/lib -lcfitsio" --with-appspack-cpp="-I$HOME/local/include" --with-appspack-libs="-L$HOME/local/lib -lappspack" Note that the above example is included in the script located at "platforms/osx_macports.sh" and you can just run that if you have the same set up. Note that the example assumes that APPSPACK was installed at "--prefix=$HOME/local." Exactly what you do depends largely on taste. Ted Kisner deserves thanks for helping with the ES build system. Atomic Lines ============ ES makes use of atomic line data files. These are available in the format ES needs on the web (see the `Downloads`_ section). They are bundled into a single tar.gz file. Once downloaded, unpack them. You can put them anywhere, but I personally like to place them alongside the sample control files that come with ES. Following the example above where ES is installed into $HOME/local, I would do:: $ mv es-data.tar.gz $HOME/local/share/es $ cd $HOME/local/share/es $ tar zxvf es-data.tar.gz $ mv es-data/lines . $ mv es-data/refs.dat . Then, in the control files for the ES executables, I set:: opacity : line_dir : /Users/rthomas/local/share/es/lines ref_file : /Users/rthomas/local/share/es/refs.dat Running SYN++ ============= The "syn++" executable has only one required argument, the name of the YAML [4]_ control file:: $ syn++ syn++.yaml This will compute one or more synthetic spectra and write them to standard output. If more than one spectrum is output, they are separated by a blank line. The format is multi-column ASCII, with the first two columns being wavelength and flux. Sending the result to a file is done by redirect, for example:: $ syn++ syn++.yaml > my_spectrum.dat And one can redirect the output to a process like xmgrace [5]_:: $ syn++ syn++.yaml | xmgrace observed_spectrum.dat - If you run "syn++" from within a scripting language like Python or Ruby, you can capture the output via pipe (no temporary files) and do something interesting with it. Maybe you could automate writing your papers this way. There are some command-line options. - The "--verbose" option provides a counter (written to standard error) so you know that SYN++ is doing something and how it's progressing. - The "--wl-from=SPECTRUM" option makes SYN++ read in the specified spectrum file (three column ASCII format) and use the first column as the wavelength output grid. Especially handy if you need the wavelengths of the spectrum you are fitting to match up with the synthetic one for subtracting. If you've compiled with OpenMP enabled, you can use multi-core computing to improve SYN++ performance. Don't expect it to scale without bound, I only parallelized part of the code (the slowest part). To take advantage of it, you need to set OMP_NUM_THREADS to something like the number of cores you want to use. If you have a quad-core Opteron, you can do something like:: $ export OMP_NUM_THREADS=4 $ syn++ syn++.yaml > my_spectrum.dat It should run faster than with OMP_NUM_THREADS=1. Your default value may already equal the number of cores available on your machine. A sample SYN++ control file is distributed with the code. After the code is installed, it should be found under the "share/es" directory of the installation, with the name "syn++.yaml." Copy it somewhere and open it up. Note that the ordering of the sections of the control file is not really important, but each variable belonging to a section must be listed under the section header. That is, you cannot put "min_wl" into the "source" section; it goes under "output." Now, let's go over the example control file, section by section. Note that by convention, all wavelength quantities in the SYN++ and SYNAPPS control files are in Angstroms, all temperatures are in 10^3 K, and all velocities are in 10^3 km/s. The "output" section controls the wavelength grid of the synthetic spectrum:: output : min_wl : 2500.0 # minimum wavelength in AA max_wl : 10000.0 # maximum wavelength in AA wl_step : 5.0 # wavelength spacing in AA Note that the "--wl-from" command line option will override this section. Each of the variables should be easy enough to understand. The "grid" section controls the velocity, line opacity, and line source function grids:: grid : bin_width : 0.3 # opacity bin size in kkm/s v_size : 100 # size of line-forming region grid v_outer_max : 30.0 # fastest ejecta velocity in kkm/s Most of the time you will not mess with any of these variables. The most important one to know about is "v_outer_max," which is set to a value at least as big as any "v_outer" you are going to specify in any of the setups. This setting establishes the wavelength limits of the atomic line list to be loaded for all setups. You might think that the output wavelength limits were enough to establish this, but actually more lines in the blue and red are needed to establishe the source function and compute the full spectrum. The next section is the "opacity" section:: opacity : line_dir : /usr/local/share/es/lines # path to atomic line data ref_file : /usr/local/share/es/refs.dat # path to ref. line data form : exp # parameterization (only exp for now) v_ref : 10.0 # reference velocity for parameterization log_tau_min : -2.0 # opacity threshold The first two variables contain the path to the atomic line list directory on disk. The "form" variable should always be set to "exp" until we add new parameterizations; it is the same for all opacity profiles in all setups. The value of "v_ref" is the reference velocity for all opacity profiles, they are scaled to the value of "log_tau" at this velocity (given by each profile in each setup). Bins with integrated opacity (over all lines in a bin) smaller than "log_tau_min" are ignored in the calculation of the source function and spectrum. Next is the "source" section, which is very simple:: source : mu_size : 10 # number of angles for source integration Again, you don't really need to mess with this. However, you might find a performance boost if you are using OpenMP and this number is set to a multiple of the number of cores available. And next is "spectrum" which controls how the output spectrum is calculated:: spectrum : p_size : 60 # number of phot. impact parameters for spectrum flatten : No # divide out continuum or not First is the number of impact parameter rays subtending the photosphere as viewed in projection from infinity. More rays are added going out to "v_outer" as needed to integrate the projected flux. The "flatten" option computes the spectrum without the underlying thermal continuum or any warping parameters. Each synthetic spectrum computation is governed by a "setup." Multiple setups can be placed into a SYN++ YAML control file. They are simply expressed as YAML lists: Each setup is preceded on its first line by a "-" character. This is useful, since if different setups all use the same ions, the line list is only loaded once per run of SYN++, not per setup. Even if the list of ions changes from setup to setup, those ions in common between subsequent setups will not be dumped and re-loaded. Here's the start of the "setups" section and the first setup:: setups : - a0 : 1.0 # constant term a1 : 0.0 # linear warp term a2 : 0.0 # quadratic warp term v_phot : 8.0 # velocity at photosphere (kkm/s) v_outer : 30.0 # outer velocity of line forming region (kkm/s) t_phot : 12.0 # blackbody photosphere temperature (kK) ions : [ 1601, 2201, 2401, 2601 ] # ions (100*Z+I, I=0 is neutral) active : [ Yes, Yes, Yes, Yes ] # actually use the ion or not log_tau : [ 0.1, 1.0, 1.0, 1.0 ] # ref. line opacity at v_ref v_min : [ 10.0, 10.0, 10.0, 10.0 ] # lower cutoff (kkm/s) v_max : [ 30.0, 30.0, 30.0, 30.0 ] # upper cutoff (kkm/s) aux : [ 1.0, 10.0, 10.0, 10.0 ] # e-folding for exp form temp : [ 10.0, 10.0, 10.0, 10.0 ] # Boltzmann exc. temp. (kK) The parameters "a0," "a1," and "a2" are the coefficients of a quadratic warping function that can be multiplied by the synthetic spectrum once it is computed. This can be helpful if the target spectrum has a low-frequency trend that SYN++ cannot replicate (a sharp photosphere blackbody and no electron scattering has its limitations). Parameters "v_phot" and "v_outer" are the inner and outer boundaries of the line-forming region, respectively. Remember that no "v_outer" may exceed "v_outer_max" in any of the setups. Parameter "t_phot" is the Blackbody temperature of the photospheric lower boundary of the line-forming region. The next two variables, "ions" and "active" are not really parameters, but rather ion labels for each opacity profile, and a boolean value for whether or not to actually turn the opacity profile on or off. Being able to do this when iteratively fitting can be handy. The format of the ion species is 100 * atomic number + ionization state, with an ionization state of "0" being neutral. The last five variables control each opacity profile. The opacity profiles are listed from left to right, in columns. The same ion may be listed more than once, but there are precedence rules for opacity profiles with repeated ions. First, each successive profile which repeats an ion gets precedence to set the reference opacities between its specified values of "v_min" and "v_max." Second, the rightmost temperature listed for an ion wins. These two rules also hold for SYNOW, and though arbitrary we have kept to them. The opacity profile parameters in more detail. Parameter "log_tau" is the base-10 logarithm of the reference line opacity at the value of "v_ref" set in the "opacity" section. Parameters "v_min" and "v_max" are the limits in velocity of the opacity profile, but "v_phot" and "v_outer" set hard limits. The "aux" parameter is a generic name for whatever the opacity "form" needs it to be. For an "exp" form, it is the e-folding length of the opacity profile. Finally, "temp" is the Boltzmann excitation temperature for parameterizing line strengths. Running SYNAPPS =============== The "synapps" executable has only one required argument, the name of the YAML control file:: $ mpirun -np 16 synapps synapps.yaml Your command line may look different, depending on MPI. While SYNAPPS runs it channels APPSPACK, which is set to be quite chatty, and when it starts up it will output a bunch of diagnostics about the problem. Then it will start solving, and you can watch as each new best minimum of the objective function is found. When it's done, it will output a summary. If you have enabled OpenMP and want to use OpenMP to increase performance of the objective function, you need to hierarchically distribute your cores. This should not be too hard, but it may vary from system to system:: $ export OMP_NUM_THREADS=2 $ mpirun -npernode 4 synapps synapps.yaml Here we specify that each worker gets 2 OpenMP threads with which to compute the objective function, but there are 4 MPI tasks per node. The above example is for a case where there are 8 cores total per node. You will likely want to capture this output into a log file, because it can come in handy later. For example, in bash:: $ mpirun -np 16 synapps synapps.yaml | tee synapps.log The tee command will let you watch the progreess of the code but also save it to the "synapps.log" file. Let's examine the "synapps.yaml" example YAML control file that came with ES, it should be under "share/es" in the install tree. The sections "grid," "opacity," "source," and "spectrum" are the same as with SYN++, so we skip those. Also, note the absence of the "output" section. This is because the wavelength grid for the output will be determined by the target spectrum being fit. There are two new sections, "evaluator" and "config" which I discuss below. For "evaluator," things look like this:: evaluator : target_file : "target.dat" # spectrum to fit (format: wl, flux, flux_error) vector_norm : 2 # objective function norm regions : apply : [ Yes, No, Yes ] # fit this wavelength region or not weight : [ 0, 1, 1 ] # weight the region by this number lower : [ 0, 3900, 5600 ] # min. wavelength for region definition upper : [ 10000, 4100, 6400 ] # max. wavelength for region definition This section controls how the objective function is evaluated. This depends on the target file being fit, what vector norm is being used, and the regions of the target spectrum to be fit. The target file is a three-column ASCII input file with columns wavelength, flux, and flux error; note the flux error is not the variance, it should be like standard error. Of course the spectrum must be in rest-frame: There are better codes out there for determining the redshift and phase of your spectrum! [6]_ The "vector_norm" parameter is the exponent of an L-norm specification. So, a value of 1 is like absolute value, a value of 2 is like a chi-squared, etc. Note that the residuals that go into the calculation are all weighted by the standard error. The "regions" sub-section specifies whether there are parts of the target spectrum to be ignored (for telluric lines, or simply there is known to be no line opacity to fit in certain parts). The regions are listed in columns, and if no regions are listed then the entire spectrum is fit. The way regions work is that they are applied from left to right by logical "or" if they are applied. In the above example, first we mask out everything between a wavelength of 0 and 10000 (that is, weight it by 0). The next region is skipped (apply=No) altogether. The last region is applied, activating SYNAPPS to fit the region between 5600 and 6400 AA. To discuss the "config" section, we break it up into parts. The top of the "config" section looks like:: config : fit_file : "target.fit" # when done, put answer here cache_file : "target.cache" # evaluated point cache Pretty straightforward, the final best fit will be output to the file given by "fit_file" and the name of the cache input/output file is given by "cache_file." Next comes the configuration of scalar parameters:: a0 : { fixed: No, start: 1, lower: 0, upper: 10, scale: 10 } a1 : { fixed: No, start: -2.6, lower: -10, upper: 10, scale: 20 } a2 : { fixed: No, start: -5.0, lower: -10, upper: 10, scale: 20 } v_phot : { fixed: No, start: 10.7, lower: 5, upper: 15, scale: 10 } v_outer : { fixed: Yes, start: 30, lower: 15, upper: 30, scale: 1 } t_phot : { fixed: No, start: 11.4, lower: 5, upper: 25, scale: 20 } These parameters should be recognizable from the discussion of the SYN++ YAML control file. Each parameter has 5 specifications for SYNAPPS. First is "fixed," which is "Yes" if the parameter is to be left at the "start" value and "No" if you want SYNAPPS to fit for it. Keyword "start" is the initial value for the parameter. The "lower" and "upper" keywords give the lower and upper bounds constraints for the parameter, and the "scale" keyword basically gives SYNAPPS a way to rescale the parameter in a numerically convenient way (think of it as a normalizing factor). In the above example, "v_outer" will be held fixed to 30,000 km/s, but all the other parameters will be fit for by SYNAPPS. Next after the scalar parameters, we see some lists. These are not of parameters, but are switches and labels that go with each of the opacity profiles that follow:: ions : [ 1401, 1100, ] active : [ Yes, No, ] detach : [ No, No, ] The "ions" labels and "active" switches should be familiar from the SYN++ YAML control file discussion. The "detach" switch is used to disengage a linear equality constraint that fixes "v_min" of the opacity profile to the value of "v_phot." Disengaging this constraint will "detach" the profile and allow "v_min" and "v_phot" to be fit independently. To be clear: Setting it to "no" forces "v_min" to be exactly equal to "v_phot." If you set detach to "no" then be sure the "start" value of "v_min" equals "v_phot" or you will get an "infeasible point" crash. Finally we discuss one of the opacity profile parameters only ("log_tau"), the extension to the other parameters is trivial:: log_tau : fixed : [ No, No, ] start : [ 1.14, 0, ] lower : [ -2, -2, ] upper : [ 2, 2, ] scale : [ 1, 1, ] Each opacity profile has parameters "log_tau," "v_min," "v_max," "aux," and "temp." Each of these in turn has the 5 keywords to specify, as in the case of the scalar parameters. They have the same function as in that case. In all cases, be sure that the "start" value is between the "lower" and "upper" bounds constraints or you will get an infeasible point error. Python Code Included ==================== There are a couple scripts and modules that I wrote for managing the YAML control files. One of them takes a SYNAPPS YAML control file and the SYNAPPS log and turns the last best fit into a SYN++ YAML control file. You can then turn ions on or off or tweak the fit if you like, or think you can do better than SYNAPPS. Citing SYN++ or SYNAPPS ======================= A code description paper is in the works. When it is published, please cite that. Until then, please make some form of acknowledgement that you used this code. Frequently Asked Questions ========================== Is a precompiled binary version available? ------------------------------------------ Sorry, no can do. APPSPACK is licensed under LGPL, and ES is licensed under BSD. I cannot link APPSPACK into ES and distribute the product as a binary. If you have problems compiling, please don't hesitate to ask for help. How can I get a PDF copy of this manual? ---------------------------------------- You can create it yourself from the README using rst2pdf, or you can get a copy of it here: http://c3.lbl.gov/es/es_manual.pdf. Why does SYNAPPS crash right away, with an "infeasible point" message? ---------------------------------------------------------------------- You may have specified in your control file a starting point that is considered infeasible. That is, you might have a variable set outside its bounds constraints. Another way this could happen is if you are re-using a previously written cache file, but the number of parameters cached is different. Also, be aware that if you re-use a cache file and do something like swap one ion out for another, the cache will be invalidated and you cannot re-use it with the new run. These errors are not that helpful, but they are at least thrown as exceptions we could catch and handle. It's on the roadmap to work on this issue. Why doesn't SYNAPPS figure out the ions for me? ----------------------------------------------- SYNAPPS uses an optimizer which does not handle categorical variables. Using categorical variables would be one way to potentially solve the problem. We're looking at other optimizers all the time. Stay tuned. Another approach would be to work out a bunch of heuristics for it, but that'd all be pre-processing and wouldn't have to be part of SYNAPPS. It's an interesting problem. How do I stop SYNAPPS and still get the fit output? --------------------------------------------------- The answer is sort of at an Easter-egg level. I would like to think of a better way to do this. The first step is to run the "ps" command to find out the process ID's of all the MPI tasks running synapps. Then, take the first synapps process ID (NOT the "mpirun" one) that is listed in numerically ascending order. Then send it a TERM signal like:: $ kill -15 PID If you did it right, SYNAPPS will catch the signal and tell all the workers to shut down. The master will run an evaluation of the most recent best fit it has and write it out to the file requested in the "config" section of the YAML control file. On most batch queue systems you don't get enough time to write out before the KILL signal is sent, and I think that goes to the process leader (not the SYNAPPS master process). Why use YAML for control file input? ------------------------------------ SYN++ and SYNAPPS use structured input files. The older SYNOW code uses a Fortran feature called "namelist input" which can map values into program variables at runtime. I know of nothing similar that is just built into C++ that could compete (let alone go farther). The easiest solution is to flatten the input into a sequence of unique key key-value pairs, but this produces rather large control files. More complicated solutions involve writing your own parser, which I believe is best left to the professionals. So, early on I experimented with embedding the Lua [7]_ interpreter into the code. This worked rather well, and raised a numer of very interesting possibilities for user customization. But, I found that it would be very useful to be able to have a code serialize an internal state, and then use that serialized state as input to another (or the same) code. Lua doesn't do this. I also had a hard time convincing myself people would learn Lua (but really, check it out, especially if you are a developer). I also doubted people would want to mess with XML, I wanted it to be easier to read. YAML [8]_ seemed the best option. It handles structure intuitively by indenting and blocking. YAML documents can be created or ingested by programs written in many languages, and we include some Python scripts that help users manage these. The only thing it's lacking is that its output is not completely deterministically formatted, so some of the Python included in ES is dedicated to just formatting it nicely. One day we may set up a web-service where spectra could be fit using cloud computing (I am not joking here) and YAML is easily converted to JSON, so it might be an additional advantage. I hope that the choice of YAML isn't alienating and that people can get used to the idea of indentation for structuring their input. It's common in Python, so people do seem to be able to get used to it. What does "ES" mean? -------------------- ES stands for "Elementary Supernova," a term used by Jeffery and Branch (1990) [9]_ to describe a conceptual model that serves as the basis for a number of computer codes used in the analysis of supernova spectra. See Branch, Baron, and Jeffery (2003) [10]_ and references therein to learn more. SYNOW is the classic example ES code. Originally written by David Branch, and then updated and modernized (in 1993) by Adam Fisher [11]_ at the University of Oklahoma, SYNOW has become a widely used tool among supernova scientists. Learning to use SYNOW is a rite of passage for many budding supernova spectroscopists. Author ====== ES was developed by R. C. Thomas at the Computational Cosmology Center, Lawrence Berkeley Lab. License ======= ES is distributed under the terms of the BSD software license. See the LICENSE file for further details and contact information. Copyright ========= ES: Elementary Supernova Spectrum Synthesis, Copyright (c) 2010, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved. If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov. NOTICE. This software was developed under partial funding from the U.S. Department of Energy. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, and perform publicly and display publicly. Beginning five (5) years after the date permission to assert copyright is obtained from the U.S. Department of Energy, and subject to any subsequent five (5) year renewals, the U.S. Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, distribute copies to the public, perform publicly and display publicly, and to permit others to do so. ------------------------------------------------------------------------------- .. [1] http://www.nhn.ou.edu/~parrent/synow.html .. [2] https://software.sandia.gov/appspack/version5.0/index.html .. [3] http://github.com/ .. [4] http://en.wikipedia.org/wiki/YAML .. [5] http://plasma-gate.weizmann.ac.il/Grace/ .. [6] http://adsabs.harvard.edu/abs/2007ApJ...666.1024B .. [7] http://www.lua.org/ .. [8] http://www.yaml.org/ .. [9] http://adsabs.harvard.edu/abs/1990sjws.conf..149J .. [10] http://adsabs.harvard.edu/abs/2003LNP...598...47B .. [11] http://adsabs.harvard.edu/abs/2000PhDT.........6F