double AiryInfoObs::chord(double r, double h, double rsq, double hsq) const { if (r==0.) return 0.; #ifdef AIRY_DEBUG else if (r<h) throw SBError("Airy calculation r<h"); else if (h < 0.) throw SBError("Airy calculation h<0"); #endif else return rsq*std::asin(h/r) - h*sqrt(rsq-hsq); }
double MoffatCalculateScaleRadiusFromHLR(double re, double rm, double beta) { dbg<<"Start MoffatCalculateScaleRadiusFromHLR\n"; // The basic equation that is relevant here is the flux of a Moffat profile // out to some radius. // flux(R) = int( (1+r^2/rd^2 )^(-beta) 2pi r dr, r=0..R ) // = (pi rd^2 / (beta-1)) (1 - (1+R^2/rd^2)^(1-beta) ) // For now, we can ignore the first factor. We call the second factor fluxfactor below, // or in this function f(R). // // We are given two values of R for which we know that the ratio of their fluxes is 1/2: // f(re) = 0.5 * f(rm) // if (rm == 0.) { // If rm = infinity (which we actually indicate with rm=0), then we can solve for // rd analytically: // // f(rm) = 1 // f(re) = 0.5 = 1 - (1+re^2/rd^2)^(1-beta) // re^2/rd^2 = 0.5^(1/(1-beta)) - 1 double rerd = std::sqrt( std::pow(0.5, 1./(1.-beta)) - 1.); dbg<<"rm = 0, so analytic.\n"; xdbg<<"rd = re/rerd = "<<re<<" / "<<rerd<<" = "<<re/rerd<<std::endl; return re / rerd; } else { // If trunc < infinity, then the equations are slightly circular: // f(rm) = 1 - (1 + rm^2/rd^2)^(1-beta) // 2*f(re) = 2 - 2*(1 + re^2/rd^2)^(1-beta) // 2*(1+re^2/rd^2)^(1-beta) = 1 + (1+rm^2/rd^2)^(1-beta) // // As rm decreases, rd increases. // Eventually rd increases to infinity. When does that happen: // Take the limit as rd->infinity in the above equation: // 2 + 2*(1-beta) re^2/rd^2) = 1 + 1 + (1-beta) rm^2/rd^2 // 2 re^2 = rm^2 // rm = sqrt(2) * re // So this is the limit for how low rm is allowed to be for a given re if (rm <= std::sqrt(2.) * re) throw SBError("Moffat truncation radius must be > sqrt(2) * half_light_radius."); dbg<<"rm != 0, so not analytic.\n"; MoffatScaleRadiusFunc func(re,rm,beta); // For the lower bound of rd, we can use the untruncated value: double r1 = re / std::sqrt( std::pow(0.5, 1./(1.-beta)) - 1.); xdbg<<"r1 = "<<r1<<std::endl; // For the upper bound, we don't really have a good choice, so start with 2*r1 // and we'll expand it if necessary. double r2 = 2. * r1; xdbg<<"r2 = "<<r2<<std::endl; Solve<MoffatScaleRadiusFunc> solver(func,r1,r2); solver.setMethod(Brent); solver.bracketUpper(); xdbg<<"After bracket, range is "<<solver.getLowerBound()<<" .. "<< solver.getUpperBound()<<std::endl; double rd = solver.root(); xdbg<<"Root is "<<rd<<std::endl; return rd; } }
void SBConvolve::SBConvolveImpl::add(const SBProfile& rhs) { dbg<<"Start SBConvolveImpl::add. Adding item # "<<_plist.size()+1<<std::endl; // Add new terms(s) to the _plist: assert(GetImpl(rhs)); const SBProfileImpl* p = GetImpl(rhs); const SBConvolveImpl* sbc = dynamic_cast<const SBConvolveImpl*>(p); const SBAutoConvolve::SBAutoConvolveImpl* sbc2 = dynamic_cast<const SBAutoConvolve::SBAutoConvolveImpl*>(p); const SBAutoCorrelate::SBAutoCorrelateImpl* sbc3 = dynamic_cast<const SBAutoCorrelate::SBAutoCorrelateImpl*>(p); if (sbc) { dbg<<" (Item is really "<<sbc->_plist.size()<<" items.)"<<std::endl; // If rhs is an SBConvolve, copy its list here for (ConstIter pptr = sbc->_plist.begin(); pptr!=sbc->_plist.end(); ++pptr) { if (!pptr->isAnalyticK() && !_real_space) throw SBError("SBConvolve requires members to be analytic in k"); if (!pptr->isAnalyticX() && _real_space) throw SBError("Real_space SBConvolve requires members to be analytic in x"); _plist.push_back(*pptr); } } else if (sbc2) { dbg<<" (Item is really AutoConvolve.)"<<std::endl; // If rhs is an SBAutoConvolve, put two of its item here: const SBProfile& obj = sbc2->getAdaptee(); if (!obj.isAnalyticK() && !_real_space) throw SBError("SBConvolve requires members to be analytic in k"); if (!obj.isAnalyticX() && _real_space) throw SBError("Real_space SBConvolve requires members to be analytic in x"); _plist.push_back(obj); _plist.push_back(obj); } else if (sbc3) { dbg<<" (Item is really AutoCorrelate items.)"<<std::endl; // If rhs is an SBAutoCorrelate, put its item and 180 degree rotated verion here: const SBProfile& obj = sbc3->getAdaptee(); if (!obj.isAnalyticK() && !_real_space) throw SBError("SBConvolve requires members to be analytic in k"); if (!obj.isAnalyticX() && _real_space) throw SBError("Real_space SBConvolve requires members to be analytic in x"); _plist.push_back(obj); SBProfile temp = obj; temp.applyRotation(180. * degrees); _plist.push_back(temp); } else { if (!rhs.isAnalyticK() && !_real_space) throw SBError("SBConvolve requires members to be analytic in k"); if (!rhs.isAnalyticX() && _real_space) throw SBError("Real-space SBConvolve requires members to be analytic in x"); _plist.push_back(rhs); } }
void SBConvolve::SBConvolveImpl::add(const SBProfile& sbp) { dbg<<"Start SBConvolveImpl::add. Adding item # "<<_plist.size()+1<<std::endl; // Add new terms(s) to the _plist: assert(GetImpl(sbp)); const SBProfileImpl* p = GetImpl(sbp); const SBConvolveImpl* sbc = dynamic_cast<const SBConvolveImpl*>(p); const SBAutoConvolve::SBAutoConvolveImpl* sbc2 = dynamic_cast<const SBAutoConvolve::SBAutoConvolveImpl*>(p); const SBAutoCorrelate::SBAutoCorrelateImpl* sbc3 = dynamic_cast<const SBAutoCorrelate::SBAutoCorrelateImpl*>(p); if (sbc) { dbg<<" (Item is really "<<sbc->_plist.size()<<" items.)"<<std::endl; // If sbp is an SBConvolve, copy its list here for (ConstIter pptr = sbc->_plist.begin(); pptr!=sbc->_plist.end(); ++pptr) add(*pptr); } else if (sbc2) { dbg<<" (Item is really AutoConvolve.)"<<std::endl; // If sbp is an SBAutoConvolve, put two of its item here: const SBProfile& obj = sbc2->getAdaptee(); add(obj); add(obj); } else if (sbc3) { dbg<<" (Item is really AutoCorrelate items.)"<<std::endl; // If sbp is an SBAutoCorrelate, put its item and 180 degree rotated verion here: const SBProfile& obj = sbc3->getAdaptee(); add(obj); SBProfile temp = obj.rotate(180. * degrees); add(temp); } else { if (!sbp.isAnalyticK() && !_real_space) throw SBError("SBConvolve requires members to be analytic in k"); if (!sbp.isAnalyticX() && _real_space) throw SBError("Real-space SBConvolve requires members to be analytic in x"); _plist.push_back(sbp); } _x0 += sbp.centroid().x; _y0 += sbp.centroid().y; _isStillAxisymmetric = _isStillAxisymmetric && sbp.isAxisymmetric(); _fluxProduct *= sbp.getFlux(); }
SpergelInfo::SpergelInfo(double nu, const GSParamsPtr& gsparams) : _nu(nu), _gsparams(gsparams), _gamma_nup1(boost::math::tgamma(_nu+1.0)), _gamma_nup2(_gamma_nup1 * (_nu+1)), _xnorm0((_nu > 0.) ? _gamma_nup1 / (2. * _nu) * std::pow(2., _nu) : INFINITY), _maxk(0.), _stepk(0.), _re(0.) { dbg<<"Start SpergelInfo constructor for nu = "<<_nu<<std::endl; if (_nu < sbp::minimum_spergel_nu || _nu > sbp::maximum_spergel_nu) throw SBError("Requested Spergel index out of range"); }
double AiryInfoNoObs::kValue(double ksq_over_pisq) const { if (ksq_over_pisq >= 4.) return 0.; if (ksq_over_pisq == 0.) return M_PI; /* in between we calculate half-height at intersection */ double hsq = 1. - ksq_over_pisq/4.; #ifdef AIRY_DEBUG if (hsq<0.) throw SBError("Airy calculation half-height invalid"); #endif double h = sqrt(hsq); return 2. * (std::asin(h) - h*sqrt(1.-hsq)); }
/* area inside intersection of 2 circles both with radius r, seperated by t*/ double AiryInfoObs::circle_intersection( double r, double rsq, double tsq) const { assert(r >= 0.); if (tsq >= 4.*rsq) return 0.; if (tsq == 0.) return M_PI*rsq; /* in between we calculate half-height at intersection */ double hsq = rsq - tsq/4.; #ifdef AIRY_DEBUG if (hsq<0.) throw SBError("Airy calculation half-height invalid"); #endif double h = sqrt(hsq); return 2.*chord(r,h,rsq,hsq); }
boost::shared_ptr<PhotonArray> SBConvolve::SBConvolveImpl::shoot(int N, UniformDeviate u) const { dbg<<"Convolve shoot: N = "<<N<<std::endl; dbg<<"Target flux = "<<getFlux()<<std::endl; std::list<SBProfile>::const_iterator pptr = _plist.begin(); if (pptr==_plist.end()) throw SBError("Cannot shoot() for empty SBConvolve"); boost::shared_ptr<PhotonArray> result = pptr->shoot(N, u); // It may be necessary to shuffle when convolving because we do // do not have a gaurantee that the convolvee's photons are // uncorrelated, e.g. they might both have their negative ones // at the end. // However, this decision is now made by the convolve method. for (++pptr; pptr != _plist.end(); ++pptr) result->convolve(*pptr->shoot(N, u), u); dbg<<"Convolve Realized flux = "<<result->getTotalFlux()<<std::endl; return result; }
void SBConvolve::SBConvolveImpl::shoot(PhotonArray& photons, UniformDeviate ud) const { const int N = photons.size(); dbg<<"Convolve shoot: N = "<<N<<std::endl; dbg<<"Target flux = "<<getFlux()<<std::endl; std::list<SBProfile>::const_iterator pptr = _plist.begin(); if (pptr==_plist.end()) throw SBError("Cannot shoot() for empty SBConvolve"); pptr->shoot(photons, ud); // It may be necessary to shuffle when convolving because we do // do not have a gaurantee that the convolvee's photons are // uncorrelated, e.g. they might both have their negative ones // at the end. // However, this decision is now made by the convolve method. for (++pptr; pptr != _plist.end(); ++pptr) { PhotonArray temp(N); pptr->shoot(temp, ud); photons.convolve(temp, ud); } dbg<<"Convolve Realized flux = "<<photons.getTotalFlux()<<std::endl; }
double SBConvolve::SBConvolveImpl::xValue(const Position<double>& pos) const { // Perform a direct calculation of the convolution at a particular point by // doing the real-space integral. // Note: This can only really be done one pair at a time, so it is // probably rare that this will be more efficient if N > 2. // For now, we don't bother implementing this for N > 2. if (_plist.size() == 2) { const SBProfile& p1 = _plist.front(); const SBProfile& p2 = _plist.back(); if (p2.isAxisymmetric()) return RealSpaceConvolve(p2,p1,pos,_fluxProduct,this->gsparams); else return RealSpaceConvolve(p1,p2,pos,_fluxProduct,this->gsparams); } else if (_plist.empty()) return 0.; else if (_plist.size() == 1) return _plist.front().xValue(pos); else throw SBError("Real-space integration of more than 2 profiles is not implemented."); }
/* area inside intersection of 2 circles radii r & s, seperated by t*/ double AiryInfoObs::circle_intersection( double r, double s, double rsq, double ssq, double tsq) const { assert(r >= s); assert(s >= 0.); double rps_sq = (r+s)*(r+s); if (tsq >= rps_sq) return 0.; double rms_sq = (r-s)*(r-s); if (tsq <= rms_sq) return M_PI*ssq; /* in between we calculate half-height at intersection */ double hsq = 0.5*(rsq + ssq) - (tsq*tsq + rps_sq*rms_sq)/(4.*tsq); #ifdef AIRY_DEBUG if (hsq<0.) throw SBError("Airy calculation half-height invalid"); #endif double h = sqrt(hsq); if (tsq < rsq - ssq) return M_PI*ssq - chord(s,h,ssq,hsq) + chord(r,h,rsq,hsq); else return chord(s,h,ssq,hsq) + chord(r,h,rsq,hsq); }
SBMoffat::SBMoffatImpl::SBMoffatImpl(double beta, double size, RadiusType rType, double trunc, double flux, const GSParamsPtr& gsparams) : SBProfileImpl(gsparams), _beta(beta), _flux(flux), _trunc(trunc), _ft(Table<double,double>::spline), _re(0.), // initially set to zero, may be updated by size or getHalfLightRadius(). _stepk(0.), // calculated by stepK() and stored. _maxk(0.) // calculated by maxK() and stored. { xdbg<<"Start SBMoffat constructor: \n"; xdbg<<"beta = "<<_beta<<"\n"; xdbg<<"flux = "<<_flux<<"\n"; xdbg<<"trunc = "<<_trunc<<"\n"; if (_trunc == 0. && beta <= 1.1) throw SBError("Moffat profiles with beta <= 1.1 must be truncated."); if (_trunc < 0.) throw SBError("Invalid negative truncation radius provided to SBMoffat."); // First, relation between FWHM and rD: double FWHMrD = 2.* std::sqrt(std::pow(2., 1./_beta)-1.); xdbg<<"FWHMrD = "<<FWHMrD<<"\n"; // Set size of this instance according to type of size given in constructor: switch (rType) { case FWHM: _rD = size / FWHMrD; break; case HALF_LIGHT_RADIUS: { _re = size; // This is a bit complicated, so break it out into its own function. _rD = MoffatCalculateScaleRadiusFromHLR(_re,_trunc,_beta); } break; case SCALE_RADIUS: _rD = size; break; default: throw SBError("Unknown SBMoffat::RadiusType"); } _rD_sq = _rD * _rD; _inv_rD = 1./_rD; _inv_rD_sq = _inv_rD*_inv_rD; if (_trunc > 0.) { _maxRrD = _trunc * _inv_rD; xdbg<<"maxRrD = "<<_maxRrD<<"\n"; // Analytic integration of total flux: _fluxFactor = 1. - std::pow( 1+_maxRrD*_maxRrD, (1.-_beta)); } else { _fluxFactor = 1.; // Set maxRrD to the radius where missing fractional flux is xvalue_accuracy // (1+R^2)^(1-beta) = xvalue_accuracy _maxRrD = std::sqrt(std::pow(this->gsparams->xvalue_accuracy, 1. / (1. - _beta))- 1.); xdbg<<"Not truncated. Calculated maxRrD = "<<_maxRrD<<"\n"; } _FWHM = FWHMrD * _rD; _maxR = _maxRrD * _rD; _maxR_sq = _maxR * _maxR; _maxRrD_sq = _maxRrD * _maxRrD; _norm = _flux * (_beta-1.) / (M_PI * _fluxFactor * _rD_sq); _knorm = _flux; dbg << "Moffat rD " << _rD << " fluxFactor " << _fluxFactor << " norm " << _norm << " maxR " << _maxR << std::endl; if (std::abs(_beta-1) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_1; else if (std::abs(_beta-1.5) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_15; else if (std::abs(_beta-2) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_2; else if (std::abs(_beta-2.5) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_25; else if (std::abs(_beta-3) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_3; else if (std::abs(_beta-3.5) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_35; else if (std::abs(_beta-4) < this->gsparams->xvalue_accuracy) _pow_beta = &SBMoffatImpl::pow_4; else _pow_beta = &SBMoffatImpl::pow_gen; if (_trunc > 0.) _kV = &SBMoffatImpl::kV_trunc; else if (std::abs(_beta-1.5) < this->gsparams->kvalue_accuracy) _kV = &SBMoffatImpl::kV_15; else if (std::abs(_beta-2) < this->gsparams->kvalue_accuracy) _kV = &SBMoffatImpl::kV_2; else if (std::abs(_beta-2.5) < this->gsparams->kvalue_accuracy) _kV = &SBMoffatImpl::kV_25; else if (std::abs(_beta-3) < this->gsparams->kvalue_accuracy) { _kV = &SBMoffatImpl::kV_3; _knorm /= 2.; } else if (std::abs(_beta-3.5) < this->gsparams->kvalue_accuracy) { _kV = &SBMoffatImpl::kV_35; _knorm /= 3.; } else if (std::abs(_beta-4) < this->gsparams->kvalue_accuracy) { _kV = &SBMoffatImpl::kV_4; _knorm /= 8.; } else { _kV = &SBMoffatImpl::kV_gen; _knorm *= 4. / (boost::math::tgamma(beta-1.) * std::pow(2.,beta)); } }