tangos

Tangos is a system for building and querying databases summarising the results of numerical galaxy simulations.

Learn more in the following categories:

View the Project on GitHub

The live-calculation mini-language

Halo properties that are stored in the database can be re-processed using a set of powerful tools that are designed to pull out insight into the data. Using this framework with Timestep.calculate_all or Halo.calculate_for_descendants can result in far faster evaluation than would be achieved by manually evaluating the equivalent on each individual halo.

The language used to express these operations is python-like but not actually python. The rest of this document explains the language, first through examples and then with a list of possible operations.

Examples

Let’s suppose you have an example timestep ts and halo h, e.g. by calling

import tangos
ts = tangos.get_timestep(...)
h  = tangos.get_halo(...)

One can gather properties that do not explicitly exist as properties within the database, but do not require a new calculation using simulation data. For example, the virial velocity, “Vvir” is calculated using only the virial mass and virial radius already calculated. However, because it doesn’t exist yet as a halo property, h['Vvir'] will return an error. Instead, you must perform a a “live-calculation” of “Vvir”. h.calculate('Vvir()') will return the virial velocity of the halo. Since this is similar to calling a function, there are parenthesis associated with live calculated values.

Similarly, this method can be used within the calculate_all and calculate_for_descendants functions, e.g. ts.calculate_all("Vvir()") will reaturn the newly calculated virial velocity for all halos in the step. The same syntax would apply to calculate_for_descendants

There are some live calculations that exist which take arguments. For example, at(r,property) returns the value of the profile property "property" at radius r. While the second argument taken by the function must be an already existing halo profile property, the first argument could be either a number or even a halo property itself. Here are some examples of how one might use this function with the example profile property "dm_density_profile". The values for r are in units of kpc.

at(5.0,dm_density_profile) returns the dm density at 5.0 kpc

at(Rhalf_V,dm_density_profile) returns the dm density at the V-band half light radius

at(Rvir/2,dm_density_profile) returns the dm density at half of the virial radius of the halo

This syntax works the same in h.calculate or ts.calculate_all or h.calculate_for_descendants. Note as well that it is possible to do arithmetic on your inputs (Rvir/2, Rhalf_V*2, Rhalf_V+10, etc would all work). Similarly, one can input operations on multiple halo properties, for example

at(5.0,ColdGasMass_encl/GasMass_encl) returns the fraction of cold/total gas within the inner 5 kpc of a given halo.

Some functions, rather than return a property, return a linked object. For example, the function later(N) returns a given halo’s descendant halo N snapshots forward in time. The purpose of this is to connect a halo’s properties at a given step to those of that halo N steps in the future (or past if you use the earlier function). To get properties from these links, add your target property after the function following a period. All of the above live calculation syntax also applies. For example:

ts.calculate_all('later(5).Mvir', 'Mvir') returns the the virial mass of each halo 5 snapshots later and the current virial mass of each halo in the current step

ts.calculate_all('earlier(10).Vvir()') returns the virial mass of each halo 10 snapshots earlier than the current step.

ts.calculate_all('earlier(2).at(Rvir/2,GasMass_encl') returns the gas mass within half of the virial radius of each halo’s main progenitor 2 snapshots previous.

Special case use for histogram properties

For histogram properties (currently these are just SFR_histogram and BH_mdot_histogram), the live calculation language is also the interface to special use of the stored histograms.

Let’s take the SFR as an example. If you have a halo h, and ask for h['SFR_histogram'], you just get a SFR histogram back as you’d expect, one bin per 20 Myr by default. However, the database is actually storing chunks of the star formation history and automatically recreating it for you on the major progenitor branch.

You can instead request the SFR summed over all branches by typing h.calculate("reassemble(SFR_histogram, 'sum')"). Similarly, for a BH accretion history you could do h.calculate("BH.reassemble(BH_mdot_histogram, 'sum')")._

If you want to manually handle the reassembly, one useful option is h.calculate("reassemble(SFR_histogram, 'place')"). This correctly zero-pads the histogram, but does not fill in any of the data from preceding steps, so you are free to do that yourself.

Under the hood, this is implemented using the reassemble property of TimeChunkedProperty which you can find in properties.__init__.py. In principle it’s therefore possible to implement further methods for reconstructing SFR where even more complex manipulations of the little mini-history chunks is undertaken.

Technical note: to access the data that is actually stored in the database, as opposed to the default reconstruction, you can ask for h.calculate("raw(SFR_histogram)"). The default data access h['SFR_histogram'] or h.calculate("SFR_histogram")_ actually expands to something equivalent to h.calculate("reassemble(SFR_histogram"), and the default parameter to reassemble is major, which (as previously stated) sums only over the major progenitor branch.

Getting information on linked objects

Often halos have several linked objects associated with them. For example, any given halo may have several black holes and so will have several black hole objects in the database linked to it. Live calculations using the link() function will return an object linked to the halo under a given name having the maximum or minimum of a given quantity among all other linked objects of the same name associated with that same halo. The general syntax is as follows.

h.calculate('link(my_link_name, link_property, "max/min", constraint1, constraint2, ...constrantN)')

An example would be the following, looking among black holes linked to their host halo via the “BH” link name.

h.calculate('link(BH,BH_mass,"max")')

The above command will return an object linked under the “BH” name to halo h. The black hole that is returned will be the one that has the largest value of mass (based on the property called “BH_mass” of the linked black hole objects).

It is also possible to put any arbitrary number of constraints on which link you want other than simply the maximum or minimum of some value. For example:

h.calculate('link(BH, BH_mass, "max", BH_central_distance<10)')

This will return the same as the first example, but this time returning the black hole with maximum mass among only those that are within 10 kpc of halo center. Any number of additional constraints like this can be used. Once you’ve decided how you want to pick the linked object, you can return any of that object’s properties.

h.calculate('link(BH, BH_mass, "max", BH_central_distance<10, BH_mass>1e6).BH_mdot')

This will return the accretion rate of the most massive linked black hole within halo 6 that has a mass of at least 1e6 solar masses and is within 10 kpc of halo center.

Finally, one can also get a link to a progenitor with a particular property. For example, if you want to find the progenitors of a galaxy where the SFR property was maximum, and return its mass at that time, you could use:

h.calculate('find_progenitor(SFR, "max").mass')

General Syntax Notes

List of built-in mini-language functions

Note that string inputs must have quotes when used, but property names and expressions do not need quotes.

Intrinsic object information

Mathematics and logic

Links

Array extraction

Array reassembly