<jupyter_start><jupyter_text>2. Quickstart: Running a single Schwarzschild model By the end of the notebook, you will have run a Schwarzschild model. This will involve,1. understanding the configuration file2. executing commands to create and run a Schwarzschild model3. plotting some output for this model  Setup You should be in the directory ```docs/tutorial_notebooks```. If this is the first time you have run this tutorial, this directory should have the following structure, ```tutorial_notebooks  |-   | |- NGC6278_input  |     |- mge.ecvs  |     |- gauss_hermite_kins.ecvs  |     |- bins.dat  |     |- aperture.dat  |  |- NGC6278_config.yaml  |- *.ipynb ```We have provided example input data files and a configution file for the galaxy NGC6278. The input data files are:- ``mge.ecvs`` - the Multi Gaussian Expansion (MGE) describing the stellar surface density- ``gauss_hermite_kins.ecvs`` - the kinematics extracted for this galaxy- ``aperture.dat`` - information about the spatial apertures/bins using for kinematic extraction- ``bins.dat`` - information about the spatial apertures/bins using for kinematic extractionThe MGE and kinematics files must be in the form of [Astropy ECSV files](https://docs.astropy.org/en/stable/api/astropy.io.ascii.Ecsv.html). This is a convenient and flexible file format, so we use it wherever possible for storing our input and output data.We have provided test data for one galaxy. A very basic set of instructions for generating your own input data are as follows,- fit and MGE to a photometric image e.g. using [mge](http://www-astro.physics.ox.ac.uk/~mxc/software/mge)- Voronoi bin your IFU datacube e.g. using [e.g. vorbin](http://www-astro.physics.ox.ac.uk/~mxc/software/binning)- extract kinematics from the binned datacube e.g. using [e.g. PPXF](http://www-astro.physics.ox.ac.uk/~mxc/software/ppxf)- make the apertures and binfile files. An example script which has created these files is the file ``generate_input_califa.py`` in [this directory](https://github.com/dynamics-of-stellar-systems/triaxschwarz/tree/sabine/schwpy/data_prepare)In the future, we will provide more examples of test data, and more detailed instructions for preparing your own data.To get started, let's import DYNAMITE and print the version and installation path,<jupyter_code>import dynamite as dyn

print('DYNAMITE')
print('    version', dyn.__version__)
print('    installed at ', dyn.__path__)<jupyter_output>DYNAMITE
    version 2.0.0
    installed at  ['/Users/leahskusa/Desktop/uni/dynamics_assistant/dynamite/dynamite_test/DYN_TEST/lib/python3.9/site-packages/dynamite-2.0.0-py3.9.egg/dynamite']<jupyter_text>Reading the configuration file The configuration file controls all of the settings that you may wish to vary when running your Schwarschild models, e.g.- specifying the components of the gravitational potential- specifying the potential parameters, or parameter ranges, etc- specify what type of kinematic data you are providing, e.g.    - discrete vs continuous,    - Gauss-Hermite vs Histograms- the location of the input and output files- the number of models you want to runThis list of options is incomplete - for a more detailed description of the configuration file, see the documentation.The configuration file for this tutorial is```NGC6278_config.yaml```Open this file in a text editor, alongside this notebook, to see how it is organised. The file is in ``yaml`` format. The basic structure of a yaml files are pairs of keys and values```key : value```which can be organised into hierarchical levels separated by tabs```main_key:    sub_key1 : value1    sub_key2 : value2```Comments begin with a ````. Values can be any type of variable e.g. integers, floats, strings, booleans etc.To read in the congfiguration file we can use the following command, creating a configuration object which here we call ``c``,<jupyter_code>fname = 'NGC6278_config.yaml'
c = dyn.config_reader.Configuration(fname, reset_logging=True)<jupyter_output>[INFO] 12:45:28 - dynamite.config_reader.Configuration - Config file NGC6278_config.yaml read.
[INFO] 12:45:28 - dynamite.config_reader.Configuration - io_settings...
[INFO] 12:45:28 - dynamite.config_reader.Configuration - Output directory tree: NGC6278_output/.
[INFO] 12:45:28 - dynamite.config_reader.Configuration - system_attributes...
[INFO] 12:45:28 - dynamite.config_reader.Configuration - model_components...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - system_parameters...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - orblib_settings...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - weight_solver_settings...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - Will attempt to recover partially run models.
[INFO] 12:45:29 - dynamite.config_reader.Configuration - parameter_space_settings...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - multiprocessing_settings...
[INFO] 12:45:29 - dynamite.config_reader.Configuration - ... using 4 CP[...]<jupyter_text>On making this object, some output is printed telling us whether any previous models have been found. Assuming that you're running this tutorial for the first time, then no models will be found and an empty table is created at ``AllModels.table``. This table holds holds information about all the models which have been run so far.The configuration object ``c`` is structured in a similar way to the the configuration file itself. For example, the configuration file is split into two sections. The top section defines aspects the physical system we wish to model - e.g. the globular cluster, galaxy or galaxy cluster - while the second section contains all other settings we need for running a model - e.g. settings about the orbit integration and input/output options. The two sections are stored in the ``system`` and ``settings`` attributes of the configuration object, respectively,<jupyter_code>print(type(c.system))
print(type(c.settings))<jupyter_output><class 'dynamite.physical_system.System'>
<class 'dynamite.config_reader.Settings'><jupyter_text>The physical system is comprised of components, which are stored in a list ``c.system.cmp_list``<jupyter_code>print(f'cmp_list is a {type(c.system.cmp_list)}')
print(f'cmp_list has length {len(c.system.cmp_list)}')<jupyter_output>cmp_list is a <class 'list'>
cmp_list has length 3<jupyter_text>Let's print information about the components,<jupyter_code>for i in range(3):
    
    print(f'Information about component {i}:')

    # extract component i from the component list
    component = c.system.cmp_list[i]

    # print the name
    print(f'   name =  {component.name}')

    # print a list of the names of the parameters of this component
    parameters = component.parameters
    parameter_names = [par0.name for par0 in parameters]
    string = '   has parameters : '
    for name in parameter_names:
        string += f'{name} , '
    print(string)

    # print the type of this component
    print(f'   type =  {type(component)}')

    # does it contribute to the potential?
    print(f'   contributes to the potential? -  {component.contributes_to_potential}')<jupyter_output>Information about component 0:
   name =  bh
   has parameters : m-bh , a-bh , 
   type =  <class 'dynamite.physical_system.Plummer'>
   contributes to the potential? -  True
Information about component 1:
   name =  dh
   has parameters : c-dh , f-dh , 
   type =  <class 'dynamite.physical_system.NFW'>
   contributes to the potential? -  True
Information about component 2:
   name =  stars
   has parameters : q-stars , p-stars , u-stars , 
   type =  <class 'dynamite.physical_system.TriaxialVisibleComponent'>
   contributes to the potential? -  True<jupyter_text>Each component has a name, some parameters, and a type. **Currently, dynamite only supports this exact combination of component types**, i.e.- one ``Plummer``  component representing the black hole- one ``NFW`` component representing the dark halo (as of v1.0.0, DYNAMITE additionally supports ``Hernquist``, ``TriaxialCoredLogPotential``, and ``GeneralisedNFW`` dark halos, see documentation)- one ``TriaxialVisibleComponent`` representing the stellar body of the galaxyIn the future, we want to support other components, and more flexible combinations of components.For the stars - i.e. component 2 - we must provide some input data files. The location of these files is specified in the configuration file, at```settings -> io_settings -> input_directory```which takes the value,<jupyter_code>c.settings.io_settings['input_directory']<jupyter_output><empty_output><jupyter_text>The names of the following files are also specified in the configuration file, in the locations```system_components -> stars -> mge_potsystem_components -> stars -> mge_lumsystem_components -> stars -> kinematics ->  --> datafilesystem_components -> stars -> kinematics ->  --> aperturefilesystem_components -> stars -> kinematics ->  --> binfile```which take values of the appropriate filenames. You are free to give these files whatever name you like, as long as it is specified in the configuration file.Let's have a look at the MGE,<jupyter_code>c.system.cmp_list[2].mge_pot, c.system.cmp_list[2].mge_lum<jupyter_output><empty_output><jupyter_text>The kinematics are stored here,<jupyter_code>type(c.system.cmp_list[2].kinematic_data)<jupyter_output><empty_output><jupyter_text>Note that this object has type ``list``. This is because a single component can have multiple different sets of kinematics. In this example, the first (and only) entry in the list is<jupyter_code>type(c.system.cmp_list[2].kinematic_data[0])<jupyter_output><empty_output><jupyter_text>We see that this kinematics object has type ``GaussHermite``. This has also been specified in the configuration file, under```system_components -> stars -> kinematics --> kinset1 --> type```In addition to ``GaussHermite``, DYNAMITE also supports ``BayesLOSVD`` kinematics (see documentation).The kinemtic data itself can be accessed as follows,<jupyter_code>c.system.cmp_list[2].kinematic_data[0].data<jupyter_output><empty_output><jupyter_text>Creating a Schwarzschild model Our next step will be to create a model i.e. a ``dyn.model.Model`` object. To help understand what the Model object is, let's read the internal documentation (i.e. the docstring) for the class:```class Model(object):    """A DYNAMITE model.    The model can be run by running the methods (i) get_orblib, (ii) get_weights    and (iii) (in the future) do_orbit_colouring. Running each of these methods    will return the appropriate object, e.g. model.get_orblib() --> returns an    OrbitLibrary object model.get_weights(...) --> returns a WeightSolver object    Parameters    ----------    config : a ``dyn.config_reader.Configuration`` object    parset : row of an Astropy Table        contains the values of the potential parameters for this model    directory : str        The model directory name (without path). If None or not specified,        the all_models_file will be searched for the directory name. If the        all_models file does not exist, the model directory will be set to        ``orblib_000_000/ml{ml}``.    Returns    -------    Nothing returned. Attributes holding outputs are are added to the    object when methods are run.    """    def __init__(self, config=None, parset=None, directory=None):```Looking at the init signature for this class, we see that a ``Model`` requires 3 input arguments. We've already met the first, the configuration object ``c``. The remaining input parameter we need to provide is ``parset``. This is a particular set of values for each of the parameters of the model. In the configuration file, every parameter has a been given a ``value``. We can extract a parameter set specified by these values as follows,<jupyter_code>parset = c.parspace.get_parset()
print(parset)<jupyter_output>m-bh    a-bh c-dh f-dh q-stars p-stars u-stars  ml
-------- ----- ---- ---- ------- ------- ------- ---
100000.0 0.001  8.0 10.0    0.54    0.99  0.9999 5.0<jupyter_text>Note that, compared to values specified in the configuration file, parameters which have been specified as logarithmic, i.e. those configured as ```parameters -> XXX -> logarithmic : True```have been exponentiated in this table. More details can be found in the tutorial/documentation `parameter_space.ipynb`.With the first 2 input arguments at hand, we can now create our model object,<jupyter_code>model = dyn.model.Model(config=c, parset=parset)<jupyter_output><empty_output><jupyter_text>As this is our first model and we did not specify an explicit model directory, a standard directory name has been assigned to the model. Note that this path has not been created in the file system so far.Having created the model object, we can now run it. First, let's create directories for the output,<jupyter_code>model.setup_directories()<jupyter_output><empty_output><jupyter_text>This should have created a directory, where the name has been specified in the config file,<jupyter_code>c.settings.io_settings['output_directory']<jupyter_output><empty_output><jupyter_text>inside of which you should find ```NGC6278_output/models/```inside of which a unique directory for *this particular* model has been created, called<jupyter_code>model.get_model_directory()<jupyter_output><empty_output><jupyter_text>The directory name is constructed from the standard name for new, directly created models and their ``ml`` value.The next step is to calculate the orbit library. This step will take a few minutes,<jupyter_code>model.get_orblib()<jupyter_output><empty_output><jupyter_text>Having calculated an orbit library, we now need to find out which orbits are useful for reproducing the observations. This is an Non-Negative Least Squares (NNLS) optimization problem, which can be solved as follows<jupyter_code>model.get_weights()<jupyter_output>[INFO] 12:51:56 - dynamite.weight_solvers.LegacyWeightSolver - Using WeightSolver: LegacyWeightSolver
[INFO] 12:51:56 - dynamite.weight_solvers.LegacyWeightSolver - NNLS solution read from existing output<jupyter_text>Congratulations! You have run your first Schwarzschild model using DYNAMITE. The chi-squared of this model is,<jupyter_code>model.chi2<jupyter_output><empty_output><jupyter_text>Is that good? I don't know! To find out, we'll have to run more models and compare their chi-squared values. For information about this, see the tutorial `running_a_grid_of_models.ipynb`.  Plot the ModelsNow let's look at some output for the model that we have just run<jupyter_code>plotter = dyn.plotter.Plotter(config=c)

figure = plotter.plot_kinematic_maps(model)<jupyter_output>[INFO] 12:52:16 - dynamite.plotter.Plotter - Plotting kinematic maps for kin_set no 0: califa
/Users/leahskusa/Desktop/uni/dynamics_assistant/dynamite/dynamite_test/DYN_TEST/lib/python3.9/site-packages/plotbin-3.1.3-py3.9.egg/plotbin/sauron_colormap.py:105: UserWarning: Trying to register the cmap 'sauron' which already exists.
/Users/leahskusa/Desktop/uni/dynamics_assistant/dynamite/dynamite_test/DYN_TEST/lib/python3.9/site-packages/plotbin-3.1.3-py3.9.egg/plotbin/sauron_colormap.py:106: UserWarning: Trying to register the cmap 'sauron_r' which already exists.<jupyter_text>The top row shows the data, the middle row shows the model, and the bottom row shows the residuals. The columns, from left to right, are the stellar surface density, $V$, $\sigma$, $h_3$ and $h_4$. We can see the following features in the fit,- the model and data surface densities are very similar- the sense of rotation of the $v$ map is reproduced well, though the amplitude is lower than observed- the $\sigma$, $h_3$ and $h_4$ maps are less well reproducedWhile the fit is certainly not perfect, it's reassuring to see that some features are reproduced well. To improve the fit, we will have to explore parameter space more fully. See the tutorial on "running a grid of models" for more details.  ExerciseChange one of the potential parameters, then run another Schwarzschild model. You could do this by creating a new configuration file, or manually changing one of the parameters in this notebook. For example, to change the parameter ``ml`` - which is the mass-to-light ratio of the stars - to six, then you can uncomment the next line,<jupyter_code># parset['f'] = 6.<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09016443547894125, 'fraction_numerical': 0.013692768506632435, 'mean_word_length': 4.772759350741002, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 105, 'https://': 2, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '20', 'dir': 'jupyter-structured-clean-dedup', 'id': '63105', 'license': 'MIT', 'n_tokens_mistral': 4880, 'n_tokens_neox': 4553, 'n_words': 1810, 'path': 'docs/tutorial_notebooks/2_quickstart.ipynb', 'repo_name': 'dynamics-of-stellar-systems/dynamite', 'size': '16359'}