Saving and loading simulations#
Now you’ve run a simulation and you understand the outputs, but what about when you want to save your work for later? Let’s talk about how to do that!
Learning Goals#
By the end of this tutorial you should know how to:
Use
cogsworth.sfh.StarFormationHistory.save()to save a galaxy model and understand the files it createsDo the same for populations with
cogsworth.pop.Population.save()Load back a previously created population using
load()Save a population with custom created StarFormationHistory models
[1]:
# import cogsworth and set some plotting things
import cogsworth
import matplotlib.pyplot as plt
%config InlineBackend.figure_format = 'retina'
plt.rc('font', family='serif')
plt.rcParams['text.usetex'] = False
fs = 24
# update various fontsizes to match
params = {'figure.figsize': (12, 8),
'legend.fontsize': fs,
'axes.labelsize': fs,
'xtick.labelsize': 0.9 * fs,
'ytick.labelsize': 0.9 * fs,
'axes.linewidth': 1.1,
'xtick.major.size': 7,
'xtick.minor.size': 4,
'ytick.major.size': 7,
'ytick.minor.size': 4}
plt.rcParams.update(params)
Saving and loading star formation histories#
Let’s say you’ve just sampled a StarFormationHistory such as the one below and decide it looks so good that you’d like to save it for later.
[2]:
s = cogsworth.sfh.Wagg2022()
s.sample(100000)
s.plot()
Well saving it is remarkably easy, all you need to do is this
[3]:
s.save("gorgeous_galaxy")
This has just saved everything to gorgeous_galaxy.h5, which contains both the details of the galaxy class and the sampled parameters. But you should never need to interact with these directly, since you can load things back in with a simple call
[4]:
loaded_s = cogsworth.sfh.load("gorgeous_galaxy")
assert all(loaded_s.Z == s.Z)
And there’s your galaxy, as good as new!
Saving and loading populations#
Okay now what about a whole cogsworth population? These build upon the galaxy save/load functions but now save even more information. Let’s take a look!
[5]:
p = cogsworth.pop.Population(100, use_default_BSE_settings=True)
p.create_population()
Run for 100 binaries
Sampled 123 binaries
[4e-03s] Sample initial binaries
[0.1s] Evolve binaries (run COSMIC)
Integrating orbits: 100%|██████████| 123/123 [00:00<00:00, 1407.84it/s]
[0.3s] Integrate galactic orbits (run gala)
Overall: 0.4s
Lovely. Now let’s perhaps pick some random things to prove that our loaded population is identical - the total final primary mass of the population and what the first orbit looks like
[6]:
p.final_bpp["mass_1"].sum()
[6]:
np.float64(55.40806637695781)
[7]:
orb = p.orbits[0][0] if isinstance(p.orbits[0], list) else p.orbits[0]
fig, ax = plt.subplots()
orb.cylindrical.plot(["rho", "z"], axes=[ax]);
Okay now for our favourite part - let’s save the population for later:
[8]:
p.save("perfect_population")
That function a bunch of informtion about your population to perfect_population.h5, including all of the same galaxy information as the earlier example, initial conditions for your stellar population and full evolutionary and orbital history of each binary.
But as before, no need to worry too much about that as cogsworth will bring it all back into a population object for you like this
[9]:
new_p = cogsworth.pop.load("perfect_population")
And let’s just confirm that everything looks the before - spoiler alert, it does ;)
[10]:
new_p.final_bpp["mass_1"].sum()
[10]:
np.float64(55.40806637695781)
[11]:
orb = new_p.orbits[0][0] if isinstance(new_p.orbits[0], list) else new_p.orbits[0]
fig, ax = plt.subplots()
orb.cylindrical.plot(["rho", "z"], axes=[ax]);
Populations with custom StarFormationHistory models#
Now there’s one slight wrinkle here - what if you define a whole new StarFormationHistory model that cogsworth isn’t familiar with (check out the tutorial for how to do this)? In this case everything will proceed almost the same, except if you need to resample your initial galaxy. Let’s take a look
First we can define some new galaxy class
[20]:
import numpy as np
import astropy.units as u
# new galaxy where all stars were formed 42 Myr ago
class TomsSwankyNewGalaxy(cogsworth.sfh.BurstUniformDisc):
def draw_lookback_times(self, size=None, component="low_alpha_disc"):
return np.ones(size) * 42 * u.Myr
[21]:
# make a population with this galaxy class
meaning_of_life = cogsworth.pop.Population(
100, sfh_model=TomsSwankyNewGalaxy(R_max=15 * u.kpc, z_max=1 * u.kpc),
use_default_BSE_settings=True)
meaning_of_life.create_population()
Run for 100 binaries
Sampled 124 binaries
[6e-03s] Sample initial binaries
[0.1s] Evolve binaries (run COSMIC)
Integrating orbits: 100%|██████████| 124/124 [00:00<00:00, 3065.81it/s]
[0.2s] Integrate galactic orbits (run gala)
Overall: 0.3s
[22]:
meaning_of_life.initial_galaxy.tau
[22]:
Now if we try to save this then cogsworth will let you, but it’ll warn you that it’s not sure what to do with TomsSwankyNewGalaxy and will rely on the parent class instead.
[24]:
meaning_of_life.save("42", overwrite=True)
cogsworth warning: StarFormationHistory class being saved as `BurstUniformDisc` instead of `TomsSwankyNewGalaxy`. Data will be copied but new sampling will draw from the functions in `BurstUniformDisc` rather than the custom class you used.
And as promised we can load it back and the data will be correct
[25]:
loaded_meaning = cogsworth.pop.load("42")
loaded_meaning.initial_galaxy.tau
[25]:
But if you try to sample again things may get confusing so beware!
[26]:
loaded_meaning.sfh_model
[26]:
<BurstUniformDisc, size=124>
[27]:
loaded_meaning.sample_initial_galaxy()
loaded_meaning.initial_galaxy.tau
[27]:
Wrap-up#
Oh no you’ve reached the end of the tutorial, don’t worry I’ve got a bunch more to satisfy your cravings!
Note
This tutorial was generated from a Jupyter notebook that can be found here.