- numpy

Machine Learning Tools

The purpose of this page is to offer an easy way for the community to test and provide feedback on the published machine learning tools produced by the Montreal team. New tools or versions will also be made available here as they are published. To try the tools, simply upload your favorite spectrum in a two-column csv file. The first and second columns must correspond to the wavelength (in Angstroms) and flux, respectively.

Please take note of the following:

  • The main spectral type classifier requires a spectral coverage of 3840-7000 Angstroms
  • The main sequence companion detector requires a spectral coverage of 3840-9000 Angstroms
  • While the neural networks provide predictions for any spectrum, they are trained on SDSS white dwarf and subdwarf spectra and work best when used on similar data. In other words, non-white dwarf spectra or spectra with resolutions differing from SDSS may give confident, yet erroneous results.
  • Due to practical considerations, the tools here only use a single neural network rather than deep ensembles. Thus, predictions may slightly differ from published values and prediction uncertainties are not available.

More details can be found in the following paper: Vincent et al. 2023

Comments and suggestions are welcome! Send them to o.vincent(at)umontreal.ca.











Porting of this machine learning model to the Montreal White Dwarf Database was realized by Dr. Simon Blouin (University of Victoria).

The Montreal White Dwarf Database is produced and maintained by Prof. Patrick Dufour (Université de Montréal) and Dr. Simon Blouin (University of Victoria). Please send database updates, corrections, suggestions or bug reports to patrick.dufour@umontreal.ca.


import numpy as np import js from js import document from pyodide.ffi import create_proxy, to_js def cont_pixels(flux, window_size=30, percent=90): # pad spectrum if it cant be split into equal windows nWindows = flux.size // window_size nPads = 0 if flux.size % window_size > 0: nWindows += 1 nPads = nWindows * window_size - flux.size padded_flux = np.concatenate([flux, flux[-nPads:]]) else: padded_flux = np.copy(flux) # split into windows split_flux = np.array_split(padded_flux, nWindows) # take xth percentile of pixels in each window percentiles = np.percentile(split_flux, percent, axis=1) idx_percentiles = [np.where(split_flux[i] >= percentiles[i])[0] + i * window_size for i in range(nWindows)] concat_idx_percentiles = np.concatenate(idx_percentiles) # remove indexes that go beyond wl grid concat_idx_percentiles = concat_idx_percentiles[np.where(concat_idx_percentiles < flux.size)] return np.unique(concat_idx_percentiles) def continuum_normalization(freqs, spec): cpixels = cont_pixels(spec, 50, 85) fullcontfit = np.polynomial.chebyshev.Chebyshev.fit(freqs[cpixels], spec[cpixels], 4) return spec / fullcontfit(freqs) def runPython(dummy): from js import dataCSV, pixelStd, pixelMeans, sdssgrid, sdssgridMS, pixelStdMS, pixelMeansMS from js import tfcall, createObject, createObjectMS, jsbug try: data = dataCSV lam = [] flx = [] for i,val in enumerate(data): try: x,y = str(val).split(',') lam.append(float(x)) flx.append(float(y)) except: continue lam = np.array(lam) flx = np.array(flx) spec0 = continuum_normalization(lam, flx) mean = [] std = [] sdss = [] sdssMS = [] meanMS = [] stdMS = [] for val in list(pixelMeans)[:-1]: mean.append(float(str(val))) for val in list(pixelStd)[:-1]: std.append(float(str(val))) for val in list(pixelMeansMS)[:-1]: meanMS.append(float(str(val))) for val in list(pixelStdMS)[:-1]: stdMS.append(float(str(val))) for val in list(sdssgrid)[:-1]: sdss.append(float(str(val))) for val in list(sdssgridMS)[:-1]: sdssMS.append(float(str(val))) sdss = np.array(sdss) sdssMS = np.array(sdssMS) spec = np.interp(sdss,lam,spec0) mean = np.array(mean) std = np.array(std) spec = (spec-mean)/std spec = list(spec) createObject(create_proxy(spec), "specjs") if np.max(lam)>=9000 and np.min(lam)<=3842: specMS = np.interp(sdssMS,lam,spec0) mean = np.array(meanMS) std = np.array(stdMS) specMS = (specMS-mean)/std specMS = list(specMS) createObjectMS(create_proxy(specMS), "specMSjs") tfcall(2) elif np.max(lam)>=7000 and np.min(lam)<=3842: tfcall(1) else: tfcall(0) return except: jsbug(0) return def setup(): function_proxy = create_proxy(runPython) document.getElementById("process-file").addEventListener("click", function_proxy) setup()