sweep.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /****************************************************************************
  2. *
  3. * MODULE: r.terraflow
  4. *
  5. * COPYRIGHT (C) 2007 Laura Toma
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. *****************************************************************************/
  18. #include <stdlib.h>
  19. #include <assert.h>
  20. #include <stdio.h>
  21. #include <unistd.h>
  22. #include <string.h>
  23. #include <grass/iostream/ami.h>
  24. #include "option.h"
  25. #include "stats.h"
  26. #include "sweep.h"
  27. #include "common.h"
  28. #include "weightWindow.h"
  29. #include "nodata.h"
  30. #include "sortutils.h"
  31. /*
  32. #define CHECKPARAM //output the parameters during sweeping
  33. #define CHECK_WEIGHTS //output weights as computed
  34. #define SWEEP_PRINT_PQSIZE //output priority queue size during sweeping
  35. #define CHECK_MEMORY //enables printing available memory in between steps
  36. */
  37. /* frequency; used to print progress dots */
  38. static const int DOT_CYCLE = 50;
  39. static const int PQSIZE_CYCLE = 100;
  40. /* globals in common.H
  41. extern statsRecorder *stats; stats file
  42. extern userOptions *opt; command-line options
  43. extern struct Cell_head *region; header of the region
  44. extern dimension_type nrows, ncols;
  45. */
  46. /* SELECT FLOW DATA STRUCTURE */
  47. #ifdef IM_PQUEUE
  48. typedef pqheap_t1<flowStructure> FLOW_DATASTR;
  49. #endif
  50. #ifdef EM_PQUEUE
  51. typedef em_pqueue<flowStructure, flowPriority> FLOW_DATASTR;
  52. #endif
  53. #ifdef EMPQ_ADAPTIVE
  54. typedef EMPQueueAdaptive<flowStructure, flowPriority> FLOW_DATASTR;
  55. #endif
  56. /* defined in this module */
  57. void pushFlow(const sweepItem& swit, const flowValue &flow,
  58. FLOW_DATASTR* flowpq, const weightWindow &weight);
  59. /* ------------------------------------------------------------*/
  60. sweepOutput::sweepOutput() {
  61. i = (dimension_type) nodataType::ELEVATION_NODATA;
  62. j = (dimension_type) nodataType::ELEVATION_NODATA;
  63. accu = (flowaccumulation_type) nodataType::ELEVATION_NODATA;
  64. #ifdef OUTPUT_TCI
  65. tci = (tci_type) nodataType::ELEVATION_NODATA;
  66. #endif
  67. };
  68. /* ------------------------------------------------------------ */
  69. /* computes output parameters of cell (i,j) given the flow value, the
  70. elevation of that cell and the weights of that cell; */
  71. void
  72. sweepOutput::compute(elevation_type elev,
  73. dimension_type i_crt, dimension_type j_crt,
  74. const flowValue &flow,
  75. const weightWindow &weight,
  76. const elevation_type nodata) {
  77. float correct_tci; /* this the correct value of tci; we're going to
  78. truncate this on current precision tci_type set by user in types.H*/
  79. assert(elev != nodata);
  80. assert(flow.get() >= 0);
  81. assert(weight.sumweight >= 0 && weight.sumcontour >= 0);
  82. i = i_crt;
  83. j = j_crt;
  84. if (weight.sumweight == 0 || weight.sumcontour == 0) {
  85. accu = (flowaccumulation_type)nodata;
  86. #ifdef OUTPUT_TCI
  87. tci = (tci_type)nodata;
  88. #endif
  89. } else {
  90. accu = flow.get();
  91. #ifdef OUTPUT_TCI
  92. correct_tci = log(flow.get()*weight.dx()*weight.dy()/weight.totalContour());
  93. /* assert(correct_tci > 0); //is this true? */
  94. /* there is no need for this warning. tci can be negative if the flow is small. */
  95. /* if (correct_tci < 0) {
  96. fprintf(stderr, "warning: tci negative, [flow=%f,dx=%f,dy=%f,cont=%f]\n",
  97. flow.get(), weight.dx(), weight.dy(), weight.totalContour());
  98. }
  99. */
  100. tci = (tci_type)correct_tci;
  101. #endif
  102. }
  103. return;
  104. }
  105. FLOW_DATASTR*
  106. initializePQ() {
  107. if (stats)
  108. stats->comment("sweep:initialize flow data structure", opt->verbose);
  109. FLOW_DATASTR *flowpq;
  110. #ifdef IM_PQUEUE
  111. if (stats)
  112. stats->comment("FLOW_DATASTRUCTURE: in-memory pqueue");
  113. flowpq = new FLOW_DATASTR(PQ_SIZE);
  114. char buf[1024];
  115. sprintf(buf, "initialized to %.2fMB\n", (float)PQ_SIZE / (1<<20));
  116. if (stats)
  117. *stats << buf;
  118. #endif
  119. #ifdef EM_PQUEUE
  120. if (stats)
  121. stats->comment("FLOW_DATASTRUCTURE: ext-memory pqueue");
  122. flowpq = new FLOW_DATASTR(nrows * ncols);
  123. #endif
  124. #ifdef EMPQ_ADAPTIVE
  125. if (opt->verbose && stats) stats->comment("FLOW_DATASTRUCTURE: adaptive pqueue");
  126. flowpq = new FLOW_DATASTR();
  127. #endif
  128. return flowpq;
  129. }
  130. #define INIT_PRINT_PROGRESS() \
  131. long out_frequency, pqsize_frequency; \
  132. out_frequency = nrows*ncols / DOT_CYCLE; \
  133. pqsize_frequency = nrows*ncols / PQSIZE_CYCLE; \
  134. assert(out_frequency); \
  135. assert(pqsize_frequency);
  136. #define PRINT_PROGRESS(k) \
  137. if ((k) % out_frequency == 0) { \
  138. fprintf(stderr,"."); \
  139. fflush(stderr); \
  140. };
  141. #ifdef SWEEP_PRINT_PQSIZE
  142. #define PRINT_PQSIZE(k,flowpq) \
  143. if ((k) % pqsize_frequency == 0) { \
  144. fprintf(stderr," %ld ", (long)(flowpq)->size()); \
  145. fflush(stderr); \
  146. }
  147. #else
  148. #define PRINT_PQSIZE(k,flowpq)
  149. #endif
  150. /***************************************************************/
  151. /* Read the points in order from the sweep stream and process them.
  152. If trustdir = 1 then trust and use the directions contained in the
  153. sweep stream. Otherwise push flow to all downslope neighbors and
  154. use the direction only for points without downslope neighbors. */
  155. /***************************************************************/
  156. AMI_STREAM<sweepOutput>*
  157. sweep(AMI_STREAM<sweepItem> *sweepstr, const flowaccumulation_type D8CUT,
  158. const int trustdir) {
  159. flowPriority prio;
  160. flowValue flow;
  161. sweepItem* crtpoint;
  162. AMI_err ae;
  163. flowStructure x;
  164. long nitems;
  165. Rtimer rt;
  166. AMI_STREAM<sweepOutput>* outstr;
  167. /* INIT_PRINT_PROGRESS(); */
  168. rt_start(rt);
  169. assert(sweepstr);
  170. if (stats)
  171. *stats << "sweeping\n";
  172. fprintf(stderr, "sweeping: ");
  173. /* create and initialize flow data structure */
  174. FLOW_DATASTR *flowpq;
  175. flowpq = initializePQ();
  176. /* create output stream */
  177. outstr = new AMI_STREAM<sweepOutput>();
  178. /* initialize weights and output */
  179. weightWindow weight(region->ew_res, region->ns_res);
  180. sweepOutput output;
  181. nitems = sweepstr->stream_len();
  182. #ifndef NDEBUG
  183. flowPriority prevprio = flowPriority(SHRT_MAX); /* XXX */
  184. #endif
  185. /* scan the sweep stream */
  186. ae = sweepstr->seek(0);
  187. assert(ae == AMI_ERROR_NO_ERROR);
  188. for (long k = 0; k < nitems; k++) {
  189. /* cout << k << endl; cout.flush(); */
  190. /* read next sweepItem = (prio, elevwin, topoRankwin, dir) */
  191. ae = sweepstr->read_item(&crtpoint);
  192. if (ae != AMI_ERROR_NO_ERROR) {
  193. fprintf(stderr, "sweep: k=%ld: cannot read next item..\n", k);
  194. exit(1);
  195. }
  196. /* cout << "k=" << k << " prio =" << crtpoint->getPriority() << "\n"; */
  197. /* nodata points should not be in sweep stream */
  198. assert(!is_nodata(crtpoint->getElev()));
  199. #ifndef NDEBUG
  200. assert(crtpoint->getPriority() > prevprio); /* XXX */
  201. prevprio = crtpoint->getPriority(); /* XXX */
  202. #endif
  203. /* compute flow accumulation of current point; initial flow value
  204. is 1 */
  205. flowValue flowini((double)1);
  206. /* add flow which was pushed into current point by upslope
  207. neighbours */
  208. assert(flowpq->is_empty() ||
  209. (flowpq->min(x), x.getPriority() >= crtpoint->getPriority())); /* XXX */
  210. assert(flowpq->is_empty() != flowpq->min(x)); /* XXX */
  211. if (flowpq->min(x) && ((prio=x.getPriority()) == crtpoint->getPriority())) {
  212. flowpq->extract_all_min(x);
  213. /* cout << "EXTRACT: " << x << endl; */
  214. flow = x.getValue();
  215. flow = flow + flowini;
  216. } else {
  217. flow = flowini;
  218. }
  219. assert(flowpq->is_empty() ||
  220. (flowpq->min(x), x.getPriority() > crtpoint->getPriority())); /* XXX */
  221. /* compute weights of current point given its direction */
  222. if (flow > D8CUT) {
  223. /* consider just the dominant direction */
  224. weight.makeD8(crtpoint->getI(), crtpoint->getJ(),
  225. crtpoint->getElevWindow(), crtpoint->getDir(), trustdir);
  226. } else {
  227. /* consider multiple flow directions */
  228. weight.compute(crtpoint->getI(), crtpoint->getJ(),
  229. crtpoint->getElevWindow(), crtpoint->getDir(), trustdir);
  230. }
  231. /* distribute the flow to its downslope neighbours */
  232. pushFlow(*crtpoint, flow, flowpq, weight);
  233. /* compute parameters */
  234. output.compute(crtpoint->getElev(), crtpoint->getI(), crtpoint->getJ(),
  235. flow, weight, nodataType::ELEVATION_NODATA);
  236. #ifdef CHECKPARAM
  237. printf("%7ld: (%5d, %5d, %5d) flow: %7.3f, weights:[",
  238. k, crtpoint->getElev(), crtpoint->getI(),crtpoint->getJ(),
  239. flow.get());
  240. for (int l=0;l<9;l++) printf("%2.1f ",weight.get(l));
  241. cout <<"] ";
  242. cout << output << "\n";
  243. #endif
  244. /* write output to sweep output stream */
  245. ae = outstr->write_item(output);
  246. assert(ae == AMI_ERROR_NO_ERROR);
  247. /* PRINT_PROGRESS(k); */
  248. /* PRINT_PQSIZE(k, flowpq); */
  249. G_percent(k, nitems, 2);
  250. } /* for k */
  251. G_percent(1, 1, 2); /* finish it */
  252. if (stats)
  253. *stats << "sweeping done\n";
  254. char buf[1024];
  255. sprintf(buf, "pqsize = %ld \n", (long)flowpq->size());
  256. if (stats)
  257. *stats << buf;
  258. assert(outstr->stream_len() == nitems);
  259. delete flowpq;
  260. rt_stop(rt);
  261. if (stats) {
  262. stats->recordTime("sweeping", rt);
  263. stats->recordLength("sweep output stream", outstr);
  264. }
  265. return outstr;
  266. }
  267. /***************************************************************/
  268. /* push flow to neighbors as indicated by flow direction and reflected
  269. by the weights of the neighbors; flow is the accumulated flow value
  270. of current point; The neighbours which receive flow from current
  271. point are inserted in the FLOW_DATASTR */
  272. /***************************************************************/
  273. void
  274. pushFlow(const sweepItem& swit, const flowValue &flow,
  275. FLOW_DATASTR *flowpq,
  276. const weightWindow &weight) {
  277. dimension_type i_crt, j_crt, i_neighb, j_neighb;
  278. short di, dj;
  279. elevation_type elev_crt, elev_neighb;
  280. toporank_type toporank_crt;
  281. assert(flow >= 0);
  282. /* get current coordinates, elevation, topological rank */
  283. i_crt = swit.getI();
  284. j_crt = swit.getJ();
  285. elev_crt = swit.getElev();
  286. toporank_crt = swit.getTopoRank();
  287. assert(!is_nodata(elev_crt));
  288. for (di = -1; di <= 1; di++) {
  289. for (dj = -1; dj <= 1; dj++) {
  290. if (weight.get(di,dj) > 0) {
  291. /* push flow to this neighbor */
  292. i_neighb = i_crt + di;
  293. j_neighb = j_crt + dj;
  294. elev_neighb = swit.getElev(di,dj);
  295. /*assert(IS_BOUNDARY(i_crt,j_crt,hdr) || elev_neighb !=hdr.get_nodata());*/
  296. /* it's not simple to check what nodata is on boundary, so we'll
  297. just assume directions are correct even if they point to nodata
  298. elevation values. */
  299. if (!is_nodata(elev_neighb)) {
  300. flowPriority prio(elev_neighb, swit.getTopoRank(di,dj),
  301. i_neighb, j_neighb);
  302. flowPriority prio_crt(elev_crt,toporank_crt, i_crt, j_crt);
  303. /* assert(prio >= prio_crt); */
  304. #if (defined WARNING_FLAG)
  305. if (prio < prio_crt) {
  306. printf("\n(row=%d,col=%d,ele=%d): ",
  307. i_crt, j_crt, elev_crt);
  308. cout << "attempt to push flow uphill\n";
  309. }
  310. #endif
  311. flowValue elt(weight.get(di,dj)*flow.get());
  312. flowStructure x(prio, elt);
  313. assert(x.getPriority() > swit.getPriority());
  314. flowpq->insert(x);
  315. /* cout << "INSERT: " << x << endl; */
  316. } /* if (!is_nodata(elev_neighb)) */
  317. }
  318. } /* for dj */
  319. } /* for di */
  320. return;
  321. }