-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from ConorMacBride/gallery-models
Example for mcalf.models.IBIS8542Model
- Loading branch information
Showing
5 changed files
with
357 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Models | ||
====== | ||
|
||
Below are examples of how to use the models included within the models module: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,347 @@ | ||
""" | ||
Using IBIS8542Model | ||
=================== | ||
This is an example showing how to fit an array of | ||
spectra using the :class:`mcalf.models.IBIS8542Model` | ||
class, and plot and export the results. | ||
""" | ||
|
||
#%% | ||
# Generate sample data | ||
# -------------------- | ||
# | ||
# Create spectral grid | ||
# ~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# First, we shall generate some random sample data to | ||
# demonstrate the API. | ||
# The randomly generated spectra are not intended to | ||
# be representative of observed spectra numerically, | ||
# they just have a similar shape. | ||
|
||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
|
||
from mcalf.profiles.voigt import voigt, double_voigt | ||
from mcalf.visualisation import plot_spectrum | ||
|
||
# Create demo wavelength grid | ||
w = np.linspace(8541, 8543, 20) | ||
|
||
#%% | ||
# We shall generate demo spectra in a 2x3 grid. | ||
# Half of the spectra will have one spectral | ||
# component (absorption only) and the other | ||
# half will have two spectral components | ||
# (mixed absorption and emission components). | ||
# | ||
# Demo spectral components will be modelled as | ||
# Voigt functions with randomly generated | ||
# parameters, each within a set range of values. | ||
|
||
|
||
def v(classification, w, *args): | ||
"""Voigt function wrapper.""" | ||
if classification < 1.5: # absorption only | ||
return voigt(w, *args[:4], args[-1]) | ||
return double_voigt(w, *args) # absorption + emission | ||
|
||
|
||
def s(): | ||
"""Generate random spectra for a 2x3 grid.""" | ||
np.random.seed(0) # same spectra every time | ||
p = np.random.rand(9, 6) # random Voigt parameters | ||
# 0 1 2 3 4 5 6 7 8 # p index | ||
# a1 b1 s1 g1 a2 b2 s2 g2 d # Voigt parameter | ||
# absorption |emission |background | ||
|
||
p[0] = 100 * p[0] - 1000 # absorption amplitude | ||
p[4] = 100 * p[4] + 1000 # emission amplitude | ||
|
||
for i in (1, 5): # abs. and emi. peak positions | ||
p[i] = 0.05 * p[i] - 0.025 + 8542 | ||
|
||
for i in (2, 3, 6, 7): # Voigt sigmas and gammas | ||
p[i] = 0.1 * p[i] + 0.1 | ||
|
||
p[8] = 300 * p[8] + 2000 # intensity background constant | ||
|
||
# Define each spectrum's classification | ||
c = [0, 2, 0, 2, 0, 2] | ||
# Choose single or double component spectrum | ||
# based on this inside the function `v()`. | ||
|
||
# Generate the spectra | ||
specs = [v(c[i], w, *p[:, i]) for i in range(6)] | ||
|
||
# Reshape to 2x3 grid | ||
return np.asarray(specs).reshape((2, 3, len(w))) | ||
|
||
|
||
raw_data = s() | ||
|
||
print('shape of spectral grid:', raw_data.shape) | ||
|
||
#%% | ||
# These spectra look as follows, | ||
|
||
fig, axes = plt.subplots(2, 3, constrained_layout=True) | ||
for ax, spec in zip(axes.flat, raw_data.reshape((6, raw_data.shape[-1]))): | ||
|
||
plot_spectrum(w, spec, normalised=False, ax=ax) | ||
|
||
plt.show() | ||
|
||
#%% | ||
# Compute the background intensity | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# `MCALF` does not model a constant background value, | ||
# i.e., the fitting process assumes the intensity | ||
# values far out in the spectrum's wings are zero. | ||
# | ||
# You can however tell `MCALF` what the constant | ||
# background value is, and it will subtract it | ||
# from the spectrum before fitting. | ||
# | ||
# This process was not made automatic as we wanted | ||
# to give the user full control on setting the | ||
# background value. | ||
# | ||
# For these demo data, we shall simply set the background | ||
# to the first intensity value of every spectrum. | ||
# For a real dataset, you may wish to average the | ||
# background value throughout a range of spectral points, | ||
# or even do a moving average throughout time. | ||
# Functions are provided in ``mcalf.utils.smooth`` to | ||
# assist with this process. | ||
|
||
backgrounds = raw_data[:, :, 0] | ||
|
||
print('shape of background intensity grid:', backgrounds.shape) | ||
|
||
#%% | ||
# Define a demo classifier | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# In this example we will not demonstrate how to create | ||
# a neural network classifier. `MCALF` offers a lot of | ||
# flexibility when it comes to the classifier. This | ||
# demo classifier outlines the basic API that is | ||
# required. By default, the model has a | ||
# :class:`sklearn.neural_network.MLPClassifier` | ||
# object preloaded for use as the classifier. | ||
# The methods :meth:`mcalf.models.ModelBase.train` | ||
# and :meth:`mcalf.models.ModelBase.test` are | ||
# provided by `MCALF` to assist with training the | ||
# neural network. There is also a useful script | ||
# in the `Getting Started` section under | ||
# `User Documentation` in the sidebar for | ||
# semi-automating the process of creating the | ||
# ground truth dataset. | ||
# | ||
# Please see tutorials for packages such | ||
# as scikit-learn for more in-depth advice on | ||
# creating classifiers. | ||
# | ||
# As we only have six spectra with two distinct | ||
# shapes, we can create a very simple classifier | ||
# that classifies spectra based on whether their | ||
# central intensity is greater or smaller than | ||
# the left most intensity. | ||
|
||
|
||
class DemoClassifier: | ||
@staticmethod | ||
def predict(X): | ||
y = np.zeros(len(X), dtype=int) | ||
y[X[:, len(X[0]) // 2] > X[:, 0]] = 2 | ||
return y | ||
|
||
|
||
#%% | ||
# Using ``IBIS8542Model`` with the generated data | ||
# ----------------------------------------------- | ||
# | ||
# Initialise the model | ||
# ~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# Everything we have been doing up to this point has been | ||
# creating the demo data and classifier. Now we can actually | ||
# create a model and load in the demo data, although the | ||
# following steps would be identical for a real dataset. | ||
|
||
from mcalf.models import IBIS8542Model | ||
|
||
# Initialise the model with the wavelength grid | ||
model = IBIS8542Model(original_wavelengths=w) | ||
|
||
# Load the spectral shape classifier | ||
model.neural_network = DemoClassifier | ||
|
||
# Load the array of spectra and background intensities | ||
model.load_array(raw_data, ['row', 'column', 'wavelength']) | ||
model.load_background(backgrounds, ['row', 'column']) | ||
|
||
#%% | ||
# Fit the loaded data | ||
# ~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# We have now fully initialised the model. We can now | ||
# call methods to fit the model to the loaded spectra. | ||
# In the following step we fit all the loaded spectra | ||
# and a 1D list of :class:`~mcalf.models.FitResult` | ||
# objects is returned. | ||
|
||
fits = model.fit(row=[0, 1], column=range(3)) | ||
|
||
print(fits) | ||
|
||
#%% | ||
# Plot the fitted parameters | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# The individual components of each fit can now be | ||
# plotted separately, | ||
|
||
fig, axes = plt.subplots(2, 3, constrained_layout=True) | ||
for ax, fit in zip(axes.flat, fits): | ||
|
||
model.plot_separate(fit, show_legend=False, ax=ax) | ||
|
||
plt.show() | ||
|
||
#%% | ||
# Extract processed spectra | ||
# ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# As well as fitting spectra, we can call other methods. | ||
# In this step we'll extract the array of loaded spectra. | ||
# However, these spectra have been re-interpolated to a | ||
# new finer wavelength grid. (This grid can be customised | ||
# when initialising the model.) | ||
|
||
spectra = model.get_spectra(row=[0, 1], column=range(3)) | ||
|
||
print('new shape of spectral grid:', spectra.shape) | ||
|
||
spectra_1d = spectra.reshape((6, spectra.shape[-1])) | ||
|
||
fig, axes = plt.subplots(2, 3, constrained_layout=True) | ||
for ax, spec in zip(axes.flat, spectra_1d): | ||
|
||
plot_spectrum(model.constant_wavelengths, spec, | ||
normalised=False, ax=ax) | ||
|
||
plt.show() | ||
|
||
#%% | ||
# Classify spectra | ||
# ~~~~~~~~~~~~~~~~ | ||
# | ||
# We can also classify the loaded spectra and create plots, | ||
|
||
classifications = model.classify_spectra(row=[0, 1], column=range(3)) | ||
|
||
print('classifications are:', classifications) | ||
|
||
#%% | ||
# This function plots the spectra grouped | ||
# by classification, | ||
|
||
from mcalf.visualisation import plot_classifications | ||
|
||
plot_classifications(spectra_1d, classifications.flatten()) | ||
|
||
#%% | ||
# This function plots a spatial map of | ||
# the classifications on the 2x3 grid, | ||
|
||
from mcalf.visualisation import plot_class_map | ||
|
||
plot_class_map(classifications) | ||
|
||
#%% | ||
# Merge output data | ||
# ~~~~~~~~~~~~~~~~~ | ||
# | ||
# The :class:`~mcalf.models.FitResult` objects in the | ||
# ``fits`` 1D list can be merged into a grid. | ||
# Each of these objects can be appended to a | ||
# single :class:`~mcalf.models.FitResults` object. | ||
# This allows for increased data portability, | ||
|
||
from mcalf.models import FitResults | ||
|
||
# Initialise with the grid shape and num. params. | ||
results = FitResults((2, 3), 8) | ||
|
||
# Append each fit to the object | ||
for fit in fits: | ||
results.append(fit) | ||
|
||
#%% | ||
# Now that we have appended all 6 fits, | ||
# we can access the merged data, | ||
|
||
print(results.classifications) | ||
print(results.profile) | ||
print(results.success) | ||
print(results.parameters) | ||
|
||
#%% | ||
# Calculate velocities | ||
# ~~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# And finally, we can calculate Doppler | ||
# velocities for both the quiescent (absorption) | ||
# and active (emission) regimes. | ||
# (The model needs to be given so the stationary | ||
# line core wavelength is available.) | ||
|
||
quiescent = results.velocities(model, vtype='quiescent') | ||
active = results.velocities(model, vtype='active') | ||
|
||
from mcalf.visualisation import plot_map | ||
|
||
fig, axes = plt.subplots(2, constrained_layout=True) | ||
plot_map(quiescent, ax=axes[0]) | ||
plot_map(active, ax=axes[1]) | ||
plt.show() | ||
|
||
#%% | ||
# Export to FITS file | ||
# ~~~~~~~~~~~~~~~~~~~ | ||
# | ||
# The :class:`~mcalf.models.FitResults` object can | ||
# also be exported to a FITS file, | ||
|
||
results.save('ibis8542model_demo_fit.fits', model) | ||
|
||
#%% | ||
# The file has the following structure, | ||
|
||
from astropy.io import fits | ||
|
||
with fits.open('ibis8542model_demo_fit.fits') as hdul: | ||
hdul.info() | ||
|
||
#%% | ||
# This example outlines the basics of using the | ||
# :class:`mcalf.models.IBIS8542Model` class. | ||
# Typically millions of spectra would need to be | ||
# fitted and processed at the same time. | ||
# For more details on running big jobs and how | ||
# spectra can be fitted in parallel, see | ||
# `example1` in the `Getting Started` section | ||
# of `User Documentation` in the sidebar. | ||
# | ||
# .. note:: | ||
# Due to limitations with the documentation | ||
# hosting provider, the examples in the MCALF | ||
# documentation are computed using a Python | ||
# based implementation of the Voigt profile | ||
# instead of the more efficient C version. | ||
# When this code is run on your own machine | ||
# it should take much less time than the | ||
# value reported below. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters