-
Notifications
You must be signed in to change notification settings - Fork 0
/
MarcExportFFdataNCDF4.h
1932 lines (1658 loc) · 78.5 KB
/
MarcExportFFdataNCDF4.h
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Infos on the module:
Purpose: include in a FEFLOW IFM plugin code written in C++, to export results of a transient run
Author and copyright: Marc Laurencelle
Last updates: Oct. 2017
*/
/* TODO list:
- Replace the NC_SHARE mode (which may prevent perf. optim. due to frequent write ops to disk) by autom. calling of
nc_sync(...) at regular time intervals. Store time vars in the nc object. With t=now when initializing nc.
[not a serious optimization, except in cases with high-freq. export...; therefore, would be nice for the General uses]
- Consider using CZ_dynA_Eindeces for the usual CZsums computations, to avoid looping over all elements. I may even
remove from memory the CZid arrays once CZ_dynA_Eindeces is built, then!...
- Consider using the vector-specialized library includes for simpler calculations maybe: <functional> <numeric>. However, it may be harder to read the code, since it seems it requires a transform call in order to work... (e.g. look http://www.cplusplus.com/reference/functional/plus/)
DONE | ABANDONED list:
x ABANDONED IDEA: Change the order of dimensions for CZ_quantiles so that it's easier to use the data in R: currently it's «conc_quantiles : num [1:101, 1:4, 1:199]». [CZ#, Q#, T#] would be better I think.
*/
#include "stdifm.h"
#include "Pathcch.h"
#include "MarcGeneralFEFLOWtools.h"
#include <stdio.h>
#include <string.h>
#include <netcdf.h>
#include <vector>
#include <algorithm>
#include <numeric>
#define NDIMS2 2
#define VECTCNDIMS3 3 //for vectors' components (Darcy fluxes...); here, 3 is for the export-array dimensions[time,vect.comp.,node]
#define CZNDIMS2 2
#define CZQNDIMS3 3
#define MZNDIMS2 2
#define NBNEGCNDIMS2 2 //related to NbCuppBnds...
#define VECTC_FIXED_LEN 2 //2D models ONLY, for now (~NUMBER OF SPATIAL DIMENSIONS IN THE MODEL)
#define NOD_DIM_NAME "nodei"
#define REC_DIM_NAME "time"
#define VECTC_DIM_NAME "vectcompi"
#define CZ_DIM_NAME "contzonei"
#define QPROB_DIM_NAME "qprob"
#define MZ_DIM_NAME "monitzonei"
#define NEGCUBND_DIM_NAME "negconc_ubnd"
/* Names of things. */
#define UNITS "units"
#define NODEFF_NAME "nodenumber"
#define NODIDV_NAME "nodeidvalue"
#define XCOORD_NAME "x_local"
#define YCOORD_NAME "y_local"
#define YOFTOPROCK_NAME "yoftoprock_local"
#define HEAD_NAME "hydraulic_head"
#define CONC_NAME "concentration"
#define DFLUX_NAME "Darcy_flux_nodal"
#define MRBUDG_NAME "mass_rate_budget"
#define CQUANT_NAME "conc_quantiles"
#define VCOMD_NAME "vCOM_depth"
#define DPFD_NAME "DPF_depth"
#define GLOBMINCONC_NAME "glob_min_concentration"
#define GLOBMAXCONC_NAME "glob_max_concentration"
#define VCONCGRAD_NAME "vertical_conc_gradient"
#define NBNEGC_NAME "nb_negative_concs"
/* For the units attributes. */
#define UNITS "units"
//a. for Dimensions:
#define NODE_UNITS "node_as_ordered"
#define TIME_UNITS "days_since_sim_start"
#define VECTC_UNITS "vect_component_index"
#define CZ_UNITS "content_zone_raw_index"
#define MZ_UNITS "monit_zone_raw_index"
#define QPROB_UNITS "probability_at_quantile"
#define NEGCUBND_UNITS "negative_conc_upper_bound"
//b. for Variables:
#define NODEFF_UNITS "index_in_FF_mesh"
#define NODIDV_UNITS "id_value_by_user"
#define XCOORD_UNITS "m_rel_to_local_orig"
#define YCOORD_UNITS "m_rel_to_local_orig"
#define YOFTOPROCK_UNITS "m_rel_to_local_orig"
#define HEAD_UNITS "meter"
#define CONC_UNITS "mg_per_liter"
#define DFLUX_UNITS "meter_per_day"
#define MRBUDG_UNITS "g_per_day"
#define CQUANT_UNITS "quantile_at_Prob"
#define VCOMD_UNITS "meters below ref. top (vertical center of solute mass)"
#define DPFD_UNITS "meters below ref. top (depth of deepest plume front)"
#define VCONCGRAD_UNITS "mg_per_liter_per_meter"
#define NBNEGC_UNITS "number of nodal conc. values below the upper bounds"
//#define MAX_ATT_LEN 80
/* Handle errors by printing an error message and exiting with a non-zero status. */
#define ERR(e) {sprintf_s(nh.errtxtbuffer, 180, "[NetCDF] Error: %s", nc_strerror(e)); IfmWarning(pDoc, nh.errtxtbuffer); return 2;}
#define ERRpropag(e) {return e;}
const int MAXCTYPE = IfmENERGY_TOTAL; //DO NOT use this constant directly; instead, please use NBCTYPES!
const int NBCTYPES = MAXCTYPE + 1;
const int HWnbProbDivs = 100; //HARD-WIRED, for now... (TODO minor: Make it customizable from the FEM?)
const double nodata_double_ncdf = std::numeric_limits<double>::quiet_NaN();
#define NbNegCuppBnds 6
const double negCubounds[NbNegCuppBnds] = { nodata_double_ncdf, 0.0, -1000.0, -5000.0, -10000.0, -50000.0 };
//based on http://stackoverflow.com/questions/2185443/does-c-support-constant-arrays-of-type-string
const char* CTYPE_namesA[NBCTYPES] = { "total_volume", "void_volume", "fluid_content", "diluted_mass", "sorbed_mass", "energy_fluid", "energy_solid", "total_energy" };
const char* CTYPE_unitsA[NBCTYPES] = { "m3", "m3", "m3", "g", "g", "J", "J", "J" }; //based on help documentation on 'IfmGetElementalContent'
struct ncdfheader {
//OPTIONS FOR CONTROLLING WHAT TO EXPORT:
bool putNodeFF; //node index in the FEFLOW model mesh (beware: it changes very often when mesh geometry is modified)
bool putX, putY;
bool putYoftoprock; //NEW; when true, requires a fully set-up Yoftoprock nodal user data
bool puthead, putconc;
bool putDfluxcomponents;
bool putmratebudg;
bool putconcquantiles; //flag for activation of quantiles computing & export, throughout the simulation; ignored IF nbcontentzones == 0
bool putmassconvectmonit; //flag for potential activation of MZ recording, during the _init_ stage ONLY; later, we rather check if nbmonitzones > 0 to know if it's effectively active!
bool putnbnegconcs; //NEW flag... BETA
bool putglobextrconcs; //NEW flag... BETA
bool putvconcgrads; //NEW flag... BETA
//Some parameters which can be read from outside this module (but which are set internally ONLY!)
int nbcontentzones = 0;
int nbmonitzones = 0;
bool arefixednodes = false; //NEW!
//Special arrays pointers to customize some variables
double *PtocompmrbudgetA = nullptr;
//Internal copies of nodal data for all nodes in their original FEFLOW ordering
// (all have a fixed length = the numb
// ('fixed' ones are filled with steady values only once, during the netCDF initialization)
double *copyoffixednodalXcoordsA = nullptr;
double *copyoffixednodalYcoordsA = nullptr;
// ('transient' ones are updated with new values during each export-time-step write call)
double *copyoftransientnodalconcsA = nullptr;
//Arrays for computation of some types of export data
int compnbnegconcsA[NbNegCuppBnds]; //array for computation of the number of negative concentrations below each of the conc. upper bounds hard-wired in this code; (fixed-length static array, hence: no need to deallocate it at the end)
double *compvconcgradsA = nullptr; //array for computation of vertical concentration gradients at the export nodes
//Data arrays for the export nodes (expN)
int *Pnodeff_expN_outA; //array of the 'effective' i.e. current node FF-indeces for the export nodes (values change occasionally if not .arefixednodes)
double *Pxcoord_expN_outA; //array of the current X coordinates for the export nodes (values change occasionally if not .arefixednodes)
double *Pycoord_expN_outA; //array of the current Y coordinates for the export nodes (values change occasionally if not .arefixednodes)
double *Pyoftoprock_expN_outA; //array of the current Y-of-top-rock user-data-specified coordinates for the export nodes (values are updated occasionally if not .arefixednodes... though this normally should not change the values)
long opt_Yoftoprock_rID = -1; //Optional nodal reference distribution ID for the 'Yoftoprock' r.d. (required only if putYoftoprock == true)
//Special dyn.-sized array of vectors storing elem. indeces for faster CZ quantile, or MZ indicators, computations
//(NOTE: They are sized only once, at netcdf initialization, and keep a constant size afterwards.)
std::vector<int> *CZ_dynA_Eindeces = nullptr; //one vector per CZ id
std::vector<int> *MZ_dynA_Eindeces = nullptr; //one vector per MZ id
std::vector<double> CZ_fixed_qprobsV; //fixed probabilities P(q) for quantile computations
double *CZ_2DquantsA; //row i = CZ index; column j = quantile index
double *DFLUX_2DvaluesA; //row i = vector-component index; column j = node-as-ordered index
double *MZ_vCOMdA; //internal storage of most recent computed vCOM_depth values: array[nbMZ]
double *MZ_DPFdA; //internal storage of most recent computed DPF_depth values: array[nbMZ]
double comp_globminc; //computed global minimum concentration at current export time step; from scan of all active nodes (i.e. non-NaN conc. values)
double comp_globmaxc; //computed global maximum concentration at current export time step; from scan of all active nodes (i.e. non-NaN conc. values)
//FILE ID
char filepath[_MAX_PATH];
int ncid; //ID for the netCDF file
//For handling error messages
char errtxtbuffer[180];
//DIMENSIONS IDs
int nod_dimid; //ID for the nodal dimension (not the same as FEFLOW node index)
int rectime_dimid; //ID for the recorded time step (not the same as FEFLOW time step index)
int vectc_dimid; //ID for the vectors' components dimension (Darcy flux export...)
int dimids[NDIMS2];
int Dflux_dimids[VECTCNDIMS3];
//variants for CZ:
int CZ_dimid;
int qprob_dimid;
int CZdimids[CZNDIMS2];
int CZQdimids[CZQNDIMS3];
//variants for MZ:
int MZ_dimid;
int MZdimids[MZNDIMS2];
//variants for NEGC:
int negcubnd_dimid;
int nbnegcdimids[NBNEGCNDIMS2];
//VARIABLE IDs
//a. for Dimensions
int rectime_varid;
int nodindex_varid;
int vectc_varid;
int CZindex_varid;
int qprob_varid;
int MZindex_varid;
int negcubnd_varid;
//b. for Variables (dep. on the put___ selection)
int nodeff_varid;
int nodidv_varid; //NEW: id value by user
int xcoord_varid;
int ycoord_varid;
int yoftoprock_varid;
int head_varid;
int conc_varid;
int Dflux_varid;
int mrbudg_varid;
int cquant_varid;
int *CZ_varidA; //NEW
int vCOMd_varid;
int DPFd_varid;
int vconcgrad_varid; //NEW
int globminconc_varid; //NEW
int globmaxconc_varid; //NEW
int nbnegc_varid; //NEW
/* Special external-assigned parameters to make it possible to compute DPFd (if putmassconvectmonit...) */
// (these parameters MUST be set prior to the simulation run, if .putmassconvectmonit & then nbmonitzones > 0)
double DPF_normconcmin = 0.0; //minimum normalized concentration to look for, to locate the DPF...
double ref_C0 = -9999.0; //reference conc. for minimum density (in mg/L): rho(C0) = rho0 + 0.0*densityratio
double ref_CS = -9999.0; //reference conc. for maximum density (in mg/L): rho(CS) = rho0 + 1.0*densityratio
int nbelems_fixed = 0; //used by several tools of this module; must be defined prior to calling the netcdftools_init_... functions; TODO-minor: Could add a protection which verifies that nbelems_fixed>0 before using it
int nbnodes_fixed = 0; //used by several tools of this module; must be defined prior to calling the netcdftools_init_... functions; TODO-minor: Could add a protection which verifies that nbnodes_fixed>0 before using it
int *contentzoneidsA = nullptr; //optional; set from within the main IFM plugin code, if the related elemental User Data is found; otherwise, it remains null while nbcontentzones = 0 still.
int *uniqsortedczidsA = nullptr;
double **CZcomputedsums; //BETA: first dimension is based on the IfmContentType enum type, which current last value in FEFLOW IFM document.h is IfmENERGY_TOTAL (--> MAXCTYPE) on which NBCTYPES is defined
bool dothiscontenttype[NBCTYPES]; //booleans[0 to MAXCTYPE]
bool isporosityloaded = false;
bool istotvolumloaded = false;
double *intern_steadyPorosA; //array of steady (constant-in-time) elemental EFFECTIVE porosities (though also playing the role of a 'total porosity' in most contexts, due to the presence of n_eff in the transient storage term of the ADE!
double *intern_steadyTotVolumA; //array of steady (constant-in-time) elemental 'total volumes' (solid + saturated voids)
bool steadyActiveElems = false; //tells if ALL elements remain active throughout the entire simulation (true), or else that the active-state of the elements should be updated-stored-verified during the simulation due to changes in element states (activated/deactivated) by the WCM mode
bool *extern_transientACTIVelemA = nullptr; //array of transient elemental active/inactive states (which may be changed at some time steps by the host plugin
double *intern_steadyXelemA; //array of steady (constant-in-time) elemental centroids.X position (local coord. syst.)
double *intern_steadyYelemA; //array of steady (constant-in-time) elemental centroids.Y position (local coord. syst.)
double *intern_steadyZelemA; //array of steady (constant-in-time) elemental centroids.Z position (local coord. syst.)
double *intern_steadyDelemA; //array of steady (constant-in-time) elemental centroids.Depth below the refered 'top': calculated depth below top as specified in netcdftools_init_monitzones(...)
int *monitzoneidsA = nullptr; //optional; set from within the main IFM plugin code, if the related elemental User Data is found; otherwise, it remains null while nbmonitzones = 0 still.
int *uniqsortedmzidsA = nullptr;
int *nodeindexA; //FEFLOW-node-index array; (values can change but not the length of the array); array of export nodes only (this is NOT a full array!)
int *nodeifixedA; //ordered for-export node-index ('nodei') array; (fixed integer values starting with 0)
int *nodeidvaluesA = nullptr; //NEW optional array of IDs for the export nodes, to be written once, as time-indep. data; this is indeed time-indep. because ONLY internal special idvalues actually correspond to non-fixed nodes... (e.g. the adaptative glob.top.nodes with Id = expNautotopId = -999 from PaleoSea2D.cpp)
size_t nodeindex_len = 0; //length of nodeindex and nodeifixed arrays (this length must be kept constant through time!)
//REMOVED: size_t Len_DimNodes = 0; //Length in the 'node' dimension (with an initial value to know when it is initialized); essentially for type conversion of nodeindex_len from int to size_t
/* The start and count arrays will tell the netCDF library where to write our data. */
size_t start[NDIMS2], count[NDIMS2];
size_t CZstart[CZNDIMS2], CZcount[CZNDIMS2];
size_t CZQstart[CZQNDIMS3], CZQcount[CZQNDIMS3];
size_t MZstart[MZNDIMS2], MZcount[MZNDIMS2];
size_t DFLUXstart[VECTCNDIMS3], DFLUXcount[VECTCNDIMS3];
size_t NBNEGCstart[NBNEGCNDIMS2], NBNEGCcount[NBNEGCNDIMS2];
//time record index (starts at 0; unlimited)
size_t rec_index;
};
//Note: If the model contains elements which are initially inactive, please use an external array as alreadyV, which should contain the extracted IfmTOTAL_VOLUME elem. content for ALL elements (by tricking the IFM function by temporarily activating the i^th element in the extraction loop)
bool compute_all_element_centroids(IfmDocument pDoc, double *outX, double *outY, double *outZ = nullptr, double *alreadyV = nullptr)
{
bool is3D = IfmGetNumberOfDimensions(pDoc) == IfmNDM_3D;
bool doX = outX != nullptr;
bool doY = outY != nullptr;
bool doZ = is3D && outZ != nullptr;
bool extV = alreadyV != nullptr; //use the external alreadyV array of total elem. volume data, if specified
//TODO: Add warning msgs
int nnpe = IfmGetNumberOfNodesPerElement(pDoc); //number of nodes per element (constant; at least assumed & required)
int ne = IfmGetNumberOfElements(pDoc); //number of elements
double Evolum; //current element's volume
double *EnodcoordA;
EnodcoordA = new double[nnpe];
int ei; //current element index
int j, cni; //current loop iterator inside current element, and actual node index
for (ei = 0; ei < ne; ei++) {
Evolum = extV ? alreadyV[ei] : IfmGetElementalContent(pDoc, IfmTOTAL_VOLUME, ei);
if (doX) {
for (j = 0; j < nnpe;j++) {
cni = IfmGetNode(pDoc, ei, j);
EnodcoordA[j] = IfmGetX(pDoc, cni);
}
outX[ei] = IfmIntegrateNodalQuantitiesOfElement(pDoc, ei, EnodcoordA) / Evolum;
}
if (doY) {
for (j = 0; j < nnpe;j++) {
cni = IfmGetNode(pDoc, ei, j);
EnodcoordA[j] = IfmGetY(pDoc, cni);
}
outY[ei] = IfmIntegrateNodalQuantitiesOfElement(pDoc, ei, EnodcoordA) / Evolum;
}
if (doZ) {
for (j = 0; j < nnpe;j++) {
cni = IfmGetNode(pDoc, ei, j);
EnodcoordA[j] = IfmGetZ(pDoc, cni);
}
outZ[ei] = IfmIntegrateNodalQuantitiesOfElement(pDoc, ei, EnodcoordA) / Evolum;
}
}
if(is3D) IfmInfo(pDoc, "BETA: compute_all_element_centroids: done... but not yet VERIFIED for 3D models!");
return true;
}
bool compute_quantiles(std::vector<double> &values, std::vector<double> &probs, std::vector<double> &quants_out)
{
std::vector<double> sv; //sorted values
sv = values; //assigns the values (~copy); works: http://www.cplusplus.com/reference/vector/vector/operator=/
size_t nv = sv.size(); //number of values
size_t np = probs.size(); //number of quantiles requested (= nb of probs)
if (nv > 0) std::sort(sv.begin(), sv.end()); //do sort the values so that sv indeed becomes sorted
//vector<size_t> qiv;
//qiv.resize(np);
quants_out.resize(np);
size_t i;
size_t qvi;
for (i = 0; i < np; i++) {
qvi = (size_t)floor((nv - 1.0)*probs[i]);
//qiv[i] = qvi;
quants_out[i] = nv > 0 ? sv[qvi] : nodata_double_ncdf; //sv[qiv[i]];
}
return true;
}
template<class T>
size_t count_duplicates_in_vector(std::vector<T> &values, std::vector<int> &counts_out, bool alreadysorted = false)
{
std::vector<T> sv; //sorted values
sv = values; //assigns the values (~copy); works: http://www.cplusplus.com/reference/vector/vector/operator=/
if (!alreadysorted) std::sort(sv.begin(), sv.end()); //do sort the values so that sv indeed becomes sorted
size_t nv = sv.size(); //number of values
counts_out.clear();
if (nv == 0) return 0; //and counts_out is empty...
T currval, prevval;
int currcnt = 0;
bool newval;
prevval = sv[0];
size_t i;
for (i = 0; i < nv; i++) {
currval = sv[i];
newval = currval != prevval;
if (newval) {
counts_out.push_back(currcnt); //stores the count for the previous distinct value & its duplicates
currcnt = 1; //initializes the count for the current (~new) distinct value & its duplicates
prevval = currval;
}
else {
currcnt++;
}
}
counts_out.push_back(currcnt);
return counts_out.size();
}
////BETA!
//bool netcdftools_extract_nodal_param(IfmDocument pDoc, ncdfheader &nh, IfmParamID nId, double* destpValarray)
//{
// bool good;
// int parsiz = IfmGetParamSize(pDoc, nId);
// if (parsiz != nh.nbnodes_fixed) IfmWarning(pDoc, "[netcdftools_extract_nodal_param] STRANGELY, param.size != nb.Nodes !!?");
// int nbread = IfmGetParamValues(pDoc, nId, destpValarray, 0, nh.nbnodes_fixed);
// good = nbread == nh.nbnodes_fixed;
// if (!good) IfmWarning(pDoc, "[netcdftools_extract_nodal_param] STRANGELY, nb.par.values.read != nb.Nodes !!?");
// //IF DONE with no warning, should be OK then, and ready for use by other 'compute' functions.
// return good;
//}
//Note: netcdftools_init_contentzones should be called BERFORE calling this function (as nh.dothiscontenttype is needed)
void netcdftools_init_steadydata(IfmDocument pDoc, ncdfheader &nh, bool forceporo = false, bool forcetotvolum = false)
{
nh.isporosityloaded = false; //initial default
bool isporosityneeded = nh.dothiscontenttype[IfmVOID_VOLUME] || nh.dothiscontenttype[IfmFLUID_CONTENT] || nh.putmassconvectmonit || forceporo;
nh.istotvolumloaded = false;
bool istotvolumneeded = ((nh.dothiscontenttype[IfmTOTAL_VOLUME] || isporosityneeded) && true) || nh.putmassconvectmonit || forcetotvolum; //RETRYING SOMETHING (TODO: If that works nicely, remove old comments, and deBETAize it): nh.steadyActiveElems; //The last condition is important, since inactive elements will return a volume of 0.0. Therefore, as we don't want to interfere with the model setup by temporarily activating all elements to extract their volume, we'll rather get (up-to-date, current) elem. volumes each time this info is needed. (reviewed on Dec. 16th, 2016)
const bool BETAtmpactivElem = true; //NEW BETA necessary operation: seems to work properly!
int tmpEactiv; //temporary storage of the activ. state in the initial model
if (isporosityneeded) nh.intern_steadyPorosA = new double[nh.nbelems_fixed];
if (istotvolumneeded) nh.intern_steadyTotVolumA = new double[nh.nbelems_fixed];
int i;
for (i = 0; i < nh.nbelems_fixed; i++) {
/* Porosity (if needed) */
if (isporosityneeded) {
nh.intern_steadyPorosA[i] = IfmGetMatMassPorosity(pDoc, i);
}
/* Elemental volume (if needed) */
/* 1. First, temporarily activating the element so that V != 0.0 (which is the usual returned value for inactive elements, since we use IfmGetElementalContent(...) */
if (istotvolumneeded) {
if (BETAtmpactivElem) {
tmpEactiv = IfmGetMatElementActive(pDoc, i);
if (tmpEactiv == 0) IfmSetMatElementActive(pDoc, i, 1);
}
/* 2. Extracting V */
nh.intern_steadyTotVolumA[i] = IfmGetElementalContent(pDoc, IfmTOTAL_VOLUME, i);
/* 3. Finally, setting the element back to its previous active/inactive state */
if (BETAtmpactivElem && tmpEactiv == 0) {
IfmSetMatElementActive(pDoc, i, 0);
}
//TODO++: Verify if that perturbates the initial values in the model parts which started as inactive...
}
}
nh.isporosityloaded = isporosityneeded;
nh.istotvolumloaded = istotvolumneeded;
if (nh.putvconcgrads) {
//X coordinates (nodal, full domain, active & inactive):
nh.copyoffixednodalXcoordsA = new double[nh.nbnodes_fixed];
if (!genFFtools_extract_nodal_param(pDoc, IfmP_MSH_X, nh.copyoffixednodalXcoordsA)) {
delete[] nh.copyoffixednodalXcoordsA;
nh.putvconcgrads = false;
IfmWarning(pDoc, "[netcdftools_init_steadydata.putvconcgrads] Unsuccessful read of IfmP_MSH_X FF param. array, hence: disabling .putvconcgrads (= false).");
return;
}
//Y coordinates (nodal, full domain, active & inactive):
nh.copyoffixednodalYcoordsA = new double[nh.nbnodes_fixed];
if (!genFFtools_extract_nodal_param(pDoc, IfmP_MSH_Y, nh.copyoffixednodalYcoordsA)) {
delete[] nh.copyoffixednodalYcoordsA;
nh.putvconcgrads = false;
IfmWarning(pDoc, "[netcdftools_init_steadydata.putvconcgrads] Unsuccessful read of IfmP_MSH_X FF param. array, hence: disabling .putvconcgrads (= false).");
return;
}
//temp. buffer storing computed vertical concentration gradient values... (nodal, only at export nodes)
nh.compvconcgradsA = new double[nh.nodeindex_len];
//SEEMS TO WORK PROPERLY: IfmInfo(pDoc, "BETA: [netcdftools_init_steadydata].putvconcgrads done.");
}
}
void netcdftools_init_dynamicdatabuffers(IfmDocument pDoc, ncdfheader &nh)
{
if (nh.putnbnegconcs || nh.putglobextrconcs || nh.putvconcgrads) {
nh.copyoftransientnodalconcsA = new double[nh.nbnodes_fixed];
}
}
//TODO: Use the functional bool output to decide if plugin should continue or not (by completing both this code and the plugin's main code)
//Note: netcdftools_init_steadydata should be called AFTER having called this function
bool netcdftools_init_contentzones(IfmDocument pDoc, ncdfheader &nh, const IfmContentType* types2compute, const int ntypes, const char* elCZrdname = nullptr, long elCZrdid = -1)
{
int i;
bool validCZrd = (elCZrdname != nullptr) || (elCZrdid >= 0);
if (validCZrd && elCZrdid < 0) {
elCZrdid = IfmGetElementalRefDistrIdByName(pDoc, elCZrdname);
validCZrd = elCZrdid >= 0;
}
nh.contentzoneidsA = nullptr; //pre-initialization null pointer, to know if it must be deallocated or not if !validCZrd
if (validCZrd) {
/* Determine which contents should be computed */
/* 1. Initialization of the array = all false */
for (i = 0; i < NBCTYPES; i++) {
nh.dothiscontenttype[i] = false;
}
/* 2. Assigning true to desired c. types */
for (i = 0; i < ntypes; i++) {
nh.dothiscontenttype[types2compute[i]] = true;
}
/* 3. Counting desired c. types */
int nbuniqctypes = 0;
for (i = 0; i < NBCTYPES; i++) {
if (nh.dothiscontenttype[i]) nbuniqctypes++;
}
/* 4. Finally, disabling CZ monitoring if no c. type is true */
if (nbuniqctypes < 1) validCZrd = false;
}
if (validCZrd) {
std::vector<int> czidV(nh.nbelems_fixed); //vector of CZ ids
double readzv; //CZ Id value for current i^th element
/* importing zone id data */
for (i = 0; i < nh.nbelems_fixed; i++) {
/* ContentZone Id */
readzv = IfmGetElementalRefDistrValue(pDoc, elCZrdid, i);
czidV[i] = isnan(readzv) ? -1 : (int)floor(readzv + 0.5);
if (czidV[i] < 0) czidV[i] = -1;
//Therefore, values will be either valid integers >= 0, else -1... but never lower.
}
nh.contentzoneidsA = new int[nh.nbelems_fixed];
std::copy(czidV.begin(), czidV.end(), nh.contentzoneidsA); //based on http://stackoverflow.com/questions/2923272/how-to-convert-vector-to-array-c or http://stackoverflow.com/questions/12866912/fastest-way-to-copy-the-contents-of-a-vector-into-an-array
//Compiler build warning msg explanations for std copy: http://stackoverflow.com/questions/903064/compiler-error-function-call-with-parameters-that-may-be-unsafe
//Anyway, we can ignore this warning with no stress, as both source vector and dest. array are sized with the same length = nh.nbelems_fixed!
/* extracting sorted unique zone id values */
//Based on http://en.cppreference.com/w/cpp/algorithm/unique
// and http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
std::sort(czidV.begin(), czidV.end());
std::vector<int> nbelperCZ; //element count for each (sorted) CZ Id (aligned with nh.uniqsortedczidsA, thus)
size_t nbCZconfirm = count_duplicates_in_vector(czidV, nbelperCZ, true);
auto tmpV = std::unique(czidV.begin(), czidV.end());
czidV.erase(tmpV, czidV.end());
/* ...then we must remove the first value if it is -1 */
if (czidV.size() > 0 && czidV[0] == -1) {
czidV.erase(czidV.begin() + 0);
nbelperCZ.erase(nbelperCZ.begin() + 0);
nbCZconfirm--;
}
validCZrd = czidV.size() > 0;
if (validCZrd) {
nh.nbcontentzones = (int)czidV.size();
nh.uniqsortedczidsA = new int[nh.nbcontentzones];
std::copy(czidV.begin(), czidV.end(), nh.uniqsortedczidsA);
//Compiler build warning msg related to std copy can be ignored here again (see explanations above).
char txtbuff[180];
sprintf_s(txtbuff, 180, "[NetCDF] netcdftools_init_contentzones: %d different CZ IDs were detected.", nh.nbcontentzones);
IfmInfo(pDoc, txtbuff);
/* Allocate the summation arrays */
// based on http://stackoverflow.com/questions/936687/how-do-i-declare-a-2d-array-in-c-using-new and http://stackoverflow.com/questions/14829105/2d-dynamic-memory-allocation-array-in-c
nh.CZcomputedsums = new double*[NBCTYPES];
for (i = 0; i < NBCTYPES; i++) {
nh.CZcomputedsums[i] = nh.dothiscontenttype[i] ? new double[nh.nbcontentzones] : nullptr;
}
/* memorize the indeces of the elements of each CZ */
if (nh.putconcquantiles) {
/* Mostly copied from the code of function netcdftools_compute_contentzones(...) */
int *foundczptr; //pointer to the found value
int fndczintpos; //position in the internal array
int currCZ;
int *findfirst = nh.uniqsortedczidsA + 0;
int *findlastplus1 = nh.uniqsortedczidsA + nh.nbcontentzones + 1;
int cnterrs = 0;
nh.CZ_dynA_Eindeces = new std::vector<int>[nh.nbcontentzones]; //based on: http://stackoverflow.com/a/29358808/3433903
for (i = 0; i < nh.nbcontentzones; i++) {
nh.CZ_dynA_Eindeces[i].reserve(nbelperCZ[i]); //should match exactly with final 'actual' size of the vectors
}
for (i = 0; i < nh.nbelems_fixed; i++) {
currCZ = nh.contentzoneidsA[i];
if (currCZ < 0) continue;
//find, based on http://www.cplusplus.com/reference/algorithm/find/
foundczptr = std::find(findfirst, findlastplus1, currCZ);
if (foundczptr != findlastplus1) {
fndczintpos = (int)(foundczptr - findfirst);
nh.CZ_dynA_Eindeces[fndczintpos].push_back(i); //TODO: Consider some optimization here, since this repeated insertion at vector end is not cool at all!
}
else {
cnterrs++;
}
}
for (i = 0; i < nh.nbcontentzones; i++) {
nh.CZ_dynA_Eindeces[i].shrink_to_fit();
}
nh.CZ_fixed_qprobsV.resize(HWnbProbDivs + 1);
for (i = 0; i <= HWnbProbDivs; i++) { //Programmer's note: Note the '<=' here.
nh.CZ_fixed_qprobsV[i] = (double)i / HWnbProbDivs;
}
nh.CZ_2DquantsA = new double[nh.nbcontentzones * (HWnbProbDivs + 1)];
if (cnterrs > 0) IfmWarning(pDoc, "[NetCDF] There were errors due to invalid Content Zone Ids while preparing CZ elem. indeces storage...");
}
}
}
if (!validCZrd) {
if (nh.contentzoneidsA != nullptr) {
delete[] nh.contentzoneidsA;
nh.contentzoneidsA = nullptr;
}
nh.contentzoneidsA = nullptr;
nh.uniqsortedczidsA = nullptr;
nh.nbcontentzones = 0;
for (i = 0; i < NBCTYPES; i++) {
nh.dothiscontenttype[i] = false;
}
nh.CZ_dynA_Eindeces = nullptr;
IfmInfo(pDoc, "[NetCDF] netcdftools_init_contentzones: INACTIVE, since NO related user data is present, or it is all nodata.");
}
return true;
}
//TODO: Use the functional bool output to decide if plugin should continue or not (by completing both this code and the plugin's main code)
//(TODO-minor-longterm: make it compatible with 3D problems... at least the typical ones with structured mesh)
//Note: assign nullptr to unused char, and -1 to unused long arguments; netcdftools_init_steadydata must be called before
bool netcdftools_init_monitzones(IfmDocument pDoc, ncdfheader &nh, const char* elMZrdname, long elMZrdid, const char* eltoprdname, long eltoprdid)
{
/* 2D problems only (X,Y) for now */
bool valid;
int i;
char txtbuff[180];
if (!nh.putmassconvectmonit) {
nh.nbmonitzones = 0;
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones was called although ncdf.putmassconvectmonit == false. Please improve the code to avoid this.");
return false;
}
if (nh.ref_C0 < 0.0) {
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: ref_C0 was not set to >= 0.0 mg/L, in the plugin code.");
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: TODO program an Alert + Stop of the plugin right after this event.");
return false;
}
if (nh.ref_CS <= nh.ref_C0) {
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: ref_CS was not set to a value > ref_C0, in the plugin code.");
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: TODO program an Alert + Stop of the plugin right after this event.");
return false;
}
/* Prepare centroid (~barycenter) coordinates of ALL elements */
if (!nh.istotvolumloaded) {
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: TotVolum was not loaded as expected. The plugin will likely bug very soon.");
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: TODO program an Alert + Stop of the plugin right after this event.");
return false;
}
else {
nh.intern_steadyXelemA = new double[nh.nbelems_fixed];
nh.intern_steadyYelemA = new double[nh.nbelems_fixed];
valid = compute_all_element_centroids(pDoc, nh.intern_steadyXelemA, nh.intern_steadyYelemA, nullptr, nh.intern_steadyTotVolumA);
}
/* Detecting the user Data ref. distribution for the local top of the aquifer of interest (~datum) (OPTIONAL) */
bool validtoprd = (eltoprdname != nullptr) || (eltoprdid >= 0);
if (validtoprd && eltoprdid < 0) {
eltoprdid = IfmGetElementalRefDistrIdByName(pDoc, eltoprdname);
validtoprd = eltoprdid >= 0;
}
/* importing top data, and calculating depths */
double readtv; //top value for current i^th element
nh.intern_steadyDelemA = new double[nh.nbelems_fixed]; //allocating the complementary array with calculated depth in the rock aquifer
for (i = 0; i < nh.nbelems_fixed; i++) {
/* top elevation data */
readtv = validtoprd ? IfmGetElementalRefDistrValue(pDoc, eltoprdid, i) : 0.0; //Note: This way, it works even if not valid top ref.distrib. was specified... but it won't give usable | inferable depth values, except in very simple models with a flat constant top elevation
/* calculated depth in aquifer of interest (below specified top) */
nh.intern_steadyDelemA[i] = readtv - nh.intern_steadyYelemA[i];
}
if (validtoprd) {
char tmptoprdname[60];
IfmGetElementalRefDistrName(pDoc, eltoprdid, tmptoprdname);
sprintf_s(txtbuff, 180, "[NetCDF] netcdftools_init_monitzones: elem. depths (at centroid) calculated using '%s' as top elev. ref. distrib.", tmptoprdname);
IfmInfo(pDoc, txtbuff);
}
else {
IfmWarning(pDoc, "[NetCDF] netcdftools_init_monitzones: elem. depths (at centroid) calculated using top=0.0m since no valid ref.distrib. was specified.");
}
//COPIED###########################
/* Detecting the user Data ref. distribution for the monit. zone Ids (REQUIRED!) */
bool validMZrd = (elMZrdname != nullptr) || (elMZrdid >= 0);
if (validMZrd && elMZrdid < 0) {
elMZrdid = IfmGetElementalRefDistrIdByName(pDoc, elMZrdname);
validMZrd = elMZrdid >= 0;
}
nh.monitzoneidsA = nullptr; //pre-initialization null pointer, to know if it must be deallocated or not if !validMZrd
if (validMZrd) {
std::vector<int> mzidV(nh.nbelems_fixed); //vector of MZ ids
double readzv; //MZ Id value for current i^th element
/* importing monit zone id data */
for (i = 0; i < nh.nbelems_fixed; i++) {
/* Monit Zone Id */
readzv = IfmGetElementalRefDistrValue(pDoc, elMZrdid, i);
mzidV[i] = isnan(readzv) ? -1 : (int)floor(readzv + 0.5);
if (mzidV[i] < 0) mzidV[i] = -1;
//Therefore, values will be either valid integers >= 0, else -1... but never lower.
}
nh.monitzoneidsA = new int[nh.nbelems_fixed];
std::copy(mzidV.begin(), mzidV.end(), nh.monitzoneidsA); //see comments for similar call in netcdftools_init_contentzones procedure
/* extracting sorted unique zone id values (first, sorting the data) */
std::sort(mzidV.begin(), mzidV.end());
/* (then, as a bonus: counting nb of elements per MZ zone */
std::vector<int> nbelperMZ; //element count for each (sorted) MZ Id (aligned with nh.uniqsortedmzidsA, thus)
size_t nbMZconfirm = count_duplicates_in_vector(mzidV, nbelperMZ, true);
/* (then, removing duplicates so that only the distinct values remain in the (sorted) vector */
auto tmpV = std::unique(mzidV.begin(), mzidV.end());
mzidV.erase(tmpV, mzidV.end());
/* ...then we must remove the first value if it is -1 */
if (mzidV.size() > 0 && mzidV[0] == -1) {
mzidV.erase(mzidV.begin() + 0);
nbelperMZ.erase(nbelperMZ.begin() + 0);
nbMZconfirm--;
}
validMZrd = mzidV.size() > 0;
/* Next: continue if everything looks valid and there is >0 monit zone */
if (validMZrd) {
nh.nbmonitzones = (int)mzidV.size();
nh.uniqsortedmzidsA = new int[nh.nbmonitzones];
std::copy(mzidV.begin(), mzidV.end(), nh.uniqsortedmzidsA);
//Compiler build warning msg related to std copy can be ignored here again (see explanations above).
/* Lastly, create the small arrays which will store the computed values of interest */
nh.MZ_vCOMdA = new double[nh.nbmonitzones];
nh.MZ_DPFdA = new double[nh.nbmonitzones];
sprintf_s(txtbuff, 180, "[NetCDF] netcdftools_init_monitzones: %d different MZ IDs were detected.", nh.nbmonitzones);
IfmInfo(pDoc, txtbuff);
/* Memorize the indeces of the elements of each CZ */
/* (mostly copied from the code of function netcdftools_init_contentzones, late section for putquantiles) */
int *foundMZptr; //pointer to the found value
int fndMZintpos; //position in the internal array
int currMZ;
int *findfirst = nh.uniqsortedmzidsA + 0;
int *findlastplus1 = nh.uniqsortedmzidsA + nh.nbmonitzones + 1;
int cnterrs = 0;
nh.MZ_dynA_Eindeces = new std::vector<int>[nh.nbmonitzones]; //based on: http://stackoverflow.com/a/29358808/3433903
for (i = 0; i < nh.nbmonitzones; i++) {
nh.MZ_dynA_Eindeces[i].reserve(nbelperMZ[i]); //should match exactly with final 'actual' size of the vectors
}
for (i = 0; i < nh.nbelems_fixed; i++) {
currMZ = nh.monitzoneidsA[i];
if (currMZ < 0) continue;
//find, based on http://www.cplusplus.com/reference/algorithm/find/
foundMZptr = std::find(findfirst, findlastplus1, currMZ);
if (foundMZptr != findlastplus1) {
fndMZintpos = (int)(foundMZptr - findfirst);
nh.MZ_dynA_Eindeces[fndMZintpos].push_back(i); //TODO: Consider some optimization here, since this repeated insertion at vector end is not cool at all! >> NO MORE a problem, since I've reserved the appropriate size for the vector data.
}
else {
cnterrs++;
}
}
for (i = 0; i < nh.nbmonitzones; i++) {
nh.MZ_dynA_Eindeces[i].shrink_to_fit();
}
if (cnterrs > 0) IfmWarning(pDoc, "[NetCDF] There were errors due to invalid Monit Zone Ids while preparing MZ elem. indeces storage...");
}
}
//END COPY
if (!validMZrd) {
//TODO: Write code very similar to that of initCZ... but adapted to the dyn. arrays created here.
nh.putmassconvectmonit = false;
nh.nbmonitzones = 0;
}
IfmWarning(pDoc, "TODO-BETA write code at end of initMZ to delete unused dyn. arrays if NOT validMZrd...");
IfmWarning(pDoc, "TODO-BETA write code... also so that it informs (returns) false if failed to prepare MZ!");
return true;
}
void netcdftools_compute_MZ_indicators(IfmDocument pDoc, ncdfheader &nh)
{
if (nh.nbmonitzones == 0) return; //protection
int mzi, ej, elemidx;
double Mval; //elemental diluted-mass content value at the j^th element
double PVval; //calculated porous volume within the j^th element (used to transform mass <--> concentration)
double RMval; //relative diluted-mass above background conc. (i.e. M - C0*PV)
double Dval; //depth of the centroid of the j^th element
double Cval, NCval; //calculated conc. value; calc. normalized conc. (using C0 to CS bounds)
std::vector<int> currEiV; //vector storing a temporary copy of the elem. indeces for the current MZ...
/* accumulators (reset to zero at start of each MZ) */
double cumRM; //cumulative summing of elem. relative Mass [g] (i.e. after subtracting C0*PV background conc.)
double cumRMtD; //cum. summing of elem. rel.Mass*Depth [g*m]
double updDmax; //updating through the inner loop, max. Depth [m] of element with calc. concentration > DPF_concmin.
/* Shorter names to point to already allocated arrays; for computing Cval with cleaner code */
double *tmpTotVolA = nh.intern_steadyTotVolumA;
double *tmpPorosA = nh.intern_steadyPorosA;
/* (other shorter names) */
double Cmin = nh.ref_C0;
double Cmax = nh.ref_CS;
for (mzi = 0; mzi < nh.nbmonitzones; mzi++) {
currEiV = nh.MZ_dynA_Eindeces[mzi];
cumRM = 0.0;
cumRMtD = 0.0;
updDmax = -1.0e40; //very large negative number, so that it'll be replaced right away by an in-domain depth value
for (ej = 0; ej < currEiV.size(); ej++) {
elemidx = currEiV[ej];
if (!nh.steadyActiveElems && !nh.extern_transientACTIVelemA[elemidx]) continue; //to skip inactive elements
Mval = IfmGetElementalContent(pDoc, IfmDILUTED_MASS, elemidx);
PVval = tmpTotVolA[elemidx] * tmpPorosA[elemidx];
RMval = Mval - Cmin * PVval;
Dval = nh.intern_steadyDelemA[elemidx];
Cval = Mval / PVval;
NCval = (Cval - Cmin) / (Cmax - Cmin);
cumRM = cumRM + RMval;
cumRMtD = cumRMtD + RMval*Dval;
if (NCval > nh.DPF_normconcmin && Dval > updDmax) updDmax = Dval;
}
nh.MZ_vCOMdA[mzi] = cumRMtD / cumRM;
nh.MZ_DPFdA[mzi] = updDmax;
}
/* END BETA DEMO CODE */
}
void netcdftools_compute_CZ_quantiles(IfmDocument pDoc, ncdfheader &nh)
{
/* STRUCTURE VERY SIMILAR TO THAT OF netcdftools_compute_contentzones(...) */
if (nh.nbcontentzones == 0) return; //protection
/*
if (ctype != IfmDILUTED_MASS) { //other protection (may still bug before actually stopping the simulation)
IfmAlert(pDoc, NULL, " OK ", "Unmanaged ctype != IfmDILUTED_MASS in the call netcdftools_compute_CZ_quantiles(...). Please rectify the plugin code!");
IfmSetSimulationControlFlag(pDoc, IfmCTL_ABORT);
return;
} */
//Shorter names to point to already allocated arrays
// double *destsumA = nh.CZcomputedsums[ctype];
double *tmpTotVolA = nh.intern_steadyTotVolumA;
double *tmpPorosA = nh.intern_steadyPorosA;
int czi, ej, qj, elemidx;
double dmassval, concval; //elemental diluted-mass content value at the j^th element; calculated conc. value
std::vector<int> currEiV;
std::vector<double> concsV;
std::vector<double> calcquantsV;
bool cquantok;
for (czi = 0; czi < nh.nbcontentzones; czi++) {
currEiV = nh.CZ_dynA_Eindeces[czi];
/* First, compute & extract the current elemental concentrations */
concsV.clear(); //Important so that the vector is cleared and again of zero length
concsV.reserve(currEiV.size()); //Allocates the vector capacity to the max number of values it may have to stack
for (ej = 0; ej < currEiV.size(); ej++) {
elemidx = currEiV[ej];
if (!nh.steadyActiveElems && !nh.extern_transientACTIVelemA[elemidx]) continue; //to skip inactive elements
dmassval = IfmGetElementalContent(pDoc, IfmDILUTED_MASS, elemidx);
concval = dmassval / (tmpTotVolA[elemidx] * tmpPorosA[elemidx]);
// TODO: Verify if a protection should be added in case of inactive elements?...
//REPLACED: concsV[ej] = concval;
concsV.push_back(concval); //adding/appending the concval of the current ACTIVE element to the stack-vector
}
/* Then, compute the quantiles */
cquantok = compute_quantiles(concsV, nh.CZ_fixed_qprobsV, calcquantsV);
if (cquantok) {
/* ...and copy the results into the 2D array */
for (qj = 0; qj <= HWnbProbDivs; qj++) { //Programmer's note: Note the '<=' here.
// array[i][j] is then rewritten as array[i*sizeY + j] ; source: http://stackoverflow.com/a/936709/3433903
nh.CZ_2DquantsA[czi*(HWnbProbDivs + 1) + qj] = calcquantsV[qj];
}
}
else {
IfmWarning(pDoc, "netcdftools_compute_CZ_quantiles: UNMANAGED compute_quantiles == false!... Might bug soon.");
}
}
//[LOOKS GOOD (so: no more Beta?)] IfmInfo(pDoc, "TMP BETA: netcdftools_compute_CZ_quantiles finished.");
}
/* The following function was adapted & verified to give adequate results for the following content types:
IfmTOTAL_VOLUME Total volume
*IfmVOID_VOLUME Void volume (but since it's a fully saturated problem, this is the volume of 'effective porosity' rather than 'total porosity'... at least until the workflow evolves towards Double Porosity modeling (with 'solid' phase conc. in the remaining major part of the total porosity)
*IfmFLUID_CONTENT Fluid content (same as the Void volume, since it's a fully saturated problem with single liquid fluid
IfmDILUTED_MASS Diluted mass (fluid phase)
note: Types with an asterisk needed adaptation, since we work with fully saturated models with only an effective porosity. */
void netcdftools_compute_contentzones(IfmDocument pDoc, ncdfheader &nh, IfmContentType ctype)
{
if (nh.nbcontentzones == 0) return; //protection
int i;
int *foundczptr; //pointer to the found value
int fndczintpos; //position in the internal array
int currCZ;
double readcval;
int *findfirst = nh.uniqsortedczidsA + 0;
int *findlastplus1 = nh.uniqsortedczidsA + nh.nbcontentzones + 1;
int cnterrs = 0;
bool poroVmode = (ctype == IfmVOID_VOLUME) || (ctype == IfmFLUID_CONTENT);
if (poroVmode && !nh.isporosityloaded) {
poroVmode = false;
IfmWarning(pDoc, "[NetCDF] netcdftools_compute_contentzones: Porosity was not loaded as expected. Computed VoidV and FluidC contents will not be adequate.");
}
bool totVmode = (ctype == IfmTOTAL_VOLUME);
//Shorter names to point to already allocated arrays
double *destsumA = nh.CZcomputedsums[ctype];
double *tmpTotVolA = nh.intern_steadyTotVolumA;
double *tmpPorosA = nh.intern_steadyPorosA;
/* if (poroVmode && tmpTotVolA == nullptr) {
poroVmode = false;
IfmWarning(pDoc, "netcdftools_compute_contentzones: Strangely, tmpTotVolA is null. Computed VoidV and FluidC contents will not be adequate.");
} */
for (i = 0; i < nh.nbcontentzones; i++) {
destsumA[i] = 0.0;
}
double poroVval;
for (i = 0; i < nh.nbelems_fixed; i++) {
currCZ = nh.contentzoneidsA[i];
if (currCZ < 0) continue;
if (!nh.steadyActiveElems && !nh.extern_transientACTIVelemA[i]) continue; //to skip currently inactive elements
//find, based on http://www.cplusplus.com/reference/algorithm/find/
foundczptr = std::find(findfirst, findlastplus1, currCZ);
if (foundczptr != findlastplus1) {
poroVval = poroVmode ? (nh.istotvolumloaded ? tmpTotVolA[i] : IfmGetElementalContent(pDoc, IfmTOTAL_VOLUME, i)) * tmpPorosA[i] : 0.0;
readcval = poroVmode ? poroVval : (totVmode && nh.istotvolumloaded ? tmpTotVolA[i] : IfmGetElementalContent(pDoc, ctype, i));
fndczintpos = (int)(foundczptr - findfirst);
destsumA[fndczintpos] = destsumA[fndczintpos] + readcval;
}
else {
cnterrs++;
}
}
if (cnterrs > 0) IfmWarning(pDoc, "[NetCDF] There were errors due to invalid Content Zone Ids while computing zone sums...");
}
//This function must NOT be called if nbcontentzones == 0
int netcdftools_write_CZ_headings(IfmDocument pDoc, ncdfheader &nh)
{
/* Error handling. */
int retval;
/* Define the dimensions for CZ. */
if ((retval = nc_def_dim(nh.ncid, CZ_DIM_NAME, nh.nbcontentzones, &nh.CZ_dimid)))
ERR(retval);
/* Define the coordinate variables for CZ. */
if ((retval = nc_def_var(nh.ncid, CZ_DIM_NAME, NC_INT, 1, &nh.CZ_dimid,
&nh.CZindex_varid)))
ERR(retval);
/* Assign units attributes to coordinate variables. */
if ((retval = nc_put_att_text(nh.ncid, nh.CZindex_varid, UNITS,
strlen(CZ_UNITS), CZ_UNITS)))
ERR(retval);
/* The dimids array is used to pass the dimids of the dimensions of
the netCDF variables. Both of the netCDF variables we are
creating share the same four dimensions. In C, the
unlimited dimension must come first on the list of dimids. */
nh.CZdimids[0] = nh.rectime_dimid;
nh.CZdimids[1] = nh.CZ_dimid;
nh.CZ_varidA = new int[NBCTYPES];
int cti;
int nbvarsdefined = 0;
for (cti = 0; cti < NBCTYPES; cti++) {
if (!nh.dothiscontenttype[cti]) continue;
/* Define the netCDF variable for the "CTYPE[cti]" data. */
if ((retval = nc_def_var(nh.ncid, CTYPE_namesA[cti], NC_DOUBLE, CZNDIMS2,
nh.CZdimids, &nh.CZ_varidA[cti])))
ERR(retval);
/* Assign units attributes to the netCDF variable. */
if ((retval = nc_put_att_text(nh.ncid, nh.CZ_varidA[cti], UNITS,
strlen(CTYPE_unitsA[cti]), CTYPE_unitsA[cti])))
ERR(retval);
nbvarsdefined++;
}
char txtbuffer[180];
sprintf_s(txtbuffer, 180, "[NetCDF] netcdftools_write_CZ_headings: %d content-type output variables were created.", nbvarsdefined);
IfmInfo(pDoc, txtbuffer);
/* NOW, QUANTILES */
if (nh.putconcquantiles) {
/* Define the dimension qprob. */
if ((retval = nc_def_dim(nh.ncid, QPROB_DIM_NAME, HWnbProbDivs + 1, &nh.qprob_dimid)))
ERR(retval);
/* Define the coordinate variable for qprob. */
if ((retval = nc_def_var(nh.ncid, QPROB_DIM_NAME, NC_DOUBLE, 1, &nh.qprob_dimid,
&nh.qprob_varid)))