Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add the ability to reassemble live calculated TimeChunked quantities #47

Open
wants to merge 84 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
f5c3a45
add sfhistory properties function (to new Michael folder) with proper…
Oct 31, 2017
b92e831
add appropriate __init__ file to michaels_properties and include curr…
Nov 1, 2017
1f17c4d
add appropriate environment files to source for bash and cshell
Nov 1, 2017
ea27201
attempt fix to cshell script
Nov 1, 2017
cb4daa2
delete environment files
Nov 1, 2017
7590a08
properties for Temperature (ew and mw) profiles
mtremmel Nov 5, 2017
8f66aa3
add electron density to profiles. Fix issues with temperature
mtremmel Nov 5, 2017
16ca8e0
add name for density profile
mtremmel Nov 5, 2017
5cb8f18
add profiles module into init function for my properties
mtremmel Nov 5, 2017
76ca96f
get rid of spurious rho_crit calculation to avoid having to profile a…
Nov 6, 2017
e939fc9
include all particles in profile to avoid errors
Nov 6, 2017
8610e14
go back to profiling whole simulation
Nov 6, 2017
b31ed25
addn new property under michaels_properties to calculate gas flows
mtremmel Nov 12, 2017
8c1dbfe
edit my functions for new naming scheme (SSC -> shink_center; Rvir ->…
mtremmel Nov 12, 2017
1a25fe5
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Nov 12, 2017
7f145d6
Merge branch 'master' into property_name_change
mtremmel Nov 12, 2017
dc65895
fix typo in names
mtremmel Nov 12, 2017
f71ec5a
correctly use temp cut where it is defined
mtremmel Nov 12, 2017
8ff294a
add time output to mergers and ignore DM for inflow/outflow in order …
Nov 20, 2017
0b51eb9
Merge branch 'master' of https://github.com/pynbody/tangos
Nov 20, 2017
3d1e7f4
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Dec 5, 2017
ba1eabe
Merge branch 'master' of https://github.com/pynbody/tangos
Dec 12, 2017
d8aeee9
Merge branch 'master' of https://github.com/mtremmel/tangos
Dec 12, 2017
668d8a7
Merge branch 'master' of https://github.com/mtremmel/tangos
mtremmel Dec 12, 2017
25b6aad
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Jan 2, 2018
fd7b66f
Merge branch 'master' of https://github.com/mtremmel/tangos
Jan 2, 2018
4f35edb
Merge branch 'master' of https://github.com/pynbody/tangos
Jan 15, 2018
2e943a2
Merge branch 'master' of https://github.com/pynbody/tangos
Mar 29, 2018
06df11a
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Mar 29, 2018
84ba316
Merge branch 'master' of https://github.com/mtremmel/tangos
mtremmel Apr 4, 2018
ebec14e
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Apr 4, 2018
bb3dd1b
Merge branch 'master' of https://github.com/pynbody/tangos
Apr 4, 2018
3c74ee5
remove michaels_properties
Apr 4, 2018
1189e5e
Merge branch 'master' of https://github.com/mtremmel/tangos
Apr 4, 2018
e1ecc28
add to .gitignore
mtremmel Apr 4, 2018
c4eb1ec
add the ability to reassemble live calculated TimeChunked quantities …
Apr 4, 2018
3ed09bc
fix error in previous commit. Added unnecessary argujment to _place_d…
Apr 5, 2018
8f3feaa
revert back to old default nbins=1000 from 2000, which was needed to …
Apr 5, 2018
2916854
attempt to handle when there are no user defined property modules
Apr 5, 2018
30f4151
Merge branch 'mjt_live_reassemble' of https://github.com/mtremmel/tan…
mtremmel Apr 7, 2018
ecf504b
fix evaluation_pattern for live calculations so that reassemble optio…
mtremmel May 6, 2018
07168f6
make it so that reassembly on live calculation response the same way …
mtremmel May 7, 2018
9b84a56
Merge remote-tracking branch 'upstream/master'
mtremmel May 13, 2018
bee20db
Merge branch 'master' of https://github.com/mtremmel/tangos
mtremmel May 13, 2018
3a2c058
Merge remote-tracking branch 'upstream/master' into mjt_live_reassemble
mtremmel May 13, 2018
9d93b3c
begin new Live Reassemble documentation, add SpecSFR_histogram as a p…
mtremmel May 13, 2018
94d2eb2
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Jun 13, 2018
1361067
fix SpecSFR_histogram, add/fix documentation
mtremmel Jun 15, 2018
a8380a2
edits to documentation
mtremmel Jun 15, 2018
51c7543
Merge remote-tracking branch 'upstream/master' into mjt_live_reassemble
mtremmel Jun 15, 2018
814b511
combine the two different evaluation methods, using a flag instead fo…
Jun 20, 2018
f081b44
Merge remote-tracking branch 'upstream/master' into mjt_live_reassemble
Jun 21, 2018
2a807ef
remove commented out code
Jun 21, 2018
a1e3c16
Merge remote-tracking branch 'upstream/master'
Jun 22, 2018
27ee542
fix propery module importing
Jul 6, 2018
0534ad8
Merge remote-tracking branch 'upstream/master'
mtremmel Jul 11, 2018
4953ba4
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Sep 4, 2018
340dfd2
merge with master, fix specSFR calculation
mtremmel Sep 8, 2018
b1d8329
merge with some updates
mtremmel Sep 8, 2018
9aefdb8
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Sep 8, 2018
f9216bf
Merge branch 'master' of https://github.com/mtremmel/tangos
mtremmel Sep 8, 2018
f4ee038
Merge remote-tracking branch 'upstream/master'
Oct 8, 2018
51e2595
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Nov 20, 2018
f056837
Merge branch 'master' of https://github.com/pynbody/tangos
Dec 4, 2018
8591f8c
Merge branch 'master' of https://github.com/pynbody/tangos
Dec 6, 2018
d51b987
Merge branch 'master' of https://github.com/mtremmel/tangos
Dec 6, 2018
616e42a
Merge branch 'master' of https://github.com/pynbody/tangos
Jul 10, 2019
365024f
make stellar mass profiles not count black holes (particles with tfor…
Jul 11, 2019
6e5dd43
remove import pynbody within _get_profile since it is now global
Jul 11, 2019
d7ec0ec
Merge branch 'star_profile_fix'
Jul 11, 2019
0e50a12
move import of pynbody in profile functions
Jul 11, 2019
e4d3ced
move import of pynbody in profile functions
Jul 11, 2019
ab43ea2
Merge branch 'star_profile_fix'
Jul 11, 2019
33a32d8
Merge branch 'master' of https://github.com/pynbody/tangos
mtremmel Mar 20, 2020
3c7f5ee
Merge branch 'master' into mjt_live_reassemble
mtremmel Mar 20, 2020
c358cb5
Merge branch 'master' of https://github.com/pynbody/tangos into mjt_l…
mtremmel Apr 29, 2020
cabad3f
Merge branch 'mjt_live_reassemble' of https://github.com/mtremmel/tan…
mtremmel Apr 29, 2020
246629a
correct missing argument to reassemble call in extraction_patterns. U…
mtremmel Apr 30, 2020
eceec76
fix missing halo input to _reassemble_using_finding_strategy() in rea…
mtremmel Apr 30, 2020
e1fdd80
remove changes accidentally included to examples/mergers
mtremmel Apr 30, 2020
5c47a87
remove superfluous whitespace change
mtremmel Apr 30, 2020
503617b
remove changes to config.py that were spuriously included here
mtremmel Apr 30, 2020
86b5857
remove spurious changes to .gitignore which were included
mtremmel Apr 30, 2020
6a6db25
remove bad line from config.py accidentally left from previous commit
mtremmel Apr 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions docs/histogram_properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,26 @@ Note that it is a _very_ bad idea to change this after you have already written
for a simulation. You will almost certainly end up getting inconsistent results. Always set `histogram_delta_t_Gyr`
before your first `tangos write`.

Reassembling live calculations
------------------------------

It is possible to define a [live calculation](live_calculation.md) that manipulates an already stored histogram
property (like `SFR_histogram`) and returns a new histogram. Because such a calculation only requires data already stored
in the database, it can be performed on the fly and reassembled accordingly in the same way as the original stored
histogram data.

The property `SpecSFR_histogram`, for example, calculates the specific star formation rate history of a halo given `SFR_histogram` and
normalizing it by the mass at every intermediate step. We can call this in the same way as any other histogram property, but
this time we are doing a live calculation, which is called like any other live calculation (in this case there are
no relevant arguments to give, but any argument taken by the live calculation will work like normal).

```
halo.calculate('reassemble(SpecSFR_histogram())')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is reassemble required?

```

It is still important to make sure that the time resolution is set correctly. For any live calculation acting on a histogram
property, the time resolution, defined for each simulation by `sim["histogram_delta_t_Gyr"]`, should be set to what it
was when the stored histogram data was originally calculated.

Exercising caution
-------------------
Expand Down
2 changes: 1 addition & 1 deletion tangos/core/extraction_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def _postprocess_one_result(self, property_object):

if hasattr(self._providing_class, 'reassemble'):
instance = self._providing_class(property_object.halo.timestep.simulation)
return instance.reassemble(property_object, *self._options)
return instance.reassemble(property_object, property_object.halo, *self._options)
else:
self._setup_data_mapper(property_object)
return self._mapper.get(property_object)
Expand Down
24 changes: 21 additions & 3 deletions tangos/live_calculation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ def __init__(self, *tokens):
super(LiveProperty, self).__init__()
self._name = str(tokens[0])
self._inputs = list(tokens[1:])
self._evaluation_with_reassemble = True
self._evaluation_options= []

def __str__(self):
return self._name + "(" + (",".join(str(x) for x in self._inputs)) + ")"
Expand All @@ -392,6 +394,18 @@ def _calculation_retrieves(self):
result = result.union(providing_instance.requires_property())
return result

def set_reassemble(self):
self._evaluation_with_reassemble = True

def set_raw(self):
self._evaluation_with_reassemble = False

def set_evaluation_options(self,*options):
self._evaluation_options = options

def _evaluate(self, halos, input_descriptions, input_values):
return self._evaluate_function(halos, input_descriptions, input_values, *self._evaluation_options)

def values_and_description(self, halos):
if len(halos)==0:
return np.array([[]]), None
Expand All @@ -402,7 +416,7 @@ def values_and_description(self, halos):
input_values.append(iv)
input_descriptions.append(id)

calculator, results = self._evaluate_function(halos, input_descriptions, input_values)
calculator, results = self._evaluate(halos, input_descriptions, input_values)

return results, calculator

Expand All @@ -413,14 +427,17 @@ def _input_value_and_description(self, input_id, halos):
raise ValueError("Functions cannot receive more than one value per input")
return val[0], desc

def _evaluate_function(self, halos, input_descriptions, input_values):
def _evaluate_function(self, halos, input_descriptions, input_values, *options):
from .. import properties
sim = consistent_collection.consistent_simulation_from_halos(halos)
results = []
calculator = properties.providing_class(self.name())(sim, *input_descriptions)
for inputs in zip(halos, *input_values):
if self._has_required_properties(inputs[0]) and all([x is not None for x in inputs]):
results.append(calculator.live_calculate_named(self.name(), *inputs))
if hasattr(calculator,'reassemble') & self._evaluation_with_reassemble is True:
results.append(calculator.reassemble(self, inputs[0], *options))
else:
results.append(calculator.live_calculate_named(self.name(), *inputs))
else:
results.append(None)
return calculator, self._as_1xn_array(results)
Expand Down Expand Up @@ -488,6 +505,7 @@ def __init__(self, *tokens):
super(BuiltinFunction, self).__init__(*tokens)
self._func = self.__registered_functions[self._name]['function']
self._info = self.__registered_functions[self._name]
self.set_raw()
for i in range(len(self._inputs)):
assert_class = self._get_input_option(i, 'assert_class')
if assert_class is not None and not isinstance(self._inputs[i], assert_class):
Expand Down
18 changes: 11 additions & 7 deletions tangos/live_calculation/builtin_functions/reassembly.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
from __future__ import absolute_import
from tangos.core import extraction_patterns
from . import BuiltinFunction
from .. import StoredProperty, FixedInput
from .. import StoredProperty, FixedInput, LiveProperty


@BuiltinFunction.register
def raw(halos, values):
return values
raw.set_input_options(0, assert_class=StoredProperty)

@raw.set_initialisation
def raw_initialisation(input):
input.set_extraction_pattern(extraction_patterns.HaloPropertyRawValueGetter())
if isinstance(input, LiveProperty):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to avoid things like this wherever possible as it spreads around knowledge of the inner workings of a LiveProperty as opposed to a normal value retrieval. That knowledge belongs in the LiveProperty class itself.

I suggest always calling set_raw. Create a set_raw method for the base Calculation object, which is then overriden in LiveProperty and also overriden in StoredProperty. The StoredProperty implementation just wants to call set_extraction_pattern(...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this issue was resolved

input.set_raw()
else:
input.set_extraction_pattern(extraction_patterns.HaloPropertyRawValueGetter())


@BuiltinFunction.register
def reassemble(halos, values, *options):
return values
reassemble.set_input_options(0, assert_class=StoredProperty)

@reassemble.set_initialisation
def reassemble_initialisation(input, *options):
Expand All @@ -27,6 +28,9 @@ def reassemble_initialisation(input, *options):
options_values.append(option.proxy_value())
else:
raise TypeError("Options to 'reassemble' must be fixed numbers or strings")

input.set_extraction_pattern(
extraction_patterns.HaloPropertyValueWithReassemblyOptionsGetter(*options_values))
if isinstance(input,LiveProperty):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment as above; you want a single set_reassemble(*options_values) which is implemented differently for LiveProperty and for StoredProperty

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this seems to be pending resolution

input.set_reassemble()
input.set_evaluation_options(*options_values)
else:
input.set_extraction_pattern(
extraction_patterns.HaloPropertyValueWithReassemblyOptionsGetter(*options_values))
34 changes: 23 additions & 11 deletions tangos/properties/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from six.moves import zip
import importlib
import warnings
from ..live_calculation import LiveProperty
import functools
from .. import input_handlers
from .. import util
Expand Down Expand Up @@ -229,10 +230,10 @@ class TimeChunkedProperty(PropertyCalculation):
minimum_store_Gyr = 0.5

def __init__(self, simulation):
self.pixel_delta_t_Gyr = simulation.get("histogram_delta_t_Gyr", self.pixel_delta_t_Gyr)
if simulation is not None:
self.pixel_delta_t_Gyr = simulation.get("histogram_delta_t_Gyr", self.pixel_delta_t_Gyr)
super(TimeChunkedProperty, self).__init__(simulation)


def bin_index(self, time):
"""Convert a time (Gyr) to a bin index in the histogram"""
index = int(time/self.pixel_delta_t_Gyr)
Expand All @@ -245,7 +246,8 @@ def store_slice(self, time):
they should store."""
return slice(self.bin_index(time-self.minimum_store_Gyr), self.bin_index(time))

def reassemble(self, property, reassembly_type='major'):

def reassemble(self, property, halo, reassembly_type='major'):
"""Reassemble a histogram by suitable treatment of the merger tree leading up to the current halo.

This function is normally called by the framework (see Halo.get_data_with_reassembly_options) and you would
Expand All @@ -266,16 +268,24 @@ def reassemble(self, property, reassembly_type='major'):
from tangos import relation_finding as rfs

if reassembly_type=='major':
return self._reassemble_using_finding_strategy(property, strategy = rfs.MultiHopMajorProgenitorsStrategy)
return self._reassemble_using_finding_strategy(property, halo, strategy = rfs.MultiHopMajorProgenitorsStrategy)
elif reassembly_type=='major_across_simulations':
return self._reassemble_using_finding_strategy(property, strategy = rfs.MultiHopMajorProgenitorsStrategy,
return self._reassemble_using_finding_strategy(property, halo, strategy = rfs.MultiHopMajorProgenitorsStrategy,
strategy_kwargs = {'target': None, 'one_simulation': False})
elif reassembly_type=='sum':
return self._reassemble_using_finding_strategy(property, strategy = rfs.MultiHopAllProgenitorsStrategy)
return self._reassemble_using_finding_strategy(property, halo, strategy = rfs.MultiHopAllProgenitorsStrategy)
elif reassembly_type=='place':
return self._place_data(property.halo.timestep.time_gyr, property.data_raw)
if not isinstance(property, LiveProperty):
place_data = property.data_raw
else:
place_data = halo.calculate(property.__str__())
return self._place_data(property.halo.timestep.time_gyr, place_data)

elif reassembly_type=='raw':
return property.data_raw
if not isinstance(property, LiveProperty):
return property.data_raw
else:
return halo.calculate('raw('+property.__str__()+')')
else:
raise ValueError("Unknown reassembly type")

Expand All @@ -286,9 +296,11 @@ def _place_data(self, time, raw_data):
final[start:] = raw_data
return final

def _reassemble_using_finding_strategy(self, property, strategy, strategy_kwargs={}):
name = property.name.text
halo = property.halo
def _reassemble_using_finding_strategy(self, property, halo, strategy, strategy_kwargs={}):
if not isinstance(property, LiveProperty):
name = property.name.text
else:
name = property.__str__()
t, stack = halo.calculate_for_descendants("t()", "raw(" + name + ")", strategy=strategy, strategy_kwargs=strategy_kwargs)
final = np.zeros(self.bin_index(t[0]))
previous_time = -1
Expand Down
32 changes: 29 additions & 3 deletions tangos/properties/pynbody/SF.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .. import TimeChunkedProperty
from .. import TimeChunkedProperty, LiveHaloProperties
from . import pynbody_handler_module, PynbodyPropertyCalculation

import numpy as np
Expand Down Expand Up @@ -33,10 +33,36 @@ def calculate(self, halo, existing_properties):

return M

def reassemble(self, *options):
reassembled = super(StarFormHistogram, self).reassemble(*options)
def reassemble(self, halo, *options):
reassembled = super(StarFormHistogram, self).reassemble(halo, *options)
return reassembled/1e9 # Msol per Gyr -> Msol per yr

class SpecStarFormationHistogram(TimeChunkedProperty,LiveHaloProperties):
names = "SpecSFR_histogram"

def __init__(self, simulation):
super(SpecStarFormationHistogram, self).__init__(simulation)

def plot_xlabel(self):
return "t/Gyr"

def plot_ylabel(self):
return r"$SFR/M_{\odot} yr^{-1}$"

def requires_property(self):
return ['Mstar','SFR_histogram']

def live_calculate(self, halo, *args):
sfr = halo.calculate('raw(SFR_histogram)')
t_sfr = halo.timestep.time_gyr - np.arange(len(sfr))*self.pixel_delta_t_Gyr
Marray = halo['Mstar'] - np.cumsum(sfr*self.pixel_delta_t_Gyr)
Marray = Marray[::-1]
return sfr/Marray

def reassemble(self, halo, *options):
reassembled = super(SpecStarFormationHistogram, self).reassemble(halo, *options)
return reassembled / 1e9 # Gyr^-1 -> yr^-1

class StarForm(PynbodyPropertyCalculation):
names = "SFR_10Myr", "SFR_100Myr"

Expand Down
4 changes: 2 additions & 2 deletions tangos/properties/pynbody/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def plot_ylabel(self):

def _get_profile(self, halo, maxrad):
import pynbody

delta = self.plot_xdelta()
nbins = int(maxrad / delta)
maxrad = delta * (nbins + 1)
Expand Down Expand Up @@ -50,6 +49,7 @@ class BaryonicHaloDensityProfile(HaloDensityProfile):

@centred_calculation
def calculate(self, data, existing_properties):
import pynbody
gas_a, gas_b = self._get_profile(data.gas, existing_properties["max_radius"])
star_a, star_b = self._get_profile(data.star, existing_properties["max_radius"])
star_a, star_b = self._get_profile(data.star[pynbody.filt.HighPass('tform',0)], existing_properties["max_radius"])
return gas_a, gas_b, star_a, star_b
36 changes: 35 additions & 1 deletion tests/test_live_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,25 @@ class DummyPropertyWithReassemblyOptions(properties.PropertyCalculation):
names = "dummy_property_with_reassembly"

@classmethod
def reassemble(cls, property, test_option=25):
def reassemble(cls, property, halo, test_option=25):
if test_option=='actual_data':
return property.data_raw
else:
return test_option

class DummyLivePropertyWithReassembly(properties.LivePropertyCalculation):
names = "dummy_live_property_with_reassembly"

def live_calculate(self, db_halo_entry, factor=2):
return db_halo_entry.calculate('raw(dummy_property_with_reassembly)') * factor

@classmethod
def reassemble(cls, property, halo, test_option=10):
if test_option == 'actual_data':
return halo.calculate('raw('+property.__str__()+')')
else:
return halo.calculate('raw('+property.__str__()+')') * test_option

class LivePropertyRequiringRedirectedProperty(properties.LivePropertyCalculation):
names = "first_BHs_BH_mass"

Expand Down Expand Up @@ -251,6 +264,27 @@ def test_reassembly():
# our dummy reassembly function can return the actual value if suitably requested
assert h.calculate('reassemble(dummy_property_with_reassembly, "actual_data")') == 101

def test_live_reassembly():
h = tangos.get_halo("sim/ts1/1")
h['dummy_property_with_reassembly'] = 101

# default reassembly takes in the dummy property and multiplies it first by 2, then by 10
assert h.calculate('dummy_live_property_with_reassembly()') == 2020

# default live_calculation without reassemble takes dummy property and multiplies by 2
assert h.calculate('raw(dummy_live_property_with_reassembly())') == 202

# ability to give input into live calculation
assert h.calculate('raw(dummy_live_property_with_reassembly(4))') == 404

# pass option to reassembly and change how the dummpy property is manipulated
assert h.calculate('reassemble(dummy_live_property_with_reassembly(),3)') == 606

# reassemble on live calculation with new variable
assert h.calculate('reassemble(dummy_live_property_with_reassembly(3))') == 3030

# will return the original value of the live calculation without reassembly if requested correctly
assert h.calculate('reassemble(dummy_live_property_with_reassembly(3),"actual_data")') == 303

def test_liveproperty_requiring_redirection():
h = tangos.get_halo("sim/ts1/1")
Expand Down