<jupyter_start><jupyter_text>Read spectrum data This step is outside the scope of the backend. The UI is assumed to feed the spectrum data to the backed via a properly initialized Spectrum1D instance.<jupyter_code># fn = download_file('https://data.sdss.org/sas/dr14/manga/spectro/redux/v2_1_2/7495/stack/manga-7495-12704-LOGCUBE.fits.gz', cache=True)
fn = download_file('file:/Users/busko/Downloads/manga-7495-12704-LOGCUBE.fits', cache=True)
f = fits.open(fn)

flux = f[1].data
err = f[2].data
wave = f[4].data
# Units handling is presumed to come from above, perhaps at the loader level?
# Here we pretend we got them already somehow
funit_string = f[1].header['BUNIT']
funit = u.def_unit(funit_string, 1.E-17 * u.erg/u.s/u.cm/u.cm/u.Angstrom)
u.add_enabled_units([funit])
wunit = u.Unit('Angstrom')

flux = flux[:,35,35] * funit
err = err[:,35,35] * funit
wave = wave * wunit

spectrum = Spectrum1D(spectral_axis=wave, flux=flux, uncertainty=StdDevUncertainty(err))

# The spectrum may be already clipped or masked or otherwise pre-processed by 
# other functionality available outside the fitting plugin. Here, we pretened 
# it was limited to a small region.
region = SpectralRegion(6500*wunit, 7000*wunit)

# The object the backend expects via its API.
spectrum = extract_region(spectrum, region)<jupyter_output><empty_output><jupyter_text>Visual check<jupyter_code>pl.figure()
pl.gcf().set_size_inches((8.,4.))
pl.plot(spectrum.spectral_axis, spectrum.flux, linewidth=0.5, label='obs')
pl.xlabel(str(spectrum.spectral_axis.unit))
pl.ylabel(str(spectrum.flux.unit))<jupyter_output><empty_output><jupyter_text>Read model from UI The UI is assumed to feed an instance of an Astropy compound model, plus an arithmetic expression, to the backed API.<jupyter_code># Model subcomponents come from a list or similar, generated by the UI. The UI
# is expected to initialize each subcomponent down to the level of optional
# properties such as the fixed and tied parameters, subcomponent names, and such.
#
# Here, we pretend and generate them by hand.
g1 = models.Gaussian1D(amplitude=0.7*funit, mean=6776.*wunit, stddev=3*wunit, name='g1')
g2 = models.Gaussian1D(amplitude=0.5*funit, mean=6755.*wunit, stddev=3*wunit, name='g2')
g3 = models.Gaussian1D(amplitude=0.4*funit, mean=6740.*wunit, stddev=3*wunit, name='g3')
zero_level = models.Const1D(amplitude=1.*funit, name='const1d')
# ...
# ... 
# other additional components...

# The second part of the model is the arithmetic expression. 
# The UI feeds this string to the backend.
expression = "g1 + g2 + g3 + const1d"

# The expression parser needs the subcomponents stored in a dict, 
# with keys taken from their names. 
# This mechanism can be augmented with status feedback to the
# UI (see specviz/specviz/plugins/model_editor/models.py around
# lines 200-230)
model_dict = {g1.name:g1,
              g2.name:g2,
              g3.name:g3,
              zero_level.name:zero_level}
aeval = Interpreter(usersyms=model_dict)
compound_model_init = aeval(expression)<jupyter_output><empty_output><jupyter_text>Fit model<jupyter_code>fitted_model = fit_lines(spectrum, compound_model_init)
fitted_values = fitted_model(spectrum.spectral_axis)<jupyter_output><empty_output><jupyter_text>Result The backed returns to the UI the fitted model computed above, plus a model realization in the form of a Spectrum1D instance.<jupyter_code>fitted_spectrum = Spectrum1D(spectral_axis=spectrum.spectral_axis, flux=fitted_values*funit)<jupyter_output><empty_output><jupyter_text>Check result<jupyter_code>fitted_model
pl.figure()
pl.gcf().set_size_inches((8.,4.))
pl.plot(spectrum.spectral_axis, spectrum.flux, linewidth=0.5, label='spectrum')
pl.plot(spectrum.spectral_axis, fitted_values, linewidth=0.5, label='model')
pl.legend()
pl.xlabel(str(spectrum.spectral_axis.unit))
pl.ylabel(str(spectrum.flux.unit))<jupyter_output><empty_output><jupyter_text>Backend code in a module The backend code performs the steps above (read spectrum and model from UI, performs fit) and will live in a module. Here is an example. Although the API still needs to be defined in detail. E.g., parameters such as fit windows, regions, fitter parameters and the like still need to be passed via API.<jupyter_code>from jdaviz.configs.default.plugins.model_fitting import fitting_backend as fb

model_list = [g1,g2,g3,zero_level]
fitted_model, fitted_spectrum = fb.fit_model_to_spectrum(spectrum, model_list, expression, run_fitter=True)<jupyter_output>/Users/busko/Projects/software/miniconda3/envs/jdaviz/lib/python3.7/site-packages/glue/external/echo/__init__.py:3: UserWarning: glue.external.echo is deprecated, import from echo directly instead
  warnings.warn('glue.external.echo is deprecated, import from echo directly instead')<jupyter_text>Verify results from the backend implementation<jupyter_code>pl.figure()
pl.gcf().set_size_inches((8.,4.))
pl.plot(spectrum.spectral_axis, spectrum.flux, linewidth=0.5, label='spectrum')
pl.plot(spectrum.spectral_axis, fitted_spectrum.flux.value, linewidth=0.5, label='model')
pl.legend()
pl.xlabel(str(spectrum.spectral_axis.unit))
pl.ylabel(str(spectrum.flux.unit))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11283143023030773, 'fraction_numerical': 0.02438552351461196, 'mean_word_length': 6.031292517006802, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 32, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '426904', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1840, 'n_tokens_neox': 1733, 'n_words': 441, 'path': 'notebooks/concepts/Example_fitting.ipynb', 'repo_name': 'kecnry/jdaviz', 'size': '5167'}