//# SpectralElement.cc: Describes (a set of related) spectral lines //# Copyright (C) 2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include <components/SpectralComponents/GaussianMultipletSpectralElement.h> #include <casacore/casa/IO/ArrayIO.h> #include <casa/Arrays/ArrayLogical.h> #include <casa/Arrays/ArrayMath.h> #include <casa/Containers/Record.h> #include <components/SpectralComponents/GaussianSpectralElement.h> #include <casa/iostream.h> using namespace casacore; namespace casa { //# NAMESPACE CASA - BEGIN #define _ORIGIN String("GaussianMultipletSpectralElement::") + __FUNCTION__ + ":" + String::toString(__LINE__) + ": " GaussianMultipletSpectralElement::GaussianMultipletSpectralElement( const vector<GaussianSpectralElement>& estimates, const Matrix<Double>& constraints ) : CompiledSpectralElement(SpectralElement::GMULTIPLET), _gaussians(estimates),_constraints(constraints), _paramIndices(estimates.size(), 3, 0) { if(estimates.size() != constraints.nrow()+1) { throw AipsError( _ORIGIN + "Mismatch between size of estimates and constraints" ); } if (constraints.ncolumn() != 3) { throw AipsError(_ORIGIN + "constraints does not have 3 columns"); } Matrix<Bool> fixed(LogicalArray(constraints != 0.0)); for (uInt i=1; i<estimates.size(); i++) { if (! estimates[0].fixedAmpl() && estimates[i].fixedAmpl() && fixed(i-1, 0) ) { throw AipsError(_ORIGIN + "You cannot fix the amplitude of a " "non-reference Gaussian if the reference Gaussian's " "amplitude is not fixed and there is a relationship " "between the two amplitudes." ); } if (! estimates[0].fixedCenter() && estimates[i].fixedCenter() && fixed(i-1, 1) ) { throw AipsError(_ORIGIN + "You cannot fix the center of a non-reference " "Gaussian if the reference Gaussian's center is not fixed and there " "is a relationship between the two centers." ); } if (! estimates[0].fixedWidth() && estimates[i].fixedWidth() && fixed(i-1, 2) ) { throw AipsError(_ORIGIN + "You cannot fix the width of a non-reference " "Gaussian if the reference Gaussian's width is not fixed and there " "is a relationship between the two widths." ); } } ostringstream myfunc; myfunc << "p0*exp(-0.5 * (x-p1)*(x-p1) / p2/p2)"; Vector<Double> parm(3 + nfalse(fixed), 0); Vector<Double> errs = parm.copy(); parm[0] = _gaussians[0].getAmpl(); parm[1] = _gaussians[0].getCenter(); parm[2] = _gaussians[0].getSigma(); errs[0] = _gaussians[0].getAmplErr(); errs[1] = _gaussians[0].getCenterErr(); errs[2] = _gaussians[0].getSigmaErr(); Vector<Bool> f(parm.size(), true); f[0] = _gaussians[0].fixedAmpl(); f[1] = _gaussians[0].fixedCenter(); f[2] = _gaussians[0].fixedSigma(); _paramIndices(0, 0) = 0; _paramIndices(0, 1) = 1; _paramIndices(0, 2) = 2; uInt p = 3; for (uInt i=0; i<constraints.nrow(); i++) { String amp; if (constraints(i, 0) != 0) { amp = String::toString(constraints(i, 0)) + "*p0"; } else { amp = "p" + String::toString(p); parm[p] = _gaussians[i+1].getAmpl(); errs[p] = _gaussians[i+1].getAmplErr(); f[p] = _gaussians[i+1].fixedAmpl(); _paramIndices(i+1, 0) = p; p++; } String center; if (constraints(i, 1) != 0) { center = "x-(p1+(" + String::toString(constraints(i, 1)) + "))"; } else { center = "x-p" + String::toString(p); parm[p] = _gaussians[i+1].getCenter(); errs[p] = _gaussians[i+1].getCenterErr(); f[p] = _gaussians[i+1].fixedCenter(); _paramIndices(i+1, 1) = p; p++; } String sigma; if (constraints(i, 2) != 0) { sigma = String::toString(constraints(i, 2)) + "*p2"; } else { sigma = "p" + String::toString(p); parm[p] = _gaussians[i+1].getSigma(); errs[p] = _gaussians[i+1].getSigmaErr(); f[p] = _gaussians[i+1].fixedSigma(); _paramIndices(i+1, 2) = p; p++; } myfunc << " + " << amp << "*exp(-0.5 * (" << center << ")*(" << center << ") / (" << sigma << ") / (" << sigma << "))"; } _setFunction(myfunc.str()); // have to set the GaussianSpectralElement parameters set(parm); setError(errs); fix(f); } GaussianMultipletSpectralElement::GaussianMultipletSpectralElement( const GaussianMultipletSpectralElement& other ) : CompiledSpectralElement(other), _gaussians(other._gaussians), _constraints(other._constraints), _paramIndices(other._paramIndices) {} GaussianMultipletSpectralElement::~GaussianMultipletSpectralElement() {} SpectralElement* GaussianMultipletSpectralElement::clone() const { return new GaussianMultipletSpectralElement(*this); } GaussianMultipletSpectralElement& GaussianMultipletSpectralElement::operator=( const GaussianMultipletSpectralElement &other ) { if (this != &other) { CompiledSpectralElement::operator=(other); _gaussians = other._gaussians; _constraints.resize(other._constraints.shape()); _constraints = other._constraints.copy(); _paramIndices.resize(other._paramIndices.shape()); _paramIndices = other._paramIndices.copy(); } return *this; } Bool GaussianMultipletSpectralElement::operator==( const GaussianMultipletSpectralElement& other ) const { return( CompiledSpectralElement::operator==(other) && allTrue(Vector<GaussianSpectralElement>(_gaussians) == Vector<GaussianSpectralElement>(other._gaussians)) && allTrue(_constraints == other._constraints) ); } const vector<GaussianSpectralElement>& GaussianMultipletSpectralElement::getGaussians() const { return _gaussians; } const Matrix<Double>& GaussianMultipletSpectralElement::getConstraints() const { return _constraints; } Bool GaussianMultipletSpectralElement::toRecord(RecordInterface& out) const { out.define(RecordFieldId("type"), fromType(getType())); Record gaussians; for (uInt i=0; i<_gaussians.size(); i++) { Record gaussian; _gaussians[i].toRecord(gaussian); gaussians.defineRecord( "*" + String::toString(i), gaussian ); } out.defineRecord("gaussians", gaussians); out.define("fixedMatrix", _constraints); return true; } void GaussianMultipletSpectralElement::set(const Vector<Double>& param) { if (get().size() > 0 && param.size() != get().size()) { ostringstream x; x << _ORIGIN << "Inconsistent number of parameters. Got " << param.size() << ". Must be " << get().size(); throw AipsError(x.str()); } SpectralElement::_set(param); Double amp0 = param[0]; Double center0 = param[1]; Double sigma0 = param[2]; _gaussians[0].setAmpl(amp0); _gaussians[0].setCenter(center0); _gaussians[0].setSigma(sigma0); uInt p = 3; for (uInt i=3; i<_paramIndices.size(); i++) { uInt gNumber = i/3; uInt pNumber = i%3; uInt pIndex = _paramIndices(gNumber, pNumber); Double fRel = _constraints(gNumber-1, pNumber); if (pNumber == 0) { Double amp = pIndex == 0 ? fRel*amp0 : param[p]; _gaussians[gNumber].setAmpl(amp); } else if (pNumber == 1) { Double center = pIndex == 0 ? fRel+center0 : param[p]; _gaussians[gNumber].setCenter(center); } else if (pNumber == 2) { Double sigma = pIndex == 0 ? fRel*sigma0 : param[p]; _gaussians[gNumber].setSigma(sigma); } if (pIndex > 0) { p++; } } } void GaussianMultipletSpectralElement::setError(const Vector<Double> &err) { SpectralElement::setError(err); Double amp0 = err[0]; Double center0 = err[1]; Double sigma0 = err[2]; Vector<Double> errors(3); errors[0] = err[0]; errors[1] = err[1]; errors[2] = err[2]; _gaussians[0].setError(errors); uInt p = 3; for (uInt i=3; i<_paramIndices.size(); i++) { uInt gNumber = i/3; uInt pNumber = i%3; uInt pIndex = _paramIndices(gNumber, pNumber); Double fRel = _constraints(gNumber-1, pNumber); if (pNumber == 0) { errors[0] = pIndex == 0 ? fRel*amp0 : err[p]; } else if (pNumber == 1) { errors[1] = pIndex == 0 ? center0 : err[p]; } else if (pNumber == 2) { errors[2] = pIndex == 0 ? fRel*sigma0 : err[p]; } _gaussians[gNumber].setError(errors); if (pIndex > 0) { p++; } } } void GaussianMultipletSpectralElement::fix(const Vector<Bool>& fix) { SpectralElement::fix(fix); Bool amp0 = fix[0]; Bool center0 = fix[1]; Bool sigma0 = fix[2]; Vector<Bool> fixed(3); fixed[0] = fix[0]; fixed[1] = fix[1]; fixed[2] = fix[2]; _gaussians[0].fix(fixed); uInt p = 3; for (uInt i=3; i<_paramIndices.size(); i++) { uInt gNumber = i/3; uInt pNumber = i%3; uInt pIndex = _paramIndices(gNumber, pNumber); if (pNumber == 0) { fixed[0] = pIndex == 0 ? amp0 : fix[p]; } else if (pNumber == 1) { fixed[1] = pIndex == 0 ? center0 : fix[p]; } else if (pNumber == 2) { fixed[2] = pIndex == 0 ? sigma0 : fix[p]; } _gaussians[gNumber].fix(fixed); if (pIndex > 0) { p++; } } } ostream &operator<<(ostream& os, const GaussianMultipletSpectralElement& elem) { os << SpectralElement::fromType((elem.getType())) << " element: " << endl; os << " Function: " << elem.getFunction() << endl; os << " Gaussians:" << endl; Vector<GaussianSpectralElement> gaussians(elem.getGaussians()); for (uInt i=0; i<gaussians.size(); i++) { os << "Gaussian " << i << ": " << gaussians[i] << endl; } Matrix<Double> r = elem.getConstraints(); os << "Constraints: " << endl; for (uInt i=1; i<gaussians.size(); i++) { for (uInt j=0; j<3; j++) { Double v = r(i-1, j); if (v != 0) { switch (j) { case 0: os << " Amplitude ratio of component " << i << " to the reference component is fixed at " << v; break; case 1: os << " Center offset of component " << i << " to the reference component is fixed at " << v; break; case 2: os << " FWHM ratio of component " << i << " to the reference component is fixed at " << v; break; default: throw AipsError(_ORIGIN + "Logic Error"); } } } } return os; } } //# NAMESPACE CASA - END