WWZMultiperiodicModel.java

/**
 * VStar: a statistical analysis tool for variable star data.
 * Copyright (C) 2010  AAVSO (http://www.aavso.org/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
package org.aavso.tools.vstar.util.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.aavso.tools.vstar.data.DateInfo;
import org.aavso.tools.vstar.data.Magnitude;
import org.aavso.tools.vstar.data.SeriesType;
import org.aavso.tools.vstar.data.ValidObservation;
import org.aavso.tools.vstar.exception.AlgorithmError;
import org.aavso.tools.vstar.ui.model.plot.ContinuousModelFunction;
import org.aavso.tools.vstar.util.comparator.JDComparator;
import org.aavso.tools.vstar.util.period.wwz.WWZStatistic;
import org.aavso.tools.vstar.util.period.wwz.WeightedWaveletZTransform;
import org.aavso.tools.vstar.util.prefs.NumericPrecisionPrefs;

/**
 * This class represents a WWZ derived multi-period fit model.
 */
public class WWZMultiperiodicModel implements IModel {

	private WeightedWaveletZTransform wwt;
	private List<Double> periods;
	private List<ValidObservation> obs;

	private List<ValidObservation> fit;
	private List<ValidObservation> residuals;

	private String desc;

	private boolean interrupted;

	/**
	 * Constructor
	 * 
	 * @param wwt
	 *            The WWZ algorithm object.
	 * @param periods
	 *            A list of periods from which to extract (from the stats) the
	 *            best-fit sinusoid values.
	 */
	public WWZMultiperiodicModel(WeightedWaveletZTransform wwt,
			List<Double> periods) {
		super();
		this.wwt = wwt;
		this.periods = periods;
		obs = wwt.getObs();

		fit = new ArrayList<ValidObservation>();
		residuals = new ArrayList<ValidObservation>();

		interrupted = false;
	}

	/**
	 * @see org.aavso.tools.vstar.util.IAlgorithm#execute()
	 */
	@Override
	public void execute() throws AlgorithmError {
		interrupted = false;

		// TODO: For multiple periods, do we need instead to average the
		// best-fit and residual values?

		// TODO: pass in wwt object so we can always use the full stats list and
		// get observations

		for (double period : periods) {
			// Iterate over each statistic, looking for entries for which the
			// period is the same as our target. When one is found, create a fit
			// observation. For all observations since the previous period was
			// found, create residuals from the observations in that range.
			int i = 0;

			// Always use all statistcs vs maximal statistics to create model.
			// TODO: sanity check that!
			for (WWZStatistic stat : wwt.getStats()) {

				if (stat.getPeriod() == period) {

					String comment = "From WWZ, period "
							+ NumericPrecisionPrefs.formatOther(period);

					// Create a fit observation from the average magnitude for
					// this time-frequency/period combination.
					ValidObservation fitOb = new ValidObservation();
					fitOb.setDateInfo(new DateInfo(stat.getTau()));
					fitOb.setMagnitude(new Magnitude(stat.getMave(), 0));
					fitOb.setBand(SeriesType.Model);
					fitOb.setComments(comment);
					fit.add(fitOb);

					// Create a residual observation for each observation since
					// the previous period. TODO: sanity check this approach,
					// i.e. does it actually make sense for a tau value to
					// represent a range of observation time values for the
					// purpose of residual creation?
					while (i < obs.size()
							&& obs.get(i).getJD() <= stat.getTau()) {
						double residual = obs.get(i).getMag() - stat.getMave();
						ValidObservation residualOb = new ValidObservation();
						residualOb
								.setDateInfo(new DateInfo(obs.get(i).getJD()));
						residualOb.setMagnitude(new Magnitude(residual, 0));
						residualOb.setBand(SeriesType.Residuals);
						residualOb.setComments(comment);
						residuals.add(residualOb);
						i++;
					}
				}

				if (interrupted) {
					return;
				}
			}
		}

		// For multiple periods, observations will be out of time order, so sort
		// by JD.
		Collections.sort(fit, JDComparator.instance);
		Collections.sort(residuals, JDComparator.instance);
	}

	/**
	 * @see org.aavso.tools.vstar.util.model.IModel#getDescription()
	 */
	@Override
	public String getDescription() {
		if (desc == null) {
			desc = getKind() + " from periods: ";
			for (Double period : periods) {
				desc += NumericPrecisionPrefs.formatOther(period) + " ";
			}
		}

		return desc;
	}

	/**
	 * @see org.aavso.tools.vstar.util.model.IModel#getKind()
	 */
	@Override
	public String getKind() {
		return "Multi-periodic fit from WWZ";
	}

	/**
	 * @see org.aavso.tools.vstar.util.model.IModel#getFit()
	 */
	@Override
	public List<ValidObservation> getFit() {
		return fit;
	}

	/**
	 * @see org.aavso.tools.vstar.util.model.IModel#getResiduals()
	 */
	@Override
	public List<ValidObservation> getResiduals() {
		return residuals;
	}

	@Override
	public List<PeriodFitParameters> getParameters() {
		return null;
	}

	@Override
	public boolean hasFuncDesc() {
		return false;
	}

	public String toString() {
		return getDescription();
	}

	@Override
	public void interrupt() {
		interrupted = true;
	}

	@Override
	public Map<String, String> getFunctionStrings() {
		return null;
	}

	@Override
	public ContinuousModelFunction getModelFunction() {
		return null;
	}
}