view src/ddm.cpp @ 40:eaa99e09607d

Remove indentation due to namespace scope
author Jordi Gutiérrez Hermoso <jordigh@gmail.com>
date Fri, 12 Mar 2010 09:21:07 -0600
parents e32da6c38b59
children
line wrap: on
line source

#include <map>
#include <set>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "include/ddm.hpp"
#include "include/bvp.hpp"
#include "include/rbf.hpp"
#include "include/interpolator.hpp"
#include "include/utils.hpp"
#include "include/error.hpp"

//debug
#include <iostream>

namespace kwantix
{
using namespace std;
using kwantix::vector;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
  
//************* ddm_bdry_diff_op stuff *****************

ddm_bdry_diff_op::ddm_bdry_diff_op(shared_ptr<const bdry_diff_op> B_in, 
                                   shared_ptr<const bdry_diff_op> Bprime_in,
                                   const set<point>& ibps) 
{
  B = B_in ;
  intr_bdry_pts = ibps;
  Bprime = Bprime_in;
}


double ddm_bdry_diff_op::at(const realfunc &f, const point &p) const
{
  if( kwantix::contains(intr_bdry_pts, p))
    return Bprime -> at(f,p);
    
  return B -> at(f,p);
}
  
double ddm_bdry_diff_op::at(const realfunc &f, const point &p, 
                            const vector &n) const
{
  if( kwantix::contains(intr_bdry_pts, p))     
    return Bprime -> at(f,p,n);
    
  return B -> at(f,p,n);
}
  
// ************ ddm stuff ******************************

ddm::ddm(const set<shared_ptr<const domain> >& ds, 
         shared_ptr<const BVP> thebvp)
{
  //Gotta check this is actually a domain decomposition...
  set<point> union_interior;
  set<point> union_boundary;

  for(auto i = ds.begin(); i != ds.end(); i++)
  {
    set<point> intr = (*i) -> get_interior();
    set<point> bdry = (*i) -> get_boundary();
    union_interior.insert(intr.begin(), intr.end());
    union_boundary.insert(bdry.begin(), bdry.end());
  }

  bvp = thebvp;
  shared_ptr<const domain> Omega = bvp -> get_domain();
  set<point> interior = Omega -> get_interior();
  set<point> boundary = Omega -> get_boundary();

  if( interior != union_interior)
  {
    badArgument exc;
    exc.reason = 
      "Bad argument in domain decomposition method constructor: \n"
      "The union of the interior of the proposed domains does not \n"
      "equal the interior of the domain.";
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }

  if(!kwantix::includes(boundary,union_boundary) )
  {
    badArgument exc;
    exc.reason = 
      "Bad argument in domain decomposition method constructor: \n"
      "The union of the boundary of the proposed domains does not \n"
      "contain the boundary of the  domain.";
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }
    
  domains = ds;
  tolerance = 1e-6;

}

ddm::~ddm()
{
  //Nothing!
}

void ddm::set_tolerance(double tol)
{
  tolerance = tol;    
}

double ddm::operator()(const point& p) const
{
  return at(p);
}

// ************** additive_schwarz_ddm stuff *************************

template <typename RBF> additive_schwarz_ddm<RBF>::
additive_schwarz_ddm(const set<shared_ptr<const domain> >& ds, 
                     const shared_ptr<const linear_BVP2> thebvp) : 
  ddm(ds,thebvp)
{

  shared_ptr<const linear_diff_op2> 
    L = dynamic_pointer_cast<
    const linear_diff_op2>(this -> bvp -> get_diff_op());

  shared_ptr<const bdry_diff_op> 
    B = this -> bvp -> get_bdry_diff_op();
    
  map<point, double> global_f = this -> bvp -> get_f();
  map<point, double> global_g = this -> bvp -> get_g();
    
  //Define a bvp for each domain and assign it an interpolator. 
  for(auto  i = this -> domains.begin(); i != this -> domains.end(); i++)
  {
    shared_ptr<const overlapping_domain> this_domain =
      dynamic_pointer_cast<const overlapping_domain>(*i);
      
    //Define the interiors and f's
    set<point> this_intr = this_domain -> get_interior();
    map<point, double> this_f;
    for(auto spi = this_intr.begin(); spi != this_intr.end(); spi++)
      this_f[*spi] = global_f[*spi];

    //Define the boundaries and g's
    set<point> this_bdry = this_domain -> get_boundary();
    map<point, double> this_g;
    set<point> interior_boundary_pts;
    for(auto spi = this_bdry.begin(); spi != this_bdry.end(); spi++)
    {
      if(this_domain -> which_domain(*spi).get() == 0)
      {
        this_g[*spi] = global_g[*spi]; 
      }
      else
      {
        interior_boundary_pts.insert(*spi);
        this_g[*spi] = 0; //Init to zero artificial boundary conditions. 
      }
    }

    //Define the boundary operator, using Dirichlet for artificial
    //boundaries
    shared_ptr<dirichlet_op> D(new dirichlet_op);
    shared_ptr<ddm_bdry_diff_op> 
      this_B( new ddm_bdry_diff_op(B, D, interior_boundary_pts) );
      
    //Put all this info into an interpolator.
    shared_ptr<linear_BVP2>
      this_bvp(new linear_BVP2(this_domain, L, this_B, this_f, this_g) );
    shared_ptr<interpolator<RBF> > 
      rbf_ptr( new interpolator<RBF>(this_bvp) );
    phis[this_domain] = rbf_ptr;
  }
    
  //Apply actual additive Schwarz DDM algorithm here
  solve();
}

template <typename RBF> 
double additive_schwarz_ddm<RBF>::at(const point& p) const
{
  //If p is one of the domain points, then it's used. If more than
  //one domain contains p, then the average is taken. If no domain
  //contains p, we exhaustively search for the closest point in the
  //domains and use the interpolator from that domain to evaluate.
   
  set<shared_ptr<const interpolator<RBF> > >
    relevant_interpolators = which_interps(p);
    
  if(relevant_interpolators.size() != 0)//p is in one of the
    //domains.
    return avg_interp(relevant_interpolators, p);
    
    
  //Else, p is not one of the grid points. Find closest grid point
  //and evaluate on the domain(s) that the point belongs to.

    
  //Uh, just begin with closest being some point in the whatever
  //domain. 
  point c = *(((*( this->domains.begin() )) 
               -> get_interior()).begin() );

  //Search each domain's interior and boundary. Can't do better than
  //exhaustive search. 
  for(auto i = this -> domains.begin(); i != this -> domains.end(); i++)
  {
    for(set<point>::iterator j = (*i) -> get_interior().begin();
        j != (*i) -> get_interior().end(); j++)
      if( norm(*j - p) < norm(c - p))
        c = *j;

    for(set<point>::iterator j = (*i) -> get_boundary().begin();
        j != (*i) -> get_boundary().end(); j++)
      if( norm(*j - p) < norm(c - p))
        c = *j;
  }
    
  return avg_interp( which_interps(c), p);
    
}

template <typename RBF>
set<shared_ptr<const interpolator<RBF> > > additive_schwarz_ddm<RBF>::
which_interps(const point& p) const
{
  set<shared_ptr<const interpolator<RBF> > > relevant_interpolators;
  for(set<shared_ptr<const domain> >::iterator i = this -> domains.begin();
      i != this -> domains.end(); i++){
    if( (*i) -> contains(p)){
      shared_ptr<const overlapping_domain> j = 
        dynamic_pointer_cast<const overlapping_domain>(*i);
      relevant_interpolators.insert(phis.at(j)); 
      //at is not in current STL standard; but it is necessary here
      //because operator[] can't be used here since this is a const
      //function. at is currently a GNU extension, and we're using
      //it.
    }
  }
  return relevant_interpolators;
}

template <typename RBF>
double additive_schwarz_ddm<RBF>::
avg_interp(set<shared_ptr<const interpolator<RBF> > > relevant_interpolators,
           const point& p) const
{
  double result = 0;
  int n = 0;
  for(typename set<shared_ptr<const interpolator<RBF> > >::
        iterator i = relevant_interpolators.begin() ;
      i != relevant_interpolators.end(); i++){
    result += (*i) -> at(p);
    n++;
  }
  return result/n;
}

template <typename RBF> 
void additive_schwarz_ddm<RBF>::solve()
{
  //Recap: each domain already has an interpolator associated to it
  //and all overlapping domain information has been defined. Just
  //need to iterate on the boundary conditions. Method converges
  //when 2-norm (Frobenius, if it were a matrix) of the artificial
  //boundary does not change by more than tolerance.
  using std::make_pair;

  vector newv = at_all_points();
  vector oldv(newv.size());
  double change;
  do{
    oldv = newv;

    //Each domain will have new g's.
    map<shared_ptr<const overlapping_domain>, map<point, double> > 
      new_bdry_assts;
    for(set<shared_ptr<const domain> >::iterator 
          i = this -> domains.begin(); i != this -> domains.end(); i++){
      shared_ptr<const overlapping_domain> d = 
        dynamic_pointer_cast<const overlapping_domain>(*i);
      for(set<point>::iterator j = (*i) -> get_boundary().begin();
          j != (*i) -> get_boundary().end(); j++)
        if( d -> which_domain(*j).get() != 0){


          new_bdry_assts[d].
            insert(make_pair(*j, 
                             phis[d -> which_domain(*j)] -> at(*j))
                   );
        }
    }

    //Now assign to each interpolator the modified boundary.
    for(typename map<shared_ptr<const overlapping_domain>,
          shared_ptr<interpolator<RBF> > >::
          iterator i = phis.begin(); i != phis.end(); i++)
      i -> second -> set_g(new_bdry_assts[i -> first]);
      
    newv = at_all_points();
    change = norm(oldv-newv);
      
    //debug
    cout << "Change: " << change << endl;
  }while( change > this -> tolerance);
}    

//Evaluate problem at all artificial boundary points.
template <typename RBF> 
vector additive_schwarz_ddm<RBF>::at_all_points() const
{
  set<point> art_bdry;
  for(set<shared_ptr<const domain> >::const_iterator i = 
        this -> domains.begin(); i != this -> domains.end(); i++)
  {
    shared_ptr<const overlapping_domain> j = 
      dynamic_pointer_cast<const overlapping_domain>(*i),
      zero; //The zero pointer.

    for(set<point>::iterator p = j -> get_boundary().begin();
        p != j -> get_boundary().end(); p++)
    {
      if(j -> which_domain(*p) != zero)
        art_bdry.insert(*p);
    }
  }

  set<point>::iterator I = art_bdry.begin();
  vector result(art_bdry.size() );
  for(size_t i = 1; i <= result.size(); i++){
    result(i) = at(*I);
    I++;
  }
  return result;
}

//************** overlapping_domain stuff *************
 

overlapping_domain::
overlapping_domain(string intr, string bdry, string ns)
  :domain(intr,bdry,ns)
{
} 

overlapping_domain::
overlapping_domain(string intr, string bdry, string ns, 
                   const set<shared_ptr<const overlapping_domain> >& ols,
                   const map<point, 
                   shared_ptr<const overlapping_domain> >& bdry_asst)
  :domain(intr,bdry,ns)
{
  set<point> bdry_copy = get_boundary();
  for(map<point, shared_ptr<const overlapping_domain> >::const_iterator 
        i = bdry_asst.begin(); i != bdry_asst.end(); i++)
  {
    if(!kwantix::contains(ols, i->second) or 
       !kwantix::contains(bdry_copy, i->first)){
      badArgument exc;
      exc.reason = 
        "Bad argument in overlapping_domain constructor: \n"
        "Every boundary assignment must be a boundary point \n"
        "assigned to some overlapping domain.";
      exc.line = __LINE__;
      exc.file = __FILE__;
      throw exc;
    }
  }
  overlappers = ols;
  boundary_assignments = bdry_asst;
}

overlapping_domain::overlapping_domain(size_t dimension) 
  :domain(dimension){}
overlapping_domain::
overlapping_domain(size_t dimension, set<point> intr, 
                   set<point> bdry, map<point, vector> ns)
  :domain(dimension, intr, bdry, ns){}
    
    
set<shared_ptr<const overlapping_domain> > 
overlapping_domain::get_domains() const
{
  return overlappers;
}

shared_ptr<const overlapping_domain>
overlapping_domain::which_domain(const point& p) const
{
  if( !kwantix::contains(boundary_assignments, p) )
  {
    shared_ptr<const overlapping_domain> zero;
    return zero;
  }
  return boundary_assignments.at(p);
}
  
void
overlapping_domain::
set_overlapper_info(const point& p, const shared_ptr<overlapping_domain> o)
{
  if(kwantix::contains(this -> get_boundary(), p))
    boundary_assignments[p] = o;     
}

//************** friends of overlapping_domain *******************

void 
set_overlapper_info(set<shared_ptr<overlapping_domain> > domains)
{
  for(auto d = domains.begin(); d != domains.end(); d++)
  {
    for(auto p = (*d) -> get_boundary().begin();
        p != (*d) -> get_boundary().end(); p++)
    {
      for(auto d_other = domains.begin(); d_other != domains.end(); d_other++)
      {
        if(
           kwantix::contains((*d_other ) -> get_interior(), *p)
           ){
          (*d) -> boundary_assignments[*p] = *d_other;
          (*d) -> overlappers.insert(*d_other);
          break; //FIXME: We're assuming no three domains overlap at
          //one point.
        }
      }
    }
  }
}


//Instantiations
template class additive_schwarz_ddm<piecewise_polynomial>;
template class additive_schwarz_ddm<thin_plate_spline>;
template class additive_schwarz_ddm<multiquadric>;
template class additive_schwarz_ddm<inverse_multiquadric>;
template class additive_schwarz_ddm<inverse_quadratic>;
template class additive_schwarz_ddm<gaussian>;
}