Extension: Geometries
#include "dg/geometries/geometries.h"
separatrix_orthogonal.h
Go to the documentation of this file.
1#pragma once
2
3#include "dg/algorithm.h"
4#include "generator.h"
5#include "generatorX.h"
6#include "utilitiesX.h"
7
8#include "simple_orthogonal.h"
9
10
11
12namespace dg
13{
14namespace geo
15{
17namespace orthogonal
18{
19
20namespace detail
21{
22
23//compute the vector of r and z - values that form one psi surface
24//assumes y_0 = 0
25template<class real_type>
26void computeX_rzy( const CylindricalFunctorsLvl1& psi,
27 const thrust::host_vector<real_type>& y_vec,
28 const unsigned nodeX0, const unsigned nodeX1,
29 thrust::host_vector<real_type>& r, //output r - values
30 thrust::host_vector<real_type>& z, //output z - values
31 const real_type* R_init, const real_type* Z_init, //2 input coords on perp line
32 real_type f_psi, //input f
33 int mode, bool verbose = false )
34{
35 thrust::host_vector<real_type> r_old(y_vec.size(), 0), r_diff( r_old);
36 thrust::host_vector<real_type> z_old(y_vec.size(), 0), z_diff( z_old);
37 r.resize( y_vec.size()), z.resize(y_vec.size());
38 std::array<real_type,2> begin{ {0,0} }, end(begin), temp(begin);
39 begin[0] = R_init[0], begin[1] = Z_init[0];
40 dg::geo::ribeiro::FieldRZY fieldRZYconf(psi);
41 dg::geo::equalarc::FieldRZY fieldRZYequi(psi);
42 fieldRZYconf.set_f(f_psi);
43 fieldRZYequi.set_f(f_psi);
44 unsigned steps = 1; real_type eps = 1e10, eps_old=2e10;
45 using Vec = std::array<real_type,2>;
47 if( mode == 0)
48 odeint.construct( dg::RungeKutta<Vec>( "Feagin-17-8-10", begin),
49 fieldRZYconf);
50 if(mode == 1)
51 odeint.construct( dg::RungeKutta<Vec>( "Feagin-17-8-10", begin),
52 fieldRZYequi);
53 while( (eps < eps_old||eps > 1e-7) && eps > 1e-11)
54 {
55 eps_old = eps, r_old = r, z_old = z;
57 if( nodeX0 != 0)
58 {
59 begin[0] = R_init[1], begin[1] = Z_init[1];
60 odeint.integrate_steps( 0, begin, y_vec[nodeX0-1], end, steps);
61 r[nodeX0-1] = end[0], z[nodeX0-1] = end[1];
62 }
63 for( int i=nodeX0-2; i>=0; i--)
64 {
65 temp = end;
66 odeint.integrate_steps( y_vec[i+1], temp, y_vec[i], end, steps);
67 r[i] = end[0], z[i] = end[1];
68 }
70 begin[0] = R_init[0], begin[1] = Z_init[0];
71 odeint.integrate_steps( 0, begin, y_vec[nodeX0], end, steps);
72 r[nodeX0] = end[0], z[nodeX0] = end[1];
73 for( unsigned i=nodeX0+1; i<nodeX1; i++)
74 {
75 temp = end;
76 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
77 r[i] = end[0], z[i] = end[1];
78 }
79 temp = end;
80 odeint.integrate_steps( y_vec[nodeX1-1], temp, 2.*M_PI, end, steps);
81 eps = sqrt( (end[0]-R_init[0])*(end[0]-R_init[0]) + (end[1]-Z_init[0])*(end[1]-Z_init[0]));
82 if(verbose)std::cout << "abs. error is "<<eps<<" with "<<steps<<" steps\n";
84 if( nodeX0!= 0)
85 {
86 begin[0] = R_init[1], begin[1] = Z_init[1];
87 odeint.integrate_steps( 2.*M_PI, begin, y_vec[nodeX1], end, steps);
88 r[nodeX1] = end[0], z[nodeX1] = end[1];
89 }
90 for( unsigned i=nodeX1+1; i<y_vec.size(); i++)
91 {
92 temp = end;
93 odeint.integrate_steps( y_vec[i-1], temp, y_vec[i], end, steps);
94 r[i] = end[0], z[i] = end[1];
95 }
96 //compute error in R,Z only
97 dg::blas1::axpby( 1., r, -1., r_old, r_diff);
98 dg::blas1::axpby( 1., z, -1., z_old, z_diff);
99 real_type er = dg::blas1::dot( r_diff, r_diff);
100 real_type ez = dg::blas1::dot( z_diff, z_diff);
101 real_type ar = dg::blas1::dot( r, r);
102 real_type az = dg::blas1::dot( z, z);
103 eps = sqrt( er + ez)/sqrt(ar+az);
104 if(verbose)std::cout << "rel. error is "<<eps<<" with "<<steps<<" steps\n";
105 steps*=2;
106 }
107 r = r_old, z = z_old;
108}
109
110
111} //namespace detail
112}//namespace orthogonal
113
120struct SimpleOrthogonalX : public aGeneratorX2d
121{
122 SimpleOrthogonalX(): f0_(1), firstline_(0){}
123 SimpleOrthogonalX( const CylindricalFunctorsLvl2& psi, double psi_0,
124 double xX, double yX, double x0, double y0, int firstline =0): psi_(psi)
125 {
126 firstline_ = firstline;
127 orthogonal::detail::Fpsi fpsi(psi_, CylindricalSymmTensorLvl1(), x0, y0, firstline);
128 double R0, Z0;
129 f0_ = fpsi.construct_f( psi_0, R0, Z0);
130 zeta0_=f0_*psi_0;
131 dg::geo::orthogonal::detail::InitialX initX(psi_, xX, yX);
132 initX.find_initial(psi_0, R0_, Z0_);
133 }
134 virtual SimpleOrthogonalX* clone()const override final{return new SimpleOrthogonalX(*this);}
135 private:
136 bool isConformal()const{return false;}
137 virtual bool do_isOrthogonal()const override final{return true;}
138 double f0() const{return f0_;}
139 virtual void do_generate( //this one doesn't know if the separatrix comes to lie on a cell boundary or not
140 const thrust::host_vector<double>& zeta1d,
141 const thrust::host_vector<double>& eta1d,
142 unsigned nodeX0, unsigned nodeX1,
143 thrust::host_vector<double>& x,
144 thrust::host_vector<double>& y,
145 thrust::host_vector<double>& zetaX,
146 thrust::host_vector<double>& zetaY,
147 thrust::host_vector<double>& etaX,
148 thrust::host_vector<double>& etaY) const override final
149 {
150
151 thrust::host_vector<double> r_init, z_init;
152 orthogonal::detail::computeX_rzy( psi_, eta1d, nodeX0, nodeX1, r_init, z_init, R0_, Z0_, f0_, firstline_);
153 dg::geo::orthogonal::detail::Nemov nemov(psi_, CylindricalSymmTensorLvl1(), f0_, firstline_);
154 thrust::host_vector<double> h;
155 orthogonal::detail::construct_rz(nemov, zeta0_, zeta1d, r_init, z_init, x, y, h);
156 unsigned size = x.size();
157 zetaX.resize(size), zetaY.resize(size),
158 etaX.resize(size), etaY.resize(size);
159 for( unsigned idx=0; idx<size; idx++)
160 {
161 double psipR = psi_.dfx()(x[idx], y[idx]);
162 double psipZ = psi_.dfy()(x[idx], y[idx]);
163 zetaX[idx] = f0_*psipR;
164 zetaY[idx] = f0_*psipZ;
165 etaX[idx] = -h[idx]*psipZ;
166 etaY[idx] = +h[idx]*psipR;
167 }
168 }
169 virtual double do_zeta0(double fx) const override final{ return zeta0_; }
170 virtual double do_zeta1(double fx) const override final{ return -fx/(1.-fx)*zeta0_;}
171 virtual double do_eta0(double fy) const override final{ return -2.*M_PI*fy/(1.-2.*fy); }
172 virtual double do_eta1(double fy) const override final{ return 2.*M_PI*(1.+fy/(1.-2.*fy));}
173 CylindricalFunctorsLvl2 psi_;
174 double R0_[2], Z0_[2];
175 double zeta0_, f0_;
176 int firstline_;
177};
179
192{
206 SeparatrixOrthogonal( const CylindricalFunctorsLvl2& psi, const CylindricalSymmTensorLvl1& chi, double psi_0, //psi_0 must be the closed surface, 0 the separatrix
207 double xX, double yX, double x0, double y0, int firstline, bool verbose = false ):
208 psi_(psi), chi_(chi),
209 sep_( psi, chi, xX, yX, x0, y0, firstline, verbose)
210 {
211 firstline_ = firstline;
212 f0_ = sep_.get_f();
213 psi_0_=psi_0;
214 }
215 virtual SeparatrixOrthogonal* clone()const override final{return new SeparatrixOrthogonal(*this);}
216 private:
217 bool isConformal()const{return false;}
218 virtual bool do_isOrthogonal()const override final{return false;}
219 double f0() const{return sep_.get_f();}
220 virtual void do_generate( //this one doesn't know if the separatrix comes to lie on a cell boundary or not
221 const thrust::host_vector<double>& zeta1d,
222 const thrust::host_vector<double>& eta1d,
223 unsigned nodeX0, unsigned nodeX1,
224 thrust::host_vector<double>& x,
225 thrust::host_vector<double>& y,
226 thrust::host_vector<double>& zetaX,
227 thrust::host_vector<double>& zetaY,
228 thrust::host_vector<double>& etaX,
229 thrust::host_vector<double>& etaY) const override final
230 {
231
232 thrust::host_vector<double> r_init, z_init;
233 sep_.compute_rzy( eta1d, nodeX0, nodeX1, r_init, z_init);
234 dg::geo::orthogonal::detail::Nemov nemov(psi_, chi_, f0_, firstline_);
235
236 //separate integration of inside and outside
237 unsigned inside=0;
238 for(unsigned i=0; i<zeta1d.size(); i++)
239 if( zeta1d[i]< 0) inside++;//how many points are inside
240 thrust::host_vector<double> zeta1dI( inside, 0), zeta1dO( zeta1d.size() - inside, 0);
241 for( unsigned i=0; i<inside; i++)
242 zeta1dI[i] = zeta1d[ inside-1-i];
243 for( unsigned i=inside; i<zeta1d.size(); i++)
244 zeta1dO[i-inside] = zeta1d[ i];
245 //separate integration close and far from separatrix
246 //this is done due to performance reasons (it takes more steps to integrate close to the X-point)
247 thrust::host_vector<int> idxC, idxF;
248 thrust::host_vector<double> r_initC, r_initF, z_initC, z_initF;
249 for( unsigned i=0; i<eta1d.size(); i++)
250 {
251 if( fabs(eta1d[i]) < 0.05 || fabs( eta1d[i] - 2.*M_PI) < 0.05)
252 {
253 idxC.push_back( i);
254 r_initC.push_back( r_init[i]);
255 z_initC.push_back( z_init[i]);
256 }
257 else
258 {
259 idxF.push_back( i);
260 r_initF.push_back( r_init[i]);
261 z_initF.push_back( z_init[i]);
262 }
263 }
264
265 thrust::host_vector<double> xIC, yIC, hIC, xOC,yOC,hOC;
266 thrust::host_vector<double> xIF, yIF, hIF, xOF,yOF,hOF;
267 orthogonal::detail::construct_rz(nemov, 0., zeta1dI, r_initC, z_initC,
268 xIC, yIC, hIC);
269 orthogonal::detail::construct_rz(nemov, 0., zeta1dO, r_initC, z_initC,
270 xOC, yOC, hOC);
271 orthogonal::detail::construct_rz(nemov, 0., zeta1dI, r_initF, z_initF,
272 xIF, yIF, hIF);
273 orthogonal::detail::construct_rz(nemov, 0., zeta1dO, r_initF, z_initF,
274 xOF, yOF, hOF);
275 //now glue far and close back together
276 thrust::host_vector<double> xI(inside*eta1d.size()), xO( (zeta1d.size()-inside)*eta1d.size());
277 thrust::host_vector<double> yI(xI), hI(xI), yO(xO),hO(xO);
278 for( unsigned i=0; i<idxC.size(); i++)
279 for(unsigned j=0; j<zeta1dI.size(); j++)
280 {
281 xI[idxC[i]*zeta1dI.size() + j] = xIC[i*zeta1dI.size() + j];
282 yI[idxC[i]*zeta1dI.size() + j] = yIC[i*zeta1dI.size() + j];
283 hI[idxC[i]*zeta1dI.size() + j] = hIC[i*zeta1dI.size() + j];
284 }
285 for( unsigned i=0; i<idxF.size(); i++)
286 for(unsigned j=0; j<zeta1dI.size(); j++)
287 {
288 xI[idxF[i]*zeta1dI.size() + j] = xIF[i*zeta1dI.size() + j];
289 yI[idxF[i]*zeta1dI.size() + j] = yIF[i*zeta1dI.size() + j];
290 hI[idxF[i]*zeta1dI.size() + j] = hIF[i*zeta1dI.size() + j];
291 }
292 for( unsigned i=0; i<idxC.size(); i++)
293 for(unsigned j=0; j<zeta1dO.size(); j++)
294 {
295 xO[idxC[i]*zeta1dO.size() + j] = xOC[i*zeta1dO.size() + j];
296 yO[idxC[i]*zeta1dO.size() + j] = yOC[i*zeta1dO.size() + j];
297 hO[idxC[i]*zeta1dO.size() + j] = hOC[i*zeta1dO.size() + j];
298 }
299 for( unsigned i=0; i<idxF.size(); i++)
300 for(unsigned j=0; j<zeta1dO.size(); j++)
301 {
302 xO[idxF[i]*zeta1dO.size() + j] = xOF[i*zeta1dO.size() + j];
303 yO[idxF[i]*zeta1dO.size() + j] = yOF[i*zeta1dO.size() + j];
304 hO[idxF[i]*zeta1dO.size() + j] = hOF[i*zeta1dO.size() + j];
305 }
306
307 //now glue inside and outside together
308 unsigned size = zeta1d.size()*eta1d.size();
309 x.resize( size); y.resize( size);
310 thrust::host_vector<double> h(size);
311 for( unsigned i=0; i<eta1d.size(); i++)
312 for( unsigned j=0; j<inside; j++)
313 {
314 x[i*zeta1d.size()+j] = xI[i*zeta1dI.size() + inside-1-j];
315 y[i*zeta1d.size()+j] = yI[i*zeta1dI.size() + inside-1-j];
316 h[i*zeta1d.size()+j] = hI[i*zeta1dI.size() + inside-1-j];
317 }
318 for( unsigned i=0; i<eta1d.size(); i++)
319 for( unsigned j=inside; j<zeta1d.size(); j++)
320 {
321 x[i*zeta1d.size()+j] = xO[i*zeta1dO.size() + j-inside];
322 y[i*zeta1d.size()+j] = yO[i*zeta1dO.size() + j-inside];
323 h[i*zeta1d.size()+j] = hO[i*zeta1dO.size() + j-inside];
324 }
325
326 zetaX.resize(size), zetaY.resize(size),
327 etaX.resize(size), etaY.resize(size);
328 for( unsigned idx=0; idx<size; idx++)
329 {
330 double psipX = psi_.dfx()(x[idx], y[idx]);
331 double psipY = psi_.dfy()(x[idx], y[idx]);
332 double chiXX = chi_.xx()( x[idx], y[idx]),
333 chiXY = chi_.xy()( x[idx], y[idx]),
334 chiYY = chi_.yy()( x[idx], y[idx]);
335 zetaX[idx] = f0_*psipX;
336 zetaY[idx] = f0_*psipY;
337 etaX[idx] = -h[idx]*(chiXY*psipX + chiYY*psipY);
338 etaY[idx] = +h[idx]*(chiXX*psipX + chiXY*psipY);
339 }
340 }
341 virtual double do_zeta0(double fx) const override final{ return f0_*psi_0_; }
342 virtual double do_zeta1(double fx) const override final{ return -fx/(1.-fx)*f0_*psi_0_;}
343 virtual double do_eta0(double fy) const override final{ return -2.*M_PI*fy/(1.-2.*fy); }
344 virtual double do_eta1(double fy) const override final{ return 2.*M_PI*(1.+fy/(1.-2.*fy));}
345 private:
346 double f0_, psi_0_;
347 int firstline_;
348 CylindricalFunctorsLvl2 psi_;
349 CylindricalSymmTensorLvl1 chi_;
350 dg::geo::detail::SeparatriX sep_;
351};
352
360{
365 SeparatrixOrthogonalAdaptor( const CylindricalFunctorsLvl2& psi, const CylindricalSymmTensorLvl1& chi, double psi_0, //psi_0 must be the closed surface, 0 the separatrix
366 double xX, double yX, double x0, double y0, int firstline, bool verbose = false, double fx = 0 ):
367 m_fx(fx),
368 m_sep( psi, chi, psi_0, xX, yX, x0, y0, firstline, verbose) { }
369 virtual SeparatrixOrthogonalAdaptor* clone() const override final{return new SeparatrixOrthogonalAdaptor(*this);}
370 private:
371 virtual double do_width() const override final{
372 return m_sep.zeta1(m_fx) - m_sep.zeta0(m_fx);
373 }
374 virtual double do_height() const override final{return 2.*M_PI;}
375 virtual bool do_isOrthogonal()const override final{return false;}
376 virtual void do_generate(
377 const thrust::host_vector<double>& zeta1d,
378 const thrust::host_vector<double>& eta1d,
379 thrust::host_vector<double>& x,
380 thrust::host_vector<double>& y,
381 thrust::host_vector<double>& zetaX,
382 thrust::host_vector<double>& zetaY,
383 thrust::host_vector<double>& etaX,
384 thrust::host_vector<double>& etaY) const override final
385 {
386 double zeta0 = m_sep.zeta0(m_fx);
387 auto zeta1d_trafo(zeta1d);
388 // zeta1d is given between [0; zeta1-zeta0] and must be transformed to [zeta0; zeta1]
389 for( unsigned i=0; i<zeta1d.size(); i++)
390 zeta1d_trafo[i] += zeta0;
391 if( !dg::is_divisable( (double)zeta1d.size(), 1./m_fx ))
392 throw dg::Error( dg::Message(_ping_) << "Size of zeta1d "<<zeta1d.size()<<"is not divisable by fx "<<m_fx);
393 m_sep.generate( zeta1d_trafo, eta1d, 0, eta1d.size(), x, y, zetaX, zetaY, etaX, etaY);
394 }
395 double m_fx;
396 SeparatrixOrthogonal m_sep;
397};
398
399}//namespace geo
400}//namespace dg
401
#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)
bool is_divisable(double a, double b, double eps=1e-15)
dg::geo::aRealGeneratorX2d< double > aGeneratorX2d
Definition: generatorX.h:93
void construct(Params &&...ps)
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:219
const CylindricalFunctor & dfy() const
Definition: fluxfunctions.h:247
const CylindricalFunctor & dfx() const
Definition: fluxfunctions.h:245
Definition: fluxfunctions.h:361
const CylindricalFunctor & yy() const
yy component
Definition: fluxfunctions.h:400
const CylindricalFunctor & xy() const
xy component
Definition: fluxfunctions.h:398
const CylindricalFunctor & xx() const
xy component
Definition: fluxfunctions.h:396
An Adaptor to use SeparatrixOrthogonal as aGenerator2d instead of aGeneratorX2d.
Definition: separatrix_orthogonal.h:360
SeparatrixOrthogonalAdaptor(const CylindricalFunctorsLvl2 &psi, const CylindricalSymmTensorLvl1 &chi, double psi_0, double xX, double yX, double x0, double y0, int firstline, bool verbose=false, double fx=0)
Construct.
Definition: separatrix_orthogonal.h:365
virtual SeparatrixOrthogonalAdaptor * clone() const override final
Abstract clone method that returns a copy on the heap.
Definition: separatrix_orthogonal.h:369
Choose points on separatrix and construct grid from there.
Definition: separatrix_orthogonal.h:192
virtual SeparatrixOrthogonal * clone() const override final
Abstract clone method that returns a copy on the heap.
Definition: separatrix_orthogonal.h:215
SeparatrixOrthogonal(const CylindricalFunctorsLvl2 &psi, const CylindricalSymmTensorLvl1 &chi, double psi_0, double xX, double yX, double x0, double y0, int firstline, bool verbose=false)
Construct.
Definition: separatrix_orthogonal.h:206
The abstract generator base class.
Definition: generator.h:20
The abstract generator base class.
Definition: generatorX.h:19
real_type zeta0(real_type fx) const
Definition: generatorX.h:20
real_type zeta1(real_type fx) const
Definition: generatorX.h:21
void generate(const thrust::host_vector< real_type > &zeta1d, const thrust::host_vector< real_type > &eta1d, unsigned nodeX0, unsigned nodeX1, thrust::host_vector< real_type > &x, thrust::host_vector< real_type > &y, thrust::host_vector< real_type > &zetaX, thrust::host_vector< real_type > &zetaY, thrust::host_vector< real_type > &etaX, thrust::host_vector< real_type > &etaY) const
Generate grid points and elements of the Jacobian.
Definition: generatorX.h:43