File: boys_function_test.cc

package info (click to toggle)
ergo 3.8.2-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 17,568 kB
  • sloc: cpp: 94,763; ansic: 17,785; sh: 10,701; makefile: 1,403; yacc: 127; lex: 116; awk: 23
file content (149 lines) | stat: -rw-r--r-- 5,761 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* Ergo, version 3.8.2, a program for linear scaling electronic structure
 * calculations.
 * Copyright (C) 2023 Elias Rudberg, Emanuel H. Rubensson, Pawel Salek,
 * and Anastasia Kruchinina.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Primary academic reference:
 * Ergo: An open-source program for linear-scaling electronic structure
 * calculations,
 * Elias Rudberg, Emanuel H. Rubensson, Pawel Salek, and Anastasia
 * Kruchinina,
 * SoftwareX 7, 107 (2018),
 * <http://dx.doi.org/10.1016/j.softx.2018.03.005>
 * 
 * For further information about Ergo, see <http://www.ergoscf.org>.
 */

/** @file boys_function_test.cc Tests the Boys function evaluation
    accuracy. */

#include <stdio.h>
#include <unistd.h>
#include <memory>
#include <limits>
#include <vector>

#include "integral_info.h"
#include "matInclude.h"
#include "template_blas_common.h"
#include "config.h" // Needed to get the PRECISION_SINGLE macro

static ergo_real rand_0_to_1() {
  int randomint = rand();
  ergo_real x = (ergo_real)randomint;
  return x / RAND_MAX;
}

static ergo_real rand_1_to_10() {
  ergo_real rand_0_to_9 = rand_0_to_1() * 9;
  ergo_real result = rand_0_to_9 + 1;
  return result;
}

static ergo_real BoysFuncAccurate(int n, ergo_real x, const IntegralInfo & integralInfo) {
  int noOfIntegrationIntervals = 40;
  ergo_real machine_epsilon = mat::getMachineEpsilon<ergo_real>();
  ergo_real diffLimit = template_blas_sqrt(template_blas_get_num_limit_min<ergo_real>());
  ergo_real reldiffLimit = template_blas_pow(machine_epsilon, (ergo_real)0.75);
  const int NMAX = 30;
  ergo_real list[NMAX];
  for(int i = 0; i < NMAX; i++) {
    list[i] = integralInfo.BoysFunction_expensive(n, x, noOfIntegrationIntervals);
    assert(list[i] >= 0);
    if(i >= 2) {
      // Check if difference between different results is small
      ergo_real diff1 = template_blas_fabs(list[i-0]-list[i-1]);
      ergo_real diff2 = template_blas_fabs(list[i-1]-list[i-2]);
      if(diff1 < diffLimit && diff2 < diffLimit)
	return list[i];
      ergo_real reldiff1 = diff1 / list[i];
      ergo_real reldiff2 = diff2 / list[i];
      if(reldiff1 < reldiffLimit && reldiff2 < reldiffLimit)
	return list[i];
    }
    noOfIntegrationIntervals *= 2;
  }
  printf("ERROR in BoysFuncAccurate(): failed to get accurate result.\n");
  return 0;
}

int main(int argc, char *argv[]) {
  IntegralInfo integralInfo(true);
  integralInfo.init();
  ergo_real machine_epsilon = mat::getMachineEpsilon<ergo_real>();
  ergo_real num_limit_min = template_blas_get_num_limit_min<ergo_real>();
  ergo_real min_value_for_meaningful_reldiff = template_blas_sqrt(num_limit_min);
  printf("boys_function_test start, machine_epsilon = %g\n", (double)machine_epsilon);
  // Different things to test: try different values of n and different values of x.
  // Define constant POW_OF_10_LIMIT that determines the range of sizes of x-values that are tested.
#ifdef PRECISION_SINGLE
  const int POW_OF_10_LIMIT = 4;
#else
  const int POW_OF_10_LIMIT = 10;
#endif
  int saved_n = -1;
  ergo_real saved_x = 0;
  ergo_real saved_absdiff = 0;
  ergo_real saved_value = 0;
  ergo_real saved_refvalue = 0;
  ergo_real maxreldiff = 0;
  // Try all values of n from 0 up to BOYS_N_MAX-1
  printf("Testing Boys function accuracy for 0 <= n <= %d\n", BOYS_N_MAX-1);
  for(int n = 0; n < BOYS_N_MAX; n++) {
    // Try different order of magnitude of x-values
    for(int i = -POW_OF_10_LIMIT; i < POW_OF_10_LIMIT; i++) {
      ergo_real x_base = template_blas_pow((ergo_real)10, (ergo_real)i);
      const int nTests = 10;
      for(int k = 0; k < nTests; k++) {
	// Get random x-value around x_base
	ergo_real x = x_base * rand_1_to_10();
	// Compare computed Boys func values for that x-value
	ergo_real boysFuncResultRef = BoysFuncAccurate(n, x, integralInfo);
	ergo_real boysFuncResult = integralInfo.BoysFunction(n, x);
	ergo_real absdiff = template_blas_fabs(boysFuncResult - boysFuncResultRef);
	ergo_real reldiff = 0;
	if(template_blas_fabs(boysFuncResultRef) > min_value_for_meaningful_reldiff)
	  reldiff = absdiff / template_blas_fabs(boysFuncResultRef);
	else {
	  // boysFuncResultRef is extremely small. In this case we just check that absdiff is extremely small also
	  if(absdiff > 2*min_value_for_meaningful_reldiff)
	    reldiff = 1;
	}
	if(reldiff > maxreldiff) {
	  maxreldiff = reldiff;
	  saved_n = n;
	  saved_x = x;
	  saved_absdiff = absdiff;
	  saved_refvalue = boysFuncResultRef;
	  saved_value = boysFuncResult;
	}
      }
    }
  }
  printf("maxreldiff = %g\n", (double)maxreldiff);
  printf("saved_n = %d\n", saved_n);
  printf("saved_x = %g\n", (double)saved_x);
  printf("saved_absdiff = %g\n", (double)saved_absdiff);
  printf("saved_refvalue = %g\n", (double)saved_refvalue);
  printf("saved_value    = %g\n", (double)saved_value);
  ergo_real tolerance = template_blas_pow(machine_epsilon, (ergo_real)0.75);
  if(maxreldiff > tolerance) {
    printf("Error in boys_function_test: (maxreldiff > tolerance). tolerance= %g\n", (double)tolerance);
    return -1;
  }
  printf("boys_function_test finished OK (maxreldiff was below tolerance %g).\n", (double)tolerance);
  return 0;
}