Toggle navigation
Documentation
Big picture
The finite element method
The data structure
Not-so-quick guide
Optimisation
Order of action functions
Example codes and tutorials
List of example codes and tutorials
Meshing
Solvers
MPI parallel processing
Post-processing/visualisation
Other
Change log
Creating documentation
Coding conventions
Index
FAQ
About
People
Contact/Get involved
Publications
Acknowledgements
Copyright
Picture show
Go
src
generic
fpdiff.cc
Go to the documentation of this file.
1
// LIC// ====================================================================
2
// LIC// This file forms part of oomph-lib, the object-oriented,
3
// LIC// multi-physics finite-element library, available
4
// LIC// at http://www.oomph-lib.org.
5
// LIC//
6
// LIC// Copyright (C) 2006-2022 Matthias Heil and Andrew Hazel
7
// LIC//
8
// LIC// This library is free software; you can redistribute it and/or
9
// LIC// modify it under the terms of the GNU Lesser General Public
10
// LIC// License as published by the Free Software Foundation; either
11
// LIC// version 2.1 of the License, or (at your option) any later version.
12
// LIC//
13
// LIC// This library is distributed in the hope that it will be useful,
14
// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15
// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
// LIC// Lesser General Public License for more details.
17
// LIC//
18
// LIC// You should have received a copy of the GNU Lesser General Public
19
// LIC// License along with this library; if not, write to the Free Software
20
// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
// LIC// 02110-1301 USA.
22
// LIC//
23
// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24
// LIC//
25
// LIC//====================================================================
26
27
#include "
fpdiff.h
"
28
#include "
gzip_reader.h
"
29
#include "
oomph_definitions.h
"
30
31
#include <cstdlib>
32
#include <cmath>
33
#include <fstream>
34
#include <sstream>
35
#include <string>
36
#include <regex>
37
#include <vector>
38
39
#define CHUNKSIZE 8192
40
41
namespace
oomph
42
{
43
/************************************************************************************
44
* @brief
45
*
46
************************************************************************************/
47
enum
TestStatus
48
{
49
Passed
= 0,
50
Failed
= 1
51
};
52
53
/************************************************************************************
54
* @brief
55
*
56
************************************************************************************/
57
enum class
Type
58
{
59
Number
,
60
String
61
};
62
63
/************************************************************************************
64
* @brief
65
*
66
* @param value
67
* @param ending
68
* @return true
69
* @return false
70
************************************************************************************/
71
inline
bool
ends_with
(std::string
const
& value, std::string
const
&
ending
)
72
{
73
if
(
ending
.
size
() > value.
size
())
74
{
75
return
false
;
76
}
77
return
std::equal(
ending
.rbegin(),
ending
.rend(), value.rbegin());
78
}
// End of ends_with
79
80
/************************************************************************************
81
* @brief Add number copies of the symbol to the input string.
82
*
83
* @param string
84
* @param symbol
85
* @param number
86
* @return std::string
87
************************************************************************************/
88
inline
std::string
modify_string_inplace
(std::string&
text
,
89
const
std::string&
symbol
,
90
const
unsigned
& number)
91
{
92
for
(
unsigned
i
= 0;
i
< number;
i
++)
93
{
94
text
+=
symbol
;
95
}
96
text
+=
" "
;
97
return
text
;
98
}
// End of modify_string_inplace
99
100
/************************************************************************************
101
* @brief
102
*
103
* @param str
104
************************************************************************************/
105
inline
std::vector<std::string>
split_string
(
const
std::string&
text
)
106
{
107
std::stringstream
text_stream
{
text
};
108
std::string
word
;
109
std::vector<std::string>
words
;
110
while
(std::getline(
text_stream
,
word
,
' '
))
111
{
112
words
.push_back(
word
);
113
}
114
return
words
;
115
}
// End of split_string
116
117
/************************************************************************************
118
* @brief
119
*
120
* @param text
121
* @return std::string
122
************************************************************************************/
123
inline
std::string
lower
(
const
std::string&
text
)
124
{
125
std::string
text_copy
=
text
;
126
std::transform(
text_copy
.begin(),
127
text_copy
.end(),
128
text_copy
.begin(),
129
[](
unsigned
char
c) { return std::tolower(c); });
130
return
text_copy
;
131
}
132
133
/************************************************************************************
134
* @brief Distinguish between a number and a string:
135
*
136
* Returns integer 1 if the argument is a number,
137
* 2 if the argument is a string.
138
*
139
* @param text
140
* @return true
141
* @return false
142
************************************************************************************/
143
Type
get_type
(
const
std::string&
text
)
144
{
145
std::regex
is_number
{
146
"(^[+-]?[0-9]*[.]?[0-9]+$)|(^[+-]?[0-9]+[.]?[0-9]*$)|(^[+-]?[0-9]?"
147
"[.]?[0-9]+[EeDd][+-][0-9]+$)"
};
148
if
(std::regex_match(
text
,
is_number
))
149
{
150
return
Type::Number
;
151
}
152
return
Type::String
;
153
}
// End of get_type
154
155
/************************************************************************************
156
* @brief
157
*
158
* @param filename
159
* @return std::vector<std::string>
160
************************************************************************************/
161
std::vector<std::string>
gzip_load
(
const
std::string&
filename
)
162
{
163
if
(!
ends_with
(
filename
,
".gz"
))
164
{
165
throw
OomphLibError
(
"File:\n\t"
+
filename
+
"\nis not a gzip file!"
,
166
OOMPH_CURRENT_FUNCTION
,
167
OOMPH_EXCEPTION_LOCATION
);
168
}
169
return
GZipReader
(
filename
).
read_all
();
170
}
// End of gzip_load
171
172
173
/************************************************************************************
174
* @brief
175
*
176
* @param filename
177
* @return std::vector<std::string>
178
************************************************************************************/
179
std::vector<std::string>
load_file
(
const
std::string&
filename
)
180
{
181
std::string
line
;
182
std::vector<std::string>
file_data
;
183
184
if
(
ends_with
(
filename
,
".gz"
))
185
{
186
file_data
= std::move(
gzip_load
(
filename
));
187
}
188
else
189
{
190
std::ifstream
file
(
filename
);
191
while
(std::getline(
file
,
line
))
192
{
193
file_data
.push_back(
line
);
194
}
195
file
.close();
196
}
197
return
file_data
;
198
}
// End of load_file
199
200
/************************************************************************************
201
* @brief
202
*
203
* @param filename1
204
* @param filename2
205
* @param relative_error
206
* @param small
207
* @param outstream
208
* @return int
209
************************************************************************************/
210
int
fpdiff
(
const
std::string&
filename1
,
211
const
std::string&
filename2
,
212
std::ostream&
outstream
,
213
const
double
&
relative_error
,
214
const
double
&
small
)
215
{
216
// Storage for worst case error sizes
217
double
max_rel_diff
= 0.0;
218
double
max_wrong_entry
= 0.0;
219
220
// Open the files (if run as a script then open Faileds are handled higher
221
// up by catching the error, otherwise it is the parent program's job to
222
// handle the error and so we shouldn't do anything weird here).
223
auto
file1
=
load_file
(
filename1
);
224
auto
file2
=
load_file
(
filename2
);
225
226
// Find the number of lines in each file
227
auto
n1
=
file1
.
size
();
228
auto
n2
=
file2
.
size
();
229
auto
min_lines
= std::min(
n1
,
n2
);
230
231
if
(
n1
<
n2
)
232
{
233
std::vector<std::string>
temp
(std::move(
file1
));
234
file1
= std::move(
file2
);
235
file2
= std::move(
temp
);
236
}
237
238
// Counter for the number of errors
239
unsigned
long
long
nerr
= 0;
240
241
// Counter for the number of lines
242
unsigned
long
long
count
= -1;
243
244
// Counter for the number of lines with errors
245
unsigned
long
long
nline_error
= 0;
246
247
// Loop over the lines in file1 (the file with the most lines!)
248
for
(
const
auto
&
line1
:
file1
)
249
{
250
// Increase the counter
251
count
++;
252
253
// If we've run over the end of the file2, issue a warning and end the
254
// loop
255
if
(
count
>=
min_lines
)
256
{
257
outstream
<<
"\nWarning: files have different numbers of lines\n"
258
<<
"\nResults are for first "
<<
count
259
<<
" lines of both files\n"
260
<< std::endl;
261
nerr
++;
262
break
;
263
}
264
265
// Read the next line from file2
266
auto
line2
=
file2
[
count
];
267
268
// If the lines are the same, we're done
269
if
(
line1
==
line2
)
continue
;
270
271
// We need to do more work
272
273
// Split each line into its separate fields
274
auto
fields1
=
split_string
(
line1
);
275
auto
fields2
=
split_string
(
line2
);
276
277
// Find the number of fields in each line
278
auto
n_field1
=
fields1
.
size
();
279
auto
n_field2
=
fields2
.
size
();
280
281
if
(
n_field1
!=
n_field2
)
282
{
283
outstream
<<
"\n =====> line "
<< (
count
+ 1)
284
<<
": different number of fields\n"
285
<<
n_field1
<<
" fields: "
<<
line1
<<
"\n"
286
<<
n_field2
<<
" fields: "
<<
line2
<< std::endl;
287
nerr
++;
288
continue
;
289
}
290
291
// Flag to indicate whether there has been a problem in the field
292
bool
encountered_problem
=
false
;
293
294
// Strings that will hold the output data
295
std::string
outputline1
=
""
;
296
std::string
outputline2
=
""
;
297
std::string
outputline3
=
""
;
298
299
// Loop over the fields
300
for
(
unsigned
i
= 0;
i
<
n_field1
;
i
++)
301
{
302
// Start by loading the fields into the outputlines (plus whitespace)
303
outputline1
+= (
fields1
[
i
] +
" "
);
304
outputline3
+= (
fields2
[
i
] +
" "
);
305
306
// Find the lengths of the fields
307
auto
length1
=
fields1
[
i
].length();
308
auto
length2
=
fields2
[
i
].length();
309
310
// Pad the shortest field so the lengths are the same
311
auto
field_length
=
length2
;
312
if
(
length1
<
length2
)
313
{
314
for
(
unsigned
j
= 0;
j
< (
length2
-
length1
);
j
++)
315
{
316
outputline1
+=
" "
;
317
}
318
}
319
else
320
{
321
field_length
=
length1
;
322
for
(
unsigned
j
= 0;
j
< (
length1
-
length2
);
j
++)
323
{
324
outputline3
+=
" "
;
325
}
326
}
// if (length1 < length2)
327
328
// If the fields are identical, we are fine
329
if
(
fields1
[
i
] ==
fields2
[
i
])
330
{
331
// Put spaces into the error line
332
modify_string_inplace
(
outputline2
,
" "
,
field_length
);
333
continue
;
334
}
335
336
// Find the type (numeric or string) of each field
337
Type
type1
=
get_type
(
fields1
[
i
]);
338
Type
type2
=
get_type
(
fields2
[
i
]);
339
340
// If the data-types aren't the same issue an error
341
if
((
type1
!=
type2
) || (
type1
==
Type::String
))
342
{
343
std::string
symbol
= (
type1
!=
type2
?
"*"
:
"%"
);
344
encountered_problem
=
true
;
345
nerr
++;
346
347
// Put the appropriate symbol into the error line
348
modify_string_inplace
(
outputline2
,
symbol
,
field_length
);
349
continue
;
350
}
351
352
// Convert strings to floating point number
353
auto
string_to_double
= [](
const
std::string&
text
) {
354
return
std::stod(
355
std::regex_replace(
lower
(
text
), std::regex(
"d"
),
"e"
));
356
};
357
double
x1
=
string_to_double
(
fields1
[
i
]);
358
double
x2
=
string_to_double
(
fields2
[
i
]);
359
360
// If both numbers are very small, that's fine
361
if
((std::fabs(
x1
) <=
small
) && (std::fabs(
x2
) <=
small
))
362
{
363
// Put spaces into the error line
364
modify_string_inplace
(
outputline2
,
" "
,
field_length
);
365
}
366
else
367
{
368
// Find the relative difference based on the largest number
369
// Note that this "minimises" the relative error (in some sense)
370
// but means that I don't have to separately trap the cases
371
// when x1, x2 are zero
372
double
diff = 100.0 * (std::fabs(
x1
-
x2
) /
373
std::max(std::fabs(
x1
), std::fabs(
x2
)));
374
375
// If the relative error is smaller than the tolerance, that's fine
376
if
(diff <=
relative_error
)
377
{
378
// Put spaces into the error line
379
modify_string_inplace
(
outputline2
,
" "
,
field_length
);
380
}
381
// Otherwise issue an error
382
else
383
{
384
encountered_problem
=
true
;
385
nerr
++;
386
387
// Put the appropriate symbols into the error line
388
modify_string_inplace
(
outputline2
,
"-"
,
field_length
);
389
390
// Record any changes in the worst case values
391
max_rel_diff
= std::max(
max_rel_diff
, diff);
392
// max_wrong_entry =
393
// std::max({max_wrong_entry, std::fabs(x1), std::fabs(x2)});
394
if
(std::fabs(
x1
) >
max_wrong_entry
)
395
{
396
max_wrong_entry
= std::fabs(
x1
);
397
}
398
else
if
(std::fabs(
x2
) >
max_wrong_entry
)
399
{
400
max_wrong_entry
= std::fabs(
x2
);
401
}
402
}
403
}
// if ((std::fabs(x1) <= small) && (std::fabs(x2) <= small))
404
}
// for (unsigned i = 0; i < n_field1; i++)
405
406
// If there has been any sort of error, print it
407
if
(
encountered_problem
)
408
{
409
nline_error
++;
410
outstream
<<
"\n =====> line "
<< (
count
+ 1) <<
"\n"
411
<<
outputline1
<<
"\n"
412
<<
outputline2
<<
"\n"
413
<<
outputline3
<< std::endl;
414
}
415
}
// for (const auto& line1 : file1)
416
417
std::string
border
(80,
'*'
);
418
std::cout <<
border
<<
"\nfpdiff() compared files:"
419
<<
"\n * "
<<
filename1
<<
"\n * "
<<
filename2
<< std::endl;
420
421
if
(
nerr
> 0)
422
{
423
outstream
<<
"\n In files "
<<
filename1
<<
" "
<<
filename2
424
<<
"\n number of lines processed: "
<<
count
425
<<
"\n number of lines containing errors: "
<<
nline_error
426
<<
"\n number of errors: "
<<
nerr
<<
" "
427
<<
"\n largest relative error: "
<<
max_rel_diff
<<
" "
428
<<
"\n largest abs value of an entry which caused an error: "
429
<<
max_wrong_entry
<<
" "
430
<<
"\n========================================================"
431
<<
"\n Parameters used:"
432
<<
"\n threshold for numerical zero : "
<<
small
433
<<
"\n maximum rel. difference [percent] : "
434
<<
relative_error
<<
"\n Legend: "
435
<<
"\n ******* means differences in data type "
436
"(string vs number)"
437
<<
"\n ------- means real data exceeded the relative "
438
"difference maximum"
439
<<
"\n %%%%%%% means that two strings are different"
440
<<
"\n========================================================"
441
<<
"\n\n [FAILED]"
<< std::endl;
442
std::cout <<
"Test failed!\n"
<<
border
<< std::endl;
443
return
EXIT_FAILURE
;
444
}
445
else
446
{
447
outstream
<<
"\n\n In files "
<<
filename1
<<
" "
<<
filename2
448
<<
"\n [OK] for fpdiff.py parameters: - max. rel. error = "
449
<<
relative_error
<<
" %"
450
<<
"\n - numerical zero = "
451
<<
small
<< std::endl;
452
std::cout <<
"Test passed!\n"
<<
border
<< std::endl;
453
return
EXIT_SUCCESS
;
454
}
455
}
// End of fpdiff
456
457
/************************************************************************************
458
* @brief
459
*
460
* @param filename1:
461
* @param filename2:
462
* @param log_file:
463
* @param relative_error:
464
* @param small:
465
* @return int:
466
************************************************************************************/
467
int
fpdiff
(
const
std::string&
filename1
,
468
const
std::string&
filename2
,
469
const
std::string&
log_file
,
470
const
double
&
relative_error
,
471
const
double
&
small
)
472
{
473
// File to output fpdiff output to
474
std::ofstream
log_stream
{
log_file
};
475
476
// Run test
477
int
test_status
=
fpdiff
(
filename1
,
filename2
,
log_stream
);
478
479
// Close file
480
log_stream
.close();
481
482
// Return test result
483
return
test_status
;
484
}
// End of fpdiff
485
}
// namespace oomph
i
cstr elem_len * i
Definition
cfortran.h:603
oomph::FiniteElement::size
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition
elements.cc:4320
oomph::GZipReader
Definition
gzip_reader.h:55
oomph::GZipReader::read_all
std::vector< std::string > read_all()
Definition
gzip_reader.cc:180
oomph::OomphLibError
An OomphLibError object which should be thrown when an run-time error is encountered....
Definition
oomph_definitions.h:229
oomph::TAdvectionDiffusionReactionElement
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Definition
Tadvection_diffusion_reaction_elements.h:66
fpdiff.h
gzip_reader.h
oomph
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition
advection_diffusion_elements.cc:30
oomph::modify_string_inplace
std::string modify_string_inplace(std::string &text, const std::string &symbol, const unsigned &number)
Definition
fpdiff.cc:88
oomph::ends_with
bool ends_with(std::string const &value, std::string const &ending)
Definition
fpdiff.cc:71
oomph::gzip_load
std::vector< std::string > gzip_load(const std::string &filename)
Definition
fpdiff.cc:161
oomph::Type
Type
Definition
fpdiff.cc:58
oomph::Type::String
@ String
oomph::Type::Number
@ Number
oomph::load_file
std::vector< std::string > load_file(const std::string &filename)
Definition
fpdiff.cc:179
oomph::lower
std::string lower(const std::string &text)
Definition
fpdiff.cc:123
oomph::TestStatus
TestStatus
Definition
fpdiff.cc:48
oomph::Failed
@ Failed
Definition
fpdiff.cc:50
oomph::Passed
@ Passed
Definition
fpdiff.cc:49
oomph::fpdiff
int fpdiff(const std::string &filename1, const std::string &filename2, std::ostream &outstream, const double &relative_error, const double &small)
Definition
fpdiff.cc:210
oomph::get_type
Type get_type(const std::string &text)
Definition
fpdiff.cc:143
oomph::split_string
std::vector< std::string > split_string(const std::string &text)
Definition
fpdiff.cc:105
oomph_definitions.h