<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Creating Isochrones
To use the isochrone module, you must have the isochrone library installed (see instructions here). The isochrone module provides an API to create isochrones and calculate various characteristics. The easiest way to create an isochrone is through the general factory interface shown below.
Step1:  Modifying Isochrones
Once you create an isochrone, you can modify it's parameters on the fly.
Step2:  Advanced Methods
The Isochrone class wraps several more complicated functions related to isochrones. A few examples are shown below.
<ASSISTANT_TASK:>
Python Code:
def plot_iso(iso):
    plt.scatter(iso.mag_1-iso.mag_2,iso.mag_1+iso.distance_modulus,marker='o',c='k')
    plt.gca().invert_yaxis()
    plt.xlabel('%s - %s'%(iso.band_1,iso.band_2)); plt.ylabel(iso.band_1)
iso1 = isochrone.factory(name='Padova',
                        age=12,  # Gyr
                        metallicity=0.0002, # Z
                        distance_modulus=17
                       )
print(iso1)
plot_iso(iso1)
# Change the two bands that the isochrone loads
iso2 = isochrone.factory(name='Padova',
                        age=12,  # Gyr
                        metallicity=0.0002, # Z
                        distance_modulus=17,
                        band_1 = 'i',
                        band_2 = 'z'
                       )
print(iso2)
plot_iso(iso2)
# Create a Dotter isochrone
iso3 = isochrone.factory(name='Dotter',
                        age=12,  # Gyr
                        metallicity=0.0002, # Z
                        distance_modulus=17
                       )
print(iso3)
plot_iso(iso3)
Explanation: Creating Isochrones
To use the isochrone module, you must have the isochrone library installed (see instructions here). The isochrone module provides an API to create isochrones and calculate various characteristics. The easiest way to create an isochrone is through the general factory interface shown below.
End of explanation
iso = isochrone.factory(name='Padova',
                        age=12,  # Gyr
                        metallicity=0.0002, # Z
                        distance_modulus=17
                       )
# You can set the age, metallicity, and distance modulus
iso.age = 11
iso.distance_modulus = 20
iso.metallicity = 0.00015
print(iso)
# Each parameter has bounds and will throw an error if you are outside the range (useful for fitting)
try:
    iso.distance_modulus = 40
except ValueError as e:
    print("Error:",e)
    
# However, you can increase the range
iso.params['distance_modulus'].set_bounds([10,50])
iso.distance_modulus = 40
print(iso)
iso.distance_modulus = 17
# Updating a parameters just changes the underlying isochrone file
# Note: There is no interpolation being done
for metal in [0.00011,0.00012,0.00013]:
    iso.metallicity = metal
    print("Metallicity:",iso.metallicity,iso.filename)
 
iso.metallicity = 0.000115
print("Metallicity:",iso.metallicity,iso.filename)
Explanation: Modifying Isochrones
Once you create an isochrone, you can modify it's parameters on the fly.
End of explanation
# Draw a regular grid of points from the isochrone with associated IMF
initial_mass,mass_pdf,actual_mass,mag_1,mag_2 = iso1.sample(mass_steps=1e2)
plt.scatter(mag_1-mag_2,mag_1+iso1.distance_modulus,c=mass_pdf,marker='o',facecolor='none',vmax=0.001)
plt.colorbar()
plt.gca().invert_yaxis()
plt.xlabel('%s - %s'%(iso.band_1,iso.band_2)); plt.ylabel(iso.band_1)
plt.ylim(23,15); plt.xlim(-0.5,1.0)
# Randomly sample stars from the isochrone pdf
# Note: `sample` returns the apparent magnitudes of stars
mag_1,mag_2 = iso1.simulate(stellar_mass=3e5)
n,bx,by,p = plt.hist2d(mag_1-mag_2,mag_2,bins=50,norm=LogNorm())
plt.colorbar(label="Number of Stars")
plt.gca().invert_yaxis()
plt.xlabel('%s - %s'%(iso.band_1,iso.band_2)); plt.ylabel(iso.band_1)
# The isochrone is normalized using a `richness` parameter (number of stars above a given mass threshold)
richness = 1e4
# Total stellar mass above some minimum mass
print("Stellar Mass:",richness * iso.stellar_mass())
# Luminosity calculated from the isochrone file and mass pdf
print("Stellar Luminosity:",richness * iso.stellar_luminosity())
# Calculate the richness 
print("Absolute Magnitude:",iso.absolute_magnitude(richness=richness))
# Calculate the absolute magnitude using the random sampling of Martin et al. 2008
print("Martin Absolute Magnitude:",iso.absolute_magnitude_martin(richness=richness))
Explanation: Advanced Methods
The Isochrone class wraps several more complicated functions related to isochrones. A few examples are shown below.
End of explanation
<END_TASK>