Extension: Geometries
#include "dg/geometries/geometries.h"
utilitiesX.h
Go to the documentation of this file.
1#pragma once
2
3#include "dg/algorithm.h"
4#include "utilities.h"
5
6namespace dg
7{
8namespace geo
9{
10
12namespace detail{
13struct Monitor : public aCylindricalFunctor<Monitor>
14{
15 //computes a + eps * b
16 Monitor( double value, double eps_value, double R_X, double Z_X, double sigmaR, double sigmaZ):
17 m_value(value), m_eps_value(eps_value),
18 m_cauchy(R_X, Z_X, sigmaR, sigmaZ, 1){}
19 double do_compute( double x, double y)const
20 {
21 return m_value+m_cauchy(x,y)*m_eps_value;
22 }
23 private:
24 double m_value, m_eps_value;
25 dg::Cauchy m_cauchy;
26
27};
28struct DivMonitor : public aCylindricalFunctor<DivMonitor>
29{
30 //computes a*epsX + b*epsY
31 DivMonitor( double valueX, double valueY, double R_X, double Z_X, double sigmaR, double sigmaZ):
32 m_valueX(valueX), m_valueY(valueY),
33 m_cauchy(R_X, Z_X, sigmaR, sigmaZ, 1){}
34 double do_compute( double x, double y)const
35 {
36 return m_valueX*m_cauchy.dx(x,y)+m_valueY*m_cauchy.dy(x,y);
37 }
38 private:
39 double m_valueX, m_valueY;
40 dg::Cauchy m_cauchy;
41
42};
43}//namespace detail
45
58static inline CylindricalSymmTensorLvl1 make_Xbump_monitor( const CylindricalFunctorsLvl2& psi, double& R_X, double& Z_X, double radiusX, double radiusY)
59{
60 findXpoint( psi, R_X, Z_X);
61 double x = R_X, y = Z_X;
62 double psixy = psi.dfxy()(x,y), psixx = psi.dfxx()(x,y), psiyy = psi.dfyy()(x,y);
63 double sumpsi = psixx + psiyy;
64 double diffpsi = psixx - psiyy;
65 double alpha = (psixy*psixy - psixx*psiyy)*(diffpsi*diffpsi + 4.*psixy*psixy);
66
67 double gxx = (-psiyy*diffpsi + 2.*psixy*psixy)/sqrt(alpha);
68 double gyy = ( psixx*diffpsi + 2.*psixy*psixy)/sqrt(alpha);
69 double gxy = ( - sumpsi*psixy)/sqrt(alpha);
70 detail::Monitor xx(1, gxx-1, x,y, radiusX, radiusY);
71 detail::Monitor xy(0, gxy-0, x,y, radiusX, radiusY);
72 detail::Monitor yy(1, gyy-1, x,y, radiusX, radiusY);
73 detail::DivMonitor divX(gxx-1, gxy, x,y, radiusX, radiusY);
74 detail::DivMonitor divY(gxy, gyy-1, x,y, radiusX, radiusY);
75 CylindricalSymmTensorLvl1 chi( xx, xy, yy, divX, divY);
76 //double laplace = psi.dfxx()(x,y)*chi.xx()(x,y)+2.*psi.dfxy()(x,y)*chi.xy()(x,y)+psi.dfyy()(x,y)*chi.yy()(x,y)
77 // + chi.divX()(x,y)*psi.dfx()(x,y) + chi.divY()(x,y)*psi.dfy()(x,y);
78 //std::cout << "Laplace at X-point "<<laplace<<std::endl;
79
80 return chi;
81}
92static inline CylindricalSymmTensorLvl1 make_Xconst_monitor( const CylindricalFunctorsLvl2& psi, double& R_X, double& Z_X)
93{
94 findXpoint( psi, R_X, Z_X);
95 double x = R_X, y = Z_X;
96 double psixy = psi.dfxy()(x,y), psixx = psi.dfxx()(x,y), psiyy = psi.dfyy()(x,y);
97 double sumpsi = psixx + psiyy;
98 double diffpsi = psixx - psiyy;
99 double alpha = (psixy*psixy - psixx*psiyy)*(diffpsi*diffpsi + 4.*psixy*psixy);
100
101 double gxx = (-psiyy*diffpsi + 2.*psixy*psixy)/sqrt(alpha);
102 double gyy = ( psixx*diffpsi + 2.*psixy*psixy)/sqrt(alpha);
103 double gxy = ( - sumpsi*psixy)/sqrt(alpha);
104 Constant xx(gxx);
105 Constant xy(gxy);
106 Constant yy(gyy);
107 Constant divX(0);
108 Constant divY(0);
109 CylindricalSymmTensorLvl1 chi( xx, xy, yy, divX, divY);
110 //std::cout << "px "<<psi.dfx()(x,y)<<" py "<<psi.dfy()(x,y)<<std::endl;
111 //std::cout << "gxx "<<chi.xx()(x,y)<<" gxy "<< chi.xy()(x,y)<<" gyy "<<chi.yy()(x,y)<<std::endl;
112 //std::cout << "pxx "<<psixx<<" pxy "<< psixy<<" pyy "<<psiyy<<std::endl;
113 //double laplace = psi.dfxx()(x,y)*chi.xx()(x,y)+2.*psi.dfxy()(x,y)*chi.xy()(x,y)+psi.dfyy()(x,y)*chi.yy()(x,y);
114 // //+ chi.divX()(x,y)*psi.dfx()(x,y) + chi.divY()(x,y)*psi.dfy()(x,y);
115 //std::cout << "Laplace at X-point "<<laplace<<std::endl;
116 return chi;
117}
118
119
121namespace detail
122{
123
124
128struct XCross
129{
130 XCross( const CylindricalFunctorsLvl1& psi, double R_X, double Z_X, double distance=1): fieldRZtau_(psi), psip_(psi), dist_(distance)
131 {
132 R_X_ = R_X, Z_X_ = Z_X;
133 //std::cout << "X-point set at "<<R_X_<<" "<<Z_X_<<"\n";
134 R_i[0] = R_X_ + dist_, Z_i[0] = Z_X_;
135 R_i[1] = R_X_ , Z_i[1] = Z_X_ + dist_;
136 R_i[2] = R_X_ - dist_, Z_i[2] = Z_X_;
137 R_i[3] = R_X_ , Z_i[3] = Z_X_ - dist_;
138 }
144 void set_quadrant( int quad){quad_ = quad;}
145 double operator()( double x) const
146 {
147 std::array<double,2> begin, end, end_old;
148 begin[0] = R_i[quad_], begin[1] = Z_i[quad_];
149 double eps = 1e10, eps_old = 2e10;
150 unsigned N=10;
151 if( quad_ == 0 || quad_ == 2) { begin[1] += x;}
152 else if( quad_ == 1 || quad_ == 3) { begin[0] += x;}
153
154 double psi0 = psip_.f()(begin[0], begin[1]);
155 while( (eps < eps_old || eps > 1e-4 ) && eps > 1e-10)
156 {
157 eps_old = eps; end_old = end;
158 N*=2;
159 using Vec = std::array<double,2>;
161 begin), fieldRZtau_).integrate_steps( psi0, begin, 0, end, N);
162
163 eps = sqrt( (end[0]-end_old[0])*(end[0]-end_old[0]) +
164 (end[1]-end_old[1])*(end[1]-end_old[1]));
165 if( std::isnan(eps)) { eps = eps_old/2.; end = end_old; }
166 }
167 if( quad_ == 0 || quad_ == 2){ return end_old[1] - Z_X_;}
168 return end_old[0] - R_X_;
169 }
177 void point( double& R, double& Z, double x)
178 {
179 if( quad_ == 0 || quad_ == 2){ R = R_i[quad_], Z= Z_i[quad_] +x;}
180 else if (quad_ == 1 || quad_ == 3) { R = R_i[quad_] + x, Z = Z_i[quad_];}
181 }
182
183 private:
184 int quad_;
185 dg::geo::FieldRZtau fieldRZtau_;
186 CylindricalFunctorsLvl1 psip_;
187 double R_X_, Z_X_;
188 double R_i[4], Z_i[4];
189 double dist_;
190};
191
192//compute the vector of r and z - values that form one psi surface
193//assumes y_0 = 0
194template <class FpsiX, class FieldRZYRYZY>
195void computeX_rzy(FpsiX fpsi, FieldRZYRYZY fieldRZYRYZY,
196 double psi, const thrust::host_vector<double>& y_vec,
197 const unsigned nodeX0, const unsigned nodeX1,
198 thrust::host_vector<double>& r, //output r - values
199 thrust::host_vector<double>& z, //output z - values
200 thrust::host_vector<double>& yr,
201 thrust::host_vector<double>& yz,
202 thrust::host_vector<double>& xr,
203 thrust::host_vector<double>& xz,
204 double* R_0, double* Z_0, //2 output coords on perp line
205 double& f_psi, //output f
206 bool verbose = false
207 )
208{
209 thrust::host_vector<double> r_old(y_vec.size(), 0), r_diff( r_old), yr_old(r_old), xr_old(r_old);
210 thrust::host_vector<double> z_old(y_vec.size(), 0), z_diff( z_old), yz_old(r_old), xz_old(z_old);
211 r.resize( y_vec.size()), z.resize(y_vec.size()), yr.resize(y_vec.size()), yz.resize(y_vec.size()), xr.resize(y_vec.size()), xz.resize(y_vec.size());
212 //now compute f and starting values
213 std::array<double, 4> begin{ {0,0,0,0} }, end(begin), temp(begin);
214 const double fprime = fpsi.f_prime( psi);
215 f_psi = fpsi.construct_f(psi, R_0, Z_0);
216 fieldRZYRYZY.set_f(f_psi);
217 fieldRZYRYZY.set_fp(fprime);
218 using Vec = std::array<double,4>;
219 dg::SinglestepTimeloop<Vec> odeint( dg::RungeKutta<Vec>( "Feagin-17-8-10",
220 begin), fieldRZYRYZY); // stores reference to fieldRZYRZYZ
221 unsigned steps = 1; double eps = 1e10, eps_old=2e10;
222 while( (eps < eps_old||eps > 1e-7) && eps > 1e-11)
223 {
224 eps_old = eps, r_old = r, z_old = z, yr_old = yr, yz_old = yz, xr_old = xr, xz_old = xz;
226 if( nodeX0 != 0)
227 {
228 if(psi<0)begin[0] = R_0[1], begin[1] = Z_0[1];
229 else begin[0] = R_0[0], begin[1] = Z_0[0];
230 fieldRZYRYZY.initialize( begin[0], begin[1], begin[2], begin[3]);
231 unsigned i=nodeX0-1;
232 odeint.integrate_steps( 0, begin, y_vec[i], end, steps);
233 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
234 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
235 }
236 for( int i=nodeX0-2; i>=0; i--)
237 {
238 temp = end;
239 odeint.integrate_steps( y_vec[i+1],temp, y_vec[i], end, steps);
240 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
241 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
242 }
244 begin[0] = R_0[0], begin[1] = Z_0[0];
245 fieldRZYRYZY.initialize( begin[0], begin[1], begin[2], begin[3]);
246 unsigned i=nodeX0;
247 odeint.integrate_steps( 0, begin, y_vec[i], end, steps);
248 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
249 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
250 for( unsigned i=nodeX0+1; i<nodeX1; i++)
251 {
252 temp = end;
253 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
254 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
255 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
256 }
257 temp = end;
258 odeint.integrate_steps( y_vec[nodeX1-1], temp, 2.*M_PI, end, steps);
259 if( psi <0)
260 eps = sqrt( (end[0]-R_0[0])*(end[0]-R_0[0]) + (end[1]-Z_0[0])*(end[1]-Z_0[0]));
261 else
262 eps = sqrt( (end[0]-R_0[1])*(end[0]-R_0[1]) + (end[1]-Z_0[1])*(end[1]-Z_0[1]));
263 if(verbose)std::cout << "abs. error is "<<eps<<" with "<<steps<<" steps\n";
265 if( nodeX0 != 0)
266 {
267 begin[0] = R_0[1], begin[1] = Z_0[1];
268 fieldRZYRYZY.initialize( begin[0], begin[1], begin[2], begin[3]);
269 unsigned i=nodeX1;
270 odeint.integrate_steps( 2.*M_PI, begin, y_vec[i], end, steps);
271 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
272 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
273 }
274 for( unsigned i=nodeX1+1; i<y_vec.size(); i++)
275 {
276 temp = end;
277 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i] ,end, steps);
278 r[i] = end[0], z[i] = end[1], yr[i] = end[2], yz[i] = end[3];
279 fieldRZYRYZY.derive(r[i], z[i], xr[i], xz[i]);
280 }
281 //compute error in R,Z only
282 dg::blas1::axpby( 1., r, -1., r_old, r_diff);
283 dg::blas1::axpby( 1., z, -1., z_old, z_diff);
284 double er = dg::blas1::dot( r_diff, r_diff);
285 double ez = dg::blas1::dot( z_diff, z_diff);
286 double ar = dg::blas1::dot( r, r);
287 double az = dg::blas1::dot( z, z);
288 eps = sqrt( er + ez)/sqrt(ar+az);
289 if(verbose)std::cout << "rel. error is "<<eps<<" with "<<steps<<" steps\n";
290 if( std::isnan(eps)) { eps = eps_old/2.; }
291 steps*=2;
292 }
293 r = r_old, z = z_old, yr = yr_old, yz = yz_old, xr = xr_old, xz = xz_old;
294}
295
296
297//compute psi(x) and f(x) for given discretization of x and a fpsiMinv functor
298//doesn't integrate over the x-point
299//returns psi_1
300template <class XFieldFinv>
301double construct_psi_values( XFieldFinv fpsiMinv,
302 const double psi_0, const double x_0, const thrust::host_vector<double>& x_vec, const double x_1, unsigned idxX, //idxX is the number of x_vec[i] < 0
303 thrust::host_vector<double>& psi_x, bool verbose = false )
304{
305 psi_x.resize( x_vec.size());
306 thrust::host_vector<double> psi_old(psi_x), psi_diff( psi_old);
307 unsigned N = 1;
308 //std::cout << "In psi function:\n";
309 double x0, x1;
310 const double psi_const = fpsiMinv.find_psi( x_vec[idxX]);
311 double psi_1_numerical=0;
312 double eps = 1e10, eps_old=2e10;
314 dg::RungeKutta<double>("Feagin-17-8-10", 0.), fpsiMinv);
315 while( (eps < eps_old || eps > 1e-8) && eps > 1e-11) //1e-8 < eps < 1e-14
316 {
317 eps_old = eps;
318 psi_old = psi_x;
319 x0 = x_0, x1 = x_vec[0];
320
321 double begin(psi_0), end(begin), temp(begin);
322 odeint.integrate_steps( x0, begin, x1, end, N);
323 psi_x[0] = end; fpsiMinv(0.,end,temp);
324 for( unsigned i=1; i<idxX; i++)
325 {
326 temp = end;
327 x0 = x_vec[i-1], x1 = x_vec[i];
328 odeint.integrate_steps( x0, temp, x1, end, N);
329 psi_x[i] = end; fpsiMinv(0.,end,temp);
330 //std::cout << "FOUND PSI "<<end[0]<<"\n";
331 }
332 end = psi_const;
333 //std::cout << "FOUND PSI "<<end[0]<<"\n";
334 psi_x[idxX] = end; fpsiMinv(0.,end,temp);
335 for( unsigned i=idxX+1; i<x_vec.size(); i++)
336 {
337 temp = end;
338 x0 = x_vec[i-1], x1 = x_vec[i];
339 odeint.integrate_steps( x0, temp, x1, end, N);
340 psi_x[i] = end; fpsiMinv(0.,end,temp);
341 //std::cout << "FOUND PSI "<<end[0]<<"\n";
342 }
343 temp = end;
344 odeint.integrate_steps(x1, temp, x_1, end,N);
345 psi_1_numerical = end;
346 dg::blas1::axpby( 1., psi_x, -1., psi_old, psi_diff);
347 //eps = sqrt( dg::blas2::dot( psi_diff, w1d, psi_diff)/ dg::blas2::dot( psi_x, w1d, psi_x));
348 eps = sqrt( dg::blas1::dot( psi_diff, psi_diff)/ dg::blas1::dot( psi_x, psi_x));
349
350 if(verbose)std::cout << "Effective Psi error is "<<eps<<" with "<<N<<" steps\n";
351 //std::cout << "psi 1 is "<<psi_1_numerical<<std::endl;
352 N*=2;
353 }
354 return psi_1_numerical;
355}
356
357
358
360struct PsipSep
361{
362 PsipSep( const CylindricalFunctor& psi): psip_(psi), Z_(0){}
363 void set_Z( double z){ Z_=z;}
364 double operator()(double R) { return psip_(R, Z_);}
365 private:
366 CylindricalFunctor psip_;
367 double Z_;
368};
369
371//This leightweights struct and its methods finds the initial R and Z values and the coresponding f(\psi) as
372//good as it can, i.e. until machine precision is reached (like FpsiX just for separatrix)
373struct SeparatriX
374{
375 SeparatriX( const CylindricalFunctorsLvl1& psi, const CylindricalSymmTensorLvl1& chi, double xX, double yX, double x0, double y0, int firstline, bool verbose=false):
376 mode_(firstline),
377 fieldRZYequi_(psi, chi), fieldRZYTequi_(psi, x0, y0, chi), fieldRZYZequi_(psi, chi),
378 fieldRZYconf_(psi, chi), fieldRZYTconf_(psi, x0, y0, chi), fieldRZYZconf_(psi, chi), m_verbose( verbose)
379 {
380 //find four points on the separatrix and construct y coordinate at those points and at last construct f
382 double R_X = xX; double Z_X = yX;
383 double boxR = 0.05, boxZ = 0.01; // determine bisection boundaries
384 std::array<double,4> R_min={R_X, R_X*(1.0-boxR), R_X*(1.0-boxR), R_X};
385 std::array<double,4> R_max={R_X*(1.0+boxR), R_X, R_X, R_X*(1.0+boxR)};
386 std::array<double,4> Z_0={Z_X+boxZ*R_X, Z_X+boxZ*R_X, Z_X-boxZ*R_X, Z_X-boxZ*R_X};
387 PsipSep psip_sep( psi.f());
388 for( unsigned i=0; i<4; i++)
389 {
390 psip_sep.set_Z( Z_0[i]);
391 dg::bisection1d( psip_sep, R_min[i], R_max[i], 1e-13);
392 R_i[i] = (R_min[i]+R_max[i])/2., Z_i[i] = Z_0[i];
393 if(m_verbose)std::cout << "Found "<<i+1<<"st point "<<R_i[i]
394 <<" "<<Z_i[i]<<"\n";
395 }
396 //now measure y distance to X-point
397 std::array<double, 3> begin2d{ {0,0,0} }, end2d(begin2d);
398 for( int i=0; i<4; i++)
399 {
400 unsigned N = 1;
401 begin2d[0] = end2d[0] = R_i[i];
402 begin2d[1] = end2d[1] = Z_i[i];
403 begin2d[2] = end2d[2] = 0.;
404 double eps = 1e10, eps_old = 2e10;
405 //double y=0, y_old=0;
406 //difference to X-point isn't much better than 1e-5
407 while( (eps < eps_old || eps > 5e-5))
408 {
409 eps_old = eps; N*=2;// y_old=y;
410 using Vec = std::array<double,3>;
412 if( mode_ == 0)
413 odeint.construct( dg::RungeKutta<Vec>( "Fehlberg-6-4-5",
414 begin2d), fieldRZYZconf_);
415 if(mode_==1)
416 odeint.construct( dg::RungeKutta<Vec>( "Fehlberg-6-4-5",
417 begin2d), fieldRZYZequi_);
418 odeint.integrate_steps( Z_i[i], begin2d,
419 Z_X, end2d, N);
420 //y=end2d[2];
421 //eps = fabs((y-y_old)/y_old);
422 eps = sqrt( (end2d[0]-R_X)*(end2d[0]-R_X))/R_X;
423 if( std::isnan(eps)) { eps = eps_old/2.; }
424 //std::cout << "Found y_i["<<i<<"]: "<<y<<" with eps = "<<eps<<" and "<<N<<" steps and diff "<<fabs(end2d[0]-R_X)/R_X<<"\n";
425 }
426 //remember last call
427 y_i[i] = end2d[2];
428 if( i==0 || i == 2)
429 y_i[i] *= -1;//these were integrated against y direction
430 if(m_verbose)std::cout << "Found |y_i["<<i<<"]|: "<<y_i[i]<<" with eps = "<<eps<<" and "<<N<<" steps and diff "<<fabs(end2d[0]-R_X)/R_X<<"\n";
431 }
432
433 f_psi_ = construct_f( );
434 y_i[0]*=f_psi_, y_i[1]*=f_psi_, y_i[2]*=f_psi_, y_i[3]*=f_psi_;
435 fieldRZYequi_.set_f(f_psi_);
436 fieldRZYconf_.set_f(f_psi_);
437 }
438
439 double get_f( ) const{return f_psi_;}
440
441 //compute the vector of r and z - values that form the separatrix
442 void compute_rzy( const thrust::host_vector<double>& y_vec,
443 const unsigned nodeX0, const unsigned nodeX1,
444 thrust::host_vector<double>& r, //same size as y_vec on output
445 thrust::host_vector<double>& z ) const
446 {
448 std::array<double, 2> begin{ {0,0} }, end(begin), temp(begin);
449 thrust::host_vector<double> r_old(y_vec.size(), 0), r_diff( r_old);
450 thrust::host_vector<double> z_old(y_vec.size(), 0), z_diff( z_old);
451 r.resize( y_vec.size()), z.resize(y_vec.size());
452 unsigned steps = 1; double eps = 1e10, eps_old=2e10;
453 using Vec = std::array<double,2>;
455 if( mode_ == 0)
456 odeint.construct( dg::RungeKutta<Vec>("Feagin-17-8-10", begin),
457 fieldRZYconf_);
458 if( mode_ == 1)
459 odeint.construct( dg::RungeKutta<Vec>("Feagin-17-8-10", begin),
460 fieldRZYequi_);
461 while( (eps < eps_old||eps > 1e-7) && eps > 1e-11)
462 {
463 eps_old = eps, r_old = r, z_old = z;
465 if( nodeX0 != 0) //integrate to start point
466 {
467 begin[0] = R_i[3], begin[1] = Z_i[3];
468 odeint.integrate_steps( -y_i[3], begin, y_vec[nodeX0-1], end,
469 N_steps_);
470 r[nodeX0-1] = end[0], z[nodeX0-1] = end[1];
471 }
472 for( int i=nodeX0-2; i>=0; i--)
473 {
474 temp = end;
475 odeint.integrate_steps( y_vec[i+1], temp, y_vec[i], end,steps);
476 r[i] = end[0], z[i] = end[1];
477 }
479 begin[0] = R_i[0], begin[1] = Z_i[0];
480 odeint.integrate_steps( y_i[0], begin, y_vec[nodeX0], end, N_steps_);
481 r[nodeX0] = end[0], z[nodeX0] = end[1];
482 for( unsigned i=nodeX0+1; i<nodeX1; i++)
483 {
484 temp = end;
485 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
486 r[i] = end[0], z[i] = end[1];
487 }
488 temp = end;
489 odeint.integrate_steps( y_vec[nodeX1-1], temp, 2.*M_PI-y_i[1], end,
490 N_steps_);
491 eps = sqrt( (end[0]-R_i[1])*(end[0]-R_i[1]) +
492 (end[1]-Z_i[1])*(end[1]-Z_i[1]));
493 //std::cout << "abs. error is "<<eps<<" with "<<steps<<" steps\n";
495
496 if( nodeX0!= 0)
497 {
498 begin[0] = R_i[2], begin[1] = Z_i[2];
499 odeint.integrate_steps( 2.*M_PI+y_i[2], begin, y_vec[nodeX1],
500 end, N_steps_);
501 r[nodeX1] = end[0], z[nodeX1] = end[1];
502 }
503 for( unsigned i=nodeX1+1; i<y_vec.size(); i++)
504 {
505 temp = end;
506 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
507 r[i] = end[0], z[i] = end[1];
508 }
509 //compute error in R,Z only
510 dg::blas1::axpby( 1., r, -1., r_old, r_diff);
511 dg::blas1::axpby( 1., z, -1., z_old, z_diff);
512 double er = dg::blas1::dot( r_diff, r_diff);
513 double ez = dg::blas1::dot( z_diff, z_diff);
514 double ar = dg::blas1::dot( r, r);
515 double az = dg::blas1::dot( z, z);
516 eps = sqrt( er + ez)/sqrt(ar+az);
517 if(m_verbose)std::cout << "rel. Separatrix error is "<<eps<<" with "<<steps<<" steps\n";
518 steps*=2;
519 }
520 r = r_old, z = z_old;
521 }
522 private:
523 //compute f for psi=0
524 double construct_f( )
525 {
526 if(m_verbose)std::cout << "In construct f function!\n";
527
528 std::array<double, 3> begin{ {0,0,0} }, end(begin), end_old(begin);
529 begin[0] = R_i[0], begin[1] = Z_i[0];
530 double eps = 1e10, eps_old = 2e10;
531 unsigned N = 32;
532 using Vec = std::array<double,3>;
533 dg::SinglestepTimeloop<Vec> odeintT, odeintZ;
534 if( mode_ == 0)
535 {
537 "Feagin-17-8-10", begin), fieldRZYTconf_);
539 "Feagin-17-8-10", begin), fieldRZYZconf_);
540 }
541 if( mode_ == 1)
542 {
544 "Feagin-17-8-10", begin), fieldRZYTequi_);
546 "Feagin-17-8-10", begin), fieldRZYZequi_);
547 }
548 while( (eps < eps_old || eps > 1e-7) && N < 1e6)
549 {
550 eps_old = eps, end_old = end;
551 N*=2;
552 odeintZ.integrate_steps( begin[1], begin, 0., end, N);
553 std::array<double,3> temp(end);
554 odeintT.integrate_steps( 0., temp, M_PI, end, N);
555 temp = end;
556 odeintZ.integrate_steps( temp[1], temp, Z_i[1], end, N);
557 eps = sqrt( (end[0]-R_i[1])*(end[0]-R_i[1]) + (end[1]-Z_i[1])*(end[1]-Z_i[1]));
558 //std::cout << "Found end[2] = "<< end_old[2]<<" with eps = "<<eps<<"\n";
559 if( std::isnan(eps)) { eps = eps_old/2.; end = end_old; }
560 }
561 N_steps_=N;
562 if(m_verbose)std::cout << "Found end[2] = "<< end_old[2]<<" with eps = "<<eps<<"\n";
563 if(m_verbose)std::cout << "Found f = "<< 2.*M_PI/(y_i[0]+end_old[2]+y_i[1])<<" with eps = "<<eps<<"\n";
564 f_psi_ = 2.*M_PI/(y_i[0]+end_old[2]+y_i[1]);
565 return f_psi_;
566 }
567 int mode_;
568 dg::geo::equalarc::FieldRZY fieldRZYequi_;
569 dg::geo::equalarc::FieldRZYT fieldRZYTequi_;
570 dg::geo::equalarc::FieldRZYZ fieldRZYZequi_;
571 dg::geo::ribeiro::FieldRZY fieldRZYconf_;
572 dg::geo::ribeiro::FieldRZYT fieldRZYTconf_;
573 dg::geo::ribeiro::FieldRZYZ fieldRZYZconf_;
574 unsigned N_steps_;
575 double R_i[4], Z_i[4], y_i[4];
576 double f_psi_;
577 bool m_verbose;
578
579};
580} //namespace detail
581namespace orthogonal
582{
583namespace detail
584{
585//find points on the perp line through the X-point
586struct InitialX
587{
588
589 InitialX( const CylindricalFunctorsLvl1& psi, double xX, double yX, bool verbose = false):
590 psip_(psi), fieldRZtau_(psi),
591 xpointer_(psi, xX, yX, 1e-4), m_verbose( verbose)
592 {
593 //constructor finds four points around X-point and integrates them a bit away from it
594 dg::geo::FieldRZtau fieldRZtau_(psi);
595 using Vec = std::array<double,2>;
597 "Fehlberg-6-4-5", Vec{0.,0.}), fieldRZtau_);
598 double eps[] = {1e-11, 1e-12, 1e-11, 1e-12};
599 for( unsigned i=0; i<4; i++)
600 {
601 xpointer_.set_quadrant( i);
602 double x_min = -1e-4, x_max = 1e-4;
603 dg::bisection1d( xpointer_, x_min, x_max, eps[i]);
604 xpointer_.point( R_i_[i], Z_i_[i], (x_min+x_max)/2.);
605 //std::cout << "Found initial point: "<<R_i_[i]<<" "<<Z_i_[i]<<" "<<psip_(R_i_[i], Z_i_[i])<<"\n";
606 std::array<double,2> begin, end, end_old;
607 begin[0] = R_i_[i], begin[1] = Z_i_[i];
608 double eps = 1e10, eps_old = 2e10;
609 unsigned N=10;
610 double psi0 = psip_.f()(begin[0], begin[1]), psi1 = 1e3*psi0;
611 while( (eps < eps_old || eps > 1e-5 ) && eps > 1e-9)
612 {
613 eps_old = eps; end_old = end;
614 N*=2;
615 odeint.integrate_steps( psi0, begin, psi1, end, N); //lower order integrator is better for difficult field
616
617 eps = sqrt( (end[0]-end_old[0])*(end[0]-end_old[0]) + (end[1]-end_old[1])*(end[1]-end_old[1]));
618 if( std::isnan(eps)) { eps = eps_old/2.; end = end_old; }
619 //std::cout << " for N "<< N<<" eps is "<<eps<<"\n";
620 }
621 R_i_[i] = end_old[0], Z_i_[i] = end_old[1];
622 begin[0] = R_i_[i], begin[1] = Z_i_[i];
623 eps = 1e10, eps_old = 2e10; N=10;
624 psi0 = psip_.f()(begin[0], begin[1]), psi1 = -0.01;
625 if( i==0||i==2)psi1*=-1.;
626 while( (eps < eps_old || eps > 1e-5 ) && eps > 1e-9)
627 {
628 eps_old = eps; end_old = end;
629 N*=2;
630 odeint.integrate_steps( psi0, begin, psi1, end, N); //lower order integrator is better for difficult field
631
632 eps = sqrt( (end[0]-end_old[0])*(end[0]-end_old[0]) + (end[1]-end_old[1])*(end[1]-end_old[1]));
633 if( std::isnan(eps)) { eps = eps_old/2.; end = end_old; }
634 //std::cout << " for N "<< N<<" eps is "<<eps<<"\n";
635 }
636 R_i_[i] = end_old[0], Z_i_[i] = end_old[1];
637 if(m_verbose)std::cout << "Quadrant "<<i<<" Found initial point: "<<R_i_[i]<<" "<<Z_i_[i]<<" "<<psip_.f()(R_i_[i], Z_i_[i])<<"\n";
638
639 }
640 }
648 void find_initial( double psi, double* R_0, double* Z_0)
649 {
650 std::array<double, 2> begin{ {0,0} }, end(begin), end_old(begin);
651 for( unsigned i=0; i<2; i++)
652 {
653 if(psi<0)
654 {
655 begin[0] = R_i_[2*i+1], begin[1] = Z_i_[2*i+1]; end = begin;
656 }
657 else
658 {
659 begin[0] = R_i_[2*i], begin[1] = Z_i_[2*i]; end = begin;
660 }
661 unsigned steps = 1;
662 double eps = 1e10, eps_old=2e10;
663 using Vec = std::array<double,2>;
665 "Fehlberg-6-4-5", Vec{0.,0.}), fieldRZtau_);
666 while( (eps < eps_old||eps > 1e-7) && eps > 1e-11)
667 {
668 eps_old = eps; end_old = end;
669 odeint.integrate_steps( psip_.f()(begin[0], begin[1]), begin,
670 psi, end, steps);
671 eps = sqrt( (end[0]-end_old[0])*(end[0]- end_old[0]) +
672 (end[1]-end_old[1])*(end[1]-end_old[1]));
673 //std::cout << "rel. error is "<<eps<<" with "<<steps<<" steps\n";
674 if( std::isnan(eps)) { eps = eps_old/2.; end = end_old; }
675 steps*=2;
676 }
677 //std::cout << "Found initial point "<<end_old[0]<<" "<<end_old[1]<<"\n";
678 if( psi<0)
679 {
680 R_0[i] = R_i_[2*i+1] = begin[0] = end_old[0], Z_i_[2*i+1] =
681 Z_0[i] = begin[1] = end_old[1];
682 }
683 else
684 {
685 R_0[i] = R_i_[2*i] = begin[0] = end_old[0], Z_i_[2*i] = Z_0[i]
686 = begin[1] = end_old[1];
687 }
688
689 }
690 }
691
692
693 private:
694 CylindricalFunctorsLvl1 psip_;
695 const dg::geo::FieldRZtau fieldRZtau_;
696 dg::geo::detail::XCross xpointer_;
697 double R_i_[4], Z_i_[4];
698 bool m_verbose;
699
700};
701}//namespace detail
702}//namespace orthogonal
704} //namespace geo
705} //namespace dg
706
#define M_PI
void axpby(get_value_type< ContainerType > alpha, const ContainerType1 &x, get_value_type< ContainerType > beta, ContainerType &y)
get_value_type< ContainerType1 > dot(const ContainerType1 &x, const ContainerType2 &y)
RealCylindricalFunctor< double > CylindricalFunctor
Most of the times we use double.
Definition: fluxfunctions.h:50
int bisection1d(UnaryOp &op, double &x_min, double &x_max, const double eps)
static void findXpoint(const CylindricalFunctorsLvl2 &psi, double &RC, double &ZC)
This function finds X-points of psi.
Definition: fluxfunctions.h:350
static CylindricalSymmTensorLvl1 make_Xconst_monitor(const CylindricalFunctorsLvl2 &psi, double &R_X, double &Z_X)
construct a monitor metric in which the Laplacian vanishes at the X-point
Definition: utilitiesX.h:92
static CylindricalSymmTensorLvl1 make_Xbump_monitor(const CylindricalFunctorsLvl2 &psi, double &R_X, double &Z_X, double radiusX, double radiusY)
construct a monitor metric in which the Laplacian vanishes at the X-point
Definition: utilitiesX.h:58
void construct(Params &&...ps)
void integrate_steps(value_type t0, const container_type &u0, value_type t1, container_type &u1, unsigned steps)
Definition: fluxfunctions.h:114
This struct bundles a function and its first and second derivatives.
Definition: fluxfunctions.h:219
const CylindricalFunctor & dfxy() const
Definition: fluxfunctions.h:251
const CylindricalFunctor & dfxx() const
Definition: fluxfunctions.h:249
const CylindricalFunctor & dfyy() const
Definition: fluxfunctions.h:253
Definition: fluxfunctions.h:361