gs2crmod 0.11.20 → 0.11.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/ext/gs2crmod_ext.c +407 -0
- data/gs2crmod.gemspec +4 -6
- data/include/gs2crmod_ext.h +13 -0
- data/lib/gs2crmod/gs2.rb +84 -0
- data/lib/gs2crmod/gsl_data_3d.rb +2 -2
- data/test/test_gs2crmod.rb +6 -0
- metadata +37 -56
- checksums.yaml +0 -7
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.11.
|
1
|
+
0.11.21
|
data/ext/gs2crmod_ext.c
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
#include "gs2crmod_ext.h"
|
2
|
+
#include <math.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <gsl/gsl_spline.h>
|
5
|
+
#include <gsl/gsl_spline.h>
|
2
6
|
|
3
7
|
VALUE gs2crmod_tensor_complexes_field_gsl_tensor_complex_2(VALUE self, VALUE options)
|
4
8
|
{
|
@@ -345,6 +349,408 @@ VALUE gs2crmod_tensor_field_gsl_tensor(VALUE self, VALUE options)
|
|
345
349
|
return field_real_space;
|
346
350
|
}
|
347
351
|
|
352
|
+
inline int arr_index(int ix, int iy, int ith, int it, int *c_shape, int t_size)
|
353
|
+
{
|
354
|
+
return ix*c_shape[0]*c_shape[2]*t_size + iy*c_shape[0]*t_size + ith*t_size + it;
|
355
|
+
}
|
356
|
+
|
357
|
+
//Calculates normalized correlation function of 2 time series and stores result
|
358
|
+
void correlate_norm(double *A, double *B, double *C, int t_size)
|
359
|
+
{
|
360
|
+
//printf("%lf, %lf, %d\n", A[0], B[0], size);
|
361
|
+
|
362
|
+
int i, j, jmax;
|
363
|
+
double temp_sum, norm_a=0., norm_b=0.;
|
364
|
+
|
365
|
+
//printf("%lf, %lf, %lf, %d\n", A[0], B[0], C[0], t_size);
|
366
|
+
|
367
|
+
//Sum over appropriate terms to get vector of correlations as function of time delay
|
368
|
+
//Split into two loops instead of trying to be too smart with the indices.
|
369
|
+
for(i=0; i<=t_size; i++){
|
370
|
+
temp_sum=0;
|
371
|
+
for(j=0; j<=i; j++)
|
372
|
+
{
|
373
|
+
temp_sum += A[j] * B[t_size - i - 1 + j];
|
374
|
+
}
|
375
|
+
C[i] = temp_sum;
|
376
|
+
}
|
377
|
+
//Second half
|
378
|
+
for(i=t_size+1; i<2*t_size-1; i++){
|
379
|
+
jmax = 2*t_size - 2 - i;
|
380
|
+
temp_sum=0;
|
381
|
+
for(j=0; j<=2*t_size-2-i; j++){
|
382
|
+
temp_sum += A[t_size - jmax - 1 + j] * B[j];
|
383
|
+
}
|
384
|
+
C[i] = temp_sum;
|
385
|
+
}
|
386
|
+
|
387
|
+
// for(i=0; i<t_size; i++)
|
388
|
+
// printf("%lf, ", A[i]);
|
389
|
+
// printf("\n\n");
|
390
|
+
// for(i=0; i<2*t_size-1; i++)
|
391
|
+
// printf("%lf, ", C[i]);
|
392
|
+
// printf("\n\ntemp_sum=%lf, Cmax=%lf\n", temp_sum, C[50]);
|
393
|
+
|
394
|
+
//Normalize each array with its zero time delay correlation value
|
395
|
+
for(i=0; i<t_size; i++){
|
396
|
+
norm_a += A[i]*A[i];
|
397
|
+
norm_b += B[i]*B[i];
|
398
|
+
}
|
399
|
+
//Final answer written to C array
|
400
|
+
for(i=0; i<2*t_size-1; i++)
|
401
|
+
C[i] = C[i]/sqrt(norm_a * norm_b);
|
402
|
+
}
|
403
|
+
|
404
|
+
VALUE gs2crmod_tensor_field_correlation_gsl_tensor(VALUE self, VALUE options)
|
405
|
+
{
|
406
|
+
VALUE field_graphkit, shape, datakit, datakit_array;
|
407
|
+
VALUE x_narray, y_narray, z_narray, field_narray;
|
408
|
+
VALUE t_gsl_vector;
|
409
|
+
//Write some as floats to avoid flooring to wrong (lower) number later
|
410
|
+
float *lx_bin, *ly_bin, *lz_bin, *lt_bin;
|
411
|
+
float lxmin, lxmax, lymin, lymax, lzmin, lzmax, ltmin, ltmax, lx, ly, lz, *lt;
|
412
|
+
double *x, *y, *z, *t, *field, *coarray, *corr_norm, amin;
|
413
|
+
int *c_shape, tot_size, ith, ix, iy, it, t_size, first=1;
|
414
|
+
int index, i, i2, i3, i4, tot_bins, *count;
|
415
|
+
long int i1;
|
416
|
+
|
417
|
+
printf("Starting correlation analysis\n");
|
418
|
+
|
419
|
+
/*Find time steps*/
|
420
|
+
/*self.gsl_vector('t')*/
|
421
|
+
//t_gsl_vector = RFCALL_11("gsl_vector", rb_str_new2("t"));
|
422
|
+
t_gsl_vector = rb_funcall(self, rb_intern("gsl_vector"), 2, rb_str_new2("t"), options);
|
423
|
+
t_size = NUM2INT(RFCALL_10_ON(t_gsl_vector, "size"));
|
424
|
+
t = ALLOC_N(double, t_size);
|
425
|
+
for(it=0; it<t_size; it++)
|
426
|
+
{
|
427
|
+
t[it] = NUM2DBL(CR_TELMT_R1(t_gsl_vector, it));
|
428
|
+
}
|
429
|
+
|
430
|
+
//Read from options
|
431
|
+
if(RTEST(CR_HKS(options, "amin")))
|
432
|
+
amin = NUM2DBL(CR_HKS(options, "amin"));
|
433
|
+
else
|
434
|
+
amin = 1.0;
|
435
|
+
|
436
|
+
VALUE nbins_array=CR_HKS(options, "nbins_array");
|
437
|
+
int *nbins;
|
438
|
+
if(RTEST(nbins_array) && RTEST(rb_obj_is_kind_of(nbins_array, RGET_CLASS_TOP("Array")))){
|
439
|
+
CR_INT_ARY_R2C_STACK(nbins_array, nbins);
|
440
|
+
}
|
441
|
+
else
|
442
|
+
rb_raise(RGET_CLASS_TOP("TypeError"), "Please specify nbins_array as a 4D array");
|
443
|
+
|
444
|
+
//correlation_type = options[:correlation_type]
|
445
|
+
//Test which correlation type is to be calculated using ruby string comparison in a proc
|
446
|
+
VALUE test_proc = rb_eval_string("Proc.new {|options| case options[:correlation_type]; when 'perp'; 0; when 'par'; 1; when 'time'; 2; when 'full'; 3 ;else; raise 'Please specify correlation_type as a string (perp/par/time/full)'; end}");
|
447
|
+
int corr_type = FIX2INT(RFCALL_11_ON(test_proc, "call", options));
|
448
|
+
|
449
|
+
for(it=0; it<t_size; it++)
|
450
|
+
{
|
451
|
+
/*options[:t_index] = it+1*/
|
452
|
+
/*options.send("[]=", :t_index, it+1)*/
|
453
|
+
|
454
|
+
//rb_funcall(options, rb_intern("[]="), ID2SYM(rb_intern("t_index")), Qnil);
|
455
|
+
CR_HKS_SET(options, "t_index", INT2FIX(it));
|
456
|
+
|
457
|
+
field_graphkit = RFCALL_11("field_real_space_graphkit", options);
|
458
|
+
|
459
|
+
/*shape = rb_funcall(field_graphkit, rb_intern("shape"), 0);*/
|
460
|
+
datakit_array = RFCALL_10_ON(field_graphkit, "data");
|
461
|
+
datakit = CR_ELEMENT_ACCESS(datakit_array, INT2FIX(0));
|
462
|
+
|
463
|
+
/*Access data: datakit.x.data.narray*/
|
464
|
+
x_narray = RFCALL_10_ON(RFCALL_10_ON(RFCALL_10_ON(datakit, "x"), "data"), "narray");
|
465
|
+
y_narray = RFCALL_10_ON(RFCALL_10_ON(RFCALL_10_ON(datakit, "y"), "data"), "narray");
|
466
|
+
z_narray = RFCALL_10_ON(RFCALL_10_ON(RFCALL_10_ON(datakit, "z"), "data"), "narray");
|
467
|
+
field_narray = RFCALL_10_ON(RFCALL_10_ON(RFCALL_10_ON(datakit, "f"), "data"), "narray");
|
468
|
+
|
469
|
+
shape = RFCALL_10_ON(x_narray, "shape");
|
470
|
+
CR_INT_ARY_R2C_STACK(shape, c_shape);
|
471
|
+
|
472
|
+
if(first)
|
473
|
+
{
|
474
|
+
tot_size = c_shape[0]*c_shape[1]*c_shape[2]*t_size;
|
475
|
+
x = ALLOC_N(double, tot_size);
|
476
|
+
y = ALLOC_N(double, tot_size);
|
477
|
+
z = ALLOC_N(double, tot_size);
|
478
|
+
field = ALLOC_N(double, tot_size);
|
479
|
+
first=0;
|
480
|
+
}
|
481
|
+
|
482
|
+
|
483
|
+
/*Copy NArrays to C arrays*/
|
484
|
+
for(ith=0; ith<c_shape[0]; ith++)
|
485
|
+
for(ix=0; ix<c_shape[1]; ix++)
|
486
|
+
for(iy=0; iy<c_shape[2]; iy++)
|
487
|
+
{
|
488
|
+
index = arr_index(ix,iy,ith,it,c_shape,t_size);
|
489
|
+
x[index] = NUM2DBL(CR_TELMT_R3(x_narray, ith, ix, iy));
|
490
|
+
y[index] = NUM2DBL(CR_TELMT_R3(y_narray, ith, ix, iy));
|
491
|
+
z[index] = NUM2DBL(CR_TELMT_R3(z_narray, ith, ix, iy));
|
492
|
+
field[index] = NUM2DBL(CR_TELMT_R3(field_narray, ith, ix, iy));
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
496
|
+
|
497
|
+
|
498
|
+
/******************************
|
499
|
+
* GSL Interpolation of field *
|
500
|
+
* in time *
|
501
|
+
*****************************/
|
502
|
+
//Read t interp from options (default is 100)
|
503
|
+
int nt_reg;
|
504
|
+
if(RTEST(CR_HKS(options, "nt_reg")))
|
505
|
+
nt_reg = NUM2INT(CR_HKS(options, "nt_reg"));
|
506
|
+
else
|
507
|
+
nt_reg = 100;
|
508
|
+
int idx, ti;
|
509
|
+
double *x_reg, *y_reg, *z_reg, *t_reg, *field_reg, delta_t_reg, *y1;
|
510
|
+
tot_size = c_shape[0]*c_shape[1]*c_shape[2]*nt_reg;
|
511
|
+
//printf("Start interpolation, %d, %d, %d, %d, %d\n", c_shape[0], c_shape[1], c_shape[2], nt_reg, tot_size);
|
512
|
+
delta_t_reg = (t[t_size-1]-t[0])/(nt_reg-1);
|
513
|
+
|
514
|
+
y1 = ALLOC_N(double, t_size);
|
515
|
+
x_reg = ALLOC_N(double, tot_size);
|
516
|
+
y_reg = ALLOC_N(double, tot_size);
|
517
|
+
z_reg = ALLOC_N(double, tot_size);
|
518
|
+
t_reg = ALLOC_N(double, nt_reg);
|
519
|
+
field_reg = ALLOC_N(double, tot_size);
|
520
|
+
|
521
|
+
for(i1=0; i1<c_shape[0]*c_shape[1]*c_shape[2]*t_size; i1+=t_size)
|
522
|
+
{
|
523
|
+
//printf("%d, %d\n", i1, c_shape[0]*c_shape[1]*c_shape[2]*t_size);
|
524
|
+
for (i2 = 0; i2 < t_size; i2++)
|
525
|
+
{
|
526
|
+
y1[i2] = field[i1+i2];
|
527
|
+
//printf ("%d, %lf %lf\n", i2, t[i2], y1[i2]);
|
528
|
+
}
|
529
|
+
|
530
|
+
gsl_interp_accel *acc
|
531
|
+
= gsl_interp_accel_alloc ();
|
532
|
+
gsl_spline *spline
|
533
|
+
= gsl_spline_alloc (gsl_interp_cspline, t_size);
|
534
|
+
|
535
|
+
gsl_spline_init (spline, t, y1, t_size);
|
536
|
+
|
537
|
+
//printf("Interpolated:\n");
|
538
|
+
for (ti = 0; ti < nt_reg; ti++)
|
539
|
+
{
|
540
|
+
t_reg[ti] = t[0]+ti*delta_t_reg;
|
541
|
+
idx = floor((i1)/t_size)*nt_reg;
|
542
|
+
x_reg[idx+ti] = x[i1];
|
543
|
+
y_reg[idx+ti] = y[i1];
|
544
|
+
z_reg[idx+ti] = z[i1];
|
545
|
+
field_reg[idx+ti] = gsl_spline_eval (spline, t_reg[ti], acc);
|
546
|
+
}
|
547
|
+
gsl_spline_free (spline);
|
548
|
+
gsl_interp_accel_free (acc);
|
549
|
+
|
550
|
+
}
|
551
|
+
|
552
|
+
//Can now free the original pointers
|
553
|
+
free(x); x=0;
|
554
|
+
free(y); y=0;
|
555
|
+
free(z); z=0;
|
556
|
+
free(t); t=0;
|
557
|
+
|
558
|
+
/*printf("interpolated:\n");
|
559
|
+
for(i1=0; i1<c_shape[1]; i1++){
|
560
|
+
index = arr_index(i1,0,0,0,c_shape,nt_reg);
|
561
|
+
printf("%lf, ", x_reg[index]);
|
562
|
+
}*/
|
563
|
+
|
564
|
+
/**********************************************************************
|
565
|
+
* First need to know max and min lengths and times in each dimension *
|
566
|
+
* in order to define bins. These need to be specified before the loop*
|
567
|
+
* since the binning will be done at each step since there is too much*
|
568
|
+
* info to store. *
|
569
|
+
* ********************************************************************/
|
570
|
+
double lx_pos_min=20;
|
571
|
+
i3=0;
|
572
|
+
lxmin=0; lymin=0; lzmin=0;
|
573
|
+
for(i1=0; i1<tot_size; i1+=nt_reg){
|
574
|
+
//printf("%lf, %lf, %lf\n", x_reg[i1], y_reg[i1], z_reg[i1]);
|
575
|
+
for(i2=0; i2<tot_size; i2+=nt_reg)
|
576
|
+
{
|
577
|
+
lx = x_reg[i2] - x_reg[i1];
|
578
|
+
ly = y_reg[i2] - y_reg[i1];
|
579
|
+
lz = z_reg[i2] - z_reg[i1];
|
580
|
+
|
581
|
+
if(lx < lxmin)
|
582
|
+
lxmin = lx;
|
583
|
+
if(ly < lymin)
|
584
|
+
lymin = ly;
|
585
|
+
if(lz < lzmin)
|
586
|
+
lzmin = lz;
|
587
|
+
}
|
588
|
+
}
|
589
|
+
lxmax = -lxmin;
|
590
|
+
lymax = -lymin;
|
591
|
+
lzmax = -lzmin;
|
592
|
+
ltmin = t_reg[0] - t_reg[nt_reg-1];
|
593
|
+
ltmax = -ltmin;
|
594
|
+
//printf("\nMin/Max = %lf, %lf\n", lxmin, lxmax);
|
595
|
+
//printf("Min/Max = %lf, %lf\n", lymin, lymax);
|
596
|
+
//printf("Min/Max = %lf, %lf\n", lzmin, lzmax);
|
597
|
+
|
598
|
+
//printf("Bins\n");
|
599
|
+
//Initialize the bin arrays
|
600
|
+
tot_bins = nbins[0]*nbins[1]*nbins[2]*nbins[3];
|
601
|
+
lx_bin = ALLOC_N(double, nbins[0]);
|
602
|
+
ly_bin = ALLOC_N(double, nbins[1]);
|
603
|
+
lz_bin = ALLOC_N(double, nbins[2]);
|
604
|
+
lt_bin = ALLOC_N(double, nbins[3]);
|
605
|
+
|
606
|
+
for(i1=0; i1<nbins[0]; i1++){
|
607
|
+
lx_bin[i1] = lxmin + i1*(lxmax-lxmin)/(nbins[0]-1);
|
608
|
+
//printf("lx_bin = %f\n ", lx_bin[i1]);
|
609
|
+
}
|
610
|
+
for(i1=0; i1<nbins[1]; i1++){
|
611
|
+
ly_bin[i1] = lymin + i1*(lymax-lymin)/(nbins[1]-1);
|
612
|
+
//printf("ly_bin = %f\n", ly_bin[i1]);
|
613
|
+
}
|
614
|
+
for(i1=0; i1<nbins[2]; i1++){
|
615
|
+
lz_bin[i1] = lzmin + i1*(lzmax-lzmin)/(nbins[2]-1);
|
616
|
+
//printf("lz_bin = %f\n", lz_bin[i1]);
|
617
|
+
}
|
618
|
+
for(i1=0; i1<nbins[3]; i1++)
|
619
|
+
lt_bin[i1] = ltmin + i1*(ltmax-ltmin)/(nbins[3]-1);
|
620
|
+
|
621
|
+
//Now define and initialize the coarray and count arrays
|
622
|
+
coarray = ALLOC_N(double, tot_bins);
|
623
|
+
count = ALLOC_N(int, tot_bins);
|
624
|
+
corr_norm = ALLOC_N(double, (2*nt_reg-1)); //store correlate result before adding to coarray
|
625
|
+
lt = ALLOC_N(double, (2*nt_reg-1)); //Can calculate lt before loops
|
626
|
+
|
627
|
+
for(i1=0; i1<tot_bins; i1++){
|
628
|
+
coarray[i1] = 0.;
|
629
|
+
count[i1] = 0;
|
630
|
+
}
|
631
|
+
/******************************************************
|
632
|
+
* Start looping and calculating correlation function *
|
633
|
+
******************************************************/
|
634
|
+
//Can predefine the time separations
|
635
|
+
for(i1=0; i1<2*nt_reg-1; i1++){
|
636
|
+
if(i1<nt_reg)
|
637
|
+
lt[i1] = t_reg[i1] - t_reg[nt_reg-1];
|
638
|
+
else if(i1>nt_reg-1)
|
639
|
+
lt[i1] = t_reg[i1-nt_reg] - t_reg[0] + delta_t_reg;
|
640
|
+
}
|
641
|
+
|
642
|
+
/* Now test which correlation function is to be calculated since full 4D correlation
|
643
|
+
* is usually intractable. The type was read in at begininning and corr_type corresponds to:
|
644
|
+
*
|
645
|
+
* 0 : perpendicular only (lz = 0)
|
646
|
+
* 1 : parallel only (lx = ly = 0)
|
647
|
+
* 2 : time only (lx = lz = 0)
|
648
|
+
* 3 : full correlation (may take very long) (all separations != 0)
|
649
|
+
*/
|
650
|
+
float eps1 = 1e-5, eps2 = 1e5; //define very small and very large numbers to test against
|
651
|
+
float lx_test, ly_test, lz_test;
|
652
|
+
switch (corr_type){
|
653
|
+
case 0: //perp
|
654
|
+
lx_test = eps2;
|
655
|
+
ly_test = eps2;
|
656
|
+
lz_test = eps1;
|
657
|
+
break;
|
658
|
+
case 1: //par
|
659
|
+
lx_test = eps1;
|
660
|
+
ly_test = eps1;
|
661
|
+
lz_test = eps2;
|
662
|
+
break;
|
663
|
+
case 2: //time
|
664
|
+
lx_test = eps1;
|
665
|
+
ly_test = eps2;
|
666
|
+
lz_test = eps1;
|
667
|
+
break;
|
668
|
+
case 3: //full
|
669
|
+
lx_test = eps2;
|
670
|
+
ly_test = eps2;
|
671
|
+
lz_test = eps2;
|
672
|
+
break;
|
673
|
+
}
|
674
|
+
|
675
|
+
//Start main loop for correlation function calculation
|
676
|
+
for(i1=0; i1<tot_size; i1+=nt_reg){
|
677
|
+
for(i2=i1; i2<tot_size; i2+=nt_reg)
|
678
|
+
{
|
679
|
+
//Calculate spatial and temporal separation
|
680
|
+
lx = x_reg[i2] - x_reg[i1];
|
681
|
+
ly = y_reg[i2] - y_reg[i1];
|
682
|
+
lz = z_reg[i2] - z_reg[i1];
|
683
|
+
|
684
|
+
if(lx<lx_test && ly<ly_test && lz<lz_test){
|
685
|
+
//Calculate correlation function:
|
686
|
+
//corr = correlate(field[i1], field[i2], answer, no of t pts)
|
687
|
+
correlate_norm(&field_reg[i1], &field_reg[i2], &corr_norm[0], nt_reg);
|
688
|
+
|
689
|
+
//Calculate appropriate bin (subtracting min ensures +ve idx)
|
690
|
+
ix = floor((lx - lxmin) / (lx_bin[1] - lx_bin[0]));
|
691
|
+
iy = floor((ly - lymin) / (ly_bin[1] - ly_bin[0]));
|
692
|
+
ith = floor((lz - lzmin) / (lz_bin[1] - lz_bin[0]));
|
693
|
+
//printf("(%lf, %lf, %lf, %lf)\n", lz, lzmin, lz_bin[1], lz_bin[0]);
|
694
|
+
//printf("indices: (%d, %d, %d)\n", ix, iy, ith);
|
695
|
+
|
696
|
+
//Loop over time calculate time bin and put into coarray
|
697
|
+
for(i3=0; i3<2*nt_reg-1; i3++){
|
698
|
+
it = floor((lt[i3] - ltmin) / (lt_bin[1] - lt_bin[0]));
|
699
|
+
coarray[ix*nbins[1]*nbins[2]*nbins[3] + iy*nbins[2]*nbins[3] + ith*nbins[3] + it] += corr_norm[i3];
|
700
|
+
count[ix*nbins[1]*nbins[2]*nbins[3] + iy*nbins[2]*nbins[3] + ith*nbins[3] + it] += 1;
|
701
|
+
}
|
702
|
+
|
703
|
+
/**************************
|
704
|
+
* Repeat for negative *
|
705
|
+
* lx, ly, lz, to avoid *
|
706
|
+
* recalculating corr_norm*
|
707
|
+
**************************/
|
708
|
+
|
709
|
+
lx = -lx; ly = -ly; lz = -lz;
|
710
|
+
|
711
|
+
//Calculate appropriate bin (subtracting min ensures +ve idx)
|
712
|
+
ix = floor((lx - lxmin) / (lx_bin[1] - lx_bin[0]));
|
713
|
+
iy = floor((ly - lymin) / (ly_bin[1] - ly_bin[0]));
|
714
|
+
ith = floor((lz - lzmin) / (lz_bin[1] - lz_bin[0]));
|
715
|
+
//printf("- = {%d, %d, %d}\n", ix, iy, ith);
|
716
|
+
|
717
|
+
//Loop over time calculate time bin and put into coarray
|
718
|
+
for(i3=0; i3<2*nt_reg-1; i3++){
|
719
|
+
it = floor((-lt[i3] - ltmin) / (lt_bin[1] - lt_bin[0]));
|
720
|
+
coarray[ix*nbins[1]*nbins[2]*nbins[3] + iy*nbins[2]*nbins[3] + ith*nbins[3] + it] += corr_norm[i3];
|
721
|
+
count[ix*nbins[1]*nbins[2]*nbins[3] + iy*nbins[2]*nbins[3] + ith*nbins[3] + it] += 1;
|
722
|
+
}
|
723
|
+
}
|
724
|
+
}
|
725
|
+
}
|
726
|
+
//End main loop
|
727
|
+
|
728
|
+
//Finally have to normalize coarray with count when count != 0
|
729
|
+
//printf("Normalization: \n");
|
730
|
+
for(i1=0; i1<tot_bins; i1++){
|
731
|
+
if(count[i1]>0){
|
732
|
+
coarray[i1] = coarray[i1]/count[i1];
|
733
|
+
}
|
734
|
+
}
|
735
|
+
//printf("Finish normalization: \n");
|
736
|
+
|
737
|
+
//Retun output to CR
|
738
|
+
VALUE cgsl_tensor, output_tensor;
|
739
|
+
cgsl_tensor = RGET_CLASS(cgsl, "Tensor");
|
740
|
+
//output_tensor = GSL::Tensor.alloc(nbins, ...)
|
741
|
+
output_tensor = rb_funcall(cgsl_tensor, rb_intern("alloc"), 4, INT2FIX(nbins[0]), INT2FIX(nbins[1]), INT2FIX(nbins[2]), INT2FIX(nbins[3]));
|
742
|
+
|
743
|
+
for(i1=0; i1<nbins[0]; i1++)
|
744
|
+
for(i2=0; i2<nbins[1]; i2++)
|
745
|
+
for(i3=0; i3<nbins[2]; i3++)
|
746
|
+
for(i4=0; i4<nbins[3]; i4++){
|
747
|
+
CR_TELMT_R4_SET(output_tensor, i1, i2, i3, i4, rb_float_new(coarray[i1*nbins[1]*nbins[2]*nbins[3] + i2*nbins[2]*nbins[3] + i3*nbins[3] + i4]));
|
748
|
+
}
|
749
|
+
|
750
|
+
printf("Finished correlation analysis\n");
|
751
|
+
return output_tensor;
|
752
|
+
}
|
753
|
+
|
348
754
|
void Init_gs2crmod_ext()
|
349
755
|
{
|
350
756
|
/*printf("HERE!!!");*/
|
@@ -368,6 +774,7 @@ void Init_gs2crmod_ext()
|
|
368
774
|
|
369
775
|
rb_define_method(ccode_runner_gs2_gsl_tensor_complexes, "field_gsl_tensor_complex_2", gs2crmod_tensor_complexes_field_gsl_tensor_complex_2, 1);
|
370
776
|
rb_define_method(ccode_runner_gs2_gsl_tensors, "field_real_space_gsl_tensor", gs2crmod_tensor_field_gsl_tensor, 1);
|
777
|
+
rb_define_method(ccode_runner_gs2_gsl_tensors, "field_correlation_gsl_tensor", gs2crmod_tensor_field_correlation_gsl_tensor, 1);
|
371
778
|
|
372
779
|
/*rb_define_method(ccode_runner_ext, "hello_world", code_runner_ext_hello_world, 0);*/
|
373
780
|
}
|
data/gs2crmod.gemspec
CHANGED
@@ -2,15 +2,12 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: gs2crmod 0.11.20 ruby lib
|
6
|
-
# stub: ext/extconf.rb
|
7
5
|
|
8
6
|
Gem::Specification.new do |s|
|
9
7
|
s.name = "gs2crmod"
|
10
|
-
s.version = "0.11.
|
8
|
+
s.version = "0.11.21"
|
11
9
|
|
12
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
13
|
-
s.require_paths = ["lib"]
|
14
11
|
s.authors = ["Edmund Highcock", "Ferdinand van Wyk"]
|
15
12
|
s.date = "2014-03-04"
|
16
13
|
s.description = "GS2 is a gyrokinetic flux tube initial value turbulence code which can be used for fusion or astrophysical plasmas. CodeRunner is a framework for the automated running and analysis of large simulations. This module allows GS2 (and its sister code AstroGK) to harness the power of the CodeRunner framework."
|
@@ -85,12 +82,13 @@ Gem::Specification.new do |s|
|
|
85
82
|
]
|
86
83
|
s.homepage = "http://gs2crmod.sourceforge.net"
|
87
84
|
s.licenses = ["GSLv3"]
|
85
|
+
s.require_paths = ["lib"]
|
88
86
|
s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
|
89
|
-
s.rubygems_version = "
|
87
|
+
s.rubygems_version = "1.8.11"
|
90
88
|
s.summary = "Module to allow CodeRunner to run and analyse the GS2 and AstroGK codes."
|
91
89
|
|
92
90
|
if s.respond_to? :specification_version then
|
93
|
-
s.specification_version =
|
91
|
+
s.specification_version = 3
|
94
92
|
|
95
93
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
96
94
|
s.add_runtime_dependency(%q<coderunner>, [">= 0.14.10"])
|
data/include/gs2crmod_ext.h
CHANGED
@@ -7,13 +7,18 @@
|
|
7
7
|
#define RFCALL_10_ON(obj, name) (rb_funcall(obj, rb_intern(name), 0))
|
8
8
|
#define RFCALL_11(name, arg1) (rb_funcall(self, rb_intern(name), 1, arg1))
|
9
9
|
#define RFCALL_11_ON(obj, name, arg1) (rb_funcall(obj, rb_intern(name), 1, arg1))
|
10
|
+
#define RFCALL_12(name, arg1, arg2) (rb_funcall(self, rb_intern(name), 2, arg1, arg2))
|
11
|
+
#define RFCALL_12_ON(obj, name, arg1, arg2) (rb_funcall(obj, rb_intern(name), 2, arg1, arg2))
|
10
12
|
#define CR_ELEMENT_ACCESS(recvr, key) ( \
|
11
13
|
RFCALL_11_ON(recvr, "[]", key) \
|
12
14
|
)
|
13
15
|
#define CR_HKS(hash, cstr) (CR_ELEMENT_ACCESS(hash, ID2SYM(rb_intern(cstr))))
|
16
|
+
#define CR_HKS_SET(hash, cstr, value)(rb_funcall(hash, rb_intern("[]="), 2, ID2SYM(rb_intern(cstr)), value))
|
14
17
|
#define CR_RANGE_INC(start, end) (rb_funcall(RGET_CLASS_TOP("Range"), rb_intern("new"), 2, INT2FIX(start), INT2FIX(end)))
|
15
18
|
#define CR_RANGE_EXC(start, end) (rb_funcall(RGET_CLASS_TOP("Range"), rb_intern("new"), 3, INT2FIX(start), INT2FIX(end), Qtrue))
|
16
19
|
|
20
|
+
#define CR_SYM(cstr)(ID2SYM(rb_intern(cstr)))
|
21
|
+
|
17
22
|
/*Allocates an integer array on the heap
|
18
23
|
with values of array. int_ptr should be
|
19
24
|
an unallocated int*. array should not
|
@@ -30,6 +35,14 @@
|
|
30
35
|
FIX2INT(RARRAY_PTR(array)[cr_internal_xaa11]);\
|
31
36
|
}
|
32
37
|
|
38
|
+
/* Get element of a rank 1,2 or 3 tensor respectively */
|
39
|
+
#define CR_TELMT_R1(tensor, i)(rb_funcall(tensor, rb_intern("[]"), 1, INT2FIX(i)))
|
40
|
+
#define CR_TELMT_R2(tensor, i, j)(rb_funcall(tensor, rb_intern("[]"), 2, INT2FIX(i), INT2FIX(j)))
|
41
|
+
#define CR_TELMT_R3(tensor, i, j, k)(rb_funcall(tensor, rb_intern("[]"), 3, INT2FIX(i), INT2FIX(j), INT2FIX(k)))
|
42
|
+
|
43
|
+
// Set the value of a 4D tensor
|
44
|
+
#define CR_TELMT_R4_SET(tensor, i, j, k, l, value)(rb_funcall(tensor, rb_intern("[]="), 5, INT2FIX(i), INT2FIX(j), INT2FIX(k), INT2FIX(l), value))
|
45
|
+
|
33
46
|
|
34
47
|
/*Allocates an integer array on the heap
|
35
48
|
with values of array. int_ptr should be
|
data/lib/gs2crmod/gs2.rb
CHANGED
@@ -1159,6 +1159,90 @@ end
|
|
1159
1159
|
end
|
1160
1160
|
end
|
1161
1161
|
|
1162
|
+
#This function will handle running the correlation analysis and writing the results to a NetCDF file.
|
1163
|
+
#Cases need to be handled differently since perp, par and full are just subsets of the full correlation function
|
1164
|
+
#but the time correlation calculation needs to deal with each radial location separately. Time correlation
|
1165
|
+
#uses the zonal flows in the toroidal direction to calculate the correlation time.
|
1166
|
+
#
|
1167
|
+
#This function takes in the same options as field_real_space_standard_representation, along with the following
|
1168
|
+
#new options dealing with interpolation and binning:
|
1169
|
+
#
|
1170
|
+
# correlation_type: determines which subset of correlation function should be calculated (perp/par/full/time)
|
1171
|
+
# nbins_array: array giving number of bins to use in the binning procedure. Index order (x, y, z ,t)
|
1172
|
+
# nt_reg: Most of the time you have many more time points than you need for spatial correlations. This sets
|
1173
|
+
# number of new interpolation points in time.
|
1174
|
+
#
|
1175
|
+
# Using this function: Since this can only be single threaded, this can be a very expensive calculation when
|
1176
|
+
# trying to do the full correlation function, so this is not recommended for highly resolved nonlinear runs. This is
|
1177
|
+
# why the perp/par/full splitting is implemented, allowing one dimension to be taken out essentially.
|
1178
|
+
def correlation_analysis(options={})
|
1179
|
+
|
1180
|
+
#Sanity checks:
|
1181
|
+
#Cannot only have one bin since require difference between bins for index calculation
|
1182
|
+
if options[:nbins_array].include?1
|
1183
|
+
raise('Cannot have only one bin in nbins_array. Minuimum is two.')
|
1184
|
+
end
|
1185
|
+
#Thetamin shouldn't be equal to thetamax to avoid possibili
|
1186
|
+
#
|
1187
|
+
|
1188
|
+
case options[:correlation_type]
|
1189
|
+
when 'perp', 'par', 'full'
|
1190
|
+
gsl_tensor = field_correlation_gsl_tensor(options)
|
1191
|
+
shape = gsl_tensor.shape
|
1192
|
+
|
1193
|
+
#Set up dimensions
|
1194
|
+
file = NumRu::NetCDF.create(@run_name + "_correlation_analysis_#{options[:correlation_type]}.nc")
|
1195
|
+
ydim = file.def_dim('x',shape[0])
|
1196
|
+
xdim = file.def_dim('y',shape[1])
|
1197
|
+
zdim = file.def_dim('z',shape[2])
|
1198
|
+
tdim = file.def_dim('t',shape[3])
|
1199
|
+
correlation_var = file.def_var("correlation", 'sfloat', [xdim, ydim, zdim, tdim])
|
1200
|
+
file.enddef
|
1201
|
+
#Write out array
|
1202
|
+
correlation_var.put(NArray.to_na(gsl_tensor.to_a))
|
1203
|
+
file.close
|
1204
|
+
when 'time'
|
1205
|
+
nakx_actual = NumRu::NetCDF.open(@run_name + ".out.nc").var('kx').get
|
1206
|
+
kx_len = nakx_actual.length
|
1207
|
+
if options[:nakx] == nil
|
1208
|
+
radial_pts = kx_len
|
1209
|
+
elsif options[:nakx] <= kx_len
|
1210
|
+
radial_pts = options[:nakx]
|
1211
|
+
else
|
1212
|
+
raise('nakx exceeds the total number of kx\'s in simulation')
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
#Check whether t_index_window is specified, if not, set to entire t range
|
1216
|
+
if options[:t_index_window] == nil
|
1217
|
+
options[:t_index_window] = [1, -1]
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
|
1221
|
+
#Now loop through the radial locations and calculate the correlation function in y and t.
|
1222
|
+
for x in 0...radial_pts
|
1223
|
+
options[:xmin] = x
|
1224
|
+
options[:xmax] = x
|
1225
|
+
gsl_tensor = field_correlation_gsl_tensor(options)
|
1226
|
+
shape = gsl_tensor.shape
|
1227
|
+
|
1228
|
+
if x == 0 #Write dimensions to NetCDF file
|
1229
|
+
file = NumRu::NetCDF.create(@run_name + "_correlation_analysis_#{options[:correlation_type]}.nc")
|
1230
|
+
ydim = file.def_dim('x',shape[0])
|
1231
|
+
xdim = file.def_dim('y',shape[1])
|
1232
|
+
zdim = file.def_dim('z',shape[2])
|
1233
|
+
tdim = file.def_dim('t',shape[3])
|
1234
|
+
end
|
1235
|
+
file.redef
|
1236
|
+
correlation_var = file.def_var("correlation_x_#{x}", 'sfloat', [xdim, ydim, zdim, tdim])
|
1237
|
+
file.enddef
|
1238
|
+
#Write out array
|
1239
|
+
correlation_var.put(NArray.to_na(gsl_tensor.to_a))
|
1240
|
+
end
|
1241
|
+
file.close #only close after loop over radial points
|
1242
|
+
else
|
1243
|
+
raise 'Please specify correlation_type as perp/par/time/full'
|
1244
|
+
end
|
1245
|
+
end
|
1162
1246
|
end # class GS2
|
1163
1247
|
# For backwards compatibility
|
1164
1248
|
|
data/lib/gs2crmod/gsl_data_3d.rb
CHANGED
@@ -171,7 +171,7 @@ class CodeRunner::Gs2
|
|
171
171
|
case options[:field_name].to_s
|
172
172
|
when /density/
|
173
173
|
options.convert_to_index(self, :species)
|
174
|
-
ep 'options', options
|
174
|
+
#ep 'options', options
|
175
175
|
options[:species_index] - 1
|
176
176
|
else
|
177
177
|
nil
|
@@ -179,7 +179,7 @@ class CodeRunner::Gs2
|
|
179
179
|
end
|
180
180
|
def field_gsl_tensor(options)
|
181
181
|
species_element = field_species_element(options)
|
182
|
-
ep 'species_element', species_element
|
182
|
+
#ep 'species_element', species_element
|
183
183
|
if options[:t_index]
|
184
184
|
#ep options; gets
|
185
185
|
#raise CRFatal.new("write_phi_over_time is not enabled so this function won't work") unless @write_phi_over_time
|
data/test/test_gs2crmod.rb
CHANGED
@@ -59,6 +59,12 @@ class TestAnalysis < Test::Unit::TestCase
|
|
59
59
|
assert_equal(6.6036e-01, @run.frequency_at_ky_at_kx[0.5][2.5133])
|
60
60
|
#p @run.gsl_vector('kx'); STDIN.gets
|
61
61
|
assert_equal(6.6036e-01, @run.frequency_at_ky_at_kx[0.5][@run.gsl_vector('kx')[3]])
|
62
|
+
|
63
|
+
#Correlation analysis testing
|
64
|
+
CodeRunner.run_command('correlation_analysis(correlation_type:"full", field_name:"density", gs2_coordinate_factor:1.0, species_index: 1, nbins_array:[4,4,4,25], nt_reg:25, Rgeo:3, n0:1)', {j:1, Y:tfolder})
|
65
|
+
corr_file = NumRu::NetCDF.open("#{tfolder}/v/id_1/v_write_moments_.true._write_line_.true._id_1_correlation_analysis_full.nc")
|
66
|
+
corr_function = corr_file.var('correlation').get
|
67
|
+
assert_equal([4,4,4,25], corr_function.shape)
|
62
68
|
end
|
63
69
|
def test_interpolation
|
64
70
|
assert_equal(5, @run.gsl_vector('kx').size)
|
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gs2crmod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.21
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Edmund Highcock
|
@@ -13,102 +14,81 @@ date: 2014-03-04 00:00:00.000000000 Z
|
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: coderunner
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirement: &23277780 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
17
19
|
requirements:
|
18
|
-
- -
|
20
|
+
- - ! '>='
|
19
21
|
- !ruby/object:Gem::Version
|
20
22
|
version: 0.14.10
|
21
23
|
type: :runtime
|
22
24
|
prerelease: false
|
23
|
-
version_requirements:
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: 0.14.10
|
25
|
+
version_requirements: *23277780
|
28
26
|
- !ruby/object:Gem::Dependency
|
29
27
|
name: rubyhacks
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &23277220 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
31
30
|
requirements:
|
32
|
-
- -
|
31
|
+
- - ! '>='
|
33
32
|
- !ruby/object:Gem::Version
|
34
33
|
version: 0.1.2
|
35
34
|
type: :runtime
|
36
35
|
prerelease: false
|
37
|
-
version_requirements:
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: 0.1.2
|
36
|
+
version_requirements: *23277220
|
42
37
|
- !ruby/object:Gem::Dependency
|
43
38
|
name: ruby-netcdf-updated
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirement: &23273380 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
45
41
|
requirements:
|
46
|
-
- -
|
42
|
+
- - ! '>='
|
47
43
|
- !ruby/object:Gem::Version
|
48
44
|
version: 0.6.6.1
|
49
45
|
type: :runtime
|
50
46
|
prerelease: false
|
51
|
-
version_requirements:
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: 0.6.6.1
|
47
|
+
version_requirements: *23273380
|
56
48
|
- !ruby/object:Gem::Dependency
|
57
49
|
name: shoulda
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirement: &23443080 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
59
52
|
requirements:
|
60
|
-
- -
|
53
|
+
- - ! '>='
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '0'
|
63
56
|
type: :development
|
64
57
|
prerelease: false
|
65
|
-
version_requirements:
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
58
|
+
version_requirements: *23443080
|
70
59
|
- !ruby/object:Gem::Dependency
|
71
60
|
name: rdoc
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirement: &23442460 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
73
63
|
requirements:
|
74
|
-
- -
|
64
|
+
- - ~>
|
75
65
|
- !ruby/object:Gem::Version
|
76
66
|
version: '3.12'
|
77
67
|
type: :development
|
78
68
|
prerelease: false
|
79
|
-
version_requirements:
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '3.12'
|
69
|
+
version_requirements: *23442460
|
84
70
|
- !ruby/object:Gem::Dependency
|
85
71
|
name: bundler
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirement: &23441920 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
87
74
|
requirements:
|
88
|
-
- -
|
75
|
+
- - ! '>'
|
89
76
|
- !ruby/object:Gem::Version
|
90
77
|
version: 1.0.0
|
91
78
|
type: :development
|
92
79
|
prerelease: false
|
93
|
-
version_requirements:
|
94
|
-
requirements:
|
95
|
-
- - ">"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: 1.0.0
|
80
|
+
version_requirements: *23441920
|
98
81
|
- !ruby/object:Gem::Dependency
|
99
82
|
name: jeweler
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
83
|
+
requirement: &23441400 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
101
85
|
requirements:
|
102
|
-
- -
|
86
|
+
- - ! '>='
|
103
87
|
- !ruby/object:Gem::Version
|
104
88
|
version: 1.8.4
|
105
89
|
type: :development
|
106
90
|
prerelease: false
|
107
|
-
version_requirements:
|
108
|
-
requirements:
|
109
|
-
- - ">="
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
version: 1.8.4
|
91
|
+
version_requirements: *23441400
|
112
92
|
description: GS2 is a gyrokinetic flux tube initial value turbulence code which can
|
113
93
|
be used for fusion or astrophysical plasmas. CodeRunner is a framework for the automated
|
114
94
|
running and analysis of large simulations. This module allows GS2 (and its sister
|
@@ -122,7 +102,7 @@ extra_rdoc_files:
|
|
122
102
|
- README.md
|
123
103
|
- README.rdoc
|
124
104
|
files:
|
125
|
-
-
|
105
|
+
- .document
|
126
106
|
- Gemfile
|
127
107
|
- LICENSE.txt
|
128
108
|
- README.md
|
@@ -185,25 +165,26 @@ files:
|
|
185
165
|
homepage: http://gs2crmod.sourceforge.net
|
186
166
|
licenses:
|
187
167
|
- GSLv3
|
188
|
-
metadata: {}
|
189
168
|
post_install_message:
|
190
169
|
rdoc_options: []
|
191
170
|
require_paths:
|
192
171
|
- lib
|
193
172
|
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
194
174
|
requirements:
|
195
|
-
- -
|
175
|
+
- - ! '>='
|
196
176
|
- !ruby/object:Gem::Version
|
197
177
|
version: 1.9.1
|
198
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
none: false
|
199
180
|
requirements:
|
200
|
-
- -
|
181
|
+
- - ! '>='
|
201
182
|
- !ruby/object:Gem::Version
|
202
183
|
version: '0'
|
203
184
|
requirements: []
|
204
185
|
rubyforge_project:
|
205
|
-
rubygems_version:
|
186
|
+
rubygems_version: 1.8.11
|
206
187
|
signing_key:
|
207
|
-
specification_version:
|
188
|
+
specification_version: 3
|
208
189
|
summary: Module to allow CodeRunner to run and analyse the GS2 and AstroGK codes.
|
209
190
|
test_files: []
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: 6573d709227e4e6a3ef38b5e91a2a798051bbe94
|
4
|
-
data.tar.gz: 34e87dca9ffb31bb3700be59cf829adb3c8a0bf7
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: b059c57412e1c7cf3dfd78d4c2d2cf8dfc588ce9d4336b39608861351210605501656b1c512997e99afdf4771d582858ccd2e051322cee777bcc4f28f198e405
|
7
|
-
data.tar.gz: 2df0e3c66ed093b41fc620775a7b8b01622fe3d7188ee5a7e7b1622ef94c7c73038e68bcd175af0f7ec727b6f3ceb57025ae6f4e7740fda93ebb6cb5a2bfe9bb
|