Advertisement
bolverk

snr_smbh_dust_cooling

May 4th, 2015
501
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 35.25 KB | None | 0 0
  1. #include <iostream>
  2. #include <cmath>
  3. #include <limits>
  4. #include "source/tessellation/static_voronoi_mesh.hpp"
  5. #include "source/newtonian/two_dimensional/hdsim2d.hpp"
  6. #include "source/newtonian/two_dimensional/interpolations/pcm2d.hpp"
  7. #include "source/newtonian/two_dimensional/spatial_distributions/uniform2d.hpp"
  8. #include "source/newtonian/common/ideal_gas.hpp"
  9. #include "source/newtonian/two_dimensional/geometric_outer_boundaries/SquareBox.hpp"
  10. #include "source/newtonian/two_dimensional/hydro_boundary_conditions/RigidWallHydro.hpp"
  11. #include "source/newtonian/common/hllc.hpp"
  12. #include "source/newtonian/two_dimensional/source_terms/zero_force.hpp"
  13. #include "source/newtonian/two_dimensional/point_motions/eulerian.hpp"
  14. #include "source/newtonian/two_dimensional/point_motions/lagrangian.hpp"
  15. #include "source/newtonian/two_dimensional/point_motions/eulerian.hpp"
  16. #include "source/newtonian/two_dimensional/point_motions/round_cells.hpp"
  17. #include "source/newtonian/two_dimensional/diagnostics.hpp"
  18. #include "source/newtonian/two_dimensional/source_terms/cylindrical_complementary.hpp"
  19. #include "source/newtonian/two_dimensional/source_terms/CenterGravity.hpp"
  20. #include "source/newtonian/two_dimensional/source_terms/SeveralSources.hpp"
  21. #include "source/misc/simple_io.hpp"
  22. #include "source/newtonian/test_2d/main_loop_2d.hpp"
  23. #include "source/newtonian/test_2d/kill_switch.hpp"
  24. #include "source/newtonian/two_dimensional/hdf5_diagnostics.hpp"
  25. #include "source/misc/mesh_generator.hpp"
  26. #include "source/tessellation/RoundGrid.hpp"
  27. #include "source/misc/int2str.hpp"
  28. #include "source/newtonian/two_dimensional/interpolations/linear_gauss_consistent.hpp"
  29. #include "source/newtonian/test_2d/consecutive_snapshots.hpp"
  30. #include <boost/foreach.hpp>
  31. #include "source/newtonian/two_dimensional/hydro_boundary_conditions/FreeFlow.hpp"
  32. #include "source/misc/utils.hpp"
  33. #include "source/tessellation/shape_2d.hpp"
  34. #include "source/newtonian/test_2d/piecewise.hpp"
  35. #include "source/mpi/MeshPointsMPI.hpp"
  36. #include "source/mpi/mpi_macro.hpp"
  37. #include "source/mpi/ConstNumberPerProc.hpp"
  38. #include "source/misc/hdf5_utils.hpp"
  39. #include "source/newtonian/test_2d/contour.hpp"
  40. #include "source/newtonian/two_dimensional/custom_evolutions/Ratchet.cpp"
  41. #include "source/newtonian/two_dimensional/diagnostics.hpp"
  42. #include "source/newtonian/two_dimensional/simple_flux_calculator.hpp"
  43. #include "source/newtonian/two_dimensional/simple_cell_updater.hpp"
  44. #include "source/misc/vector_initialiser.hpp"
  45. #include "source/misc/horner.hpp"
  46. #include <fenv.h>
  47.  
  48. using namespace std;
  49. using namespace simulation2d;
  50.  
  51. namespace {
  52.  
  53.   /*
  54.     class CellEdgesGetter: public LazyList<Edge>
  55.     {
  56.     public:
  57.     CellEdgesGetter(const Tessellation& tess,
  58.     const int n):
  59.     tess_(tess), edge_indices_(tess_.GetCellEdges(n)) {}
  60.  
  61.     size_t size(void) const
  62.     {
  63.     return edge_indices_.size();
  64.     }
  65.  
  66.     Edge operator[](size_t i) const
  67.     {
  68.     return tess_.GetEdge(edge_indices_[i]);
  69.     }
  70.  
  71.     private:
  72.     const Tessellation& tess_;
  73.     const vector<int> edge_indices_;
  74.     };
  75.   */
  76.  
  77.   class WriteConserved: public DiagnosticFunction
  78.   {
  79.   public:
  80.  
  81.     WriteConserved(const string& fname):
  82.       cons_(), fname_(fname) {}
  83.  
  84.     void operator()(const hdsim& sim)
  85.     {
  86.       Extensive buf;
  87.       buf.mass = 0;
  88.       buf.momentum.x = 0;
  89.       buf.momentum.y = 0;
  90.       buf.energy = 0;
  91.       buf.tracers["ejecta"] = 0;
  92.       for(size_t i=0;i<sim.getAllExtensives().size();++i){
  93.     if(sim.getAllCells()[i].stickers.find("dummy")->second){
  94.       const Extensive& temp = sim.getAllExtensives()[i];
  95.       buf.mass += temp.mass;
  96.       buf.momentum += temp.momentum;
  97.       buf.energy += temp.energy;
  98.       buf.tracers["ejecta"] += temp.tracers.find("ejecta")->second;
  99.     }
  100.       }
  101.       cons_.push_back(buf);
  102.       time_.push_back(sim.getTime());
  103.     }
  104.  
  105.     ~WriteConserved(void)
  106.     {
  107.       ofstream f(fname_.c_str());
  108.       for(size_t i=0;i<cons_.size();++i)
  109.     f << time_[i] << " "
  110.       << cons_[i].mass << " "
  111.       << cons_[i].momentum.x << " "
  112.       << cons_[i].momentum.y << " "
  113.       << cons_[i].energy << " "
  114.       << cons_[i].tracers.find("ejecta")->second << "\n";
  115.       f.close();
  116.     }
  117.  
  118.   private:
  119.     mutable vector<Extensive> cons_;
  120.     mutable vector<double> time_;
  121.     const string fname_;
  122.   };
  123.  
  124.   class Constants
  125.   {
  126.   public:
  127.  
  128.     // Metric prefix
  129.     const double kilo;
  130.     const double centi;
  131.  
  132.     // Length / distance
  133.     const double parsec;
  134.     const double meter;
  135.  
  136.     // Time
  137.     const double year;
  138.     const double second;
  139.  
  140.     // Mass
  141.     const double solar_mass;
  142.     const double gram;
  143.  
  144.     // Energy
  145.     const double erg;
  146.  
  147.     // Force
  148.     const double newton;
  149.  
  150.     // Parameters
  151.     const double proton_mass;
  152.     const double black_hole_mass;
  153.     const double zeta;
  154.     const double adiabatic_index;
  155.     const double gravitation_constant;
  156.     const double rho_0;
  157.     const double R_0;
  158.     const double omega_in;
  159.     const double inner_density_prefactor;
  160.     const double R_b;
  161.     const double omega_out;
  162.     const double outer_density_prefactor;
  163.     const double offset;
  164.     const double supernova_energy;
  165.     const double supernova_radius;
  166.     const double supernova_volume;
  167.     const double supernova_mass;
  168.     const double supernova_density;
  169.     const double supernova_pressure;
  170.     const Vector2D lower_left;
  171.     const Vector2D upper_right;
  172.  
  173.     Constants(void):
  174.       kilo(1e3),
  175.       centi(1e-2),
  176.       parsec(1),
  177.       meter(3.24e-17*parsec),
  178.       year(1),
  179.       second(year/3.16e7),
  180.       solar_mass(1),
  181.       gram(5.03e-34*solar_mass),
  182.       erg(gram*pow(centi*meter/second,2)),
  183.       newton(kilo*gram*meter/pow(second,2)),
  184.       proton_mass(1.67e-24*gram),
  185.       black_hole_mass(1e7*solar_mass),
  186.       zeta(pow(black_hole_mass/(4.3e6*solar_mass),7./15.)),
  187.       adiabatic_index(5./3.),
  188.       gravitation_constant(6.673e-11*newton*pow(meter/(kilo*gram),2)),
  189.       rho_0(2.2e-22*gram/pow(centi*meter,3)),
  190.       R_0(0.04*parsec*zeta),
  191.       omega_in(1),
  192.       inner_density_prefactor(rho_0*pow(R_0,omega_in)),
  193.       R_b(0.4*parsec*zeta),
  194.       omega_out(3),
  195.       outer_density_prefactor
  196.       (inner_density_prefactor*pow(R_b,omega_out-omega_in)),
  197.       offset(0.6*R_b),
  198.       supernova_energy(1e51*erg),
  199.       supernova_radius(0.1*offset),
  200.       supernova_volume((4.*M_PI/3)*pow(supernova_radius,3)),
  201.       supernova_mass(5*solar_mass),
  202.       supernova_density(supernova_mass/supernova_volume),
  203.       supernova_pressure((adiabatic_index-1)*supernova_energy/supernova_volume),
  204.       lower_left(parsec*Vector2D(0,-40)),
  205.       upper_right(parsec*Vector2D(40,40)) {}
  206.   };
  207.  
  208.   #ifdef BLA_BLA_BLA
  209.   class RadiativeCooling: public SourceTerm
  210.   {
  211.   public:
  212.  
  213.     RadiativeCooling(const double particle_mass,
  214.              const double boltzmann_constant,
  215.              const double cooling_coefficient):
  216.       particle_mass_(particle_mass),
  217.       boltzmann_constant_(boltzmann_constant),
  218.       cooling_coefficient_(cooling_coefficient) {}
  219.  
  220.     vector<Extensive> operator()
  221.     (const Tessellation& tess,
  222.      const PhysicalGeometry& /*pg*/,
  223.      const CacheData& cd,
  224.      const vector<ComputationalCell>& cells,
  225.      const vector<Extensive>& /*fluxes*/,
  226.      const vector<Vector2D>& /*point_velocities*/,
  227.      const double /*time*/) const
  228.     {
  229.       vector<Extensive> res(static_cast<size_t>(tess.GetPointNo()));
  230.       for(size_t i=0;i<res.size();++i){
  231.     res[i].mass = 0;
  232.     res[i].momentum.x = 0;
  233.     res[i].momentum.y = 0;
  234.     res[i].energy =
  235.       calcTemperature(cells[i].density, cells[i].pressure) > 5e4 ?
  236.       -cd.volumes[i]*calcEmissivity(cells[i].density,
  237.                     cells[i].pressure) : 0;
  238.       }
  239.       return res;
  240.     }
  241.  
  242.   private:
  243.     double calcTemperature(double density, double pressure) const
  244.     {
  245.       return particle_mass_*(pressure/density)/boltzmann_constant_;
  246.     }
  247.  
  248.     double calcEmissivity(double density, double /*pressure*/) const
  249.     {
  250.       //      const double T = calcTemperature(density, pressure);
  251.       const double n = (density/particle_mass_);
  252.       return cooling_coefficient_*pow(n,2);
  253.     }
  254.  
  255.     const double particle_mass_;
  256.     const double boltzmann_constant_;
  257.     const double cooling_coefficient_;
  258.   };
  259. #endif
  260.  
  261.   class RadiativeCooling2: public Manipulate
  262.   {
  263.  
  264.   public:
  265.  
  266.     RadiativeCooling2(const double particle_mass,
  267.               const double boltzmann_constant,
  268.               const double T_min,
  269.               const double T_max,
  270.               const vector<double>& coefs,
  271.               const double cfu):
  272.       particle_mass_(particle_mass),
  273.       boltzmann_constant_(boltzmann_constant),
  274.       T_min_(T_min), T_max_(T_max), coefs_(coefs),
  275.       cfu_(cfu), t_prev_(0)
  276.     {
  277.       /*
  278.       const vector<double> log_temperature_list = linspace(log(1e5), log(1e10), 1000);
  279.       vector<double> cooling_function(log_temperature_list.size(),0);
  280.       for(size_t i=0;i<cooling_function.size();++i)
  281.     cooling_function[i] = exp(horner(coefs_,min(log_temperature_list[i],
  282.                             log(T_max_))));
  283.       ofstream f("cooling_function.txt");
  284.       for(size_t i=0;i<log_temperature_list.size();++i)
  285.     f << exp(log_temperature_list[i]) << " " << cfu_*cooling_function[i] << endl;
  286.       f.close();
  287.       int buf = 0;
  288.       cout << "type 0 to stop" << endl;
  289.       cin >> buf;
  290.       assert(buf!=0);
  291.       */
  292.     }
  293.  
  294.     void operator()(hdsim& sim)
  295.     {
  296.       const double dt = sim.getTime() - t_prev_;
  297.       t_prev_ = sim.getTime();
  298.       vector<ComputationalCell>& cells = sim.getAllCells();
  299.       for(size_t i=0;i<cells.size();++i){
  300.     ComputationalCell& cell = cells[i];
  301.     const double n = cell.density/particle_mass_;
  302.     const double temperature = min(T_max_, cell.pressure/(boltzmann_constant_*n));
  303.     if(temperature<T_min_)
  304.       continue;
  305.     const double min_pressure = T_min_*boltzmann_constant_*n;
  306.     const double cooling_coefficient = cfu_*exp(horner(coefs_,log(temperature)));
  307.     cell.pressure =
  308.       max(min_pressure,cell.pressure-dt*cooling_coefficient*pow(n,2));
  309.     cell.tracers["entropy"] = sim.getEos().dp2s(cell.density,
  310.                               cell.pressure);
  311.       }
  312.       sim.recalculateExtensives();
  313.     }
  314.  
  315.   private:
  316.     const double particle_mass_;
  317.     const double boltzmann_constant_;
  318.     const double T_min_;
  319.     const double T_max_;
  320.     const vector<double> coefs_;
  321.     const double cfu_;
  322.     mutable double t_prev_;
  323.   };
  324.  
  325.   /*
  326.     class Bremsstrahlung: public DiagnosticFunction
  327.     {
  328.     public:
  329.  
  330.     Bremsstrahlung(const string& fname, const Constants& c):
  331.     fname_(fname),
  332.     // Coefficient is taken from Rybicki and Lightman 1979, page 162 equation 5.15b
  333.     boltzmann_constant_(1.38e-16*c.erg),
  334.     coefficient_(1.4e-27*c.erg/pow(c.centi*c.meter,3)/c.second),
  335.     particle_mass_(1.67e-24*c.gram),
  336.     cc_(pow(c.centi*c.meter,3)) {}
  337.  
  338.     void operator()(const hdsim& sim)
  339.     {
  340.     double total_luminosity = 0;
  341.     const PhysicalGeometry& pg = sim.getPhysicalGeometry();
  342.     const Tessellation& tess = sim.getTessellation();
  343.     for(int i=0;i<static_cast<int>(sim.getAllCells().size());++i)
  344.     total_luminosity += pg.calcVolume(serial_generate(CellEdgesGetter(tess,i)))*
  345.     calcEmissivity(sim.getAllCells()[static_cast<size_t>(i)].density,
  346.     sim.getAllCells()[static_cast<size_t>(i)].pressure);
  347.     time_luminosity_.push_back(std::pair<double,double>(sim.getTime(),total_luminosity));
  348.     }
  349.  
  350.     ~Bremsstrahlung(void)
  351.     {
  352.     ofstream f(fname_.c_str());
  353.     for(size_t i=0;i<time_luminosity_.size();++i)
  354.     f << time_luminosity_[i].first << " "
  355.     << time_luminosity_[i].second << endl;
  356.     f.close();
  357.     }
  358.  
  359.     private:
  360.  
  361.     double calcTemperature(double density, double pressure)
  362.     {
  363.     return particle_mass_*(pressure/density)/boltzmann_constant_;
  364.     }
  365.  
  366.     double calcEmissivity(double density, double pressure)
  367.     {
  368.     const double T = calcTemperature(density, pressure);
  369.     const double n = (density/particle_mass_)*cc_;
  370.     return coefficient_*sqrt(T)*pow(n,2);
  371.     }
  372.  
  373.     const string fname_;
  374.     const double boltzmann_constant_;
  375.     const double coefficient_;
  376.     const double particle_mass_;
  377.     const double cc_;
  378.     mutable vector<std::pair<double,double> > time_luminosity_;
  379.     };
  380.   */
  381.  
  382.   class HydrostaticPressure: public SpatialDistribution
  383.   {
  384.   public:
  385.  
  386.     HydrostaticPressure(double gm,
  387.             double r0,
  388.             double rho_0,
  389.             double omega_1,
  390.             double R_b,
  391.             double omega_2):
  392.       gm_(gm), r0_(r0), rho_0_(rho_0),
  393.       omega_1_(omega_1), R_b_(R_b), omega_2_(omega_2) {}
  394.  
  395.     double operator()(const Vector2D& r) const
  396.     {
  397.       if(abs(r)<R_b_)
  398.     return (gm_/r0_)*(rho_0_/(omega_1_+1))*pow(abs(r)/r0_,-omega_1_-1)-
  399.       (gm_/r0_)*(rho_0_/(omega_1_+1))*pow(R_b_/r0_,-omega_1_-1)+
  400.       (gm_/r0_)*(rho_0_/(omega_2_+1))*pow(R_b_/r0_,-omega_1_-1);
  401.       else
  402.     return (gm_/r0_)*(rho_0_/(omega_2_+1))*
  403.       pow(R_b_/r0_,-omega_1_-1)*
  404.       pow(abs(r)/R_b_,-omega_2_-1);
  405.     }
  406.  
  407.   private:
  408.     const double gm_;
  409.     const double r0_;
  410.     const double rho_0_;
  411.     const double omega_1_;
  412.     const double R_b_;
  413.     const double omega_2_;
  414.   };
  415.  
  416.   class BoundedRadialPowerLaw: public SpatialDistribution
  417.   {
  418.   public:
  419.  
  420.     BoundedRadialPowerLaw(const Vector2D& center,
  421.               double index,
  422.               double prefactor,
  423.               double lower_bound,
  424.               double upper_bound,
  425.               double min_val):
  426.       center_(center),
  427.       index_(index),
  428.       prefactor_(prefactor),
  429.       lower_bound_(lower_bound),
  430.       upper_bound_(upper_bound),
  431.       min_val_(min_val) {}
  432.  
  433.     double operator()(const Vector2D& r) const
  434.     {
  435.       const double radius = max(min(abs(r-center_),
  436.                     upper_bound_),
  437.                 lower_bound_);
  438.       return max(min_val_,
  439.          prefactor_*pow(radius,index_));
  440.     }
  441.  
  442.   private:
  443.     const Vector2D center_;
  444.     const double index_;
  445.     const double prefactor_;
  446.     const double lower_bound_;
  447.     const double upper_bound_;
  448.     const double min_val_;
  449.   };
  450.  
  451.   bool is_inside_rectangle(const Vector2D& point,
  452.                const Vector2D& lower_left,
  453.                const Vector2D& upper_right)
  454.   {
  455.     return ((point.x > lower_left.x) &&
  456.         (point.x < upper_right.x) &&
  457.         (point.y > lower_left.y) &&
  458.         (point.y < upper_right.y));
  459.   }
  460.  
  461.   vector<Vector2D> rectangle_clip(const vector<Vector2D>& grid,
  462.                   const Vector2D& lower_left,
  463.                   const Vector2D& upper_right)
  464.   {
  465.     vector<Vector2D> res;
  466.     for(size_t i=0, endp=grid.size();i<endp;++i){
  467.       const Vector2D& point = grid[i];
  468.       if(is_inside_rectangle(point,lower_left,upper_right))
  469.     res.push_back(point);    
  470.     }
  471.     return res;
  472.   }
  473.  
  474.   /*
  475.     vector<double> calc_radius_list(void)
  476.     {
  477.     const double rmin = 1e-4;
  478.     const double q = 1.01;
  479.     const size_t n = 200;
  480.     vector<double> res(n);
  481.     for(size_t i=0;i<n;++i)
  482.     res[i] = rmin*pow(q,i);
  483.     write_number(0,"finished_calc_radius_list.txt");
  484.     return res;
  485.     }
  486.   */
  487.  
  488.   /*
  489.     vector<Vector2D> create_grid(Vector2D const& lower_left,
  490.     Vector2D const& upper_right,
  491.     double dx2x)
  492.     {
  493.     vector<Vector2D> res;
  494.     for(double x = lower_left.x*(1+0.5*dx2x);
  495.     x<upper_right.x; x*=(1+dx2x)){
  496.     const double dx = x*dx2x;
  497.     for(double y=lower_left.y+dx/2;
  498.     y<upper_right.y; y += dx)
  499.     res.push_back(Vector2D(x,y));
  500.     }
  501.     return res;
  502.     }
  503.   */
  504.  
  505.   vector<Vector2D> centered_hexagonal_grid(double r_min,
  506.                        double r_max)
  507.   {
  508.     const vector<double> r_list = arange(0,r_max,r_min);
  509.     vector<Vector2D> res;
  510.     for(size_t i=0;i<r_list.size();++i){
  511.       const size_t angle_num = max<size_t>(6*i,1);
  512.       vector<double> angle_list(angle_num,0);
  513.       for(size_t j=0;j<angle_num;++j)
  514.     angle_list.at(j) = 2*M_PI*static_cast<double>(j)/static_cast<double>(angle_num);
  515.       for(size_t j=0;j<angle_num;++j)
  516.     res.push_back(r_list.at(i)*Vector2D(cos(angle_list.at(j)),
  517.                         sin(angle_list.at(j))));
  518.     }
  519.     return res;
  520.   }
  521.  
  522.   vector<Vector2D> centered_logarithmic_spiral(double r_min,
  523.                            double r_max,
  524.                            double alpha,
  525.                            const Vector2D& center)
  526.   {
  527.     const double theta_max = log(r_max/r_min)/alpha;
  528.     const vector<double> theta_list =
  529.       arange(0,theta_max,2*M_PI*alpha/(1-0.5*alpha));
  530.  
  531.     vector<double> r_list(theta_list.size(),0);
  532.     for(size_t i=0;i<r_list.size();++i)
  533.       r_list.at(i) = r_min*exp(alpha*theta_list.at(i));
  534.  
  535.     vector<Vector2D> res(r_list.size());
  536.     for(size_t i=0;i<res.size();++i)
  537.       res[i] = center+r_list[i]*Vector2D(cos(theta_list.at(i)),
  538.                      sin(theta_list.at(i)));
  539.     return res;
  540.   }
  541.  
  542.   vector<Vector2D> complete_grid(double r_inner,
  543.                  double r_outer,
  544.                  double alpha)
  545.   {
  546.     const vector<Vector2D> inner =
  547.       centered_hexagonal_grid(r_inner*alpha*2*M_PI,
  548.                   r_inner);
  549.     const vector<Vector2D> outer =
  550.       centered_logarithmic_spiral(r_inner,
  551.                   r_outer,
  552.                   alpha,
  553.                   Vector2D(0,0));
  554.     return join(inner, outer);
  555.   }
  556.  
  557.   /*
  558.   template<class T> class VectorInitializer
  559.   {
  560.   public:
  561.  
  562.     VectorInitializer(const T& t):
  563.       buf_(1,t) {}
  564.  
  565.     VectorInitializer& operator()(const T& t)
  566.     {
  567.       buf_.push_back(t);
  568.       return *this;
  569.     }
  570.  
  571.     vector<T> operator()(void)
  572.     {
  573.       return buf_;
  574.     }
  575.  
  576.   private:
  577.     vector<T> buf_;
  578.   };
  579.   */
  580.  
  581.   vector<ComputationalCell> calc_init_cond(const Tessellation& tess,
  582.                        const Constants& c,
  583.                        const EquationOfState& eos)
  584.   {
  585.     vector<ComputationalCell> res(static_cast<size_t>(tess.GetPointNo()));
  586.     for(size_t i=0;i<res.size();++i){
  587.       const Vector2D r = tess.GetMeshPoint(static_cast<int>(i));
  588.       res[i].density = Piecewise(Circle(Vector2D(0,-c.offset),c.supernova_radius),
  589.                  Uniform2D(c.supernova_density),
  590.                  Piecewise(Circle(Vector2D(0,0), c.R_b),
  591.                        BoundedRadialPowerLaw(Vector2D(0,0),
  592.                                  -c.omega_in,
  593.                                  c.inner_density_prefactor,
  594.                                  1e-6, 50,c.proton_mass/pow(c.centi*c.meter,3)),
  595.                        BoundedRadialPowerLaw(Vector2D(0,0),
  596.                                  -c.omega_out,
  597.                                  c.outer_density_prefactor,
  598.                                  1e-6,50,c.proton_mass/pow(c.centi*c.meter,3))))(r);
  599.       res[i].pressure = Piecewise(Circle(Vector2D(0,-c.offset),c.supernova_radius),
  600.                   Uniform2D(c.supernova_pressure),
  601.                   HydrostaticPressure(c.gravitation_constant*c.black_hole_mass,
  602.                               c.R_0,
  603.                               c.rho_0,
  604.                               c.omega_in,
  605.                               c.R_b,
  606.                               c.omega_out))(r);
  607.       res[i].pressure = Piecewise(Circle(Vector2D(0,-c.offset),c.supernova_radius),
  608.                   Uniform2D(c.supernova_pressure),
  609.                   Uniform2D(1e-15))(r);
  610.       res[i].velocity = Vector2D(0,0);
  611.       res[i].tracers["ejecta"] = Piecewise
  612.     (Circle(Vector2D(0,-c.offset),c.supernova_radius),
  613.      Uniform2D(1), Uniform2D(0))(r);
  614.       res[i].tracers["entropy"] = eos.dp2s
  615.     (res[i].density, res[i].pressure);
  616.       res[i].stickers["dummy"] = Circle(Vector2D(0,0),0.5*c.supernova_radius)(r);
  617.     }
  618.     return res;
  619.   }
  620.  
  621.   double calc_tracer_flux(const Edge& edge,
  622.               const Tessellation& tess,
  623.               const vector<ComputationalCell>& cells,
  624.               const string& name,
  625.               const Conserved& hf)
  626.   {
  627.     if(hf.Mass>0 &&
  628.        edge.neighbors.first>0 &&
  629.        edge.neighbors.first<tess.GetPointNo())
  630.       return hf.Mass*
  631.     cells[static_cast<size_t>(edge.neighbors.first)].tracers.find(name)->second;
  632.     if(hf.Mass<0 &&
  633.        edge.neighbors.second>0 &&
  634.        edge.neighbors.second<tess.GetPointNo())
  635.       return hf.Mass*
  636.     cells[static_cast<size_t>(edge.neighbors.second)].tracers.find(name)->second;
  637.     return 0;    
  638.   }
  639.  
  640.   class SinkFlux: public FluxCalculator
  641.   {
  642.   public:
  643.  
  644.     SinkFlux(const RiemannSolver& rs):
  645.       rs_(rs) {}
  646.  
  647.     vector<Extensive> operator()
  648.     (const Tessellation& tess,
  649.      const vector<Vector2D>& point_velocities,
  650.      const vector<ComputationalCell>& cells,
  651.      const EquationOfState& eos,
  652.      const double /*time*/,
  653.      const double /*dt*/) const
  654.     {
  655.       vector<Extensive> res(tess.getAllEdges().size());
  656.       for(size_t i=0;i<tess.getAllEdges().size();++i){
  657.     const Conserved hydro_flux =
  658.       calcHydroFlux(tess, point_velocities,
  659.             cells, eos, i);
  660.     res[i].mass = hydro_flux.Mass;
  661.     res[i].momentum = hydro_flux.Momentum;
  662.     res[i].energy = hydro_flux.Energy;
  663.     for(std::map<std::string,double>::const_iterator it =
  664.           cells.front().tracers.begin();
  665.         it!=cells.front().tracers.end();++it)
  666.       res[i].tracers[it->first] =
  667.         calc_tracer_flux(tess.getAllEdges()[i],
  668.                  tess,cells,it->first,hydro_flux);
  669.          
  670.       }
  671.       return res;
  672.     }
  673.  
  674.   private:
  675.     const RiemannSolver& rs_;
  676.  
  677.     const Conserved calcHydroFlux
  678.     (const Tessellation& tess,
  679.      const vector<Vector2D>& point_velocities,
  680.      const vector<ComputationalCell>& cells,
  681.      const EquationOfState& eos,
  682.      const size_t i) const
  683.     {
  684.       const Edge& edge = tess.GetEdge(static_cast<int>(i));
  685.       const std::pair<bool,bool> flags
  686.     (edge.neighbors.first>=0 && edge.neighbors.first<tess.GetPointNo(),
  687.      edge.neighbors.second>=0 && edge.neighbors.second<tess.GetPointNo());
  688.       assert(flags.first || flags.second);
  689.       if(!flags.first){
  690.     const size_t right_index =
  691.       static_cast<size_t>(edge.neighbors.second);
  692.     const ComputationalCell& right_cell = cells[right_index];
  693.     if(right_cell.stickers.find("dummy")->second)
  694.       return Conserved();
  695.     const Vector2D p = Parallel(edge);
  696.     const Primitive right = convert_to_primitive(right_cell,eos);
  697.     const Primitive left = reflect(right,p);
  698.     const Vector2D n = remove_parallel_component
  699.       (tess.GetMeshPoint(edge.neighbors.second) -
  700.        edge.vertices.first, p);
  701.     return rotate_solve_rotate_back
  702.       (rs_, left, right, 0, n, p);
  703.       }
  704.       if(!flags.second){
  705.     const size_t left_index =
  706.       static_cast<size_t>(edge.neighbors.first);
  707.     const ComputationalCell& left_cell = cells[left_index];
  708.     if(left_cell.stickers.find("dummy")->second)
  709.       return Conserved();
  710.     const Primitive left = convert_to_primitive(left_cell, eos);
  711.     const Vector2D p = Parallel(edge);
  712.     const Primitive right = reflect(left,p);
  713.     const Vector2D n = remove_parallel_component
  714.       (edge.vertices.second -
  715.        tess.GetMeshPoint(edge.neighbors.first), p);
  716.     return rotate_solve_rotate_back
  717.       (rs_, left, right, 0, n, p);
  718.       }
  719.       const size_t left_index =
  720.     static_cast<size_t>(edge.neighbors.first);
  721.       const size_t right_index =
  722.     static_cast<size_t>(edge.neighbors.second);
  723.       const ComputationalCell& left_cell = cells[left_index];
  724.       const ComputationalCell& right_cell = cells[right_index];
  725.       if(left_cell.stickers.find("dummy")->second &&
  726.      right_cell.stickers.find("dummy")->second)
  727.     return Conserved();
  728.       const Vector2D p = Parallel(edge);
  729.       const Vector2D n =
  730.     tess.GetMeshPoint(edge.neighbors.second) -
  731.     tess.GetMeshPoint(edge.neighbors.first);
  732.       const double velocity = Projection
  733.     (tess.CalcFaceVelocity
  734.      (point_velocities[left_index],
  735.       point_velocities[right_index],
  736.       tess.GetCellCM(edge.neighbors.first),
  737.       tess.GetCellCM(edge.neighbors.second),
  738.       calc_centroid(edge)),n);             
  739.       if(left_cell.stickers.find("dummy")->second){
  740.     const Primitive right =
  741.       convert_to_primitive(right_cell, eos);
  742.     ComputationalCell ghost;
  743.     ghost.density = right.Density/100;
  744.     ghost.pressure = right.Pressure/100;
  745.     ghost.velocity = Vector2D(0,0);
  746.     const Primitive left = convert_to_primitive(ghost,eos);
  747.       /*
  748.       ScalarProd(n,right.Velocity) < 0 ? right :
  749.       reflect(right,p);
  750.       */
  751.     return rotate_solve_rotate_back
  752.       (rs_,left,right,velocity,n,p);
  753.       }
  754.       if(right_cell.stickers.find("dummy")->second){
  755.     const Primitive left =
  756.       convert_to_primitive(left_cell, eos);
  757.     ComputationalCell ghost;
  758.     ghost.density = left.Density/100;
  759.     ghost.pressure = left.Pressure/100;
  760.     ghost.velocity = Vector2D(0,0);
  761.     const Primitive right = convert_to_primitive(ghost,eos);
  762.       /*
  763.       ScalarProd(n,left.Velocity)>0 ?
  764.       left : reflect(left,p);
  765.       */
  766.     return rotate_solve_rotate_back
  767.       (rs_,left,right,velocity,n,p);
  768.       }
  769.       const Primitive left =
  770.     convert_to_primitive(left_cell, eos);
  771.       const Primitive right =
  772.     convert_to_primitive(right_cell, eos);
  773.       return rotate_solve_rotate_back
  774.     (rs_,left,right,velocity,n,p);
  775.     }
  776.   };
  777.  
  778.   class TimeStepCalculator: public LazyList<double>
  779.   {
  780.   public:
  781.  
  782.     TimeStepCalculator(const Tessellation& tess,
  783.                const vector<ComputationalCell>& cells,
  784.                const EquationOfState& eos,
  785.                const vector<Vector2D>& point_velocities,
  786.                const double gm):
  787.       tess_(tess), cells_(cells),
  788.       point_velocities_(point_velocities), eos_(eos), gm_(gm) {}
  789.  
  790.     size_t size(void) const
  791.     {
  792.       return cells_.size();
  793.     }
  794.  
  795.     double operator[](size_t i) const
  796.     {
  797.       const double kepler_velocity = sqrt(gm_/abs(tess_.GetMeshPoint(static_cast<int>(i))));
  798.       return tess_.GetWidth(static_cast<int>(i))/
  799.     (eos_.dp2c(cells_[i].density, cells_[i].pressure)+
  800.      abs(cells_[i].velocity)+
  801.      kepler_velocity+
  802.      abs(point_velocities_[i]));
  803.     }
  804.  
  805.   private:
  806.     const Tessellation& tess_;
  807.     const vector<ComputationalCell>& cells_;
  808.     const vector<Vector2D>& point_velocities_;
  809.     const EquationOfState& eos_;
  810.     const double gm_;
  811.   };
  812.  
  813.   class LogCFL: public TimeStepFunction
  814.   {
  815.   public:
  816.  
  817.     LogCFL(double cfl, const string& fname, double gm):
  818.       cfl_(cfl), fname_(fname), gm_(gm) {}
  819.  
  820.     double operator()
  821.     (const Tessellation& tess,
  822.      const vector<ComputationalCell>& cells,
  823.      const EquationOfState& eos,
  824.      const vector<Vector2D>& point_velocities,
  825.      const double /*time*/) const
  826.     {
  827.       const TimeStepCalculator tsc(tess,cells,eos,point_velocities,gm_);
  828.       double res = 10000;
  829.       size_t argmin = 0;
  830.       for(size_t i=0;i<tsc.size();++i){
  831.     const double candidate = tsc[i];
  832.     if(candidate<res && !cells[i].stickers.find("dummy")->second){
  833.       res = candidate;
  834.       argmin = i;
  835.     }  
  836.       }
  837.       ofstream f(fname_.c_str());
  838.       f << argmin << endl;
  839.       f << tess.GetMeshPoint(static_cast<int>(argmin)).x << ", ";
  840.       f << tess.GetMeshPoint(static_cast<int>(argmin)).y << endl;
  841.       f << tess.GetWidth(static_cast<int>(argmin)) << endl;
  842.       f << eos.dp2c(cells[argmin].density, cells[argmin].pressure) << endl;
  843.       f << cells[argmin].velocity.x << ", ";
  844.       f << cells[argmin].velocity.y << endl;
  845.       f << point_velocities[argmin].x << ", ";
  846.       f << point_velocities[argmin].y << endl;
  847.       f.close();
  848.       return cfl_*res;
  849.     }
  850.  
  851.   private:
  852.     const double cfl_;
  853.     const string fname_;
  854.     const double gm_;
  855.   };
  856.  
  857.   class EntropyFix: public CellUpdater
  858.   {
  859.   public:
  860.  
  861.     EntropyFix(const double thres=1e-9):
  862.       thres_(thres) {}
  863.  
  864.     vector<ComputationalCell> operator()
  865.     (const Tessellation& /*tess*/,
  866.      const PhysicalGeometry& /*pg*/,
  867.      const EquationOfState& eos,
  868.      const vector<Extensive>& extensives,
  869.      const vector<ComputationalCell>& old,
  870.      const CacheData& cd) const
  871.     {
  872.       vector<ComputationalCell> res = old;
  873.       for(size_t i=0;i<extensives.size();++i){
  874.     assert(extensives[i].tracers.find("entropy")->second>0);
  875.     if(old[i].stickers.find("dummy")->second)
  876.       continue;
  877.     /*
  878.       const double volume = pg.calcVolume
  879.       (serial_generate(CellEdgesGetter(tess,static_cast<int>(i))));
  880.     */
  881.     const double volume = cd.volumes[i];
  882.     res[i].density = extensives[i].mass/volume;
  883.     res[i].velocity = extensives[i].momentum / extensives[i].mass;
  884.     const double total_energy = extensives[i].energy/extensives[i].mass;
  885.     const double kinetic_energy =
  886.       0.5*ScalarProd(res[i].velocity, res[i].velocity);
  887.     const double energy =
  888.       total_energy - kinetic_energy;
  889.     for(std::map<std::string,double>::const_iterator it =
  890.           extensives[i].tracers.begin();
  891.         it!=extensives[i].tracers.end();++it)
  892.       res[i].tracers[it->first] = it->second/extensives[i].mass;
  893.     //  assert(energy>thres_*kinetic_energy);
  894.     if(energy>thres_*kinetic_energy){
  895.       res[i].pressure = eos.de2p(res[i].density, energy);
  896.       res[i].tracers["entropy"] = eos.dp2s
  897.         (res[i].density, res[i].pressure);
  898.       assert(res[i].pressure>0);
  899.       //      assert(res[i].tracers["entropy"]/old[i].tracers.find("entropy")->second<1000);
  900.     }
  901.     else{
  902.       assert(res[i].tracers.count("entropy")>0);
  903.       res[i].pressure = eos.sd2p(res[i].tracers.find("entropy")->second,
  904.                      res[i].density);
  905.       assert(res[i].pressure>0);
  906.     }
  907.       }
  908.       return res;
  909.     }
  910.  
  911.   private:
  912.     const double thres_;
  913.   };
  914.  
  915.   class EdgeLengthCalculator: public LazyList<double>
  916.   {
  917.   public:
  918.  
  919.     EdgeLengthCalculator(const Tessellation& tess,
  920.              const PhysicalGeometry& pg):
  921.       tess_(tess), pg_(pg) {}
  922.  
  923.     size_t size(void) const
  924.     {
  925.       return tess_.getAllEdges().size();
  926.     }
  927.  
  928.     double operator[](size_t i) const
  929.     {
  930.       return pg_.calcArea(tess_.getAllEdges()[i]);
  931.     }
  932.  
  933.   private:
  934.     const Tessellation& tess_;
  935.     const PhysicalGeometry& pg_;
  936.   };
  937.  
  938.   bool bracketed(double low, double arg, double high)
  939.   {
  940.     return arg>=low and high>arg;
  941.   }
  942.  
  943.   class LazyExtensiveUpdater: public ExtensiveUpdater
  944.   {
  945.   public:
  946.  
  947.     LazyExtensiveUpdater(const Tessellation& tess,
  948.              const PhysicalGeometry& pg):
  949.       lengths_(serial_generate(EdgeLengthCalculator(tess,pg))) {}
  950.  
  951.     void operator()
  952.     (const vector<Extensive>& fluxes,
  953.      const PhysicalGeometry& /*pg*/,
  954.      const Tessellation& tess,
  955.      const double dt,
  956.      const CacheData& cd,
  957.      const vector<ComputationalCell>& cells,
  958.      vector<Extensive>& extensives) const
  959.     {
  960.       const vector<Edge>& edge_list = tess.getAllEdges();
  961.       for(size_t i=0;i<cells.size();++i){
  962.     extensives[i].tracers["entropy"] =
  963.       cd.volumes[i]*cells[i].density*
  964.       cells[i].tracers.find("entropy")->second;
  965.       }
  966.       for(size_t i=0;i<edge_list.size();++i){
  967.     const Edge& edge = edge_list[i];
  968.     const Extensive delta = dt*lengths_[i]*fluxes[i];
  969.     if(bracketed(0,edge.neighbors.first,tess.GetPointNo())){
  970.       extensives[static_cast<size_t>(edge.neighbors.first)] -=
  971.         delta;
  972.       assert(extensives[static_cast<size_t>(edge.neighbors.first)].tracers.find("entropy")->second>0);
  973.     }
  974.     if(bracketed(0,edge.neighbors.second,tess.GetPointNo())){
  975.       extensives[static_cast<size_t>(edge.neighbors.second)] +=
  976.         delta;
  977.       assert(extensives[static_cast<size_t>(edge.neighbors.second)].tracers.find("entropy")->second>0);
  978.     }
  979.       }
  980.     }
  981.  
  982.   private:
  983.     const vector<double> lengths_;
  984.   };
  985. }
  986.  
  987. class SimData
  988. {
  989.  
  990. public:
  991.  
  992.   SimData(const Constants& c):
  993.    pg_(Vector2D(0,0), Vector2D(0,1)),
  994.     outer_(c.lower_left, c.upper_right),
  995.     /*
  996.       init_points_(rectangle_clip
  997.       (centered_logarithmic_spiral(0.001,
  998.       abs(c.upper_right-c.lower_left),
  999.       0.005*2,
  1000.       Vector2D(-0.01*c.offset,0)),
  1001.       c.lower_left, c.upper_right)),
  1002.     */
  1003.     init_points_(rectangle_clip
  1004.          (complete_grid(0.1*c.parsec,
  1005.                 abs(c.upper_right-c.lower_left),
  1006.                 0.005*2),
  1007.           c.lower_left+Vector2D(0.001,0), c.upper_right)),
  1008.     tess_(init_points_, outer_),
  1009.     eos_(c.adiabatic_index),
  1010.     rs_(),
  1011.     //    raw_point_motion_(),
  1012.     //    point_motion_(raw_point_motion_,eos_),
  1013.     alt_point_motion_(),
  1014.     gravity_acc_(c.gravitation_constant*c.black_hole_mass,
  1015.          0.001*c.parsec,
  1016.          c.parsec*Vector2D(0,0)),
  1017.     gravity_force_(gravity_acc_),
  1018.     geom_force_(pg_.getAxis()),
  1019.   /*
  1020.     rad_cool_(1.67e-24*c.gram,
  1021.           1.38e-16*c.erg,
  1022.           1e6*1e-21*c.erg*pow(c.centi*c.meter,3)/c.second),
  1023.   */
  1024.   //Cooling data taken from Dwek 1987
  1025.           //          1.33e-19*c.erg*pow(c.centi*c.meter,3)/c.second),
  1026.   //Cooling data taken from F.D. Kahn 1976
  1027.     force_(VectorInitialiser<SourceTerm*>(&gravity_force_)(&geom_force_)()),
  1028.   //    force_(VectorInitializer<SourceTerm*>(&gravity_force_)()),
  1029.     tsf_(0.3, "dt_log.txt",c.gravitation_constant*c.black_hole_mass),
  1030.     fc_(rs_),
  1031.     eu_(tess_,pg_),
  1032.     cu_(),
  1033.     sim_(tess_,
  1034.      outer_,
  1035.      pg_,
  1036.      calc_init_cond(tess_,c,eos_),
  1037.      eos_,
  1038.      alt_point_motion_,
  1039.      force_,
  1040.      tsf_,
  1041.      fc_,
  1042.      eu_,
  1043.      cu_) {}
  1044.  
  1045.   hdsim& getSim(void)
  1046.   {
  1047.     return sim_;
  1048.   }
  1049.  
  1050. private:
  1051.   const CylindricalSymmetry pg_;
  1052.   const SquareBox outer_;
  1053.   const vector<Vector2D> init_points_;
  1054.   StaticVoronoiMesh tess_;
  1055.   const IdealGas eos_;
  1056.   const Hllc rs_;
  1057.   //  Lagrangian raw_point_motion_;
  1058.   //  RoundCells point_motion_;
  1059.   Eulerian alt_point_motion_;
  1060.   CenterGravity gravity_acc_;
  1061.   ConservativeForce gravity_force_;
  1062.   CylindricalComplementary geom_force_;
  1063.   //  RadiativeCooling rad_cool_;
  1064.   SeveralSources force_;
  1065.   //  const SimpleCFL tsf_;
  1066.   const LogCFL tsf_;
  1067.   const SinkFlux fc_;
  1068.   //const SimpleCellUpdater cu_;
  1069.   const LazyExtensiveUpdater eu_;
  1070.   const EntropyFix cu_;
  1071.   hdsim sim_;
  1072. };
  1073.  
  1074. class CustomDiagnostic
  1075. {
  1076. public:
  1077.  
  1078.   CustomDiagnostic(double dt,
  1079.            double t_start):
  1080.     dt_(dt),
  1081.     t_next_(t_start),
  1082.     counter_(0) {}
  1083.  
  1084.   void operator()(const hdsim& sim)
  1085.   {
  1086.     if(sim.getTime()>t_next_){
  1087.       write_snapshot_to_hdf5(sim, "snapshot_"+int2str(counter_)+".h5");
  1088.  
  1089.       t_next_ += dt_;
  1090.       ++counter_;
  1091.     }
  1092.   }
  1093.  
  1094. private:
  1095.   const double dt_;
  1096.   double t_next_;
  1097.   int counter_;
  1098. };
  1099.  
  1100. namespace {
  1101.   class MultipleDiagnostics: public DiagnosticFunction
  1102.   {
  1103.   public:
  1104.  
  1105.     MultipleDiagnostics(const vector<DiagnosticFunction*>& diag_list):
  1106.       diag_list_(diag_list) {}
  1107.  
  1108.     void operator()(const hdsim& sim)
  1109.     {
  1110.       BOOST_FOREACH(DiagnosticFunction* df, diag_list_)
  1111.     {
  1112.       (*df)(sim);
  1113.     }
  1114.     }
  1115.  
  1116.   private:
  1117.     const vector<DiagnosticFunction*> diag_list_;
  1118.   };
  1119. }
  1120.  
  1121. namespace {
  1122.  
  1123.   class WriteCycle: public DiagnosticFunction
  1124.   {
  1125.   public:
  1126.  
  1127.     WriteCycle(const string& fname):
  1128.       fname_(fname) {}
  1129.  
  1130.     void operator()(const hdsim& sim)
  1131.     {
  1132.       write_number(sim.getCycle(),fname_);
  1133.     }
  1134.  
  1135.   private:
  1136.     const string fname_;
  1137.   };
  1138.  
  1139.   void my_main_loop(hdsim& sim, const Constants& c)
  1140.   {
  1141.     const double tf = 50e3*c.year;
  1142.     SafeTimeTermination term_cond_raw(tf,1e6);
  1143.     ConsecutiveSnapshots diag1(new ConstantTimeInterval(tf/1000),
  1144.                    new Rubric("snapshot_",".h5"));
  1145.     WriteTime diag2("time.txt");
  1146.     WriteConserved diag4("conserved.txt");
  1147.     //Bremsstrahlung diag5("luminosity_history.txt",c);
  1148.     WriteCycle diag6("cycle.txt");
  1149.     vector<DiagnosticFunction*> diag_list;
  1150.     diag_list.push_back(&diag1);
  1151.     diag_list.push_back(&diag2);
  1152.     //    diag_list.push_back(&diag3);
  1153.     diag_list.push_back(&diag4);
  1154.     //    diag_list.push_back(&diag5);
  1155.     diag_list.push_back(&diag6);
  1156.     MultipleDiagnostics diag(diag_list);
  1157.     RadiativeCooling2 manip(1.67e-24*c.gram,
  1158.                 1.38e-16*c.erg,
  1159.                 1e5,
  1160.                 1e9,
  1161.                 VectorInitialiser<double>(-3.25588412e-06)
  1162.                 (3.95544816e-04)(-2.00975017e-02)(5.55903044e-01)
  1163.                 (-9.06748793e+00)(8.73087607e+01)(-4.57914150e+02)
  1164.                 (9.48188407e+02)(),
  1165.                 c.erg*pow(c.centi*c.meter,3)/c.second);
  1166.     main_loop(sim,
  1167.           term_cond_raw,
  1168.           &hdsim::TimeAdvance,
  1169.           &diag,
  1170.           &manip);
  1171.   }
  1172. }
  1173.  
  1174. namespace {
  1175.   void report_error(UniversalError const& eo)
  1176.   {
  1177.     cout << eo.GetErrorMessage() << endl;
  1178.     cout.precision(14);
  1179.     for(size_t i=0;i<eo.GetFields().size();++i)
  1180.       cout << eo.GetFields()[i] << " = "
  1181.        << eo.GetValues()[i] << endl;
  1182.   }
  1183. }
  1184.  
  1185. int main(void)
  1186. {
  1187.   feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
  1188.   //  MPI_Init(NULL, NULL);
  1189.  
  1190.   assert(abs(horner(VectorInitialiser<double>(1)(2)(3)(),2)-11)<1e-6);
  1191.  
  1192.   try{
  1193.  
  1194.     Constants c;
  1195.  
  1196.     SimData sim_data(c);
  1197.     hdsim& sim = sim_data.getSim();
  1198.  
  1199.     write_snapshot_to_hdf5(sim,
  1200.                "initial.h5");
  1201.  
  1202.     my_main_loop(sim,c);
  1203.  
  1204.     write_snapshot_to_hdf5(sim,
  1205.                "final.h5");
  1206.  
  1207.   }
  1208.   catch(const UniversalError& eo){
  1209.     report_error(eo);
  1210.     throw;
  1211.   }
  1212.  
  1213.   return 0;
  1214. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement