Extension: Geometries
#include "dg/geometries/geometries.h"
Loading...
Searching...
No Matches
simple_orthogonal.h
Go to the documentation of this file.
1#pragma once
2
3#include "dg/algorithm.h"
4#include "generator.h"
5#include "utilities.h"
6#include "adaption.h"
7
8
9namespace dg
10{
11namespace geo
12{
14namespace orthogonal
15{
16
17namespace detail
18{
19
20//This leightweights struct and its methods finds the initial R and Z values and the coresponding f(\psi) as
21//good as it can, i.e. until machine precision is reached
22struct Fpsi
23{
24
25 //firstline = 0 -> conformal, firstline = 1 -> equalarc
26 Fpsi( const CylindricalFunctorsLvl1& psi, const CylindricalSymmTensorLvl1& chi, double x0, double y0, int firstline):
27 psip_(psi), fieldRZYTconf_(psi, x0, y0, chi),fieldRZYTequl_(psi, x0, y0, chi), fieldRZtau_(psi, chi)
28 {
29 X_init = x0, Y_init = y0;
30 while( fabs( psi.dfx()(X_init, Y_init)) <= 1e-10 && fabs( psi.dfy()( X_init, Y_init)) <= 1e-10)
31 X_init += 1.;
32 m_firstline = firstline;
33 }
34 //finds the starting points for the integration in y direction
35 void find_initial( double psi, double& R_0, double& Z_0)
36 {
37 unsigned N = 50;
38 std::array<double, 2> begin2d{ {0,0} }, end2d(begin2d), end2d_old(begin2d);
39 begin2d[0] = end2d[0] = end2d_old[0] = X_init;
40 begin2d[1] = end2d[1] = end2d_old[1] = Y_init;
41 double eps = 1e10, eps_old = 2e10;
42 while( (eps < eps_old || eps > 1e-7) && eps > 1e-14)
43 {
44 eps_old = eps; end2d_old = end2d;
45 N*=2;
46 double psi0 = psip_.f()(X_init, Y_init);
47 using Vec = std::array<double,2>;
49 "Feagin-17-8-10", {0,0}), fieldRZtau_);
50 odeint.integrate_steps( psi0, begin2d, psi, end2d, N);
51 eps = sqrt( (end2d[0]-end2d_old[0])*(end2d[0]-end2d_old[0]) +
52 (end2d[1]-end2d_old[1])*(end2d[1]-end2d_old[1]));
53 }
54 X_init = R_0 = end2d_old[0], Y_init = Z_0 = end2d_old[1];
55 }
56
57 //compute f for a given psi between psi0 and psi1
58 double construct_f( double psi, double& R_0, double& Z_0)
59 {
60 find_initial( psi, R_0, Z_0);
61 std::array<double, 3> begin{ {0,0,0} }, end(begin);
62 begin[0] = R_0, begin[1] = Z_0;
63 double eps = 1e10, eps_old = 2e10;
64 unsigned N = 50;
65 while( (eps < eps_old || eps > 1e-7)&& eps > 1e-14)
66 {
67 eps_old = eps; N*=2;
68 using Vec = std::array<double,3>;
69 dg::RungeKutta<Vec> rk( "Feagin-17-8-10", begin);
71 if( m_firstline == 0)
72 odeint = dg::SinglestepTimeloop<Vec>( rk,
73 fieldRZYTconf_);
74 if( m_firstline == 1)
75 odeint = dg::SinglestepTimeloop<Vec>( rk,
76 fieldRZYTequl_);
77 odeint.integrate_steps( 0., begin, 2.*M_PI, end, N);
78 eps = sqrt( (end[0]-begin[0])*(end[0]-begin[0]) + (end[1]-begin[1])*(end[1]-begin[1]));
79 }
80 //std::cout << "\t error "<<eps<<" with "<<N<<" steps\t";
81 double f_psi = 2.*M_PI/end[2];
82 //std::cout <<"f_psi: "<<f_psi << " end "<<end[2] <<"\n";
83 return f_psi;
84 }
85 double operator()( double psi)
86 {
87 double R_0, Z_0;
88 return construct_f( psi, R_0, Z_0);
89 }
90
91 private:
92 int m_firstline;
93 double X_init, Y_init;
94 CylindricalFunctorsLvl1 psip_;
95 CylindricalSymmTensorLvl1 chi_;
96 dg::geo::ribeiro::FieldRZYT fieldRZYTconf_;
97 dg::geo::equalarc::FieldRZYT fieldRZYTequl_;
98 dg::geo::FieldRZtau fieldRZtau_;
99
100};
101
102//compute the vector of r and z - values that form one psi surface
103//assumes y_0 = 0
104template<class real_type>
105void compute_rzy( const CylindricalFunctorsLvl1& psi, const CylindricalSymmTensorLvl1& chi,
106 const thrust::host_vector<real_type>& y_vec,
107 thrust::host_vector<real_type>& r,
108 thrust::host_vector<real_type>& z,
109 real_type R_0, real_type Z_0, real_type f_psi, int mode )
110{
111
112 thrust::host_vector<real_type> r_old(y_vec.size(), 0), r_diff( r_old);
113 thrust::host_vector<real_type> z_old(y_vec.size(), 0), z_diff( z_old);
114 r.resize( y_vec.size()), z.resize(y_vec.size());
115 std::array<real_type,2> begin{ {0,0} }, end(begin), temp(begin);
116 begin[0] = R_0, begin[1] = Z_0;
117 //std::cout <<"f_psi "<<f_psi<<" "<<" "<< begin[0] << " "<<begin[1]<<"\t";
118 dg::geo::ribeiro::FieldRZY fieldRZYconf(psi, chi);
119 dg::geo::equalarc::FieldRZY fieldRZYequi(psi, chi);
120 fieldRZYconf.set_f(f_psi);
121 fieldRZYequi.set_f(f_psi);
122 unsigned steps = 1;
123 real_type eps = 1e10, eps_old=2e10;
124 while( (eps < eps_old||eps > 1e-7) && eps > 1e-14)
125 {
126 //begin is left const
127 eps_old = eps, r_old = r, z_old = z;
128 using Vec = std::array<real_type,2>;
129 dg::RungeKutta<Vec> rk ( "Feagin-17-8-10", {0,0});
131 if( mode == 0)
132 odeint = dg::SinglestepTimeloop<Vec>( rk, fieldRZYconf);
133 if( mode == 1)
134 odeint = dg::SinglestepTimeloop<Vec>( rk, fieldRZYequi);
135 odeint.integrate_steps( 0., begin, y_vec[0], end, steps);
136 r[0] = end[0], z[0] = end[1];
137 for( unsigned i=1; i<y_vec.size(); i++)
138 {
139 temp = end;
140 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
141 r[i] = end[0], z[i] = end[1];
142 }
143 //compute error in R,Z only
144 dg::blas1::axpby( 1., r, -1., r_old, r_diff);
145 dg::blas1::axpby( 1., z, -1., z_old, z_diff);
146 real_type er = dg::blas1::dot( r_diff, r_diff);
147 real_type ez = dg::blas1::dot( z_diff, z_diff);
148 real_type ar = dg::blas1::dot( r, r);
149 real_type az = dg::blas1::dot( z, z);
150 eps = sqrt( er + ez)/sqrt(ar+az);
151 steps*=2;
152 }
153 r = r_old, z = z_old;
154
155}
156
157//This struct computes -2pi/f with a fixed number of steps for all psi
158//and provides the Nemov algorithm for orthogonal grid
159struct Nemov
160{
161 Nemov( const CylindricalFunctorsLvl2 psi, const CylindricalSymmTensorLvl1& chi, double f0, int mode):
162 f0_(f0), mode_(mode),
163 psip_(psi), chi_(chi), lapPsi_(psi,chi) { }
164 void initialize(
165 const thrust::host_vector<double>& r_init, //1d intial values
166 const thrust::host_vector<double>& z_init, //1d intial values
167 thrust::host_vector<double>& h_init) //,
168 // thrust::host_vector<double>& hr_init,
169 // thrust::host_vector<double>& hz_init)
170 {
171 unsigned size = r_init.size();
172 h_init.resize( size);//, hr_init.resize( size), hz_init.resize( size);
173 for( unsigned i=0; i<size; i++)
174 {
175 if(mode_ == 0)
176 h_init[i] = f0_;
177 if(mode_ == 1)
178 {
179 double x = r_init[i], y = z_init[i];
180 double psipR = psip_.dfx()(x, y), psipZ = psip_.dfy()(x,y);
181 double chiRR = chi_.xx()(x, y),
182 chiRZ = chi_.xy()(x, y),
183 chiZZ = chi_.yy()(x, y);
184 double psip2 = chiRR*psipR*psipR + 2.*chiRZ*psipR*psipZ + chiZZ*psipZ*psipZ;
185 h_init[i] = f0_/sqrt(psip2); //equalarc
186 }
187 //double laplace = psipRR_(r_init[i], z_init[i]) +
188 //psipZZ_(r_init[i], z_init[i]);
189 //hr_init[i] = -f0_*laplace/psip2*psipR;
190 //hz_init[i] = -f0_*laplace/psip2*psipZ;
191 }
192 }
193
194 void operator()(double, const std::array<thrust::host_vector<double>,3 >& y, std::array<thrust::host_vector<double>,3>& yp)
195 {
196 //y[0] = R, y[1] = Z, y[2] = h, y[3] = hr, y[4] = hz
197 unsigned size = y[0].size();
198 for( unsigned i=0; i<size; i++)
199 {
200 double xx = y[0][i], yy = y[1][i];
201 double psipR = psip_.dfx()(xx, yy), psipZ = psip_.dfy()(xx,yy);
202 double chiRR = chi_.xx()(xx, yy),
203 chiRZ = chi_.xy()(xx, yy),
204 chiZZ = chi_.yy()(xx, yy);
205 double psip2 = chiRR*psipR*psipR + 2.*chiRZ*psipR*psipZ + chiZZ*psipZ*psipZ;
206 yp[0][i] = (chiRR*psipR + chiRZ*psipZ)/psip2/f0_;
207 yp[1][i] = (chiRZ*psipR + chiZZ*psipZ)/psip2/f0_;
208 yp[2][i] = y[2][i]*( - lapPsi_(xx,yy) )/psip2/f0_;
209 //yp[3][i] = ( -(2.*psipRR+psipZZ)*y[3][i] - psipRZ*y[4][i] - laplacePsipR_(y[0][i], y[1][i])*y[2][i])/psip2; //wrong with monitor metric!!
210 //yp[4][i] = ( -psipRZ*y[3][i] - (2.*psipZZ+psipRR)*y[4][i] - laplacePsipZ_(y[0][i], y[1][i])*y[2][i])/psip2; //wrong with monitor metric!!
211 }
212 }
213 private:
214 double f0_;
215 int mode_;
216 CylindricalFunctorsLvl2 psip_;
217 CylindricalSymmTensorLvl1 chi_;
218 dg::geo::detail::LaplaceChiPsi lapPsi_;
219};
220
221template<class Nemov>
222void construct_rz( Nemov nemov,
223 double x_0, //the x value that corresponds to the first psi surface
224 const thrust::host_vector<double>& x_vec, //1d x values
225 const thrust::host_vector<double>& r_init, //1d intial values of the first psi surface
226 const thrust::host_vector<double>& z_init, //1d intial values of the first psi surface
227 thrust::host_vector<double>& r,
228 thrust::host_vector<double>& z,
229 thrust::host_vector<double>& h,
230 double rtol = 1e-13,
231 bool verbose = false
232 )
233{
234 unsigned N = 1;
235 double eps = 1e10, eps_old=2e10;
236 std::array<thrust::host_vector<double>,3> begin;
237 thrust::host_vector<double> h_init( r_init.size(), 0.);
238 if(verbose)std::cout << "Initialize Nemov ...\n";
239 nemov.initialize( r_init, z_init, h_init);
240 begin[0] = r_init, begin[1] = z_init, begin[2] = h_init;
241 //now we have the starting values
242 std::array<thrust::host_vector<double>,3> end(begin), temp(begin);
243 unsigned sizeX = x_vec.size(), sizeY = r_init.size();
244 unsigned size2d = x_vec.size()*r_init.size();
245 r.resize(size2d), z.resize(size2d), h.resize(size2d);
246 double x0=x_0, x1 = x_vec[0];
247 thrust::host_vector<double> r_new(r_init), r_old(r_init), r_diff(r_init);
248 thrust::host_vector<double> z_new(z_init), z_old(z_init), z_diff(z_init);
249 thrust::host_vector<double> h_new(h_init); //, h_old(h_init), h_diff(h_init);
250 if(verbose)std::cout << "Start integration ...\n";
251 auto x_first = std::find_if( x_vec.begin(), x_vec.end(), [x_0](double x){ return x >= x_0;});
252 unsigned first_i = std::distance( x_vec.begin(), x_first);
253 // We have x_vec[first_i-1] < x_0 < x_vec[first_i];
254 for( unsigned kk=0; kk<sizeX; kk++)
255 {
256 // With this construct we can keep one single for loop over kk
257 unsigned i; // integration end index (is needed further down)
258 if( first_i + kk < sizeX)
259 {
260 i = first_i + kk;
261 // 1. integrate all points right of x_0 ->
262 x0 = kk==0 ? x_0 : x_vec[i-1], x1 = x_vec[i];
263 }
264 else
265 {
266 i = sizeX - kk - 1;
267 // 2. integrate all points left of x_0 <-
268 x0 = first_i + kk==sizeX ? x_0 : x_vec[i+1], x1 = x_vec[i];
269 }
270 //std::cout << "kk "<<kk<<" "<<i<<" "<<x0<<" "<<x1<<"\n";
271 temp = x0 == x_0 ? begin : end;
272 N = 1;
273 eps = 1e10, eps_old=2e10;
274 // integrate from (x0,temp) to (x1,end)
275 while( (eps < eps_old || eps > 1e-6) && eps > rtol)
276 {
277 r_old = r_new, z_old = z_new; eps_old = eps;
278 //h_old = h_new;
280 using Vec = std::array<thrust::host_vector<double>,3>;
281 dg::RungeKutta<Vec> rk( "Feagin-17-8-10", temp);
282 dg::SinglestepTimeloop<Vec>( rk, nemov).integrate_steps( x0, temp,
283 x1, end, N);
284 for( unsigned j=0; j<sizeY; j++)
285 {
286 r_new[j] = end[0][j], z_new[j] = end[1][j];
287 h_new[j] = end[2][j];
288 }
290 dg::blas1::axpby( 1., r_new, -1., r_old, r_diff);
291 dg::blas1::axpby( 1., z_new, -1., z_old, z_diff);
292 //dg::blas1::axpby( 1., h_new, -1., h_old, h_diff);
293 //dg::blas1::pointwiseDot( h_diff, h_diff, h_diff);
294 //dg::blas1::pointwiseDivide( h_diff, h_old, h_diff); // h is always > 0
295 dg::blas1::pointwiseDot( r_diff, r_diff, r_diff);
296 dg::blas1::pointwiseDot( 1., z_diff, z_diff, 1., r_diff);
297 //dg::blas1::axpby( 1., h_diff, 1., r_diff);
298 try{
299 eps = sqrt( dg::blas1::dot( r_diff, 1.)/sizeY); //should be relative to the interpoint distances
300 //double eps_h = sqrt( dg::blas1::dot( h_diff, 1.)/sizeY);
301 //if(verbose)std::cout << "Effective Relative diff-h error is "<<eps_h<<" with "<<N<<" steps\n";
302 } catch ( dg::Error& )
303 {
304 // Remove Nan from end
305 end = begin;
306 eps = eps_old; //make eps equal eps_old to trigger throw below
307 r_new = r_old , z_new = z_old;
308 //h_new = h_old;
309 }
310 //if(verbose)std::cout << "Effective Absolute diff-r error is "<<eps<<" with "<<N<<" steps\n";
311 N*=2;
312 if( (eps >= eps_old && N > 1024 && eps > 1e-6) || N > 64000)
313 throw dg::Error(dg::Message(_ping_) <<
314 "Grid generator encountered loss of convergence integrating between x = "
315 <<x0<<" and x = "<<x1<<"! One possible cause is integration in a region where Psi is not well defined. Reconsider the grid boundaries!" );
316 }
317 for( unsigned j=0; j<sizeY; j++)
318 {
319 unsigned idx = sizeX*j+i;
320 r[idx] = r_new[j], z[idx] = z_new[j], h[idx] = h_new[j];
321 }
322 }
323
324}
325
326} //namespace detail
327
328}//namespace orthogonal
330
331
349{
365 SimpleOrthogonal(const CylindricalFunctorsLvl2& psi, double psi_0, double
366 psi_1, double x0, double y0, double psi_firstline, int mode =0,
367 double eps = 1e-13,
368 bool verbose = false
369 ):
370 SimpleOrthogonal( psi, CylindricalSymmTensorLvl1(), psi_0, psi_1, x0, y0,
371 psi_firstline, mode, eps, verbose)
372 {
373 m_orthogonal = true;
374 }
392 CylindricalSymmTensorLvl1& chi, double psi_0, double psi_1,
393 double x0, double y0, double psi_firstline, int mode = 0,
394 double eps = 1e-13,
395 bool verbose = false
396 ):
397 psi_(psi), chi_(chi), m_eps(eps)
398 {
399 assert( psi_1 != psi_0);
400 m_firstline = mode;
401 orthogonal::detail::Fpsi fpsi(psi, chi, x0, y0, mode);
402 f0_ = fabs( fpsi.construct_f( psi_firstline, R0_, Z0_));
403 if(verbose)std::cout << "SimpleOrthogonal constructed Fpsi "<<f0_<<"\n";
404 if( psi_1 < psi_0) f0_*=-1;
405 lz_ = f0_*(psi_1-psi_0);
406 m_orthogonal = false;
407 m_zeta_first = f0_*(psi_firstline-psi_0);
408 m_verbose = verbose;
409 }
410
416 double f0() const{return f0_;}
417 virtual SimpleOrthogonal* clone() const override final{return new SimpleOrthogonal(*this);}
418
419 private:
420 // length of zeta-domain (f0*(psi_1-psi_0))
421 virtual double do_width() const override final{return lz_;}
422 virtual double do_height() const override final{return 2.*M_PI;}
423 virtual bool do_isOrthogonal() const override final{return m_orthogonal;}
424 virtual void do_generate(
425 const thrust::host_vector<double>& zeta1d,
426 const thrust::host_vector<double>& eta1d,
427 thrust::host_vector<double>& x,
428 thrust::host_vector<double>& y,
429 thrust::host_vector<double>& zetaX,
430 thrust::host_vector<double>& zetaY,
431 thrust::host_vector<double>& etaX,
432 thrust::host_vector<double>& etaY) const override final
433 {
434 thrust::host_vector<double> r_init, z_init;
435 if( m_verbose)std::cout << "Compute Firstline...\n";
436 orthogonal::detail::compute_rzy( psi_, chi_, eta1d, r_init, z_init,
437 R0_, Z0_, f0_, m_firstline);
438 orthogonal::detail::Nemov nemov(psi_, chi_, f0_, m_firstline);
439 thrust::host_vector<double> h;
440 if( m_verbose)std::cout << "Construct rz...\n";
441 orthogonal::detail::construct_rz(nemov, m_zeta_first, zeta1d, r_init,
442 z_init, x, y, h, m_eps, m_verbose);
443 unsigned size = x.size();
444 for( unsigned idx=0; idx<size; idx++)
445 {
446 double psipR = psi_.dfx()(x[idx], y[idx]);
447 double psipZ = psi_.dfy()(x[idx], y[idx]);
448 double chiRR = chi_.xx()( x[idx], y[idx]),
449 chiRZ = chi_.xy()( x[idx], y[idx]),
450 chiZZ = chi_.yy()( x[idx], y[idx]);
451 zetaX[idx] = f0_*psipR;
452 zetaY[idx] = f0_*psipZ;
453 etaX[idx] = -h[idx]*(chiRZ*psipR + chiZZ*psipZ);
454 etaY[idx] = +h[idx]*(chiRR*psipR + chiRZ*psipZ);
455 }
456 }
457 CylindricalFunctorsLvl2 psi_;
458 CylindricalSymmTensorLvl1 chi_;
459 double f0_, lz_, R0_, Z0_;
460 int m_firstline;
461 double m_zeta_first;
462 double m_eps;
463 bool m_orthogonal;
464 bool m_verbose;
465};
466
467}//namespace geo
468}//namespace dg
#define M_PI
void axpby(value_type alpha, const ContainerType1 &x, value_type1 beta, ContainerType &y)
void pointwiseDot(value_type alpha, const ContainerType1 &x1, const ContainerType2 &x2, value_type1 beta, ContainerType &y)
auto dot(const ContainerType1 &x, const ContainerType2 &y)
void integrate_steps(value_type t0, const container_type &u0, value_type t1, container_type &u1, unsigned steps)
This struct bundles a function and its first and second derivatives.
Definition fluxfunctions.h:222
const CylindricalFunctor & dfy() const
Definition fluxfunctions.h:250
const CylindricalFunctor & dfx() const
Definition fluxfunctions.h:248
Definition fluxfunctions.h:364
const CylindricalFunctor & yy() const
yy component
Definition fluxfunctions.h:403
const CylindricalFunctor & xy() const
xy component
Definition fluxfunctions.h:401
const CylindricalFunctor & xx() const
xy component
Definition fluxfunctions.h:399
Generate a simple orthogonal grid.
Definition simple_orthogonal.h:349
double f0() const
The grid constant.
Definition simple_orthogonal.h:416
SimpleOrthogonal(const CylindricalFunctorsLvl2 &psi, const CylindricalSymmTensorLvl1 &chi, double psi_0, double psi_1, double x0, double y0, double psi_firstline, int mode=0, double eps=1e-13, bool verbose=false)
Construct a simple orthogonal grid.
Definition simple_orthogonal.h:391
virtual SimpleOrthogonal * clone() const override final
Abstract clone method that returns a copy on the heap.
Definition simple_orthogonal.h:417
SimpleOrthogonal(const CylindricalFunctorsLvl2 &psi, double psi_0, double psi_1, double x0, double y0, double psi_firstline, int mode=0, double eps=1e-13, bool verbose=false)
Construct a simple orthogonal grid.
Definition simple_orthogonal.h:365
The abstract generator base class.
Definition generator.h:20