diff --git a/conf/configure b/conf/configure index 260d332..21b4572 100755 --- a/conf/configure +++ b/conf/configure @@ -741,6 +741,7 @@ enable_swan enable_ecology_custom enable_do_timing enable_mkl +enable_kdtree with_mkl with_gsl enable_da @@ -1385,6 +1386,7 @@ Optional Features: --enable-ecology-custom=standard|anm|... - The ecological model custom version --enable-do-timing enables timing profile --enable-mkl enables building with MKL + --enable-kdtree Enable KDTREE feature --enable-da enables building with the DA library --disable-openmp do not use OpenMP --enable-omp enables building with OpenMP @@ -5553,6 +5555,17 @@ if test "$ENABLE_MKL"; then fi +# Check whether --enable-kdtree was given. +if test "${enable_kdtree+set}" = set; then : + enableval=$enable_kdtree; if test "$enableval" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: KDTREE help for delaunay walk is enabled, results may vary." >&5 +$as_echo "$as_me: WARNING: KDTREE help for delaunay walk is enabled, results may vary." >&2;} + $as_echo "#define USE_KDTREE 1" >>confdefs.h + + fi +fi + + # Check whether --with-mkl was given. if test "${with_mkl+set}" = set; then : diff --git a/conf/configure.in b/conf/configure.in index 315ac50..1d3cb49 100644 --- a/conf/configure.in +++ b/conf/configure.in @@ -334,6 +334,16 @@ if test "$ENABLE_MKL"; then AC_DEFINE(HAVE_MKL) fi +dnl ########################################################################### +dnl #### kdtree vs delaunay +dnl ########################################################################### +AC_ARG_ENABLE([kdtree], + [AS_HELP_STRING([--enable-kdtree], [Enable KDTREE feature])], + [if test "$enableval" = "yes"; then + AC_MSG_WARN([KDTREE help for delaunay walk is enabled, results may vary.]) + AC_DEFINE([USE_KDTREE]) + fi]) + dnl ########################################################################### dnl #### Intel Math Kernel Library (MKL) dnl ########################################################################### diff --git a/conf/ems_version.h b/conf/ems_version.h index ca95ccc..7f3d1a4 100644 --- a/conf/ems_version.h +++ b/conf/ems_version.h @@ -1,4 +1,4 @@ -#define EMS_VERSION "v1.5.2" +#define EMS_VERSION "v1.5.3" #define EMS_IS_RELEASE 1 \ No newline at end of file diff --git a/lib/include/delaunay.h b/lib/include/delaunay.h index 9df7af9..22c9454 100644 --- a/lib/include/delaunay.h +++ b/lib/include/delaunay.h @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: delaunay.h 6922 2021-10-08 04:40:51Z her127 $ + * $Id: delaunay.h 7533 2024-04-24 04:03:08Z tho861 $ * */ @@ -21,6 +21,12 @@ #include "grid_utils.h" #include "istack.h" +#include "ems_conf.h" // added so we can switch the kdtree on/off + +#ifdef USE_KDTREE + #include "nanoflann_wrappers.h" // wrapper for c++ -> c +#endif + #if !defined(_DELAUNAY_STRUCT) #define _DELAUNAY_STRUCT @@ -59,6 +65,10 @@ typedef struct { * triangles i-th point belongs to */ int** point_triangles; /* point_triangles[i][j] is index of j-th * triangle i-th point belongs to */ + #ifdef USE_KDTREE + point* centroids; /* store the centroids for the tree*/ + KDTreeContext *rt; /* kdtree (called it rt since we originally implemented an rtree)*/ + #endif int nedges; int* edges; /* n-th edge is formed by points[edges[n*2]] diff --git a/lib/include/ems.h b/lib/include/ems.h index 07be133..f4966ad 100644 --- a/lib/include/ems.h +++ b/lib/include/ems.h @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ems.h 7477 2023-12-22 01:31:36Z riz008 $ + * $Id: ems.h 7577 2024-05-31 00:36:37Z riz008 $ */ #ifndef _EMS_H @@ -98,7 +98,7 @@ int strncasecmp(const char *s1, const char *s2, int n); /* Release verions and getters */ #define EMSLIB_MAJOR_VERSION 1 -#define EMSLIB_MINOR_VERSION 3 +#define EMSLIB_MINOR_VERSION 4 #define EMSLIB_PATCH_VERSION 1 int get_emslib_major_vers(void); diff --git a/lib/include/ems_conf.h.in b/lib/include/ems_conf.h.in index 3edf260..1b790fe 100644 --- a/lib/include/ems_conf.h.in +++ b/lib/include/ems_conf.h.in @@ -129,3 +129,6 @@ /* GSL */ #undef HAVE_GSL + +/* Define to enable KDTREE feature. */ +#undef USE_KDTREE \ No newline at end of file diff --git a/lib/include/nanoflann_wrappers.h b/lib/include/nanoflann_wrappers.h new file mode 100644 index 0000000..adbc671 --- /dev/null +++ b/lib/include/nanoflann_wrappers.h @@ -0,0 +1,24 @@ +// nanoflann_wrappers.h + +#include // Include for printf + + +#ifdef __cplusplus +extern "C" { +#endif + +// typedef struct { +// double x; +// double y; +// double z; +// double *v; +// } point; + +typedef struct KDTreeContext KDTreeContext; +void* create_kdtree(const point* points, size_t N, size_t max_leaf_size); +int find_nearest_vertex(void* context, const float query_pt[3]); +void delete_kdtree(void* context); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/interp/delaunay/delaunay.c b/lib/interp/delaunay/delaunay.c index 1505b64..81638e6 100644 --- a/lib/interp/delaunay/delaunay.c +++ b/lib/interp/delaunay/delaunay.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: delaunay.c 6924 2021-10-08 04:41:47Z her127 $ + * $Id: delaunay.c 7533 2024-04-24 04:03:08Z tho861 $ * */ @@ -31,6 +31,10 @@ #include "emsmath.h" #include "emsalloc.h" +#ifdef USE_KDTREE + #include "errfn.h" +#endif + // int verbose = 0; @@ -132,6 +136,10 @@ delaunay* delaunay_create() d->nflags = 0; d->nflagsallocated = 0; d->flagids = NULL; + #if (defined USE_KDTREE) || (defined USE_RTREE) + d->centroids = NULL; + d->rt = NULL; + #endif return d; } @@ -365,6 +373,32 @@ static void tio2delaunay_v(struct triangulateio* tio_out, struct triangulateio* } } + +void output_triangles(delaunay* d, const char* base_filename) { + char filename[1024]; + snprintf(filename, sizeof(filename), "%s_%p.txt", base_filename, (void*)d); + + FILE* file = fopen(filename, "w"); + if (!file) { + perror("Failed to open triangles data file"); + return; + } + + for (int i = 0; i < d->ntriangles; ++i) { + triangle* t = &d->triangles[i]; + // Output format: TriangleID, x1, y1, x2, y2, x3, y3 + fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n", i, + d->points[t->vids[0]].x, d->points[t->vids[0]].y, + d->points[t->vids[1]].x, d->points[t->vids[1]].y, + d->points[t->vids[2]].x, d->points[t->vids[2]].y); + } + + fclose(file); + printf("Triangle data written to %s\n", filename); +} + + + /* Builds Delaunay triangulation of the given array of points. * * @param np Number of points @@ -439,6 +473,21 @@ delaunay* delaunay_build(int np, point points[], int ns, int segments[], int nh, tio_destroy(&tio_in); tio_destroy(&tio_out); + #ifdef USE_KDTREE + // make centroids for kdtree: + d->centroids = malloc(d->ntriangles * sizeof(point)); + for (int i = 0; i < d->ntriangles; ++i) { + triangle* t = &d->triangles[i]; + double centroidX = (d->points[t->vids[0]].x + d->points[t->vids[1]].x + d->points[t->vids[2]].x) / 3.0; + double centroidY = (d->points[t->vids[0]].y + d->points[t->vids[1]].y + d->points[t->vids[2]].y) / 3.0; + d->centroids[i].x = centroidX; + d->centroids[i].y = centroidY; + // output_triangles(d,"triangles.txt"); + } + warn("building kdtree\n"); + d->rt = create_kdtree(d->centroids, d->ntriangles, 10); // last arg is max leaf size, perhaps this could be in the prm/tran + #endif + return d; } @@ -477,6 +526,12 @@ void delaunay_destroy(delaunay* d) istack_destroy(d->t_out); if (d->flagids != NULL) free(d->flagids); + #if (defined USE_KDTREE) + if (d->rt != NULL) + delete_kdtree(d->rt); + if (d->centroids != NULL) + free(d->centroids); + #endif free(d); } @@ -502,6 +557,12 @@ int delaunay_xytoi(delaunay* d, point* p, int id) if (p->x < d->xmin || p->x > d->xmax || p->y < d->ymin || p->y > d->ymax) return -1; + #ifdef USE_KDTREE + id = find_nearest_vertex(d->rt, (float[]){p->x, p->y}); + if (id < 0) warn("kdtree could not find nearest centroid to %.12f %.12f\n", p->x, p->y); + // printf("delaunay_xytoi : kdtree result = %i ", id); + #endif + if (id < 0 || id > d->ntriangles) id = 0; t = &d->triangles[id]; @@ -518,6 +579,7 @@ int delaunay_xytoi(delaunay* d, point* p, int id) } } while (i < 3); + // printf("delaunay result = %i \n", id); return id; } @@ -534,6 +596,12 @@ int delaunay_xytoi_ng(delaunay* d, point* p, int id) if (p->x < d->xmin || p->x > d->xmax || p->y < d->ymin || p->y > d->ymax) return id; + #ifdef USE_KDTREE + id = find_nearest_vertex(d->rt, (float[]){p->x, p->y}); + if (id < 0) warn("kdtree could not find nearest centroid to %.12f %.12f\n", p->x, p->y); + // printf("delaunay_xytoi_ng : kdtree result = %i ", id); + #endif + if (id < 0 || id > d->ntriangles) id = 0; t = &d->triangles[id]; @@ -554,6 +622,7 @@ int delaunay_xytoi_ng(delaunay* d, point* p, int id) } } while (i < 3); + // printf("delaunay_xytoi : kdtree result = %i ", id); return id; } @@ -591,6 +660,13 @@ int delaunay_xytoi_lag(delaunay* d, point* p, int id) if(fabs(p->x - d->points[t->vids[0]].x) < 1e-5 && fabs(p->y - d->points[t->vids[0]].y) < 1e-5) return(id); + + #ifdef USE_KDTREE + // // The walk goes into an infinite loop if seeded with an id so not implementing here. + // id = find_nearest_vertex(d->rt, (float[]){p->x, p->y}); + // printf("delaunay_xytoi_lag : kdtree result = %i ", id); + #endif + do { for (i = 0; i < 3; ++i) { int i1 = (i + 1) % 3, i2; @@ -626,7 +702,7 @@ int delaunay_xytoi_lag(delaunay* d, point* p, int id) } } } while (i < 3); - + // printf("delaunay_xytoi_lag : del = %i \n", id); return id; } @@ -646,6 +722,13 @@ int delaunay_xytoi_lago(delaunay* d, point* p, int id) if(fabs(p->x - d->points[t->vids[0]].x) < 1e-5 && fabs(p->y - d->points[t->vids[0]].y) < 1e-5) return(id); + + #ifdef USE_KDTREE + // // This function delaunay_xytoi_lago doesn't seem to be called anyway so not including + // id = find_nearest_vertex(d->rt, (float[]){p->x, p->y}); + // printf("delaunay_xytoi_lago : kdtree result = %i ", id); + #endif + do { for (i = 0; i < 3; ++i) { int i1 = (i + 1) % 3; @@ -681,6 +764,7 @@ int delaunay_xytoi_lago(delaunay* d, point* p, int id) } } while (i < 3); + // printf(" delaunay_xytoi_lago : delaunay result = %i ", id); return id; } diff --git a/lib/interp/delaunay/nanoflann.hpp b/lib/interp/delaunay/nanoflann.hpp new file mode 100644 index 0000000..08ea6bb --- /dev/null +++ b/lib/interp/delaunay/nanoflann.hpp @@ -0,0 +1,2685 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. + * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. + * Copyright 2011-2024 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ + +/** \mainpage nanoflann C++ API documentation + * nanoflann is a C++ header-only library for building KD-Trees, mostly + * optimized for 2D or 3D point clouds. + * + * nanoflann does not require compiling or installing, just an + * #include in your code. + * + * See: + * - [Online README](https://github.com/jlblancoc/nanoflann) + * - [C++ API documentation](https://jlblancoc.github.io/nanoflann/) + */ + +#pragma once + +#include +#include +#include +#include +#include // for abs() +#include +#include // for abs() +#include // std::reference_wrapper +#include +#include +#include // std::numeric_limits +#include +#include +#include +#include + +/** Library version: 0xMmP (M=Major,m=minor,P=patch) */ +#define NANOFLANN_VERSION 0x155 + +// Avoid conflicting declaration of min/max macros in Windows headers +#if !defined(NOMINMAX) && \ + (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64)) +#define NOMINMAX +#ifdef max +#undef max +#undef min +#endif +#endif +// Avoid conflicts with X11 headers +#ifdef None +#undef None +#endif + +namespace nanoflann +{ +/** @addtogroup nanoflann_grp nanoflann C++ library for KD-trees + * @{ */ + +/** the PI constant (required to avoid MSVC missing symbols) */ +template +T pi_const() +{ + return static_cast(3.14159265358979323846); +} + +/** + * Traits if object is resizable and assignable (typically has a resize | assign + * method) + */ +template +struct has_resize : std::false_type +{ +}; + +template +struct has_resize().resize(1), 0)> + : std::true_type +{ +}; + +template +struct has_assign : std::false_type +{ +}; + +template +struct has_assign().assign(1, 0), 0)> + : std::true_type +{ +}; + +/** + * Free function to resize a resizable object + */ +template +inline typename std::enable_if::value, void>::type resize( + Container& c, const size_t nElements) +{ + c.resize(nElements); +} + +/** + * Free function that has no effects on non resizable containers (e.g. + * std::array) It raises an exception if the expected size does not match + */ +template +inline typename std::enable_if::value, void>::type + resize(Container& c, const size_t nElements) +{ + if (nElements != c.size()) + throw std::logic_error("Try to change the size of a std::array."); +} + +/** + * Free function to assign to a container + */ +template +inline typename std::enable_if::value, void>::type assign( + Container& c, const size_t nElements, const T& value) +{ + c.assign(nElements, value); +} + +/** + * Free function to assign to a std::array + */ +template +inline typename std::enable_if::value, void>::type + assign(Container& c, const size_t nElements, const T& value) +{ + for (size_t i = 0; i < nElements; i++) c[i] = value; +} + +/** @addtogroup result_sets_grp Result set classes + * @{ */ + +/** Result set for KNN searches (N-closest neighbors) */ +template < + typename _DistanceType, typename _IndexType = size_t, + typename _CountType = size_t> +class KNNResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + using CountType = _CountType; + + private: + IndexType* indices; + DistanceType* dists; + CountType capacity; + CountType count; + + public: + explicit KNNResultSet(CountType capacity_) + : indices(nullptr), dists(nullptr), capacity(capacity_), count(0) + { + } + + void init(IndexType* indices_, DistanceType* dists_) + { + indices = indices_; + dists = dists_; + count = 0; + if (capacity) + dists[capacity - 1] = (std::numeric_limits::max)(); + } + + CountType size() const { return count; } + bool empty() const { return count == 0; } + bool full() const { return count == capacity; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + CountType i; + for (i = count; i > 0; --i) + { + /** If defined and two points have the same distance, the one with + * the lowest-index will be returned first. */ +#ifdef NANOFLANN_FIRST_MATCH + if ((dists[i - 1] > dist) || + ((dist == dists[i - 1]) && (indices[i - 1] > index))) + { +#else + if (dists[i - 1] > dist) + { +#endif + if (i < capacity) + { + dists[i] = dists[i - 1]; + indices[i] = indices[i - 1]; + } + } + else + break; + } + if (i < capacity) + { + dists[i] = dist; + indices[i] = index; + } + if (count < capacity) count++; + + // tell caller that the search shall continue + return true; + } + + DistanceType worstDist() const { return dists[capacity - 1]; } +}; + +/** Result set for RKNN searches (N-closest neighbors with a maximum radius) */ +template < + typename _DistanceType, typename _IndexType = size_t, + typename _CountType = size_t> +class RKNNResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + using CountType = _CountType; + + private: + IndexType* indices; + DistanceType* dists; + CountType capacity; + CountType count; + DistanceType maximumSearchDistanceSquared; + + public: + explicit RKNNResultSet( + CountType capacity_, DistanceType maximumSearchDistanceSquared_) + : indices(nullptr), + dists(nullptr), + capacity(capacity_), + count(0), + maximumSearchDistanceSquared(maximumSearchDistanceSquared_) + { + } + + void init(IndexType* indices_, DistanceType* dists_) + { + indices = indices_; + dists = dists_; + count = 0; + if (capacity) dists[capacity - 1] = maximumSearchDistanceSquared; + } + + CountType size() const { return count; } + bool empty() const { return count == 0; } + bool full() const { return count == capacity; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + CountType i; + for (i = count; i > 0; --i) + { + /** If defined and two points have the same distance, the one with + * the lowest-index will be returned first. */ +#ifdef NANOFLANN_FIRST_MATCH + if ((dists[i - 1] > dist) || + ((dist == dists[i - 1]) && (indices[i - 1] > index))) + { +#else + if (dists[i - 1] > dist) + { +#endif + if (i < capacity) + { + dists[i] = dists[i - 1]; + indices[i] = indices[i - 1]; + } + } + else + break; + } + if (i < capacity) + { + dists[i] = dist; + indices[i] = index; + } + if (count < capacity) count++; + + // tell caller that the search shall continue + return true; + } + + DistanceType worstDist() const { return dists[capacity - 1]; } +}; + +/** operator "<" for std::sort() */ +struct IndexDist_Sorter +{ + /** PairType will be typically: ResultItem */ + template + bool operator()(const PairType& p1, const PairType& p2) const + { + return p1.second < p2.second; + } +}; + +/** + * Each result element in RadiusResultSet. Note that distances and indices + * are named `first` and `second` to keep backward-compatibility with the + * `std::pair<>` type used in the past. In contrast, this structure is ensured + * to be `std::is_standard_layout` so it can be used in wrappers to other + * languages. + * See: https://github.com/jlblancoc/nanoflann/issues/166 + */ +template +struct ResultItem +{ + ResultItem() = default; + ResultItem(const IndexType index, const DistanceType distance) + : first(index), second(distance) + { + } + + IndexType first; //!< Index of the sample in the dataset + DistanceType second; //!< Distance from sample to query point +}; + +/** + * A result-set class used when performing a radius based search. + */ +template +class RadiusResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + + public: + const DistanceType radius; + + std::vector>& m_indices_dists; + + explicit RadiusResultSet( + DistanceType radius_, + std::vector>& indices_dists) + : radius(radius_), m_indices_dists(indices_dists) + { + init(); + } + + void init() { clear(); } + void clear() { m_indices_dists.clear(); } + + size_t size() const { return m_indices_dists.size(); } + size_t empty() const { return m_indices_dists.empty(); } + + bool full() const { return true; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + if (dist < radius) m_indices_dists.emplace_back(index, dist); + return true; + } + + DistanceType worstDist() const { return radius; } + + /** + * Find the worst result (farthest neighbor) without copying or sorting + * Pre-conditions: size() > 0 + */ + ResultItem worst_item() const + { + if (m_indices_dists.empty()) + throw std::runtime_error( + "Cannot invoke RadiusResultSet::worst_item() on " + "an empty list of results."); + auto it = std::max_element( + m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter()); + return *it; + } +}; + +/** @} */ + +/** @addtogroup loadsave_grp Load/save auxiliary functions + * @{ */ +template +void save_value(std::ostream& stream, const T& value) +{ + stream.write(reinterpret_cast(&value), sizeof(T)); +} + +template +void save_value(std::ostream& stream, const std::vector& value) +{ + size_t size = value.size(); + stream.write(reinterpret_cast(&size), sizeof(size_t)); + stream.write(reinterpret_cast(value.data()), sizeof(T) * size); +} + +template +void load_value(std::istream& stream, T& value) +{ + stream.read(reinterpret_cast(&value), sizeof(T)); +} + +template +void load_value(std::istream& stream, std::vector& value) +{ + size_t size; + stream.read(reinterpret_cast(&size), sizeof(size_t)); + value.resize(size); + stream.read(reinterpret_cast(value.data()), sizeof(T) * size); +} +/** @} */ + +/** @addtogroup metric_grp Metric (distance) classes + * @{ */ + +struct Metric +{ +}; + +/** Manhattan distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L1 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class T, class DataSource, typename _DistanceType = T, + typename IndexType = uint32_t> +struct L1_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L1_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + DistanceType evalMetric( + const T* a, const IndexType b_idx, size_t size, + DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const T* last = a + size; + const T* lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) + { + const DistanceType diff0 = + std::abs(a[0] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff1 = + std::abs(a[1] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff2 = + std::abs(a[2] - data_source.kdtree_get_pt(b_idx, d++)); + const DistanceType diff3 = + std::abs(a[3] - data_source.kdtree_get_pt(b_idx, d++)); + result += diff0 + diff1 + diff2 + diff3; + a += 4; + if ((worst_dist > 0) && (result > worst_dist)) { return result; } + } + /* Process last 0-3 components. Not needed for standard vector lengths. + */ + while (a < last) + { + result += std::abs(*a++ - data_source.kdtree_get_pt(b_idx, d++)); + } + return result; + } + + template + DistanceType accum_dist(const U a, const V b, const size_t) const + { + return std::abs(a - b); + } +}; + +/** **Squared** Euclidean distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L2 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class T, class DataSource, typename _DistanceType = T, + typename IndexType = uint32_t> +struct L2_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + DistanceType evalMetric( + const T* a, const IndexType b_idx, size_t size, + DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const T* last = a + size; + const T* lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) + { + const DistanceType diff0 = + a[0] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff1 = + a[1] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff2 = + a[2] - data_source.kdtree_get_pt(b_idx, d++); + const DistanceType diff3 = + a[3] - data_source.kdtree_get_pt(b_idx, d++); + result += + diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + a += 4; + if ((worst_dist > 0) && (result > worst_dist)) { return result; } + } + /* Process last 0-3 components. Not needed for standard vector lengths. + */ + while (a < last) + { + const DistanceType diff0 = + *a++ - data_source.kdtree_get_pt(b_idx, d++); + result += diff0 * diff0; + } + return result; + } + + template + DistanceType accum_dist(const U a, const V b, const size_t) const + { + return (a - b) * (a - b); + } +}; + +/** **Squared** Euclidean (L2) distance functor (suitable for low-dimensionality + * datasets, like 2D or 3D point clouds) Corresponding distance traits: + * nanoflann::metric_L2_Simple + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class T, class DataSource, typename _DistanceType = T, + typename IndexType = uint32_t> +struct L2_Simple_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L2_Simple_Adaptor(const DataSource& _data_source) + : data_source(_data_source) + { + } + + DistanceType evalMetric( + const T* a, const IndexType b_idx, size_t size) const + { + DistanceType result = DistanceType(); + for (size_t i = 0; i < size; ++i) + { + const DistanceType diff = + a[i] - data_source.kdtree_get_pt(b_idx, i); + result += diff * diff; + } + return result; + } + + template + DistanceType accum_dist(const U a, const V b, const size_t) const + { + return (a - b) * (a - b); + } +}; + +/** SO2 distance functor + * Corresponding distance traits: nanoflann::metric_SO2 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) orientation is constrained to be in [-pi, pi] + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class T, class DataSource, typename _DistanceType = T, + typename IndexType = uint32_t> +struct SO2_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + SO2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + DistanceType evalMetric( + const T* a, const IndexType b_idx, size_t size) const + { + return accum_dist( + a[size - 1], data_source.kdtree_get_pt(b_idx, size - 1), size - 1); + } + + /** Note: this assumes that input angles are already in the range [-pi,pi] + */ + template + DistanceType accum_dist(const U a, const V b, const size_t) const + { + DistanceType result = DistanceType(); + DistanceType PI = pi_const(); + result = b - a; + if (result > PI) + result -= 2 * PI; + else if (result < -PI) + result += 2 * PI; + return result; + } +}; + +/** SO3 distance functor (Uses L2_Simple) + * Corresponding distance traits: nanoflann::metric_SO3 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class T, class DataSource, typename _DistanceType = T, + typename IndexType = uint32_t> +struct SO3_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + L2_Simple_Adaptor + distance_L2_Simple; + + SO3_Adaptor(const DataSource& _data_source) + : distance_L2_Simple(_data_source) + { + } + + DistanceType evalMetric( + const T* a, const IndexType b_idx, size_t size) const + { + return distance_L2_Simple.evalMetric(a, b_idx, size); + } + + template + DistanceType accum_dist(const U a, const V b, const size_t idx) const + { + return distance_L2_Simple.accum_dist(a, b, idx); + } +}; + +/** Metaprogramming helper traits class for the L1 (Manhattan) metric */ +struct metric_L1 : public Metric +{ + template + struct traits + { + using distance_t = L1_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the L2 (Euclidean) **squared** + * distance metric */ +struct metric_L2 : public Metric +{ + template + struct traits + { + using distance_t = L2_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the L2_simple (Euclidean) + * **squared** distance metric */ +struct metric_L2_Simple : public Metric +{ + template + struct traits + { + using distance_t = L2_Simple_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO2 : public Metric +{ + template + struct traits + { + using distance_t = SO2_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO3 : public Metric +{ + template + struct traits + { + using distance_t = SO3_Adaptor; + }; +}; + +/** @} */ + +/** @addtogroup param_grp Parameter structs + * @{ */ + +enum class KDTreeSingleIndexAdaptorFlags +{ + None = 0, + SkipInitialBuildIndex = 1 +}; + +inline std::underlying_type::type operator&( + KDTreeSingleIndexAdaptorFlags lhs, KDTreeSingleIndexAdaptorFlags rhs) +{ + using underlying = + typename std::underlying_type::type; + return static_cast(lhs) & static_cast(rhs); +} + +/** Parameters (see README.md) */ +struct KDTreeSingleIndexAdaptorParams +{ + KDTreeSingleIndexAdaptorParams( + size_t _leaf_max_size = 10, + KDTreeSingleIndexAdaptorFlags _flags = + KDTreeSingleIndexAdaptorFlags::None, + unsigned int _n_thread_build = 1) + : leaf_max_size(_leaf_max_size), + flags(_flags), + n_thread_build(_n_thread_build) + { + } + + size_t leaf_max_size; + KDTreeSingleIndexAdaptorFlags flags; + unsigned int n_thread_build; +}; + +/** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */ +struct SearchParameters +{ + SearchParameters(float eps_ = 0, bool sorted_ = true) + : eps(eps_), sorted(sorted_) + { + } + + float eps; //!< search for eps-approximate neighbours (default: 0) + bool sorted; //!< only for radius search, require neighbours sorted by + //!< distance (default: true) +}; +/** @} */ + +/** @addtogroup memalloc_grp Memory allocation + * @{ */ + +/** + * Pooled storage allocator + * + * The following routines allow for the efficient allocation of storage in + * small chunks from a specified pool. Rather than allowing each structure + * to be freed individually, an entire pool of storage is freed at once. + * This method has two advantages over just using malloc() and free(). First, + * it is far more efficient for allocating small objects, as there is + * no overhead for remembering all the information needed to free each + * object or consolidating fragmented memory. Second, the decision about + * how long to keep an object is made at the time of allocation, and there + * is no need to track down all the objects to free them. + * + */ +class PooledAllocator +{ + static constexpr size_t WORDSIZE = 16; // WORDSIZE must >= 8 + static constexpr size_t BLOCKSIZE = 8192; + + /* We maintain memory alignment to word boundaries by requiring that all + allocations be in multiples of the machine wordsize. */ + /* Size of machine word in bytes. Must be power of 2. */ + /* Minimum number of bytes requested at a time from the system. Must be + * multiple of WORDSIZE. */ + + using Size = size_t; + + Size remaining_ = 0; //!< Number of bytes left in current block of storage + void* base_ = nullptr; //!< Pointer to base of current block of storage + void* loc_ = nullptr; //!< Current location in block to next allocate + + void internal_init() + { + remaining_ = 0; + base_ = nullptr; + usedMemory = 0; + wastedMemory = 0; + } + + public: + Size usedMemory = 0; + Size wastedMemory = 0; + + /** + Default constructor. Initializes a new pool. + */ + PooledAllocator() { internal_init(); } + + /** + * Destructor. Frees all the memory allocated in this pool. + */ + ~PooledAllocator() { free_all(); } + + /** Frees all allocated memory chunks */ + void free_all() + { + while (base_ != nullptr) + { + // Get pointer to prev block + void* prev = *(static_cast(base_)); + ::free(base_); + base_ = prev; + } + internal_init(); + } + + /** + * Returns a pointer to a piece of new memory of the given size in bytes + * allocated from the pool. + */ + void* malloc(const size_t req_size) + { + /* Round size up to a multiple of wordsize. The following expression + only works for WORDSIZE that is a power of 2, by masking last bits + of incremented size to zero. + */ + const Size size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); + + /* Check whether a new block must be allocated. Note that the first + word of a block is reserved for a pointer to the previous block. + */ + if (size > remaining_) + { + wastedMemory += remaining_; + + /* Allocate new storage. */ + const Size blocksize = + size > BLOCKSIZE ? size + WORDSIZE : BLOCKSIZE + WORDSIZE; + + // use the standard C malloc to allocate memory + void* m = ::malloc(blocksize); + if (!m) + { + fprintf(stderr, "Failed to allocate memory.\n"); + throw std::bad_alloc(); + } + + /* Fill first word of new block with pointer to previous block. */ + static_cast(m)[0] = base_; + base_ = m; + + remaining_ = blocksize - WORDSIZE; + loc_ = static_cast(m) + WORDSIZE; + } + void* rloc = loc_; + loc_ = static_cast(loc_) + size; + remaining_ -= size; + + usedMemory += size; + + return rloc; + } + + /** + * Allocates (using this pool) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ + template + T* allocate(const size_t count = 1) + { + T* mem = static_cast(this->malloc(sizeof(T) * count)); + return mem; + } +}; +/** @} */ + +/** @addtogroup nanoflann_metaprog_grp Auxiliary metaprogramming stuff + * @{ */ + +/** Used to declare fixed-size arrays when DIM>0, dynamically-allocated vectors + * when DIM=-1. Fixed size version for a generic DIM: + */ +template +struct array_or_vector +{ + using type = std::array; +}; +/** Dynamic size version */ +template +struct array_or_vector<-1, T> +{ + using type = std::vector; +}; + +/** @} */ + +/** kd-tree base-class + * + * Contains the member functions common to the classes KDTreeSingleIndexAdaptor + * and KDTreeSingleIndexDynamicAdaptor_. + * + * \tparam Derived The name of the class which inherits this class. + * \tparam DatasetAdaptor The user-provided adaptor, which must be ensured to + * have a lifetime equal or longer than the instance of this class. + * \tparam Distance The distance metric to use, these are all classes derived + * from nanoflann::Metric + * \tparam DIM Dimensionality of data points (e.g. 3 for 3D points) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class Derived, typename Distance, class DatasetAdaptor, int32_t DIM = -1, + typename IndexType = uint32_t> +class KDTreeBaseClass +{ + public: + /** Frees the previously-built index. Automatically called within + * buildIndex(). */ + void freeIndex(Derived& obj) + { + obj.pool_.free_all(); + obj.root_node_ = nullptr; + obj.size_at_index_build_ = 0; + } + + using ElementType = typename Distance::ElementType; + using DistanceType = typename Distance::DistanceType; + + /** + * Array of indices to vectors in the dataset_. + */ + std::vector vAcc_; + + using Offset = typename decltype(vAcc_)::size_type; + using Size = typename decltype(vAcc_)::size_type; + using Dimension = int32_t; + + /*--------------------------- + * Internal Data Structures + * --------------------------*/ + struct Node + { + /** Union used because a node can be either a LEAF node or a non-leaf + * node, so both data fields are never used simultaneously */ + union + { + struct leaf + { + Offset left, right; //!< Indices of points in leaf node + } lr; + struct nonleaf + { + Dimension divfeat; //!< Dimension used for subdivision. + /// The values used for subdivision. + DistanceType divlow, divhigh; + } sub; + } node_type; + + /** Child nodes (both=nullptr mean its a leaf node) */ + Node *child1 = nullptr, *child2 = nullptr; + }; + + using NodePtr = Node*; + using NodeConstPtr = const Node*; + + struct Interval + { + ElementType low, high; + }; + + NodePtr root_node_ = nullptr; + + Size leaf_max_size_ = 0; + + /// Number of thread for concurrent tree build + Size n_thread_build_ = 1; + /// Number of current points in the dataset + Size size_ = 0; + /// Number of points in the dataset when the index was built + Size size_at_index_build_ = 0; + Dimension dim_ = 0; //!< Dimensionality of each data point + + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename array_or_vector::type; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename array_or_vector::type; + + /** The KD-tree used to find neighbours */ + BoundingBox root_bbox_; + + /** + * Pooled memory allocator. + * + * Using a pooled memory allocator is more efficient + * than allocating memory directly when there is a large + * number small of memory allocations. + */ + PooledAllocator pool_; + + /** Returns number of points in dataset */ + Size size(const Derived& obj) const { return obj.size_; } + + /** Returns the length of each point in the dataset */ + Size veclen(const Derived& obj) { return DIM > 0 ? DIM : obj.dim; } + + /// Helper accessor to the dataset points: + ElementType dataset_get( + const Derived& obj, IndexType element, Dimension component) const + { + return obj.dataset_.kdtree_get_pt(element, component); + } + + /** + * Computes the inde memory usage + * Returns: memory used by the index + */ + Size usedMemory(Derived& obj) + { + return obj.pool_.usedMemory + obj.pool_.wastedMemory + + obj.dataset_.kdtree_get_point_count() * + sizeof(IndexType); // pool memory and vind array memory + } + + void computeMinMax( + const Derived& obj, Offset ind, Size count, Dimension element, + ElementType& min_elem, ElementType& max_elem) + { + min_elem = dataset_get(obj, vAcc_[ind], element); + max_elem = min_elem; + for (Offset i = 1; i < count; ++i) + { + ElementType val = dataset_get(obj, vAcc_[ind + i], element); + if (val < min_elem) min_elem = val; + if (val > max_elem) max_elem = val; + } + } + + /** + * Create a tree node that subdivides the list of vecs from vind[first] + * to vind[last]. The routine is called recursively on each sublist. + * + * @param left index of the first vector + * @param right index of the last vector + */ + NodePtr divideTree( + Derived& obj, const Offset left, const Offset right, BoundingBox& bbox) + { + NodePtr node = obj.pool_.template allocate(); // allocate memory + const auto dims = (DIM > 0 ? DIM : obj.dim_); + + /* If too few exemplars remain, then make this a leaf node. */ + if ((right - left) <= static_cast(obj.leaf_max_size_)) + { + node->child1 = node->child2 = nullptr; /* Mark as leaf node. */ + node->node_type.lr.left = left; + node->node_type.lr.right = right; + + // compute bounding-box of leaf points + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = dataset_get(obj, obj.vAcc_[left], i); + bbox[i].high = dataset_get(obj, obj.vAcc_[left], i); + } + for (Offset k = left + 1; k < right; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = dataset_get(obj, obj.vAcc_[k], i); + if (bbox[i].low > val) bbox[i].low = val; + if (bbox[i].high < val) bbox[i].high = val; + } + } + } + else + { + Offset idx; + Dimension cutfeat; + DistanceType cutval; + middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox); + + node->node_type.sub.divfeat = cutfeat; + + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = this->divideTree(obj, left, left + idx, left_bbox); + + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + node->child2 = this->divideTree(obj, left + idx, right, right_bbox); + + node->node_type.sub.divlow = left_bbox[cutfeat].high; + node->node_type.sub.divhigh = right_bbox[cutfeat].low; + + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + /** + * Create a tree node that subdivides the list of vecs from vind[first] to + * vind[last] concurrently. The routine is called recursively on each + * sublist. + * + * @param left index of the first vector + * @param right index of the last vector + * @param thread_count count of std::async threads + * @param mutex mutex for mempool allocation + */ + NodePtr divideTreeConcurrent( + Derived& obj, const Offset left, const Offset right, BoundingBox& bbox, + std::atomic& thread_count, std::mutex& mutex) + { + std::unique_lock lock(mutex); + NodePtr node = obj.pool_.template allocate(); // allocate memory + lock.unlock(); + + const auto dims = (DIM > 0 ? DIM : obj.dim_); + + /* If too few exemplars remain, then make this a leaf node. */ + if ((right - left) <= static_cast(obj.leaf_max_size_)) + { + node->child1 = node->child2 = nullptr; /* Mark as leaf node. */ + node->node_type.lr.left = left; + node->node_type.lr.right = right; + + // compute bounding-box of leaf points + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = dataset_get(obj, obj.vAcc_[left], i); + bbox[i].high = dataset_get(obj, obj.vAcc_[left], i); + } + for (Offset k = left + 1; k < right; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = dataset_get(obj, obj.vAcc_[k], i); + if (bbox[i].low > val) bbox[i].low = val; + if (bbox[i].high < val) bbox[i].high = val; + } + } + } + else + { + Offset idx; + Dimension cutfeat; + DistanceType cutval; + middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox); + + node->node_type.sub.divfeat = cutfeat; + + std::future right_future; + + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + if (++thread_count < n_thread_build_) + { + // Concurrent right sub-tree + right_future = std::async( + std::launch::async, &KDTreeBaseClass::divideTreeConcurrent, + this, std::ref(obj), left + idx, right, + std::ref(right_bbox), std::ref(thread_count), + std::ref(mutex)); + } + else + { + --thread_count; + } + + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = this->divideTreeConcurrent( + obj, left, left + idx, left_bbox, thread_count, mutex); + + if (right_future.valid()) + { + // Block and wait for concurrent right sub-tree + node->child2 = right_future.get(); + --thread_count; + } + else + { + node->child2 = this->divideTreeConcurrent( + obj, left + idx, right, right_bbox, thread_count, mutex); + } + + node->node_type.sub.divlow = left_bbox[cutfeat].high; + node->node_type.sub.divhigh = right_bbox[cutfeat].low; + + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + void middleSplit_( + const Derived& obj, const Offset ind, const Size count, Offset& index, + Dimension& cutfeat, DistanceType& cutval, const BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : obj.dim_); + const auto EPS = static_cast(0.00001); + ElementType max_span = bbox[0].high - bbox[0].low; + for (Dimension i = 1; i < dims; ++i) + { + ElementType span = bbox[i].high - bbox[i].low; + if (span > max_span) { max_span = span; } + } + ElementType max_spread = -1; + cutfeat = 0; + ElementType min_elem = 0, max_elem = 0; + for (Dimension i = 0; i < dims; ++i) + { + ElementType span = bbox[i].high - bbox[i].low; + if (span > (1 - EPS) * max_span) + { + ElementType min_elem_, max_elem_; + computeMinMax(obj, ind, count, i, min_elem_, max_elem_); + ElementType spread = max_elem_ - min_elem_; + if (spread > max_spread) + { + cutfeat = i; + max_spread = spread; + min_elem = min_elem_; + max_elem = max_elem_; + } + } + } + // split in the middle + DistanceType split_val = (bbox[cutfeat].low + bbox[cutfeat].high) / 2; + + if (split_val < min_elem) + cutval = min_elem; + else if (split_val > max_elem) + cutval = max_elem; + else + cutval = split_val; + + Offset lim1, lim2; + planeSplit(obj, ind, count, cutfeat, cutval, lim1, lim2); + + if (lim1 > count / 2) + index = lim1; + else if (lim2 < count / 2) + index = lim2; + else + index = count / 2; + } + + /** + * Subdivide the list of points by a plane perpendicular on the axis + * corresponding to the 'cutfeat' dimension at 'cutval' position. + * + * On return: + * dataset[ind[0..lim1-1]][cutfeat]cutval + */ + void planeSplit( + const Derived& obj, const Offset ind, const Size count, + const Dimension cutfeat, const DistanceType& cutval, Offset& lim1, + Offset& lim2) + { + /* Move vector indices for left subtree to front of list. */ + Offset left = 0; + Offset right = count - 1; + for (;;) + { + while (left <= right && + dataset_get(obj, vAcc_[ind + left], cutfeat) < cutval) + ++left; + while (right && left <= right && + dataset_get(obj, vAcc_[ind + right], cutfeat) >= cutval) + --right; + if (left > right || !right) + break; // "!right" was added to support unsigned Index types + std::swap(vAcc_[ind + left], vAcc_[ind + right]); + ++left; + --right; + } + /* If either list is empty, it means that all remaining features + * are identical. Split in the middle to maintain a balanced tree. + */ + lim1 = left; + right = count - 1; + for (;;) + { + while (left <= right && + dataset_get(obj, vAcc_[ind + left], cutfeat) <= cutval) + ++left; + while (right && left <= right && + dataset_get(obj, vAcc_[ind + right], cutfeat) > cutval) + --right; + if (left > right || !right) + break; // "!right" was added to support unsigned Index types + std::swap(vAcc_[ind + left], vAcc_[ind + right]); + ++left; + --right; + } + lim2 = left; + } + + DistanceType computeInitialDistances( + const Derived& obj, const ElementType* vec, + distance_vector_t& dists) const + { + assert(vec); + DistanceType dist = DistanceType(); + + for (Dimension i = 0; i < (DIM > 0 ? DIM : obj.dim_); ++i) + { + if (vec[i] < obj.root_bbox_[i].low) + { + dists[i] = + obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].low, i); + dist += dists[i]; + } + if (vec[i] > obj.root_bbox_[i].high) + { + dists[i] = + obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].high, i); + dist += dists[i]; + } + } + return dist; + } + + static void save_tree( + const Derived& obj, std::ostream& stream, const NodeConstPtr tree) + { + save_value(stream, *tree); + if (tree->child1 != nullptr) { save_tree(obj, stream, tree->child1); } + if (tree->child2 != nullptr) { save_tree(obj, stream, tree->child2); } + } + + static void load_tree(Derived& obj, std::istream& stream, NodePtr& tree) + { + tree = obj.pool_.template allocate(); + load_value(stream, *tree); + if (tree->child1 != nullptr) { load_tree(obj, stream, tree->child1); } + if (tree->child2 != nullptr) { load_tree(obj, stream, tree->child2); } + } + + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(const Derived& obj, std::ostream& stream) const + { + save_value(stream, obj.size_); + save_value(stream, obj.dim_); + save_value(stream, obj.root_bbox_); + save_value(stream, obj.leaf_max_size_); + save_value(stream, obj.vAcc_); + if (obj.root_node_) save_tree(obj, stream, obj.root_node_); + } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(Derived& obj, std::istream& stream) + { + load_value(stream, obj.size_); + load_value(stream, obj.dim_); + load_value(stream, obj.root_bbox_); + load_value(stream, obj.leaf_max_size_); + load_value(stream, obj.vAcc_); + load_tree(obj, stream, obj.root_node_); + } +}; + +/** @addtogroup kdtrees_grp KD-tree classes and adaptors + * @{ */ + +/** kd-tree static index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * size_t kdtree_get_point_count() const { ... } + * + * + * // Must return the dim'th component of the idx'th point in the class: + * T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor, which must be ensured to + * have a lifetime equal or longer than the instance of this class. + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType Will + * be typically size_t or int + */ +template < + typename Distance, class DatasetAdaptor, int32_t DIM = -1, + typename IndexType = uint32_t> +class KDTreeSingleIndexAdaptor + : public KDTreeBaseClass< + KDTreeSingleIndexAdaptor, + Distance, DatasetAdaptor, DIM, IndexType> +{ + public: + /** Deleted copy constructor*/ + explicit KDTreeSingleIndexAdaptor( + const KDTreeSingleIndexAdaptor< + Distance, DatasetAdaptor, DIM, IndexType>&) = delete; + + /** The data source used by this index */ + const DatasetAdaptor& dataset_; + + const KDTreeSingleIndexAdaptorParams indexParams; + + Distance distance_; + + using Base = typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexAdaptor< + Distance, DatasetAdaptor, DIM, IndexType>, + Distance, DatasetAdaptor, DIM, IndexType>; + + using Offset = typename Base::Offset; + using Size = typename Base::Size; + using Dimension = typename Base::Dimension; + + using ElementType = typename Base::ElementType; + using DistanceType = typename Base::DistanceType; + + using Node = typename Base::Node; + using NodePtr = Node*; + + using Interval = typename Base::Interval; + + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename Base::BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename Base::distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + * + * Note that there is a variable number of optional additional parameters + * which will be forwarded to the metric class constructor. Refer to example + * `examples/pointcloud_custom_metric.cpp` for a use case. + * + */ + template + explicit KDTreeSingleIndexAdaptor( + const Dimension dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params, Args&&... args) + : dataset_(inputData), + indexParams(params), + distance_(inputData, std::forward(args)...) + { + init(dimensionality, params); + } + + explicit KDTreeSingleIndexAdaptor( + const Dimension dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params = {}) + : dataset_(inputData), indexParams(params), distance_(inputData) + { + init(dimensionality, params); + } + + private: + void init( + const Dimension dimensionality, + const KDTreeSingleIndexAdaptorParams& params) + { + Base::size_ = dataset_.kdtree_get_point_count(); + Base::size_at_index_build_ = Base::size_; + Base::dim_ = dimensionality; + if (DIM > 0) Base::dim_ = DIM; + Base::leaf_max_size_ = params.leaf_max_size; + if (params.n_thread_build > 0) + { + Base::n_thread_build_ = params.n_thread_build; + } + else + { + Base::n_thread_build_ = + std::max(std::thread::hardware_concurrency(), 1u); + } + + if (!(params.flags & + KDTreeSingleIndexAdaptorFlags::SkipInitialBuildIndex)) + { + // Build KD-tree: + buildIndex(); + } + } + + public: + /** + * Builds the index + */ + void buildIndex() + { + Base::size_ = dataset_.kdtree_get_point_count(); + Base::size_at_index_build_ = Base::size_; + init_vind(); + this->freeIndex(*this); + Base::size_at_index_build_ = Base::size_; + if (Base::size_ == 0) return; + computeBoundingBox(Base::root_bbox_); + // construct the tree + if (Base::n_thread_build_ == 1) + { + Base::root_node_ = + this->divideTree(*this, 0, Base::size_, Base::root_bbox_); + } + else + { + std::atomic thread_count(0u); + std::mutex mutex; + Base::root_node_ = this->divideTreeConcurrent( + *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex); + } + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, + const SearchParameters& searchParams = {}) const + { + assert(vec); + if (this->size(*this) == 0) return false; + if (!Base::root_node_) + throw std::runtime_error( + "[nanoflann] findNeighbors() called before building the " + "index."); + float epsError = 1 + searchParams.eps; + + // fixed or variable-sized container (depending on DIM) + distance_vector_t dists; + // Fill it with zeros. + auto zero = static_cast(0); + assign(dists, (DIM > 0 ? DIM : Base::dim_), zero); + DistanceType dist = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, Base::root_node_, dist, dists, epsError); + return result.full(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices and distances are stored in the provided pointers to + * array/vector. + * + * \sa radiusSearch, findNeighbors + * \return Number `N` of valid points in the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return is less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size knnSearch( + const ElementType* query_point, const Size num_closest, + IndexType* out_indices, DistanceType* out_distances) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the + * vector if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + * + * \note If L2 norms are used, search radius and all returned distances + * are actually squared distances. + */ + Size radiusSearch( + const ElementType* query_point, const DistanceType& radius, + std::vector>& IndicesDists, + const SearchParameters& searchParams = {}) const + { + RadiusResultSet resultSet( + radius, IndicesDists); + const Size nFound = + radiusSearchCustomCallback(query_point, resultSet, searchParams); + if (searchParams.sorted) + std::sort( + IndicesDists.begin(), IndicesDists.end(), IndexDist_Sorter()); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as + * a start point for your own classes. \sa radiusSearch + */ + template + Size radiusSearchCustomCallback( + const ElementType* query_point, SEARCH_CALLBACK& resultSet, + const SearchParameters& searchParams = {}) const + { + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** + * Find the first N neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * \sa radiusSearch, findNeighbors + * \return Number `N` of valid points in the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return is less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size rknnSearch( + const ElementType* query_point, const Size num_closest, + IndexType* out_indices, DistanceType* out_distances, + const DistanceType& radius) const + { + nanoflann::RKNNResultSet resultSet( + num_closest, radius); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point); + return resultSet.size(); + } + + /** @} */ + + public: + /** Make sure the auxiliary list \a vind has the same size than the current + * dataset, and re-generate if size has changed. */ + void init_vind() + { + // Create a permutable array of indices to the input vectors. + Base::size_ = dataset_.kdtree_get_point_count(); + if (Base::vAcc_.size() != Base::size_) Base::vAcc_.resize(Base::size_); + for (Size i = 0; i < Base::size_; i++) Base::vAcc_[i] = i; + } + + void computeBoundingBox(BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : Base::dim_); + resize(bbox, dims); + if (dataset_.kdtree_get_bbox(bbox)) + { + // Done! It was implemented in derived class + } + else + { + const Size N = dataset_.kdtree_get_point_count(); + if (!N) + throw std::runtime_error( + "[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = bbox[i].high = + this->dataset_get(*this, Base::vAcc_[0], i); + } + for (Offset k = 1; k < N; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = + this->dataset_get(*this, Base::vAcc_[k], i); + if (val < bbox[i].low) bbox[i].low = val; + if (val > bbox[i].high) bbox[i].high = val; + } + } + } + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + * \return true if the search should be continued, false if the results are + * sufficient + */ + template + bool searchLevel( + RESULTSET& result_set, const ElementType* vec, const NodePtr node, + DistanceType mindist, distance_vector_t& dists, + const float epsError) const + { + /* If this is a leaf node, then do check and return. */ + if ((node->child1 == nullptr) && (node->child2 == nullptr)) + { + DistanceType worst_dist = result_set.worstDist(); + for (Offset i = node->node_type.lr.left; + i < node->node_type.lr.right; ++i) + { + const IndexType accessor = Base::vAcc_[i]; // reorder... : i; + DistanceType dist = distance_.evalMetric( + vec, accessor, (DIM > 0 ? DIM : Base::dim_)); + if (dist < worst_dist) + { + if (!result_set.addPoint(dist, Base::vAcc_[i])) + { + // the resultset doesn't want to receive any more + // points, we're done searching! + return false; + } + } + } + return true; + } + + /* Which child branch should be taken first? */ + Dimension idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) + { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = + distance_.accum_dist(val, node->node_type.sub.divhigh, idx); + } + else + { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = + distance_.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError)) + { + // the resultset doesn't want to receive any more points, we're done + // searching! + return false; + } + + DistanceType dst = dists[idx]; + mindist = mindist + cut_dist - dst; + dists[idx] = cut_dist; + if (mindist * epsError <= result_set.worstDist()) + { + if (!searchLevel( + result_set, vec, otherChild, mindist, dists, epsError)) + { + // the resultset doesn't want to receive any more points, we're + // done searching! + return false; + } + } + dists[idx] = dst; + return true; + } + + public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(std::ostream& stream) const + { + Base::saveIndex(*this, stream); + } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); } + +}; // class KDTree + +/** kd-tree dynamic index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * size_t kdtree_get_point_count() const { ... } + * + * // Must return the dim'th component of the idx'th point in the class: + * T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam DIM Dimensionality of data points (e.g. 3 for 3D points) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + typename Distance, class DatasetAdaptor, int32_t DIM = -1, + typename IndexType = uint32_t> +class KDTreeSingleIndexDynamicAdaptor_ + : public KDTreeBaseClass< + KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM, IndexType>, + Distance, DatasetAdaptor, DIM, IndexType> +{ + public: + /** + * The dataset used by this index + */ + const DatasetAdaptor& dataset_; //!< The source of our data + + KDTreeSingleIndexAdaptorParams index_params_; + + std::vector& treeIndex_; + + Distance distance_; + + using Base = typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM, IndexType>, + Distance, DatasetAdaptor, DIM, IndexType>; + + using ElementType = typename Base::ElementType; + using DistanceType = typename Base::DistanceType; + + using Offset = typename Base::Offset; + using Size = typename Base::Size; + using Dimension = typename Base::Dimension; + + using Node = typename Base::Node; + using NodePtr = Node*; + + using Interval = typename Base::Interval; + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename Base::BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename Base::distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + */ + KDTreeSingleIndexDynamicAdaptor_( + const Dimension dimensionality, const DatasetAdaptor& inputData, + std::vector& treeIndex, + const KDTreeSingleIndexAdaptorParams& params = + KDTreeSingleIndexAdaptorParams()) + : dataset_(inputData), + index_params_(params), + treeIndex_(treeIndex), + distance_(inputData) + { + Base::size_ = 0; + Base::size_at_index_build_ = 0; + for (auto& v : Base::root_bbox_) v = {}; + Base::dim_ = dimensionality; + if (DIM > 0) Base::dim_ = DIM; + Base::leaf_max_size_ = params.leaf_max_size; + if (params.n_thread_build > 0) + { + Base::n_thread_build_ = params.n_thread_build; + } + else + { + Base::n_thread_build_ = + std::max(std::thread::hardware_concurrency(), 1u); + } + } + + /** Explicitly default the copy constructor */ + KDTreeSingleIndexDynamicAdaptor_( + const KDTreeSingleIndexDynamicAdaptor_& rhs) = default; + + /** Assignment operator definiton */ + KDTreeSingleIndexDynamicAdaptor_ operator=( + const KDTreeSingleIndexDynamicAdaptor_& rhs) + { + KDTreeSingleIndexDynamicAdaptor_ tmp(rhs); + std::swap(Base::vAcc_, tmp.Base::vAcc_); + std::swap(Base::leaf_max_size_, tmp.Base::leaf_max_size_); + std::swap(index_params_, tmp.index_params_); + std::swap(treeIndex_, tmp.treeIndex_); + std::swap(Base::size_, tmp.Base::size_); + std::swap(Base::size_at_index_build_, tmp.Base::size_at_index_build_); + std::swap(Base::root_node_, tmp.Base::root_node_); + std::swap(Base::root_bbox_, tmp.Base::root_bbox_); + std::swap(Base::pool_, tmp.Base::pool_); + return *this; + } + + /** + * Builds the index + */ + void buildIndex() + { + Base::size_ = Base::vAcc_.size(); + this->freeIndex(*this); + Base::size_at_index_build_ = Base::size_; + if (Base::size_ == 0) return; + computeBoundingBox(Base::root_bbox_); + // construct the tree + if (Base::n_thread_build_ == 1) + { + Base::root_node_ = + this->divideTree(*this, 0, Base::size_, Base::root_bbox_); + } + else + { + std::atomic thread_count(0u); + std::mutex mutex; + Base::root_node_ = this->divideTreeConcurrent( + *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex); + } + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * This is the core search function, all others are wrappers around this + * one. + * + * \param result The result object in which the indices of the + * nearest-neighbors are stored. + * \param vec The vector of the query point for which to search the + * nearest neighbors. + * \param searchParams Optional parameters for the search. + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * + * \sa knnSearch(), radiusSearch(), radiusSearchCustomCallback() + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, + const SearchParameters& searchParams = {}) const + { + assert(vec); + if (this->size(*this) == 0) return false; + if (!Base::root_node_) return false; + float epsError = 1 + searchParams.eps; + + // fixed or variable-sized container (depending on DIM) + distance_vector_t dists; + // Fill it with zeros. + assign( + dists, (DIM > 0 ? DIM : Base::dim_), + static_cast(0)); + DistanceType dist = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, Base::root_node_, dist, dists, epsError); + return result.full(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices are stored inside the result object. \sa radiusSearch, + * findNeighbors + * \return Number `N` of valid points in + * the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return may be less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size knnSearch( + const ElementType* query_point, const Size num_closest, + IndexType* out_indices, DistanceType* out_distances, + const SearchParameters& searchParams = {}) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the + * vector if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + * + * \note If L2 norms are used, search radius and all returned distances + * are actually squared distances. + */ + Size radiusSearch( + const ElementType* query_point, const DistanceType& radius, + std::vector>& IndicesDists, + const SearchParameters& searchParams = {}) const + { + RadiusResultSet resultSet( + radius, IndicesDists); + const size_t nFound = + radiusSearchCustomCallback(query_point, resultSet, searchParams); + if (searchParams.sorted) + std::sort( + IndicesDists.begin(), IndicesDists.end(), IndexDist_Sorter()); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as + * a start point for your own classes. \sa radiusSearch + */ + template + Size radiusSearchCustomCallback( + const ElementType* query_point, SEARCH_CALLBACK& resultSet, + const SearchParameters& searchParams = {}) const + { + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** @} */ + + public: + void computeBoundingBox(BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : Base::dim_); + resize(bbox, dims); + + if (dataset_.kdtree_get_bbox(bbox)) + { + // Done! It was implemented in derived class + } + else + { + const Size N = Base::size_; + if (!N) + throw std::runtime_error( + "[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = bbox[i].high = + this->dataset_get(*this, Base::vAcc_[0], i); + } + for (Offset k = 1; k < N; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = + this->dataset_get(*this, Base::vAcc_[k], i); + if (val < bbox[i].low) bbox[i].low = val; + if (val > bbox[i].high) bbox[i].high = val; + } + } + } + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + */ + template + void searchLevel( + RESULTSET& result_set, const ElementType* vec, const NodePtr node, + DistanceType mindist, distance_vector_t& dists, + const float epsError) const + { + /* If this is a leaf node, then do check and return. */ + if ((node->child1 == nullptr) && (node->child2 == nullptr)) + { + DistanceType worst_dist = result_set.worstDist(); + for (Offset i = node->node_type.lr.left; + i < node->node_type.lr.right; ++i) + { + const IndexType index = Base::vAcc_[i]; // reorder... : i; + if (treeIndex_[index] == -1) continue; + DistanceType dist = distance_.evalMetric( + vec, index, (DIM > 0 ? DIM : Base::dim_)); + if (dist < worst_dist) + { + if (!result_set.addPoint( + static_cast(dist), + static_cast( + Base::vAcc_[i]))) + { + // the resultset doesn't want to receive any more + // points, we're done searching! + return; // false; + } + } + } + return; + } + + /* Which child branch should be taken first? */ + Dimension idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) + { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = + distance_.accum_dist(val, node->node_type.sub.divhigh, idx); + } + else + { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = + distance_.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + searchLevel(result_set, vec, bestChild, mindist, dists, epsError); + + DistanceType dst = dists[idx]; + mindist = mindist + cut_dist - dst; + dists[idx] = cut_dist; + if (mindist * epsError <= result_set.worstDist()) + { + searchLevel(result_set, vec, otherChild, mindist, dists, epsError); + } + dists[idx] = dst; + } + + public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(std::ostream& stream) { saveIndex(*this, stream); } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(std::istream& stream) { loadIndex(*this, stream); } +}; + +/** kd-tree dynaimic index + * + * class to create multiple static index and merge their results to behave as + * single dynamic index as proposed in Logarithmic Approach. + * + * Example of usage: + * examples/dynamic_pointcloud_example.cpp + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType + * Will be typically size_t or int + */ +template < + typename Distance, class DatasetAdaptor, int32_t DIM = -1, + typename IndexType = uint32_t> +class KDTreeSingleIndexDynamicAdaptor +{ + public: + using ElementType = typename Distance::ElementType; + using DistanceType = typename Distance::DistanceType; + + using Offset = typename KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM>::Offset; + using Size = typename KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM>::Size; + using Dimension = typename KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM>::Dimension; + + protected: + Size leaf_max_size_; + Size treeCount_; + Size pointCount_; + + /** + * The dataset used by this index + */ + const DatasetAdaptor& dataset_; //!< The source of our data + + /** treeIndex[idx] is the index of tree in which point at idx is stored. + * treeIndex[idx]=-1 means that point has been removed. */ + std::vector treeIndex_; + std::unordered_set removedPoints_; + + KDTreeSingleIndexAdaptorParams index_params_; + + Dimension dim_; //!< Dimensionality of each data point + + using index_container_t = KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM, IndexType>; + std::vector index_; + + public: + /** Get a const ref to the internal list of indices; the number of indices + * is adapted dynamically as the dataset grows in size. */ + const std::vector& getAllIndices() const + { + return index_; + } + + private: + /** finds position of least significant unset bit */ + int First0Bit(IndexType num) + { + int pos = 0; + while (num & 1) + { + num = num >> 1; + pos++; + } + return pos; + } + + /** Creates multiple empty trees to handle dynamic support */ + void init() + { + using my_kd_tree_t = KDTreeSingleIndexDynamicAdaptor_< + Distance, DatasetAdaptor, DIM, IndexType>; + std::vector index( + treeCount_, + my_kd_tree_t(dim_ /*dim*/, dataset_, treeIndex_, index_params_)); + index_ = index; + } + + public: + Distance distance_; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + */ + explicit KDTreeSingleIndexDynamicAdaptor( + const int dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params = + KDTreeSingleIndexAdaptorParams(), + const size_t maximumPointCount = 1000000000U) + : dataset_(inputData), index_params_(params), distance_(inputData) + { + treeCount_ = static_cast(std::log2(maximumPointCount)) + 1; + pointCount_ = 0U; + dim_ = dimensionality; + treeIndex_.clear(); + if (DIM > 0) dim_ = DIM; + leaf_max_size_ = params.leaf_max_size; + init(); + const size_t num_initial_points = dataset_.kdtree_get_point_count(); + if (num_initial_points > 0) { addPoints(0, num_initial_points - 1); } + } + + /** Deleted copy constructor*/ + explicit KDTreeSingleIndexDynamicAdaptor( + const KDTreeSingleIndexDynamicAdaptor< + Distance, DatasetAdaptor, DIM, IndexType>&) = delete; + + /** Add points to the set, Inserts all points from [start, end] */ + void addPoints(IndexType start, IndexType end) + { + const Size count = end - start + 1; + int maxIndex = 0; + treeIndex_.resize(treeIndex_.size() + count); + for (IndexType idx = start; idx <= end; idx++) + { + const int pos = First0Bit(pointCount_); + maxIndex = std::max(pos, maxIndex); + treeIndex_[pointCount_] = pos; + + const auto it = removedPoints_.find(idx); + if (it != removedPoints_.end()) + { + removedPoints_.erase(it); + treeIndex_[idx] = pos; + } + + for (int i = 0; i < pos; i++) + { + for (int j = 0; j < static_cast(index_[i].vAcc_.size()); + j++) + { + index_[pos].vAcc_.push_back(index_[i].vAcc_[j]); + if (treeIndex_[index_[i].vAcc_[j]] != -1) + treeIndex_[index_[i].vAcc_[j]] = pos; + } + index_[i].vAcc_.clear(); + } + index_[pos].vAcc_.push_back(idx); + pointCount_++; + } + + for (int i = 0; i <= maxIndex; ++i) + { + index_[i].freeIndex(index_[i]); + if (!index_[i].vAcc_.empty()) index_[i].buildIndex(); + } + } + + /** Remove a point from the set (Lazy Deletion) */ + void removePoint(size_t idx) + { + if (idx >= pointCount_) return; + removedPoints_.insert(idx); + treeIndex_[idx] = -1; + } + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, + const SearchParameters& searchParams = {}) const + { + for (size_t i = 0; i < treeCount_; i++) + { + index_[i].findNeighbors(result, &vec[0], searchParams); + } + return result.full(); + } +}; + +/** An L2-metric KD-tree adaptor for working with data directly stored in an + * Eigen Matrix, without duplicating the data storage. You can select whether a + * row or column in the matrix represents a point in the state space. + * + * Example of usage: + * \code + * Eigen::Matrix mat; + * + * // Fill out "mat"... + * using my_kd_tree_t = nanoflann::KDTreeEigenMatrixAdaptor< + * Eigen::Matrix>; + * + * const int max_leaf = 10; + * my_kd_tree_t mat_index(mat, max_leaf); + * mat_index.index->... + * \endcode + * + * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality + * for the points in the data set, allowing more compiler optimizations. + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam row_major If set to true the rows of the matrix are used as the + * points, if set to false the columns of the matrix are used as the + * points. + */ +template < + class MatrixType, int32_t DIM = -1, class Distance = nanoflann::metric_L2, + bool row_major = true> +struct KDTreeEigenMatrixAdaptor +{ + using self_t = + KDTreeEigenMatrixAdaptor; + using num_t = typename MatrixType::Scalar; + using IndexType = typename MatrixType::Index; + using metric_t = typename Distance::template traits< + num_t, self_t, IndexType>::distance_t; + + using index_t = KDTreeSingleIndexAdaptor< + metric_t, self_t, + row_major ? MatrixType::ColsAtCompileTime + : MatrixType::RowsAtCompileTime, + IndexType>; + + index_t* index_; //! The kd-tree index for the user to call its methods as + //! usual with any other FLANN index. + + using Offset = typename index_t::Offset; + using Size = typename index_t::Size; + using Dimension = typename index_t::Dimension; + + /// Constructor: takes a const ref to the matrix object with the data points + explicit KDTreeEigenMatrixAdaptor( + const Dimension dimensionality, + const std::reference_wrapper& mat, + const int leaf_max_size = 10) + : m_data_matrix(mat) + { + const auto dims = row_major ? mat.get().cols() : mat.get().rows(); + if (static_cast(dims) != dimensionality) + throw std::runtime_error( + "Error: 'dimensionality' must match column count in data " + "matrix"); + if (DIM > 0 && static_cast(dims) != DIM) + throw std::runtime_error( + "Data set dimensionality does not match the 'DIM' template " + "argument"); + index_ = new index_t( + dims, *this /* adaptor */, + nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size)); + } + + public: + /** Deleted copy constructor */ + KDTreeEigenMatrixAdaptor(const self_t&) = delete; + + ~KDTreeEigenMatrixAdaptor() { delete index_; } + + const std::reference_wrapper m_data_matrix; + + /** Query for the \a num_closest closest points to a given point (entered as + * query_point[0:dim-1]). Note that this is a short-cut method for + * index->findNeighbors(). The user can also call index->... methods as + * desired. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + void query( + const num_t* query_point, const Size num_closest, + IndexType* out_indices, num_t* out_distances) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + index_->findNeighbors(resultSet, query_point); + } + + /** @name Interface expected by KDTreeSingleIndexAdaptor + * @{ */ + + const self_t& derived() const { return *this; } + self_t& derived() { return *this; } + + // Must return the number of data points + Size kdtree_get_point_count() const + { + if (row_major) + return m_data_matrix.get().rows(); + else + return m_data_matrix.get().cols(); + } + + // Returns the dim'th component of the idx'th point in the class: + num_t kdtree_get_pt(const IndexType idx, size_t dim) const + { + if (row_major) + return m_data_matrix.get().coeff(idx, IndexType(dim)); + else + return m_data_matrix.get().coeff(IndexType(dim), idx); + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. Look at bb.size() to + // find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(BBOX& /*bb*/) const + { + return false; + } + + /** @} */ + +}; // end of KDTreeEigenMatrixAdaptor +/** @} */ + +/** @} */ // end of grouping +} // namespace nanoflann diff --git a/lib/interp/delaunay/nanoflann_wrappers.cpp b/lib/interp/delaunay/nanoflann_wrappers.cpp new file mode 100644 index 0000000..be0d154 --- /dev/null +++ b/lib/interp/delaunay/nanoflann_wrappers.cpp @@ -0,0 +1,79 @@ +//nanoflann_wrappers.cpp +#include "ems_conf.h" +#ifdef USE_KDTREE + +#include +#include +#include // For std::min +#include "nanoflann.hpp" +#include "utils.h" + +typedef struct { + double x; + double y; + double z; + double *v; +} point; + +extern "C" { +class KDTreeContext { +public: + using num_t = float; + PointCloud cloud; // from utils.h + // Defines KDTree as a type alias -> for nanoflann::KDTreeSingleIndexAdaptor template class from nanoflann namespace, configured for 2D points with L2 (Euclidean) distance, + using KDTree = nanoflann::KDTreeSingleIndexAdaptor< + nanoflann::L2_Simple_Adaptor>, + PointCloud, 2 /* dim */ + >; + // declare ptr: + KDTree* tree; + + // Adjusted Constructor + KDTreeContext(const point* points, size_t N, size_t max_leaf_size) { + cloud.pts.resize(N); + for (size_t i = 0; i < N; ++i) { + cloud.pts[i].x = points[i].x; + cloud.pts[i].y = points[i].y; + // printf("%i = %1.8f , %1.8f\n",i,cloud.pts[i].x,cloud.pts[i].y); + } + tree = new KDTree(2, cloud, {max_leaf_size}); + } + + // Destructor + ~KDTreeContext() { + delete tree; + } +}; + +void* create_kdtree(const point* points, size_t N, size_t max_leaf_size) { + return new KDTreeContext(points, N, max_leaf_size); +} + +int find_nearest_vertex(void* context, const float query_pt[2]) { + // convert void* to KDTreeContext* + KDTreeContext* ctx = static_cast(context); + + // do we want this safety check or do we want a failure? + if (!ctx || !ctx->tree) return -1; + + const size_t num_results = 1; // just one NN required + size_t ret_index; // index of nearest vertex found + float out_dist_sqr; // Local variable with the distance, still needed for KNNResultSet but not used outside + + // create new result set type, initialise + nanoflann::KNNResultSet resultSet(num_results); + resultSet.init(&ret_index, &out_dist_sqr); + float query_pt_2D[2] = {query_pt[0], query_pt[1]}; + // printf("NN %1.8f , %1.8f ",query_pt[0], query_pt[1]); + // execute search + ctx->tree->findNeighbors(resultSet, &query_pt_2D[0]); + // printf(" %i\n", ret_index); + return static_cast(ret_index); +} + +void delete_kdtree(void* context) { + KDTreeContext* ctx = static_cast(context); + delete ctx; +} +} +#endif \ No newline at end of file diff --git a/lib/interp/delaunay/utils.h b/lib/interp/delaunay/utils.h new file mode 100644 index 0000000..9427838 --- /dev/null +++ b/lib/interp/delaunay/utils.h @@ -0,0 +1,224 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2011-2024 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ + +#include +#include +#include + +template +struct PointCloud +{ + struct Point + { + T x, y, z; + }; + + using coord_t = T; //!< The type of each coordinate + + std::vector pts; + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { return pts.size(); } + + // Returns the dim'th component of the idx'th point in the class: + // Since this is inlined and the "dim" argument is typically an immediate + // value, the + // "if/else's" are actually solved at compile time. + inline T kdtree_get_pt(const size_t idx, const size_t dim) const + { + if (dim == 0) + return pts[idx].x; + else if (dim == 1) + return pts[idx].y; + else + return pts[idx].z; + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. Look at bb.size() to + // find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(BBOX& /* bb */) const + { + return false; + } +}; + +template +void generateRandomPointCloudRanges( + PointCloud& pc, const size_t N, const T max_range_x, const T max_range_y, + const T max_range_z) +{ + // Generating Random Point Cloud + pc.pts.resize(N); + for (size_t i = 0; i < N; i++) + { + pc.pts[i].x = max_range_x * (rand() % 1000) / T(1000); + pc.pts[i].y = max_range_y * (rand() % 1000) / T(1000); + pc.pts[i].z = max_range_z * (rand() % 1000) / T(1000); + } +} + +template +void generateRandomPointCloud( + PointCloud& pc, const size_t N, const T max_range = 10) +{ + generateRandomPointCloudRanges(pc, N, max_range, max_range, max_range); +} + +// This is an exampleof a custom data set class +template +struct PointCloud_Quat +{ + struct Point + { + T w, x, y, z; + }; + + std::vector pts; + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { return pts.size(); } + + // Returns the dim'th component of the idx'th point in the class: + // Since this is inlined and the "dim" argument is typically an immediate + // value, the + // "if/else's" are actually solved at compile time. + inline T kdtree_get_pt(const size_t idx, const size_t dim) const + { + if (dim == 0) + return pts[idx].w; + else if (dim == 1) + return pts[idx].x; + else if (dim == 2) + return pts[idx].y; + else + return pts[idx].z; + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. Look at bb.size() to + // find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(BBOX& /* bb */) const + { + return false; + } +}; + +template +void generateRandomPointCloud_Quat(PointCloud_Quat& point, const size_t N) +{ + // Generating Random Quaternions + point.pts.resize(N); + T theta, X, Y, Z, sinAng, cosAng, mag; + for (size_t i = 0; i < N; i++) + { + theta = static_cast( + nanoflann::pi_const() * (((double)rand()) / RAND_MAX)); + // Generate random value in [-1, 1] + X = static_cast(2 * (((double)rand()) / RAND_MAX) - 1); + Y = static_cast(2 * (((double)rand()) / RAND_MAX) - 1); + Z = static_cast(2 * (((double)rand()) / RAND_MAX) - 1); + mag = sqrt(X * X + Y * Y + Z * Z); + X /= mag; + Y /= mag; + Z /= mag; + cosAng = cos(theta / 2); + sinAng = sin(theta / 2); + point.pts[i].w = cosAng; + point.pts[i].x = X * sinAng; + point.pts[i].y = Y * sinAng; + point.pts[i].z = Z * sinAng; + } +} + +// This is an exampleof a custom data set class +template +struct PointCloud_Orient +{ + struct Point + { + T theta; + }; + + std::vector pts; + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { return pts.size(); } + + // Returns the dim'th component of the idx'th point in the class: + // Since this is inlined and the "dim" argument is typically an immediate + // value, the + // "if/else's" are actually solved at compile time. + inline T kdtree_get_pt(const size_t idx, const size_t dim = 0) const + { + return pts[idx].theta; + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. Look at bb.size() to + // find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(BBOX& /* bb */) const + { + return false; + } +}; + +template +void generateRandomPointCloud_Orient( + PointCloud_Orient& point, const size_t N) +{ + // Generating Random Orientations + point.pts.resize(N); + for (size_t i = 0; i < N; i++) + { + point.pts[i].theta = static_cast( + (2 * nanoflann::pi_const() * + (((double)rand()) / RAND_MAX)) - + nanoflann::pi_const()); + } +} + +inline void dump_mem_usage() +{ + FILE* f = fopen("/proc/self/statm", "rt"); + if (!f) return; + char str[300]; + size_t n = fread(str, 1, 200, f); + str[n] = 0; + printf("MEM: %s\n", str); + fclose(f); +} diff --git a/lib/makefile.in b/lib/makefile.in index 8d39008..bf86179 100644 --- a/lib/makefile.in +++ b/lib/makefile.in @@ -121,6 +121,7 @@ misc/istack.o INTERPOBJS = \ interp/csa/csa.o \ interp/delaunay/delaunay.o \ +interp/delaunay/nanoflann_wrappers.o \ interp/delaunay/triangle.o \ interp/gridmap/gridaverager.o \ interp/gridmap/gridmap.o \ @@ -173,6 +174,10 @@ tests/dftest.o all: $(EMSLIB) +# Special rule for nanoflann +interp/delaunay/nanoflann_wrappers.o: interp/delaunay/nanoflann_wrappers.cpp + $(CXX) -I./include -Ofast -c $< -o $@ + # Special rule for triangle interp/delaunay/triangle.o : interp/delaunay/triangle.c $(CC) $(CFLAGS) -DTRILIBRARY -c $< -o $*.o diff --git a/lib/misc/string_utils.c b/lib/misc/string_utils.c index d6ea771..dce1c19 100644 --- a/lib/misc/string_utils.c +++ b/lib/misc/string_utils.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: string_utils.c 5831 2018-06-26 23:48:06Z riz008 $ + * $Id: string_utils.c 7573 2024-05-30 03:37:46Z riz008 $ */ @@ -251,13 +251,13 @@ void stripend(char *str) */ int is_true(const char *tag) { - char ltag[MAXLINELEN]; + char ltag[MAXLINELEN] = {'\0'}; int i; int n = strlen(tag); for (i = 0; i < n; ++i) ltag[i] = tolower(tag[i]); - ltag[n] = 0; + ltag[n] = 0; // equivalent to '\0' if (strcmp(ltag, "true") == 0) return 1; diff --git a/model/hd-us/boundary/boundaryio.c b/model/hd-us/boundary/boundaryio.c index c94518a..9492a2c 100644 --- a/model/hd-us/boundary/boundaryio.c +++ b/model/hd-us/boundary/boundaryio.c @@ -15,7 +15,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: boundaryio.c 7456 2023-12-13 03:49:13Z her127 $ + * $Id: boundaryio.c 7502 2024-03-11 22:37:07Z her127 $ * */ @@ -2590,7 +2590,16 @@ void get_obc_list(open_bdrys_t *open, FILE *fp, int n, char *key) { sscanf(buf, "%lf %lf", &open->elon, &open->elat); sprintf(key5, "%s%1d.MID_LOC", key, n); if (prm_read_char(fp, key5, buf)) { + double x, y, d1, d2; sscanf(buf, "%lf %lf", &open->mlon, &open->mlat); + /* Check distances */ + x = open->mlon - open->slon; + y = open->mlat - open->slat; + d1 = sqrt(x * x + y * y); + x = open->elon - open->slon; + y = open->elat - open->slat; + d2 = sqrt(x * x + y * y); + if (d1 > d2) hd_warn("params_read: *** MID_LOC may be defined as END_LOC for OBC %s ***\n", open->name); } } /* else { hd_quit("params_read: cannot find %s in OBC%d (%s)\n", key5, n, open->name); diff --git a/model/hd-us/boundary/riverflow.c b/model/hd-us/boundary/riverflow.c index 60d3123..651fd49 100644 --- a/model/hd-us/boundary/riverflow.c +++ b/model/hd-us/boundary/riverflow.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: riverflow.c 6879 2021-07-29 00:43:55Z her127 $ + * $Id: riverflow.c 7503 2024-03-11 22:37:32Z her127 $ * */ @@ -144,6 +144,8 @@ double bf_u1_flow_w(geometry_t *window, window_t *windat, u1_flow_do(window, windat, data, e, cs, open->bot_t[cc]); if (windat->riverflow) windat->riverflow[cs] += data->flow / (double)open->no2_e1; + if (windat->iriverflow) + windat->iriverflow[cs] += data->flow * windat->dttr; if (windat->riverdepth) windat->riverdepth[cs] = data->hc; } return (data->v_river[e] * open->dir[ee] / (double)open->no2_e1); diff --git a/model/hd-us/closure/MY2-0.c b/model/hd-us/closure/MY2-0.c index 4e19199..060fdd7 100644 --- a/model/hd-us/closure/MY2-0.c +++ b/model/hd-us/closure/MY2-0.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: MY2-0.c 7457 2023-12-13 03:49:40Z her127 $ + * $Id: MY2-0.c 7504 2024-03-11 22:37:52Z her127 $ * */ @@ -168,6 +168,8 @@ void closure_MY2(geometry_t *window, /* Processing window */ KAPPA * ((window->cellz[c3] - window->cellz[c]) * wincon->Ds[cs] + wincon->zs); lscale = min(l1, l2); + /* Blackadar, 1962 modification for local characteristics of */ + /* flow: e.g. GOTM manual (1999) Eq. 2.61. */ lscale = lscale / (1 + lscale / lasymp); if (windat->L) windat->L[c] = lscale; diff --git a/model/hd-us/closure/closure.c b/model/hd-us/closure/closure.c index 550f7a5..98acf59 100644 --- a/model/hd-us/closure/closure.c +++ b/model/hd-us/closure/closure.c @@ -17,7 +17,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: closure.c 7357 2023-05-09 04:07:20Z riz008 $ + * $Id: closure.c 7505 2024-03-11 22:38:15Z her127 $ * */ @@ -144,7 +144,7 @@ void closure_init(parameters_t *params, /* Parameter data structure */ if (strcmp(params->s_func, "CANUTO_A") == 0) master->s_func = s_canutoA; if (strcmp(params->s_func, "CANUTO_B") == 0) - master->s_func = s_canutoA; + master->s_func = s_canutoB; if (strcmp(params->s_func, "KANTHA&CLAYSON") == 0) master->s_func = s_kantha_clayson; if (strcmp(params->s_func, "GALPERIN") == 0) @@ -229,7 +229,6 @@ void bdry_closure(geometry_t *window, /* Processing window */ open[n]->obc_t, open[n]->oi1_t, open[n]->oi2_t, open[n]->cyc_t, windat->Vz, NULL, NULL, open[n]->bcond_Vz, windat->dt, NULL, NULL, 0, 0); - } if (!(open[n]->bcond_Kz & NOTHIN)) { set_OBC(window, windat, wincon, open[n], 1, open[n]->no3_t, @@ -310,6 +309,13 @@ void s_canutoA(double aN, double aM, double *cmu, double *cmu_d) { double d, GH, GMmin; + /* Burchard and Bolding, 2001, Eq. 23 + d = (1.0 + 1977.0*aN + 0.03154*aM + 0.005832*aN*aN + 0.004127*aN*aM - 0.000042*aM*aM); + *cmu = (0.127 + 0.01526*aN - 0.00016*aM) / d; + *cmu_d = (0.1190 + 0.00429*aN + 0.00066*aM) / d; + return; + */ + /* Get max and min in terms of GH; GH = -0.5aN */ GH = -0.5 * aN; @@ -320,6 +326,7 @@ void s_canutoA(double aN, double aM, double *cmu, double *cmu_d) */ /* Set minimim and maximum shear and buoyancy numbers; Warner et. */ /* al. 2005, Table 4 and eq. 40b (note aN=-2GH). */ + d = (d0/(2.0*Cf) - d1*GH + d3*2.0*Cf*GH*GH) / (d2 - d4*2.0*Cf*GH); aM = min(aM, 2.0*d); aN = -2.0 * max(GHmin_CA, min(GH, GH0_CA)); @@ -447,7 +454,7 @@ void s_kantha_clayson(double aN, double aM, double *cmu, double *cmu_d) double d, GH; /* Scale buoyancy number applicable for these stability functions; */ - /* uses GH = -L^2/2K as an argument (Warner 92005) Eq. 32. */ + /* uses GH = -L^2*N^2/2K as an argument (Warner, 2005) Eq. 32. */ GH = -0.5 * aN; /* Smooth GH near the maximum limit (Warner et. al. (2005) Eq. 33) */ diff --git a/model/hd-us/diagnostics/monitor.c b/model/hd-us/diagnostics/monitor.c index 7b1cc85..a6ab471 100644 --- a/model/hd-us/diagnostics/monitor.c +++ b/model/hd-us/diagnostics/monitor.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: monitor.c 7427 2023-10-25 01:26:35Z her127 $ + * $Id: monitor.c 7574 2024-05-30 03:39:28Z riz008 $ * */ @@ -60,7 +60,6 @@ void print_total_mass(master_t *master); void psorts(double *a, int *c, int n); void porders(double *p, double *q, int *i, int *j); void sound_channel(geometry_t *window, window_t *windat, win_priv_t *wincon); -void quicksort(double *p, int *q, int l, int r); int partition(double *p, int *q, int l, int r); int mergeSort(int a[], int start, int end ); double vertex_mean(geometry_t *window, double *a, int c); @@ -2473,13 +2472,18 @@ void calc_cfl(geometry_t *window, /* Window geometry */ for (j = 1; j < window->npe[c]; j++) { int e = window->c2e[j][c]; int es = window->m2de[e]; + int c1 = window->e2c[e][0]; + int c2 = window->e2c[e][1]; u = fabs(windat->u1[e]); windat->courn[c] = max(windat->courn[c], u * windat->dt / window->h2au1[es]); /*windat->courn[c] = u * windat->dt / window->h2au1[es];*/ u = (u) ? window->h2au1[es] / u : HUGE; windat->cour[c] = min(windat->cour[c], u); - u = fabs(windat->u[window->e2c[e][0]] - - windat->u[window->e2c[e][1]]); + u = fabs(windat->u[c1] * window->costhu1[es] + + windat->v[c1] * window->sinthu1[es] - + windat->u[c2] * window->costhu1[es] - + windat->v[c2] * window->sinthu1[es]); + /*u = fabs(windat->u[c1] - windat->u[c2]);*/ u = (u) ? window->h2au1[es] / u : HUGE; windat->lips[c] = min(windat->lips[c], u); } @@ -4065,7 +4069,7 @@ void mass_diag(geometry_t *window, /* Window geometry */ } /* Add sediment mass if this tracer has a sediment component */ - npor = tracer_find_index("porosity", wincon->ntr, wincon->trinfo_sed); + npor = tracer_find_index("porosity", wincon->nsed, wincon->trinfo_sed); for (n = 0; n < windat->ntot; n++) { if ((trn = windat->totid_sed[n]) >= 0) { for(cc = 1; cc <= window->b2_t; cc++) { diff --git a/model/hd-us/diagnostics/run_setup.c b/model/hd-us/diagnostics/run_setup.c index b9c0572..9b206e9 100644 --- a/model/hd-us/diagnostics/run_setup.c +++ b/model/hd-us/diagnostics/run_setup.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: run_setup.c 7458 2023-12-13 03:50:02Z her127 $ + * $Id: run_setup.c 7557 2024-05-27 04:58:31Z her127 $ * */ @@ -299,11 +299,17 @@ void write_run_setup(hd_data_t *hd_data) if (params->stab & NONE) fprintf(fp, "No stability compensation\n"); - if (params->stab & SUB_STEP) + if (params->stab & SUB_STEP) { fprintf(fp, "Sub-stepping stability compensation\n"); - if (params->stab & SUB_STEP_NOSURF) + if (params->trsf != 0.8) + fprintf(fp, " Sub-step safety factor = %5.2f\n", params->trsf); + } + if (params->stab & SUB_STEP_NOSURF) { fprintf(fp, "Sub-stepping stability compensation; excluding surface layer\n"); + if (params->trsf != 0.8) + fprintf(fp, " Sub-step safety factor = %5.2f\n", params->trsf); + } if (params->stab & SUB_STEP_TRACER) fprintf(fp, "Sub-stepping stability compensation; tracers only\n"); if (params->thin_merge) @@ -688,9 +694,22 @@ void write_run_setup(hd_data_t *hd_data) if (params->trasc & FFSL && params->trasc & VANLEER) fprintf(fp, "Split FFSL/Van Leer tracer advection scheme.\n"); if (params->ultimate) { - if (params->trasc == FFSL) - fprintf(fp, "Universal flux limiter invoked.\n"); - else + if (params->trasc == FFSL) { + if (params->ultimate & UL_DO) + fprintf(fp, "Universal flux limiter invoked.\n"); + if (params->ultimate & UL_IPROJ) + fprintf(fp, " Min/max at destination cell is projected tracer value.\n"); + if (params->ultimate & UL_SPROJ) + fprintf(fp, " Min/max along streamline is projected tracer value.\n"); + if (params->ultimate & UL_ITR) + fprintf(fp, " Min/max at destination cell is tracer value.\n"); + if (params->ultimate & UL_SSTCL) + fprintf(fp, " Min/max along streamline is stencil values.\n"); + if (params->ultimate & UL_REFINE) + fprintf(fp, " Upstream refinements applied to fluxes.\n"); + if (params->ultimate & UL_CLIP) + fprintf(fp, " Limited tracer values clipped after update.\n"); + } else fprintf(fp, "Ultimate filter invoked.\n"); } @@ -2963,7 +2982,7 @@ void history_log(master_t *master, int mode) if (j == 5) fprintf(dp, "Transport model\n"); fprintf(dp, "Run%d: %s", params->hrun, ctime(&t)); fprintf(dp, "EMS Version: %s\n", version); - fprintf(dp, "Executable file: %s\n",executable); + fprintf(dp, "Executable file: %s\n", executable); getcwd(buf, MAXSTRLEN); fprintf(dp, "Working directory: %s\n",buf); fprintf(dp, "Parameter file: %s\n", params->prmname); @@ -2973,6 +2992,7 @@ void history_log(master_t *master, int mode) fprintf(dp, "Parameter header: %s\n", params->parameterheader); if (strlen(params->notes)) fprintf(dp, "Version notes: %s\n", params->notes); + if (forced_restart) fprintf(params->hstfd, "Restart run.\n"); fprintf(dp, "Start time: %s\n", params->start_time); fprintf(dp, "Stop time: %s\n", params->stop_time); @@ -3084,6 +3104,10 @@ void history_log(master_t *master, int mode) fprintf(params->hstfd, "Run successful at %s\n", ctime(&t)); fclose(params->hstfd); } + if ((dp = fopen(params->histnamem, "a+")) != NULL) { + fprintf(params->hstfd, "Run successful at %s\n", ctime(&t)); + fclose(dp); + } } if (mode == HST_NOK) { @@ -3092,6 +3116,10 @@ void history_log(master_t *master, int mode) fprintf(params->hstfd, "Crashed %4.3f days: %s\n", master->days, ctime(&t)); fclose(params->hstfd); } + if ((dp = fopen(params->histnamem, "a+")) != NULL) { + fprintf(params->hstfd, "Crashed %4.3f days: %s\n", master->days, ctime(&t)); + fclose(dp); + } } } diff --git a/model/hd-us/ecology/ecology.c b/model/hd-us/ecology/ecology.c index 5e2dc2b..eb147ab 100644 --- a/model/hd-us/ecology/ecology.c +++ b/model/hd-us/ecology/ecology.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ecology.c 7190 2022-09-09 06:25:00Z bai155 $ + * $Id: ecology.c 7519 2024-03-22 04:50:15Z riz008 $ * */ @@ -981,14 +981,25 @@ void eco_step(geometry_t *window) wincon->ecodt, windat->dt); wincon->ecodt = windat->dt; } - wincon->eco_timestep_ratio = wincon->ecodt / windat->dt; + if (ginterface_transport_mode()) + if (wincon->eco_timestep_ratio != 1) + hd_quit("Ecology transport mode eco_timestep_ratio is != 1\n"); + /* Trike introduces minor rounding errors due to timeunit conversions */ if (fabs(wincon->ecodt - (windat->dt * wincon->eco_timestep_ratio)) > 1e-3) hd_quit("Inconsistency in setting ecology time step as a multiple of physical timestep: eco_dt=%f, phys_dt=%f\n", wincon->ecodt, windat->dt); - /* Run ecology if its the right time */ - if (!(windat->nstep % wincon->eco_timestep_ratio)) { + /* + * Run ecology if its the right time + * + * For fully-coupled this will now run at ecodt-, meaning one + * hydro dt before the scheduled events are fired (eg. dumps). It + * used to run at ecodt+, meaning straight after scheduled events + * plus one hydro dt. + * ToDo: Verify any tracer resets are working as expected + */ + if (!((windat->nstep+1) % wincon->eco_timestep_ratio)) { TIMING_SET; ecology_step(wincon->e, wincon->ecodt); TIMING_DUMP_WIN(3, " eco_step", window->wn); diff --git a/model/hd-us/forcings/dhw.c b/model/hd-us/forcings/dhw.c index 766430c..77f11d3 100644 --- a/model/hd-us/forcings/dhw.c +++ b/model/hd-us/forcings/dhw.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dhw.c 6626 2020-09-08 01:34:43Z her127 $ + * $Id: dhw.c 7543 2024-05-10 05:23:14Z her127 $ * */ @@ -30,6 +30,7 @@ double thresh = 1.0; static int dhw_init(sched_event_t *event); double dhw_event(sched_event_t *event, double t); static void dhw_cleanup(sched_event_t *event, double t); +int ts_check_time(timeseries_t *ts, double r); typedef struct { master_t *master; /* Grid associated with */ @@ -171,7 +172,7 @@ double dhw_event(sched_event_t *event, double t) /* Check if the offset dhd value is in the file */ tin = t - dhw->offset; for (n = 0; n < dhw->ntsfiles; n++) { - if (ts_has_time(dhw->tsfiles[n], tin) == 0) + if (ts_check_time(dhw->tsfiles[n], tin) == 0) found = 0; } @@ -297,3 +298,40 @@ void calc_dhd(geometry_t *window, /* Window geometry */ /* END calc_dhd() */ /*-------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------*/ +/* Wrapper to check if a record exists in a file */ +/*-------------------------------------------------------------------*/ +int ts_check_time(timeseries_t *ts, double r) +{ + datafile_t *df = ts->df; + int ilow = 0; + int ihigh = df->nrecords - 1; + + if (df->records == NULL) { + return(0); + } + + /* If the time has not changed since the last function call, then + return the previously calculated bounds and fraction. */ + if (r == df->t0) { + return(1); + } + + if (r <= df->records[ilow]) { + return(0); + } + + if (r >= df->records[ihigh]) { + ts_has_time(ts, r); + if (r >= df->records[ihigh]) { + return(0); + } else + return(1); + } + return(1); +} + +/* END ts_check_time() */ +/*-------------------------------------------------------------------*/ diff --git a/model/hd-us/ginterface/ginterface.c b/model/hd-us/ginterface/ginterface.c index 5943455..8821c9f 100644 --- a/model/hd-us/ginterface/ginterface.c +++ b/model/hd-us/ginterface/ginterface.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ginterface.c 7488 2024-02-18 04:01:39Z bai155 $ + * $Id: ginterface.c 7578 2024-05-31 01:32:53Z riz008 $ * */ @@ -3950,3 +3950,15 @@ double ginterface_get_eta(void* hmodel, int b) double v = windat->eta[c2]; return v; } + +/* + * Handy utility for when singleton operations are needed in a + * multi-window environemnt. eg. ecology file operations + */ +int ginterface_is_window1(void *hmodel) +{ + if (hmodel == NULL) + return 1; + else + return (((geometry_t*)hmodel)->wn == 1); +} diff --git a/model/hd-us/include/dumpfile.h b/model/hd-us/include/dumpfile.h index 8a2b8ee..066a81f 100644 --- a/model/hd-us/include/dumpfile.h +++ b/model/hd-us/include/dumpfile.h @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dumpfile.h 7162 2022-07-07 02:33:06Z her127 $ + * $Id: dumpfile.h 7525 2024-04-03 21:45:04Z her127 $ * */ @@ -426,6 +426,7 @@ struct dump_data { int *i1s; int **i2s; int **i3s; + int **ij2c; short *crci; short *clci; diff --git a/model/hd-us/include/hd_params.h b/model/hd-us/include/hd_params.h index 664eec2..6bc65b3 100644 --- a/model/hd-us/include/hd_params.h +++ b/model/hd-us/include/hd_params.h @@ -15,7 +15,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: hd_params.h 7459 2023-12-13 03:50:20Z her127 $ + * $Id: hd_params.h 7558 2024-05-27 04:58:58Z her127 $ * */ @@ -406,6 +406,15 @@ #define TR_LIND2 0x000002 #define TR_LIND3 0x000004 +/* Ultimate limiter flags */ +#define UL_DO 0x01 +#define UL_CLIP 0x02 +#define UL_IPROJ 0x04 +#define UL_ITR 0x08 +#define UL_SPROJ 0x10 +#define UL_SSTCL 0x20 +#define UL_REFINE 0x40 + /* Momentum ommission flags */ #define ADVECT 1 #define HDIFF 2 @@ -805,6 +814,7 @@ #define DF_TAN 0x0040 #define DF_TILE 0x0080 #define DF_OBC 0x0100 +#define DF_STO 0x0200 /* DA flags */ #define NO_DA 1 @@ -899,10 +909,16 @@ /* METIS options */ #define METIS_VOLUME_WEIGHTED 0x0001 +#define METIS_REORDER 0x0002 /* Point source/sinks */ # define PSS_AW 0x0001 # define PSS_VW 0x0002 +# define PSS_DP 0x0004 +# define PSS_DR 0x0008 +# define PSS_AP 0x0010 +# define PSS_AR 0x0020 +# define PSS_F 0x0040 /* Tracer filtering */ # define TRF_FILL 0x0001 @@ -1093,8 +1109,11 @@ /* Mesh reorder */ #define MR_READ 0x001 -#define MR_WRITE 0x002 -#define MR_WRITEX 0x004 +#define MR_READM 0x002 +#define MR_WRITE 0x004 +#define MR_WRITEX 0x008 +#define MR_HORZ 0x010 +#define MR_VERT 0x020 /* Particles */ #define PT_DO 0x01 diff --git a/model/hd-us/include/proto.h b/model/hd-us/include/proto.h index 18e708e..93017af 100644 --- a/model/hd-us/include/proto.h +++ b/model/hd-us/include/proto.h @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: proto.h 7480 2024-02-01 03:49:57Z riz008 $ + * $Id: proto.h 7577 2024-05-31 00:36:37Z riz008 $ * */ @@ -26,7 +26,7 @@ /*------------------------------------------------------------------*/ #define COMPAS_MAJOR_VERSION 1 #define COMPAS_MINOR_VERSION 4 -#define COMPAS_PATCH_VERSION 1 +#define COMPAS_PATCH_VERSION 2 /*------------------------------------------------------------------*/ /* Parameter input routines */ @@ -74,6 +74,8 @@ void read_decorr(parameters_t *params, FILE *fp, int mode); void read_monotone(parameters_t *params, FILE *fp, int mode); int read_dhw(parameters_t *params, FILE *fp); void read_trflux(parameters_t *params, FILE *fp); +int read_reorder(parameters_t *params, FILE *fp); +void read_ultimate(parameters_t *params, FILE *fp); void tracer_setup(parameters_t *params, FILE *fp); int numbers_init(parameters_t *params); int import_init(parameters_t *params, FILE *fp); @@ -174,7 +176,7 @@ void convert_jigsaw_msh(parameters_t *params, char *infile, void mesh_ofile_name(parameters_t *params, char *key); delaunay *make_dual(geometry_t *geom, int kin); int mesh_expand_w(geometry_t *window, int *vec); -int mesh_expand_3d(geometry_t *window, double *u1); +int mesh_expand_3d(geometry_t *window, double *u1, int *vec); point *convex_hull(point *v, int *count); void write_swan_mesh(master_t *master, geometry_t **window, window_t **windat, win_priv_t **wincon); @@ -204,6 +206,8 @@ void get_window_cells(geometry_t *geom, int wn, int *wsa, int *wsize, int *ws2, int wsizeS); void get_window_cells_h(geometry_t *geom, int wn, int *wsa, int *wsize, int *ws2, int wsizeS); +void get_window_cells_v(geometry_t *geom, int wn, int *wsa, int *wsize, + int *ws2, int wsizeS); void get_gl_maps(geometry_t *geom, int nwindows, int **ws2, int *wsizeS); void set_mask(geometry_t *window); void window_cells_zoom(geometry_t *geom, parameters_t *params, @@ -260,7 +264,7 @@ void get_local_ghost(geometry_t *geom, geometry_t *window, int n); void windows_clear(hd_data_t *hd_data); void reset_windows(hd_data_t *hd_data); void reorder_cells(geometry_t *window, int *ncells, int *cells, - int vc, int vcs, int *bot, int mode); + int vc, int vcs, int *map, int mode); int get_local_sur(geometry_t *window, int cl, int ks); void process_cell_mask(geometry_t *window, int *cells, int ncells); void read_window_info(parameters_t *params, FILE *fp); @@ -726,6 +730,7 @@ int edge_sort_double (void const *x1, void const *x2); int edge_sort_compare (void const *x1, void const *x2); void perc_diag(geometry_t *window, window_t *windat, win_priv_t *wincon); void calc_perc(FILE *fp); +void quicksort(double *p, int *q, int l, int r); void check_flux(geometry_t *window, window_t *windat, win_priv_t *wincon, int dw, int dc); double is_night(double lat, double t); @@ -1110,6 +1115,8 @@ double hd_trans_interp(geometry_t *window, GRID_SPECS **gs, double x, double y, int c, int co, int vid); void clip_ffsl(geometry_t *window, window_t *windat, win_priv_t *wincon, double *tr); +void clip_ffsl_ultimate(geometry_t *window, window_t *windat, win_priv_t *wincon, + double *tr); void get_source_minmax(geometry_t *window, window_t *windat, win_priv_t *wincon, double *tr, double dtu, int mode); void order2_upwind(geometry_t *window, window_t *windat, win_priv_t *wincon, @@ -1436,6 +1443,7 @@ void dumpdata_init_geom(parameters_t *params, geometry_t *geom, dump_data_t *dum void dumpdata_fill(geometry_t *geom, master_t *master, dump_data_t *dumpdata); void dumpdata_cleanup(dump_data_t *dumpdata, int mode); +void dumpadata_create_stgrid(dump_data_t *dumpdata, double r); int dump_open_us(parameters_t *params, char *name, int check); void dump_close(int cdfid); int dump_choose_by_time(parameters_t *params, int fid, double t); @@ -1936,6 +1944,7 @@ void ginterface_moonvars(void *hmodel, int c, double *dist_earth_sun, double *dist_moon_earth, double *lunar_angle, double *sun_angle,double *moon_phase,double *lunar_dec); double ginterface_get_cloud(void *hmodel, int c); +int ginterface_is_window1(void *hmodel); /*------------------------------------------------------------------*/ diff --git a/model/hd-us/include/sparse.h b/model/hd-us/include/sparse.h index d759ec3..02c89dc 100644 --- a/model/hd-us/include/sparse.h +++ b/model/hd-us/include/sparse.h @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: sparse.h 7461 2023-12-13 03:51:01Z her127 $ + * $Id: sparse.h 7560 2024-05-27 04:59:55Z her127 $ * */ @@ -490,6 +490,7 @@ struct win_priv { int smagcode; /* Variables using Smagorinsky */ int diff_scale; /* Horizontal mixing scaling */ int stab; /* Stability compensation method */ + double trsf; /* Tracer advection sub-step safety factor */ int thin_merge; /* Thin layer merging flag */ int sigma; /* Sigma coordinate flag */ int nonlinear; /* Non-linearity flag */ @@ -1341,6 +1342,7 @@ typedef struct { int diff_scale; /* Horizontal diffusion scaling method */ int visc_method; /* Horizontal diffusion method */ int stab; /* Stability compensation method */ + double trsf; /* Tracer advection sub-step safety factor */ int thin_merge; /* Thin layer merging flag */ int sigma; /* Sigma coordinate flag */ int nonlinear; /* Non-linearity flag */ @@ -1873,6 +1875,7 @@ struct master { int diff_scale; /* Horizontal diffusion scaling method */ int visc_method; /* Horizontal diffusion method */ int stab; /* Stability compensation method */ + double trsf; /* Tracer advection sub-step safety factor */ int thin_merge; /* Thin layer merging flag */ int sigma; /* Sigma coordinate flag */ int nonlinear; /* Non-linearity flag */ @@ -2071,6 +2074,7 @@ struct master { double *rv_wsc; /* Wind stress curl rv tendency */ double *rv_bsc; /* Bottom stress curl rv tendency */ double *riverflow; /* River flow diagnostic */ + double *iriverflow; /* Integrated river flow diagnostic */ double *riverdepth; /* River depth diagnostic */ double *riversalt; /* River ghost salinity diagnostic */ double *equitide; /* Equilibrium tide */ @@ -3055,6 +3059,7 @@ struct window { double *rv_wsc; /* Wind stress curl rv tendency */ double *rv_bsc; /* Bottom stress curl rv tendency */ double *riverflow; /* River flow diagnostic */ + double *iriverflow; /* Integrated river flow diagnostic */ double *riverdepth; /* River depth diagnostic */ double *riversalt; /* River ghost salinity diagnostic */ double *equitide; /* Equilibrium tide */ @@ -3145,6 +3150,8 @@ struct window { double *mono; /* Monotinicity diagnostic */ double *monon; /* Monotinicity diagnostic */ double *monox; /* Monotinicity diagnostic */ + double *trmin; /* ULTIMATE minimum tacer */ + double *trmax; /* ULTIMATE maximum tacer */ double sederrstep; /* Sediment error step */ double ecoerrstep; /* Ecology error step */ int ntot; /* Number of additional total tracers */ diff --git a/model/hd-us/inputs/meshes.c b/model/hd-us/inputs/meshes.c index e9a6a52..ab58a9b 100644 --- a/model/hd-us/inputs/meshes.c +++ b/model/hd-us/inputs/meshes.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: meshes.c 7462 2023-12-13 03:51:20Z her127 $ + * $Id: meshes.c 7546 2024-05-10 05:24:35Z her127 $ * */ @@ -2918,7 +2918,7 @@ int get_mesh_obc(parameters_t *params, int **neic ) { - FILE *fp, *op; + FILE *fp, *sp, *op; int n, m, cc, c, cn, cs, i, j, jj, jn, js, jss; int np; /* Size of the perimeter array */ int *perm; /* Mesh indices of the perimeter */ @@ -3185,6 +3185,7 @@ int get_mesh_obc(parameters_t *params, mesh->obc = i_alloc_3d(2, mobc+1, mesh->nobc); } fp = fopen("boundary.txt", "w"); + sp = fopen("boundary.site", "w"); op = fopen("obc_spec.txt", "w"); /*-----------------------------------------------------------------*/ @@ -3356,6 +3357,23 @@ int get_mesh_obc(parameters_t *params, } fprintf(fp, "NaN NaN\n"); } + if (sp != NULL) { + double x = 0.5 * (mesh->xloc[mesh->obc[m][1][0]] + + mesh->xloc[mesh->obc[m][1][1]]); + double y = 0.5 * (mesh->yloc[mesh->obc[m][1][0]] + + mesh->yloc[mesh->obc[m][1][1]]); + fprintf(sp, "%f %f %s_s\n", x, y, open->name); + if (donef[m] != 1) { + double x = 0.5 * (mesh->xloc[mesh->obc[m][mesh->npts[m]][0]] + + mesh->xloc[mesh->obc[m][mesh->npts[m]][1]]); + double y = 0.5 * (mesh->yloc[mesh->obc[m][mesh->npts[m]][0]] + + mesh->yloc[mesh->obc[m][mesh->npts[m]][1]]); + fprintf(sp, "%f %f %s_e\n", x, y, open->name); + } + if (donef[m] == 0) { + fprintf(sp, "%f %f %s_m\n", open->mlon, open->mlat, open->name); + } + } } if (op != NULL) { fprintf(op, "\nBOUNDARY%d.UPOINTS %d\n", m, mesh->npts[m]); @@ -3371,6 +3389,7 @@ int get_mesh_obc(parameters_t *params, } } fclose(fp); + fclose(sp); fclose(op); i_free_1d(si); i_free_1d(ei); @@ -8774,7 +8793,8 @@ int mesh_expand_w(geometry_t *window, /* Window geometry */ /* outgoing flow. */ /*-------------------------------------------------------------------*/ int mesh_expand_3d(geometry_t *window, /* Window geometry */ - double *u /* Velocity array */ + double *u, /* Velocity array */ + int *vecin /* Cells to process */ ) { win_priv_t *wincon = window->wincon; @@ -8786,39 +8806,45 @@ int mesh_expand_3d(geometry_t *window, /* Window geometry */ double d1; int *vec = wincon->s6; int *c2cc = wincon->s7; + int *ctp = wincon->s1; + int nctp = wincon->vc; memset(vec, 0, window->szc * sizeof(int)); memset(c2cc, 0, window->szc * sizeof(int)); filla = i_alloc_1d(window->szc); memset(filla, 0, window->szc * sizeof(int)); ni = 1; - for (cc = 1; cc <=wincon->vcs; cc++) { - c = wincon->s1[cc]; - c2 = window->m2d[c]; - c2cc[c2] = cc; + /* If vec is provided, then this contains non-monotonic cells, */ + /* and only expand out from these. */ + if (vecin != NULL) { + ctp = vecin; + nctp = vecin[0]; } - for (cc = 1; cc <=wincon->vc; cc++) { - c = wincon->s1[cc]; - c2 = window->m2d[c]; - n = 0; - for (ee = 1; ee <= window->npe[c2]; ee++) { - e = window->c2e[ee][c]; - /* Direction of flow */ - dir = (window->eSc[ee][c2] * u[e] >= 0.0) ? 1 : -1; - /* Sum the edges with outgoing flow */ - if (dir == 1) n++; - } - /* All edges are outflow */ - if (!filla[c] && n == window->npe[c2]) { - /*printf("start %d %d %d(%d %d)\n",master->nstep,ni,c,window->s2i[c],window->s2j[c]);*/ - vec[ni++] = c; - filla[c] = 1; - mesh_expand_do(window, u, vec, &ni, filla); + + if (vecin == NULL) { + for (cc = 1; cc <=wincon->vc; cc++) { + c = wincon->s1[cc]; + c2 = window->m2d[c]; + n = 0; + for (ee = 1; ee <= window->npe[c2]; ee++) { + e = window->c2e[ee][c]; + /* Direction of flow */ + dir = (window->eSc[ee][c2] * u[e] >= 0.0) ? 1 : -1; + /* Sum the edges with outgoing flow */ + if (dir == 1) n++; + } + /* All edges are outflow */ + if (!filla[c] && n == window->npe[c2]) { + /*printf("start %d %d %d(%d %d)\n",master->nstep,ni,c,window->s2i[c],window->s2j[c]);*/ + vec[ni++] = c; + filla[c] = 1; + mesh_expand_do(window, u, vec, &ni, filla); + } } } /* Collect unassigned cells having at least one outflow edge */ - for (cc = 1; cc <=wincon->vc; cc++) { - c = wincon->s1[cc]; + for (cc = 1; cc <= nctp; cc++) { + c = ctp[cc]; c2 = window->m2d[c]; for (ee = 1; ee <= window->npe[c2]; ee++) { e = window->c2e[ee][c]; @@ -8832,6 +8858,13 @@ int mesh_expand_3d(geometry_t *window, /* Window geometry */ } ni--; vec[0] = ni; + + for (cc = 1; cc <=wincon->vcs; cc++) { + c = wincon->s1[cc]; + c2 = window->m2d[c]; + c2cc[c2] = cc; + } + return(ni); } @@ -9453,9 +9486,18 @@ void add_quad_grid(parameters_t *params, char *iname, int mode) prm_read_int(pp, "NCE2", &ice2); if (prm_read_char(fp, "SUBSECTION", buf)) sscanf(buf, "(%d,%d)-(%d,%d)", &si, &sj, &ei, &ej); + if (si > ei) { + i = ei; + ei = si; + si = i; + } + if (sj > ej) { + j = ej; + ej = sj; + sj = j; + } nce1 = ei - si + 1; nce2 = ej - sj + 1; - if (!prm_read_double(fp, "BATHYVAL", &nbathy)) { prm_read_darray(pp, "BATHY", &b1, &i); bathyin = d_alloc_1d(nce1 * nce2 + 1); @@ -9781,7 +9823,7 @@ void add_quad_grid(parameters_t *params, char *iname, int mode) fprintf(dp, "%f %f\n",lon[i][j], lat[i][j]); fprintf(op, "%f %f e%d\n",lon[i][j], lat[i][j], n); n++; - } + } fprintf(dp, "%f %f\n",lon[i][1], lat[i][1]); fprintf(dp, "NaN NaN\n"); } diff --git a/model/hd-us/inputs/readparam.c b/model/hd-us/inputs/readparam.c index 91c5f7f..0936470 100644 --- a/model/hd-us/inputs/readparam.c +++ b/model/hd-us/inputs/readparam.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: readparam.c 7463 2023-12-13 03:51:41Z her127 $ + * $Id: readparam.c 7561 2024-05-27 05:00:21Z her127 $ * */ @@ -109,6 +109,7 @@ char *cflname(int m); char *substepname(int m); char *heatfluxname(int m); char *meansname(int m, char *buf); +char *ulname(int m, char *buf); char *btname(int m); char *adname(int m); char *tf(int m); @@ -186,6 +187,7 @@ void set_default_param(parameters_t *params) params->visc_method = US_LAPLACIAN; params->visc_fact = 0.0; params->stab = NONE; + params->trsf = 0.8; params->atr = 0; params->ntr = 0; params->ntrS = 0; @@ -608,12 +610,10 @@ parameters_t *params_read(FILE *fp) sprintf(keyword, "MESH_INFO"); if (prm_read_char(fp, keyword, buf)) params->meshinfo = is_true(buf); - if (prm_read_char(fp, "REORDER_WRITE", params->mesh_reorder)) - params->mrf |= MR_WRITE; - if (prm_read_char(fp, "REORDER_WRITEX", params->mesh_reorder)) - params->mrf |= MR_WRITEX; - if (prm_read_char(fp, "REORDER_READ", params->mesh_reorder)) - params->mrf |= MR_READ; + + /* Mesh reordering */ + read_reorder(params, fp); + sprintf(keyword, "ETAMAX"); prm_read_double(fp, keyword, ¶ms->etamax); sprintf(keyword, "MIN_CELL_THICKNESS"); @@ -684,6 +684,8 @@ parameters_t *params_read(FILE *fp) if (strcmp(buf, "SUB-STEP-TRACER") == 0) params->stab = SUB_STEP_TRACER; } + sprintf(keyword, "SUB-STEP_SAFETY"); + prm_read_double(fp, keyword, ¶ms->trsf); /* Thin layer merging */ sprintf(keyword, "MERGE_THIN"); @@ -1011,7 +1013,7 @@ parameters_t *params_read(FILE *fp) /* River flow diagnostic */ if (check_river_bdry(params, fp)) { params->riverflow = 1; - params->ntrS++; + params->ntrS += 2; if (check_bdry_options(params, fp) & OP_DYNAHC) { params->riverflow = 2; params->ntrS++; @@ -2166,13 +2168,9 @@ parameters_t *auto_params(FILE * fp, int autof) prm_read_int(fp, keyword, &nce2); params->nce1 = nce1; params->nce2 = nce2; - if (prm_read_char(fp, "REORDER_WRITE", params->mesh_reorder)) - params->mrf |= MR_WRITE; - if (prm_read_char(fp, "REORDER_WRITEX", params->mesh_reorder)) - params->mrf |= MR_WRITEX; - if (prm_read_char(fp, "REORDER_READ", params->mesh_reorder)) - params->mrf |= MR_READ; - prm_set_errfn(hd_silent_warn); + + /* Mesh reordering */ + read_reorder(params, fp); /* Add a quad grid to the structured mesh */ sprintf(keyword, "ADD_QUAD"); @@ -2765,7 +2763,7 @@ parameters_t *auto_params(FILE * fp, int autof) /* River flow diagnostic */ if (check_river_bdry(params, fp)) { params->riverflow = 1; - params->ntrS++; + params->ntrS += 2; if (check_bdry_options(params, fp) & OP_DYNAHC) { params->riverflow = 2; params->ntrS++; @@ -6984,7 +6982,7 @@ void params_write(parameters_t *params, dump_data_t *dumpdata, char *name) fprintf(op, "# Advection\n"); fprintf(op, "MOM_SCHEME %s\n", adname(params->momsc)); fprintf(op, "TRA_SCHEME %s\n", adname(params->trasc)); - fprintf(op, "ULTIMATE %s\n\n", tf(params->ultimate)); + fprintf(op, "ULTIMATE %s\n\n", ulname(params->ultimate, key)); fprintf(op, "# Horizontal mixing\n"); if (params->u1vh > 1.0) @@ -8071,6 +8069,7 @@ void read_grid(parameters_t *params) } else if (prm_read_double(fp, "DLAMBDA", &xinc) && prm_read_double(fp, "DPHI", &yinc)) { + double deg2m = 60.0 * 1852.0; /*---------------------------------------------------------------*/ /* Structured defined by a corner location and (deg) resolution */ strcpy(params->gridtype, "GEOGRAPHIC_RECTANGULAR"); @@ -8078,6 +8077,9 @@ void read_grid(parameters_t *params) prm_read_double(fp, "X00", &x00); prm_read_double(fp, "Y00", &y00); + if (xinc > 1.0) xinc /= deg2m; + if (yinc > 1.0) yinc /= deg2m; + if (prm_read_double(fp, "POLE_LATITUDE", ¶ms->flat) && prm_read_double(fp, "POLE_LONGITUDE", ¶ms->flon)) { double elf = 0.0; @@ -8093,7 +8095,7 @@ void read_grid(parameters_t *params) if (prm_read_double(fp, "ELLIPSE", &elf)) { jigsaw_msh_t J_mesh; jigsaw_msh_t J_hfun; - double deg2m = 60.0 * 1852.0; + /*double deg2m = 60.0 * 1852.0;*/ double ores; /* Outer ellipse resolution */ strcpy(params->gridtype, "UNSTRUCTURED"); params->gridcode = UNSTRUCTURED; @@ -8105,6 +8107,7 @@ void read_grid(parameters_t *params) if (!(prm_read_double(fp, "OUTER_RES", &ores))) ores = 3000.0 / deg2m; + if (ores > 1.0) ores /= deg2m; create_ellipse(params, nce1, nce2, x00, y00, params->flon, params->flat, @@ -9251,12 +9254,12 @@ void read_window_info(parameters_t *params, FILE *fp) if (prm_read_char(fp, keyword, buf)) { if (is_true(buf)) { /*if (params->show_win == 0)*/ - params->ntrS += 2; + params->ntrS += 2; params->show_win = 1; } } - if (params->show_win) params->ntrS += 2; - + /*if (params->show_win) params->ntrS += 2;*/ + sprintf(keyword, "WINDOW_RESET"); prm_read_int(fp, keyword, ¶ms->win_reset); params->win_size = NULL; @@ -9311,7 +9314,6 @@ void read_window_info(parameters_t *params, FILE *fp) } } } - sprintf(keyword, "DP_MODE"); if(!prm_read_char(fp, keyword, params->dp_mode)) { sprintf(keyword, "DPMODE"); @@ -10074,6 +10076,53 @@ void read_advect(parameters_t *params, FILE *fp) /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Routine to read ultimate limiter options */ +/*-------------------------------------------------------------------*/ +void read_ultimate(parameters_t *params, FILE *fp) +{ + char keyword[MAXSTRLEN], buf[MAXSTRLEN], ult[MAXSTRLEN]; + char *fields[MAXSTRLEN * MAXNUMARGS]; + int n; + + params->ultimate = 0; + sprintf(keyword, "ULTIMATE"); + if (prm_read_char(fp, keyword, ult)) { + strcpy(buf, ult); + n = parseline(ult, fields, MAXNUMARGS); + if (n == 1 && is_true(buf)) { + params->ultimate = UL_DO; + if (params->trasc == FFSL) params->ultimate |=( UL_IPROJ|UL_SPROJ); + } else if (contains_token(buf, "NO") != NULL) { + params->ultimate = 0; + } else { + params->ultimate = 0; + if (contains_token(buf, "YES") != NULL) + params->ultimate |= UL_DO; + if (contains_token(buf, "CLIP") != NULL) + params->ultimate |= UL_CLIP; + if (contains_token(buf, "INIT_PROJ") != NULL) + params->ultimate |= UL_IPROJ; + if (contains_token(buf, "INIT_TR") != NULL) + params->ultimate |= UL_ITR; + if (contains_token(buf, "STREAMLINE") != NULL) + params->ultimate |= UL_SPROJ; + if (contains_token(buf, "STENCIL") != NULL) + params->ultimate |= UL_SSTCL; + if (contains_token(buf, "REFINE") != NULL) + params->ultimate |= UL_REFINE; + if (!(params->ultimate & UL_IPROJ) && !(params->ultimate & UL_ITR)) + params->ultimate |= UL_IPROJ; + if (!(params->ultimate & UL_SPROJ) && !(params->ultimate & UL_SSTCL)) + params->ultimate |= UL_SPROJ; + } + } +} + +/* END read_ultimate() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Routine to read n tidal energy extraction */ /*-------------------------------------------------------------------*/ @@ -10463,6 +10512,49 @@ void read_monotone(parameters_t *params, FILE *fp, int mode) /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Reads mesh re-ordering information. */ +/*-------------------------------------------------------------------*/ +int read_reorder(parameters_t *params, FILE *fp) +{ + char buf[MAXSTRLEN], keyword[MAXSTRLEN], val[MAXSTRLEN]; + + params->mrf |= (MR_READM|MR_HORZ); + sprintf(keyword, "MESH_REORDER"); + if (prm_read_char(fp, keyword, buf)) { + if (contains_token(buf, "NONE") != NULL) + params->mrf = NONE; + else { + params->mrf = 0; + if (contains_token(buf, "METIS") != NULL) + params->mrf |= MR_READM; + if (contains_token(buf, "HORZ") != NULL) + params->mrf |= MR_HORZ; + if (contains_token(buf, "VERT") != NULL) + params->mrf |= MR_VERT; + if (decode_tag(buf, "WRITE", val)) { + strcpy(params->mesh_reorder, val); + params->mrf |= MR_WRITEX; + } + if (decode_tag(buf, "READ", val)) { + strcpy(params->mesh_reorder, val); + params->mrf |= MR_READ; + } + } + } + /* Old code */ + if (prm_read_char(fp, "REORDER_WRITE", params->mesh_reorder)) + params->mrf |= MR_WRITE; + if (prm_read_char(fp, "REORDER_WRITEX", params->mesh_reorder)) + params->mrf |= MR_WRITEX; + if (prm_read_char(fp, "REORDER_READ", params->mesh_reorder)) + params->mrf |= MR_READ; +} + +/* END read_reorder() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Routine to create the AVHRR file list */ /*-------------------------------------------------------------------*/ @@ -12880,6 +12972,28 @@ char *adname(int m) return ("NULL"); } +char *ulname(int m, char *buf) +{ + sprintf(buf, "%c", '\0'); + if (m & UL_DO) + sprintf(buf, "YES ", buf); + if (m & UL_CLIP) + sprintf(buf, "%s CLIP ", buf); + if (m & UL_IPROJ) + sprintf(buf, "%s INIT_PROJ ", buf); + if (m & UL_ITR) + sprintf(buf, "%s INIT_TR ", buf); + if (m & UL_SPROJ) + sprintf(buf, "%s STREAMLINE ", buf); + if (m & UL_SSTCL) + sprintf(buf, "%s STENCIL ", buf); + if (m == 0) + sprintf(buf, "NO"); + if (m == 1) + sprintf(buf, "YES"); + return (buf); +} + double get_restart_time(char *filename, char *itimeunits) { double restart_time = NaN; int cdfid = 0; diff --git a/model/hd-us/inputs/readparam_t.c b/model/hd-us/inputs/readparam_t.c index e28f781..0359eac 100644 --- a/model/hd-us/inputs/readparam_t.c +++ b/model/hd-us/inputs/readparam_t.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: readparam_t.c 7405 2023-10-05 02:07:11Z her127 $ + * $Id: readparam_t.c 7562 2024-05-27 05:00:50Z her127 $ * */ @@ -187,9 +187,12 @@ FILE *fp; } if (params->trasc & LAGRANGE) params->ntr += 1; /* Volume error */ + read_ultimate(params, fp); + /* sprintf(keyword, "ULTIMATE"); if (prm_read_char(fp, keyword, buf)) params->ultimate = is_true(buf); + */ /* Runge-Kutta stages */ sprintf(keyword, "RUNGE-KUTTA"); prm_read_int(fp, keyword, ¶ms->rkstage); @@ -229,6 +232,8 @@ FILE *fp; if (strcmp(buf, "SUB-STEP-TRACER") == 0) params->stab = SUB_STEP_TRACER; } + sprintf(keyword, "SUB-STEP_SAFETY"); + prm_read_double(fp, keyword, ¶ms->trsf); params->thin_merge = 0; sprintf(keyword, "MERGE_THIN"); if (prm_read_char(fp, keyword, buf)) diff --git a/model/hd-us/inputs/sourcesink.c b/model/hd-us/inputs/sourcesink.c index 9f2c9fe..9e12e43 100644 --- a/model/hd-us/inputs/sourcesink.c +++ b/model/hd-us/inputs/sourcesink.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: sourcesink.c 6494 2020-03-26 01:18:52Z her127 $ + * $Id: sourcesink.c 7510 2024-03-11 22:39:58Z her127 $ * */ @@ -24,7 +24,7 @@ #include #include -static int parse_ss_location(pss_t *p, char *line); +static int parse_ss_location(pss_t *p, char *line, int wn); static void pss_locate(pss_t *p, double t); void ref_depth(geometry_t *window, window_t *windat, win_priv_t *wincon, @@ -40,9 +40,11 @@ int pss_init_region_w(geometry_t *window, char *dname, pss_t *pss); void write_auto_pss(char *key, char *t_units); void hd_pss_read(char *name, char *t_units, pss_t **pss, int *np, void *data, - int (*xyzijk) (void *, double, double, double, int *, int *, - int *), int (*trI) (void *, char *), int (*pss_vec) (void *, pss_t *), - int (*pss_region) (void *, char *, pss_t *)); + int (*xyzijk) (void *, double, double, double, int *, + int *, int *), int (*trI) (void *, char *), + int (*pss_vec) (void *, pss_t *), + int (*pss_region) (void *, char *, pss_t *), + int wn); /*-------------------------------------------------------------------*/ /* Initialisation routine for sources and sinks - called once at */ @@ -65,7 +67,7 @@ void sourcesink_init(parameters_t *params, /* Input parameters */ /* Initialise point source/sinks on the master */ hd_pss_read(params->prmname, master->timeunit, &master->pss, &master->npss, master, hd_xyztoindex_m, hd_get_tracer_index_m, - hd_pss_vec_m, hd_pss_region_m); + hd_pss_vec_m, hd_pss_region_m, 0); for (s = 0; s < master->npss; s++) { pss_t *pss = &master->pss[s]; @@ -123,8 +125,7 @@ void sourcesink_init(parameters_t *params, /* Input parameters */ hd_pss_read(params->prmname, master->timeunit, &wincon[n]->pss, &wincon[n]->npss, window[n], hd_xyztoindex_w, hd_get_tracer_index_w, hd_pss_vec_w, - hd_pss_region_w); - + hd_pss_region_w, n); if (wincon[n]->npss) { m += wincon[n]->npss; for (s = 0; s < wincon[n]->npss; s++) { @@ -724,7 +725,6 @@ void ss_tracer(geometry_t *window, /* Window geometry */ /* Fraction of flow in this cell */ frac = (zhigh - zlow) / pdz; - /* If this source/sink is not associated with temp or */ /* sal and a volume influx is specified (trf=CONC_NOTR) */ /* then assume tracer is input with the ambient water */ @@ -759,6 +759,7 @@ void ss_tracer(geometry_t *window, /* Window geometry */ c = window->zm1[c]; } } else { + if(p->flag & PSS_AR) continue; if (p->watertsid >= 0) { if (master->trasc == FFSL && master->conserve & CONS_PSS) val = windat->waterss[c] * window->cellarea[c2]; @@ -852,7 +853,8 @@ void hd_pss_read(char *name, /* File name */ int (*xyzijk) (void *, double, double, double, int *, int *, int *), int (*trI) (void *, char *), int (*pss_vec) (void *, pss_t *), - int (*pss_region) (void *, char *, pss_t *) + int (*pss_region) (void *, char *, pss_t *), + int wn ) { FILE* fp; @@ -908,6 +910,7 @@ void hd_pss_read(char *name, /* File name */ for (i = 0, j = 0; i < npss; i++) { p[j].vc = 0; + p[j].flag = 0; /* Store index routine pointer and data needed by it */ p[j].xyzijk = xyzijk; @@ -928,7 +931,7 @@ void hd_pss_read(char *name, /* File name */ sprintf(key, "pss%d.location", i); prm_read_char(fpp, key, buf); } - if (!parse_ss_location(&p[j], buf)) + if (!parse_ss_location(&p[j], buf, wn)) continue; /* Regions or blocks */ @@ -937,12 +940,16 @@ void hd_pss_read(char *name, /* File name */ if (prm_read_char(fpp, key, buf)) { if (!((*pss_region) (model_data, buf, &p[j]))) continue; + else + p[j].flag |= PSS_AR; } sprintf(key, "pss%d.ncells", i); if (prm_skip_to_end_of_key(fpp, key)) { read_blocks(fpp, key, &p[j].vc, &p[j].iloc, &p[j].jloc, NULL); if (!((*pss_vec) (model_data, &p[j]))) continue; + else + p[j].flag |= PSS_AR; } } @@ -964,7 +971,6 @@ void hd_pss_read(char *name, /* File name */ } /* Flags */ - p[j].flag = 0; sprintf(key, "pss%1d.flag", i); if (prm_read_char(fpp, key, buf)) { if (contains_token(buf, "NONE") != NULL) { @@ -1066,7 +1072,7 @@ static void pss_locate(pss_t *p, double t) p->name, p->x, p->y, p->z); } -static int parse_ss_location(pss_t *p, char *line) +static int parse_ss_location(pss_t *p, char *line, int wn) { int e1, e2, e3; FILE *fp; @@ -1078,14 +1084,15 @@ static int parse_ss_location(pss_t *p, char *line) /* Time independent, range of z values */ if (sscanf(line, "%lf %lf %lf %lf", &p->x, &p->y, &p->zlow, &p->zhigh) == 4) { + if (p->zlow >= p->zhigh) hd_quit("pss_read: %s has bad z range (must be low then high)\n", p->name); p->z = (p->zlow + p->zhigh) / 2; if ((*(p->xyzijk)) (p->model_data, p->x, p->y, p->z, &e1, &e2, &e3) <= 0) { - emstag(LWARN,"pss_read","%s location (%.10g,%.10g,%.10g) can't be converted to indices\n", - p->name, p->x, p->y, p->z); + emstag(LWARN,"pss_read","window%d %s location (%.10g,%.10g,%.10g) can't be converted to indices\n", + wn, p->name, p->x, p->y, p->z); /* warn ("pss_read: %s location (%.10g,%.10g,%.10g) can't be converted to indices\n", @@ -1100,13 +1107,14 @@ static int parse_ss_location(pss_t *p, char *line) p->e1[0] = e1; p->e2[0] = e2; p->e3[0] = e3; + p->flag |= (PSS_AP|PSS_DR); } /* Time independent, single z value */ else if (sscanf(line, "%lf %lf %lf", &p->x, &p->y, &p->z) == 3) { if ((*(p->xyzijk)) (p->model_data, p->x, p->y, p->z, &e1, &e2, &e3) <= 0) { - emstag(LTRACE,"pss_read","%s location (%.10g,%.10g,%.10g) can't be converted to indices\n", - p->name, p->x, p->y, p->z); + emstag(LTRACE,"pss_read","window%d %s location (%.10g,%.10g,%.10g) can't be converted to indices\n", + wn, p->name, p->x, p->y, p->z); /* warn ("pss_read: %s location (%.10g,%.10g,%.10g) can't be converted to indices\n", @@ -1123,6 +1131,7 @@ static int parse_ss_location(pss_t *p, char *line) p->e3[0] = e3; p->zlow = p->z; p->zhigh = p->z; + p->flag |= (PSS_AP|PSS_DP); } else if (sscanf(line, "%lf %lf", &p->zlow, &p->zhigh) == 2) { if (p->zlow >= p->zhigh) @@ -1130,6 +1139,7 @@ static int parse_ss_location(pss_t *p, char *line) p->name); p->z = (p->zlow + p->zhigh) / 2; p->x = p->y = NOTVALID; + p->flag |= PSS_DR; } /* Time dependent, all coords from a time series */ else if ((fp = fopen(line, "r+")) != NULL) { @@ -1156,6 +1166,7 @@ static int parse_ss_location(pss_t *p, char *line) p->e1 = i_alloc_1d(p->vc); p->e2 = i_alloc_1d(p->vc); p->e3 = i_alloc_1d(p->vc); + p->flag |= PSS_F; /* Set location at some arbitrary initial time. Probably don't really need to do this here, as any routines using the structure should call the location routine themselves. */ diff --git a/model/hd-us/master/hd_init.c b/model/hd-us/master/hd_init.c index 676f0fe..41f7079 100644 --- a/model/hd-us/master/hd_init.c +++ b/model/hd-us/master/hd_init.c @@ -15,7 +15,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: hd_init.c 7464 2023-12-13 03:52:01Z her127 $ + * $Id: hd_init.c 7563 2024-05-27 05:01:21Z her127 $ * */ @@ -610,6 +610,7 @@ void compute_constants(parameters_t *params, /* Parameter structure */ strcpy(master->u1vhci, params->u1vhc); strcpy(master->u1khci, params->u1khc); master->stab = params->stab; + master->trsf = params->trsf; master->thin_merge = params->thin_merge; master->sigma = params->sigma; master->nonlinear = params->nonlinear; diff --git a/model/hd-us/master/pp_us.c b/model/hd-us/master/pp_us.c index 66e01a4..8749856 100644 --- a/model/hd-us/master/pp_us.c +++ b/model/hd-us/master/pp_us.c @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: pp_us.c 7434 2023-10-25 01:29:54Z her127 $ + * $Id: pp_us.c 7538 2024-05-03 05:41:46Z riz008 $ * */ @@ -3273,7 +3273,7 @@ void build_sparse_grid_us(parameters_t *params, open->t_transfer = NULL; if (open->ntt) { open->ttsz = open->no3_t + i * open->no3_e1 + 1; - open->t_transfer = d_alloc_2d(open->ttsz, open->ntt); + open->t_transfer = d_alloc_2d(open->ttsz+1, open->ntt); open->t_imap = i_alloc_2d(i + 1, open->ttsz); } } @@ -4660,7 +4660,7 @@ void make_geom_obc(parameters_t *params, geometry_t *sgrid, int laus, int *maske open->t_transfer = NULL; if (open->ntt) { open->ttsz = open->no3_t + i * open->no3_e1 + 1; - open->t_transfer = d_alloc_2d(open->ttsz, open->ntt); + open->t_transfer = d_alloc_2d(open->ttsz+1, open->ntt); open->t_imap = i_alloc_2d(i + 1, open->ttsz); } } diff --git a/model/hd-us/outputs/df_ugrid.c b/model/hd-us/outputs/df_ugrid.c index b784a68..09c676a 100644 --- a/model/hd-us/outputs/df_ugrid.c +++ b/model/hd-us/outputs/df_ugrid.c @@ -17,7 +17,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: df_ugrid.c 7336 2023-04-11 02:35:01Z her127 $ + * $Id: df_ugrid.c 7553 2024-05-16 03:40:07Z riz008 $ * */ @@ -575,6 +575,13 @@ void write_dump_attributes_ugrid(dump_data_t *dumpdata, int cdfid, tracer_write_nc(cdfid, dumpdata->ntrS, dumpdata->trinfo_2d, 2, attr); } + { + tracer_att_t attr[] = { {"tracer_sed", "true"}, + {"coordinates", "t, Mesh2_face_x, Mesh2_face_y, Mesh2_layers_sed"} + }; + tracer_write_nc(cdfid, dumpdata->nsed, dumpdata->trinfo_sed, 2, attr); + } + if (dumpdata->nvars) { int i, j, dims[10]; char buf[MAXSTRLEN]; @@ -1104,6 +1111,13 @@ void write_dump_attributes_ugrid3(dump_data_t *dumpdata, dump_file_t *df, int cd tracer_write_nc(cdfid, dumpdata->ntrS, dumpdata->trinfo_2d, 2, attr); } + { + tracer_att_t attr[] = { {"tracer_sed", "true"}, + {"coordinates", "t, Mesh2_face_x, Mesh2_face_y, Mesh2_layers_sed"} + }; + tracer_write_nc(cdfid, dumpdata->nsed, dumpdata->trinfo_sed, 2, attr); + } + /* global attributes */ write_text_att(cdfid, NC_GLOBAL, "title", codeheader); write_text_att(cdfid, NC_GLOBAL, "paramhead", parameterheader); @@ -1547,11 +1561,11 @@ void df_ugrid_write(dump_data_t *dumpdata, dump_file_t *df, double t) if (dumpdata->sednz > 0) { count[1] = dumpdata->sednz; count[2] = df->nface2; - pack_ugrids(dumpdata->sednz - 1, count[2], geom->cellz_sed, dumpdata->wc, oset); + pack_ugrids(dumpdata->sednz, count[2], geom->cellz_sed, dumpdata->wc, oset); nc_d_writesub_2d(fid, ncw_var_id(fid, "Mesh2_layers_sed"), start, count, dumpdata->wc); count[1] = dumpdata->sednz + 1; - pack_ugrids(dumpdata->sednz, count[2], geom->gridz_sed, dumpdata->wc, oset); + pack_ugrids(dumpdata->sednz+1, count[2], geom->gridz_sed, dumpdata->wc, oset); nc_d_writesub_2d(fid, ncw_var_id(fid, "Mesh2_layerfaces_sed"), start, count, dumpdata->wc); } diff --git a/model/hd-us/outputs/dumpdata.c b/model/hd-us/outputs/dumpdata.c index 836d4f0..1501e7f 100644 --- a/model/hd-us/outputs/dumpdata.c +++ b/model/hd-us/outputs/dumpdata.c @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dumpdata.c 7376 2023-07-26 04:35:19Z her127 $ + * $Id: dumpdata.c 7554 2024-05-16 03:40:54Z riz008 $ * */ @@ -23,6 +23,7 @@ void sigma_vmap(master_t *master, dump_data_t *dumpdata); static int contains_string(char *p, char *tag); +void dumpdata_alloc_stgrid(dump_data_t *dumpdata); /*-------------------------------------------------------------------*/ /* Routine to allocate memory for the parameter data structure */ @@ -296,6 +297,11 @@ dump_data_t *dumpdata_build(parameters_t *params, /* Input parameter data dumpdata->we = d_alloc_2d(geom->szeS, geom->nz); dumpdata->wv = d_alloc_2d(geom->szvS, geom->nz); + /* Check to ensure sediments layers are always less than water column */ + if (sednz) + if (sednz+1 > geom->nz) + hd_quit("dumpdata: dumpdata->wc buffer not large enough for Mesh2_layerfaces_sed\n"); + /* Layer geometry and flags */ for (k = 0; k < nz; k++) { /* Assume the upper most layer is the sea surface at zero */ @@ -454,6 +460,139 @@ void dumpdata_init_geom(parameters_t *params, geometry_t *geom, dump_data_t *dum /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +void dumpdata_alloc_stgrid(dump_data_t *dumpdata) +{ + int nce1 = dumpdata->nce1; + int nce2 = dumpdata->nce2; + int nz = dumpdata->nz; + int sednz = dumpdata->sednz; + int nfe1 = dumpdata->nfe1; + int nfe2 = dumpdata->nfe2; + + dumpdata->gridx = d_alloc_2d(nce1 + 1, nce2 + 1); + dumpdata->gridy = d_alloc_2d(nce1 + 1, nce2 + 1); + dumpdata->cellx = d_alloc_2d(nce1, nce2); + dumpdata->celly = d_alloc_2d(nce1, nce2); + if (sednz) { + dumpdata->gridz_sed = d_alloc_3d(nce1, nce2, sednz + 1); + dumpdata->cellz_sed = d_alloc_3d(nce1, nce2, sednz); + dumpdata->cellzcsed = d_alloc_1d(sednz); + } + /* + dumpdata->u1x = d_alloc_2d(nce1 + 1, nce2); + dumpdata->u1y = d_alloc_2d(nce1 + 1, nce2); + dumpdata->u2x = d_alloc_2d(nce1, nce2 + 1); + dumpdata->u2y = d_alloc_2d(nce1, nce2 + 1); + */ + dumpdata->botz = d_alloc_2d(nce1, nce2); + /* + dumpdata->h1au1 = d_alloc_2d(nce1 + 1, nce2); + dumpdata->h1au2 = d_alloc_2d(nce1, nce2 + 1); + dumpdata->h1acell = d_alloc_2d(nce1, nce2); + dumpdata->h1agrid = d_alloc_2d(nce1 + 1, nce2 + 1); + dumpdata->thetau1 = d_alloc_2d(nce1 + 1, nce2); + dumpdata->h2au1 = d_alloc_2d(nce1 + 1, nce2); + dumpdata->h2au2 = d_alloc_2d(nce1, nce2 + 1); + dumpdata->h2acell = d_alloc_2d(nce1, nce2); + dumpdata->h2agrid = d_alloc_2d(nce1 + 1, nce2 + 1); + dumpdata->thetau2 = d_alloc_2d(nce1, nce2 + 1); + dumpdata->coriolis = d_alloc_2d(nce1, nce2); + */ + /* Time dependent dump variables */ + /* + dumpdata->u1av = d_alloc_2d(nfe1, nce2); + dumpdata->u2av = d_alloc_2d(nce1, nfe2); + dumpdata->wind1 = d_alloc_2d(nfe1, nce2); + dumpdata->wind2 = d_alloc_2d(nce1, nfe2); + dumpdata->wtop = d_alloc_2d(nce1, nce2); + dumpdata->topz = d_alloc_2d(nce1, nce2); + dumpdata->eta = d_alloc_2d(nce1, nce2); + dumpdata->patm = d_alloc_2d(nce1, nce2); + dumpdata->u1 = d_alloc_3d(nfe1, nce2, nz); + dumpdata->u2 = d_alloc_3d(nce1, nfe2, nz); + dumpdata->w = d_alloc_3d(nce1, nce2, nz); + dumpdata->u = d_alloc_3d(nce1, nce2, nz); + dumpdata->v = d_alloc_3d(nce1, nce2, nz); + */ + /* + * Optional velocity means, volume fluxes and streamline origin and + * courant numbers + */ + /* + if (master->u1m) + dumpdata->u1m = d_alloc_3d(nfe1, nce2, nz); + if (master->u2m) + dumpdata->u2m = d_alloc_3d(nce1, nfe2, nz); + */ + /* u1vm is now an edge centered state variable + if (master->u1vm) + dumpdata->u1vm = d_alloc_3d(nfe1, nce2, nz); + if (master->u2vm) + dumpdata->u2vm = d_alloc_3d(nce1, nfe2, nz); + */ + /* + if (master->origin) + dumpdata->origin = d_alloc_3d(nce1, nce2, nz); + if (master->pc) + dumpdata->pc = d_alloc_3d(nce1, nce2, nz); + if (master->qc) + dumpdata->qc = d_alloc_3d(nce1, nce2, nz); + if (master->rc) + dumpdata->rc = d_alloc_3d(nce1, nce2, nz); + + dumpdata->dzu1 = d_alloc_3d(nfe1, nce2, nz); + dumpdata->dzu2 = d_alloc_3d(nce1, nfe2, nz); + dumpdata->dzcell = d_alloc_3d(nfe1, nfe2, nz); + dumpdata->u1vh = d_alloc_2d(nce1, nce2); + dumpdata->u2vh = d_alloc_2d(nce1, nce2); + dumpdata->u1bot = d_alloc_2d(nfe1, nce2); + dumpdata->u2bot = d_alloc_2d(nce1, nfe2); + if (master->ntr) + dumpdata->tr_wc = d_alloc_4d(nce1, nce2, nz, dumpdata->ntr); + if (master->ntrS) + dumpdata->tr_wcS = d_alloc_3d(nce1, nce2, master->ntrS); + if (sednz && master->nsed) + dumpdata->tr_sed = d_alloc_4d(nce1, nce2, geom->sednz, master->nsed); + */ + /* Time dependent diagnostic dump variables */ + /* + dumpdata->dens = d_alloc_3d(nce1, nce2, nz); + dumpdata->dens_0 = d_alloc_3d(nce1, nce2, nz); + dumpdata->Kz = d_alloc_3d(nce1, nce2, nz); + dumpdata->Vz = d_alloc_3d(nce1, nce2, nz); + dumpdata->Cd = d_alloc_2d(nce1, nce2); + */ + /* Cell mapping */ + /* + dumpdata->crci = s_alloc_1d(nce1); + dumpdata->clci = s_alloc_1d(nce1); + dumpdata->frci = s_alloc_1d(nce1); + dumpdata->flci = s_alloc_1d(nce1); + dumpdata->crfi = s_alloc_1d(nfe1); + dumpdata->clfi = s_alloc_1d(nfe1); + dumpdata->frfi = s_alloc_1d(nfe1); + dumpdata->flfi = s_alloc_1d(nfe1); + dumpdata->cfcj = s_alloc_1d(nce2); + dumpdata->cbcj = s_alloc_1d(nce2); + dumpdata->ffcj = s_alloc_1d(nce2); + dumpdata->fbcj = s_alloc_1d(nce2); + dumpdata->cffj = s_alloc_1d(nfe2); + dumpdata->cbfj = s_alloc_1d(nfe2); + dumpdata->fffj = s_alloc_1d(nfe2); + dumpdata->fbfj = s_alloc_1d(nfe2); + */ + /* Dummies, maps */ + dumpdata->flag = (unsigned long ***)l_alloc_3d(nfe1, nfe2, nz); + dumpdata->w2 = d_alloc_2d(nfe1, nfe2); + dumpdata->w3 = d_alloc_3d(nfe1, nfe2, nz); +} + +/* END dumpdata_alloc_stgrid() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Routine to free memory in the dump data structure */ /*-------------------------------------------------------------------*/ @@ -556,6 +695,92 @@ void dumpdata_cleanup(dump_data_t *dumpdata, /* Dump data structure */ /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Creates a regular structured grid that encompasses the */ +/* unstructured mesh and sets up geographic arrays. */ +/*-------------------------------------------------------------------*/ +void dumpadata_create_stgrid(dump_data_t *dumpdata, double r) +{ + master_t *master = dumpdata->master; + geometry_t *geom = master->geom; + double dx = r; + double dy = r; + double lat, lon; + int nce1, nce2; + int n, i, j, cc, c; + double mnlon, mxlon, mnlat, mxlat; + GRID_SPECS *gs = NULL; + double *x, *y, *b; + double deg2m = 60.0 * 1852.0; + point *p; + poly_t *pl; + + /* Get the geographic bounds */ + mnlon = mnlat = HUGE; + mxlon = mxlat = -HUGE; + for (cc = 1; cc <= geom->b2_t; cc++) { + c = geom->w2_t[cc]; + mnlon = min(mnlon, geom->cellx[c]); + mnlat = min(mnlat, geom->celly[c]); + mxlon = max(mxlon, geom->cellx[c]); + mxlat = max(mxlat, geom->celly[c]); + } + nce1 = (int)((mxlon - mnlon) * deg2m / dx); + nce2 = (int)((mxlat - mnlat) * deg2m / dy); + dumpdata->nce1 = nce1; + dumpdata->nce2 = nce2; + dumpdata->nfe1 = nce1 + 1; + dumpdata->nfe2 = nce2 + 1; + + /* Allocate */ + dumpdata->ij2c = i_alloc_2d(dumpdata->nfe1, dumpdata->nfe2); + dumpdata_alloc_stgrid(dumpdata); + + /* Set the grid */ + for(j = 0; j < nce2; j++) { + lat = mnlat + (double)j * dy / deg2m; + for(i = 0; i < nce1; i++) { + lon = mnlon + (double)i * dx / deg2m; + dumpdata->cellx[j][i] = lon; + dumpdata->celly[j][i] = lat; + } + } + + /* Find the concave hull of the mesh */ + pl = poly_create(); + p = concave_hull(geom->d, NULL, &n, NULL, NULL); + for (j = 0; j < n; j++) + poly_add_point(pl, p[j].x, p[j].y); + for(j = 0; j < nce2; j++) { + for(i = 0; i < nce1; i++) { + if (poly_contains_point(pl, dumpdata->cellx[j][i], dumpdata->celly[j][i])) + dumpdata->flag[geom->nz-1][j][i] = ALLWATER; + else + dumpdata->flag[geom->nz-1][j][i] = SOLID; + } + } + poly_destroy(pl); + + /* Set the bathymetry */ + gs = grid_interp_init(geom->cellx, geom->celly, geom->botz, + geom->b2_t, "linear"); + for(j = 0; j < nce2; j++) { + for(i = 0; i < nce1; i++) { + if (!(dumpdata->flag[geom->nz-1][j][i] & SOLID)) { + dumpdata->botz[j][i] = grid_interp_on_point(gs, + dumpdata->cellx[j][i], + dumpdata->celly[j][i]); + } else + dumpdata->botz[j][i] = NaN; + } + } + grid_specs_destroy(gs); +} + +/* END dumpadata_create_stgrid() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Routine to populate the dump structure with 2d data */ /*-------------------------------------------------------------------*/ diff --git a/model/hd-us/outputs/dumpfile.c b/model/hd-us/outputs/dumpfile.c index 7991dd9..fe70669 100644 --- a/model/hd-us/outputs/dumpfile.c +++ b/model/hd-us/outputs/dumpfile.c @@ -16,7 +16,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dumpfile.c 7337 2023-04-11 02:35:26Z her127 $ + * $Id: dumpfile.c 7575 2024-05-30 03:44:13Z riz008 $ * */ @@ -69,6 +69,63 @@ typedef struct { void* data ; /* Netcdf record number */ } df_dispatch_data_t; +typedef struct { + GRID_SPECS **gs; /* Grid spec for parray interpolation */ + delaunay **d; /* Delaunay data structure for centre interpolation */ + int *cells; /* Cell locations used in the interpolation */ + int *ids; /* Delaunay indices used in the interpolation */ + int *c2k; /* Interpolation cells to layer map */ + int *botk; /* Bottom layer number for cell centres */ + int ncells; /* Number of cells used in the interpolation */ + int nz; /* Surface layer number */ + int ncs; /* Cell centres in surface layer */ + int nc; /* Total cell centres */ + + GRID_SPECS **ge; /* Grid spec for parray interpolation */ + delaunay **de; /* Delaunay data structure for edge interpolation */ + int *eells; /* Edge locations used in the interpolation */ + int *eds; /* Delaunay indices used in the interpolation */ + int *e2k; /* Interpolation edge to layer map */ + int *botek; /* Bottom layer number for edges */ + int neells; /* Number of edgess used in the interpolation */ + int nes; /* Edges in surface layer */ + int ec; /* Total edge centres */ +} df_interp_t; + +typedef struct { + void **v; /* Pointer to values */ + int ndims; /* Number of spatial dimensions */ + char name[MAXSTRLEN]; /* df_variable_t name */ + char units[MAXSTRLEN]; /* df_variable_t units */ + char long_name[MAXSTRLEN]; /* Descriptive name */ + int vector_mode; /* NONE, east, north, mag, dirn */ + nc_type fptype; /* Data type. */ + double valid_range[2]; /* df_variable_t valid range */ + double scale; /* Scale factor */ + double offset; /* Scale factor */ + int xylocation[2]; + int zlocation[2]; + int sediment; /* Sediment flag */ + void **data[2]; /* Pointer to values (scalar or vector) */ + +} df_parray_var_t; + + +/* Structure to describe each dump file. Also used for memory output */ +typedef struct { + int type; /* DF_PARRAY or DF_MEMORY */ + int fid; /* Netcdf file id */ + int nextrec; /* Netcdf record number */ + df_parray_var_t *vars; /* List of point array dump file variables */ + df_mempack_t *data; /* Output data structure for memory output */ + df_interp_t *interp; /* Interpolation data */ +} df_parray_data_t; + +INLINE double *get_data(df_parray_var_t *var) +{ + void *p = var->v; + return (*(double **)p); +} /* Prototypes for routines below */ static int read_subsection_range(FILE * fp, char *key, int nc, int nf, @@ -84,6 +141,12 @@ void write_text_att(int cdfid, int varid, const char *name, int find_next_restart_record(dump_file_t *df, int cdfid, char *timevar, double tref); void df_null_reset(dump_data_t *dumpdata, dump_file_t *df, double t); +static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, + int fid); +static double get_var_value(dump_file_t *df, df_parray_var_t *var, + df_interp_t *interp, int id, int i, int k); +static void df_interp_init_data(dump_data_t *dumpdata, dump_file_t *df, + df_interp_t *interp, int af); /* History output file prototypes */ static void *df_restart_create(dump_data_t *dumpdata, dump_file_t *df); @@ -131,8 +194,24 @@ void df_filter_2d(dump_file_t *df, dump_data_t *dumpdata, double **a, void df_filter_3d(dump_file_t *df, dump_data_t *dumpdata, double ***a, int is, int ie, int js, int je, int ks, int ke); -void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df); -void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df); +void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df, df_interp_t *interp); +void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df, df_interp_t *interp); +int df_parray_get_varinfo(dump_data_t *dumpdata, dump_file_t *df, + char *name, df_parray_var_t *var); +static void df_interp_writesub_2d(dump_data_t *dumpdata, + dump_file_t *df, df_interp_t *interp, double *values); +static void df_interp_writesub_3d(dump_data_t *dumpdata, + dump_file_t *df, df_interp_t *interp, double *values); +static void df_interp_writevec_2d(dump_data_t *dumpdata, + dump_file_t *df, df_interp_t *interp, double *values); +static void df_interp_writevec_3d(dump_data_t *dumpdata, + dump_file_t *df, df_interp_t *interp, double *values); +static void df_interp_writevec_cen_2d(dump_data_t *dumpdata, dump_file_t *df, + df_interp_t *interp, double *values_i, + double *values_j); +static void df_interp_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, + df_interp_t *interp, double *values_i, + double *values_j); /* Memory output prototypes */ void *df_memory_create(dump_data_t *dumpdata, dump_file_t *df); @@ -190,7 +269,8 @@ double dump_event(sched_event_t *event, double t) master_t *master = (master_t *)schedGetPublicData(event); geometry_t *geom = master->geom; dump_data_t *dumpdata = master->dumpdata; - df_dispatch_data_t* dispatch_data = malloc(sizeof(df_dispatch_data_t)); + df_dispatch_data_t* dispatch_data = + (df_dispatch_data_t*)schedGetPrivateData(event); int debug = 0; /* Output dump and test point values if required */ @@ -216,9 +296,13 @@ double dump_event(sched_event_t *event, double t) } #endif + if (dispatch_data == NULL) { + dispatch_data = (df_dispatch_data_t*)calloc(1,sizeof(df_dispatch_data_t)); + schedSetPrivateData(event,dispatch_data); + } + dumpdata_fill(geom, master, dumpdata); dispatch_data->t = t; - schedSetPrivateData(event,dispatch_data); /* Run through the list of dumpfiles and find the the one that */ /* next fires off - after the current one @@ -1139,6 +1223,8 @@ void read_dumpfiles(FILE *fp, dump_file_t *list, dump_data_t *dumpdata, if (!(dumpdata->us_type & US_IJ) && (strcmp(buf, "ugrid") != 0 && strcmp(buf, "sparse") != 0 && strcmp(buf, "parray") != 0 && + strcmp(buf, "simple") != 0 && + strcmp(buf, "simple_cf") != 0 && strcmp(buf, "memory") != 0)) hd_quit("read_dumpfiles: Unstructured grid can only dump filetype 'ugrid' or 'parray'.\n"); @@ -1159,6 +1245,17 @@ void read_dumpfiles(FILE *fp, dump_file_t *list, dump_data_t *dumpdata, hd_quit("dumpfile_init: '%s' is unknown output type.\n", buf); if (strcmp(list->type, "memory") == 0) list->flag |= DF_MPK; + if (!(dumpdata->us_type & US_IJ) && + (strcmp(list->type, "simple") == 0 || strcmp(list->type, "simple_cf") == 0) && + !(list->flag & DF_STO)) { + double res; + sprintf(key, "file%d.resolution", f); + if (!(prm_read_double(fp, key, &res))) + res = sqrt(master->amean); + dumpadata_create_stgrid(dumpdata, res); + list->flag |= DF_STO; + } + /* Read the time 2-way nest sync time increment */ sprintf(key, "file%d.sync_dt", f); if (prm_read_char(fp, key, buf)) { @@ -1334,9 +1431,9 @@ void read_dumpfiles(FILE *fp, dump_file_t *list, dump_data_t *dumpdata, /* Overwrite file name for chunking */ set_chunk_name(dumpdata, list, t); - if (!(params->runmode & PRE_MARVL)) list->private_data = list->create(dumpdata, list); + list->finished = 0; } @@ -2895,8 +2992,12 @@ typedef struct { int fid; /* Netcdf file id */ int nextrec; /* Netcdf record number */ df_simple_var_t *vars; /* List of simple dump file variables */ + df_parray_var_t *varsp; /* List of point array dump file variables */ + df_interp_t *interp; /* Interpolation data */ } df_simple_data_t; +static void df_simplep_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, + int n, double *values); static void df_simple_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, int vid); static void df_simple_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, @@ -2943,7 +3044,7 @@ static void *df_simple_create(dump_data_t *dumpdata, dump_file_t *df) // Get the nc_mode flag nc_mode = get_nc_mode(df); - + /* create the netCDF file */ if (ncw_create(df->name, nc_mode, &cdfid) != NC_NOERR) hd_quit("dumpfile_create: Couldn't create output dump file %s\n", @@ -3037,6 +3138,30 @@ static void *df_simple_create(dump_data_t *dumpdata, dump_file_t *df) write_text_att(cdfid, vid, "coordinate_type", "Z"); } + if (df->flag & DF_STO) { + master_t *master = dumpdata->master; + geometry_t *geom = master->geom; + int i, j, cc, c; + df->npoints = 0; + for (j = 0; j < dumpdata->nce2; j++) + for (i = 0; i < dumpdata->nce1; i++) + if (!(dumpdata->flag[dumpdata->nz-1][j][i] & SOLID)) + df->npoints += 1; + df->x = d_alloc_1d(df->npoints); + df->y = d_alloc_1d(df->npoints); + c = 0; + for (j = 0; j < dumpdata->nce2; j++) { + for (i = 0; i < dumpdata->nce1; i++) { + if (!(dumpdata->flag[dumpdata->nz-1][j][i] & SOLID)) { + df->x[c] = dumpdata->cellx[j][i]; + df->y[c] = dumpdata->celly[j][i]; + dumpdata->ij2c[j][i] = c; + c++; + } + } + } + } + df_simple_init_data(dumpdata, df, cdfid); data = (df_simple_data_t *)df->private_data; @@ -3044,8 +3169,8 @@ static void *df_simple_create(dump_data_t *dumpdata, dump_file_t *df) if (!data->vars[n].sediment && data->vars[n].ndims == 2) { dims[1] = jid; dims[2] = iid; - ncw_def_var2(df->name, cdfid, data->vars[n].name, data->vars[n].fptype, 3, dims, &vid, df->compress); + if (is_geog) write_text_att(cdfid, vid, "coordinates", "time, latitude, longitude"); @@ -3306,6 +3431,30 @@ static void *df_simple_cf_create(dump_data_t *dumpdata, dump_file_t *df) write_text_att(cdfid, vid, "coordinate_type", "Z"); } + if (df->flag & DF_STO) { + master_t *master = dumpdata->master; + geometry_t *geom = master->geom; + int i, j, cc, c; + df->npoints = 0; + for (j = 0; j < dumpdata->nce2; j++) + for (i = 0; i < dumpdata->nce1; i++) + if (!(dumpdata->flag[dumpdata->nz-1][j][i] & SOLID)) + df->npoints += 1; + df->x = d_alloc_1d(df->npoints); + df->y = d_alloc_1d(df->npoints); + c = 0; + for (j = 0; j < dumpdata->nce2; j++) { + for (i = 0; i < dumpdata->nce1; i++) { + if (!(dumpdata->flag[dumpdata->nz-1][j][i] & SOLID)) { + df->x[c] = dumpdata->cellx[j][i]; + df->y[c] = dumpdata->celly[j][i]; + dumpdata->ij2c[j][i] = c; + c++; + } + } + } + } + df_simple_init_data(dumpdata, df, cdfid); data = (df_simple_data_t *)df->private_data; @@ -3437,10 +3586,24 @@ static void *df_simple_cf_create(dump_data_t *dumpdata, dump_file_t *df) return data; } +/*-------------------------------------------------------------------*/ +/* Write the simple file. */ +/* For STRUCTURED grids, this is a copy of the structured state */ +/* variables. The dumpdata structure makes a copy of all these */ +/* variables for output purposes (filled in dumpdata_fill()); this */ +/* is quite memory inefficient, and could be streamlined by filling */ +/* 2D or 3D dummy variables with state data on the fly. */ +/* For UNSTRUCTURED meshes (df->flag & DF_STO), state variables are */ +/* interpolated onto a structured grid with specified resolution. A */ +/* copy of state variables is not made in dumpadat, rather dummy */ +/* arrays, dumpdata->w2 & w3, are used (hence void *p is set to */ +/* these). Only cell centered variables should be specified to be */ +/* interpolated onto simple grids. */ +/*-------------------------------------------------------------------*/ static void df_simple_write(dump_data_t *dumpdata, dump_file_t *df, double t) { - int n; + int n, np; size_t start[4]; size_t count[4]; double newt = t; @@ -3470,18 +3633,22 @@ static void df_simple_write(dump_data_t *dumpdata, dump_file_t *df, for (n = 0; n < df->nvars; n++) { df_simple_var_t *var = &data->vars[n]; void *p = var->v; + if (var->ndims == 2) { start[1] = df->jlower; start[2] = df->ilower; count[1] = df->nce2; count[2] = df->nce1; - if (var->vector_mode == VM_NONE) + + if (var->vector_mode == VM_NONE) { + if (df->flag & DF_STO) p = (void **)&dumpdata->w2; df_simple_writesub_2d(dumpdata, df, n, start, count, (*((double ***)p))); - else + } else { df_simple_writevec_2d(dumpdata, df, n); + } } else if (var->ndims == 3) { - + if (df->flag & DF_STO) p = (void ***)&dumpdata->w3; if (var->sediment) { start[1] = 0; count[1] = df->nz_sed; @@ -4064,8 +4231,9 @@ static int df_simple_get_varinfo(dump_data_t *dumpdata, dump_file_t *df, static void df_simple_init_data(dump_data_t *dumpdata, dump_file_t *df, int fid) { - int i; + int i, n; df_simple_data_t *data = NULL; + df_interp_t *interp = NULL; df_parse_vars(dumpdata,df,NULL,SIMPLE_ALL_VARS); @@ -4094,6 +4262,34 @@ static void df_simple_init_data(dump_data_t *dumpdata, dump_file_t *df, } data->fid = fid; + /* Initialise the interpolation structures */ + if (df->flag & DF_STO) { + /* parray variable info. The pointers in this structure differ to */ + /* those in the simple structure, and are required for */ + /* interpolation. An interpolated simple variable must be */ + /* available in both the simple and parray variable info */ + /* structures. */ + if ((data->varsp = + (df_parray_var_t *)malloc(df->nvars * sizeof(df_parray_var_t))) == + NULL) + hd_quit("dumpfile_vars: Can't allocate memory for variable info.\n"); + for (i = 0; i < df->nvars; i++) { + df_parray_var_t *var = &data->varsp[i]; + if (df_parray_get_varinfo(dumpdata, df, df->vars[i], var) == 0) + hd_quit("dumpfile:df_simple_init_data: Unknown parray variable '%s'.", df->vars[i]); + } + + /* Set up the interpolation structure */ + interp = (df_interp_t *)malloc(sizeof(df_interp_t)); + memset(interp, 0, sizeof(df_interp_t)); + /* Check for edge vectors requiring output */ + i = 1; + for (n = 0; n < df->nvars; n++) + if (data->vars[n].vector_mode & (VM_NOR|VM_TAN)) i = 0; + df_interp_init_data(dumpdata, df, interp, i); + data->interp = interp; + } + /* Set next record to zero */ data->nextrec = 0; @@ -4122,8 +4318,13 @@ static void df_simple_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, count[1] = df->nce2; count[2] = df->nce1; - vector_component(dumpdata, vi, vj, df->ilower, df->jlower, - df->nce1, df->nce2, var->vector_mode, c); + /* Get the rotated nort/east cell centered component. */ + /* Interpolated values are already expected to be rotated and */ + /* centered (e.g. uav, vav), so this is not necessary. */ + if (!(df->flag & DF_STO)) + vector_component(dumpdata, vi, vj, df->ilower, df->jlower, + df->nce1, df->nce2, var->vector_mode, c); + df_simple_writesub_2d(dumpdata, df, vid, start, count, c); d_free_2d(c); } @@ -4150,9 +4351,11 @@ static void df_simple_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, count[1] = df->nz; count[2] = df->nce2; count[3] = df->nce1; - for (k = klower, nk = 0; k <= kupper; ++k, ++nk) - vector_component(dumpdata, vi[k], vj[k], df->ilower, df->jlower, - df->nce1, df->nce2, var->vector_mode, c[nk]); + + if (!(df->flag & DF_STO)) + for (k = klower, nk = 0; k <= kupper; ++k, ++nk) + vector_component(dumpdata, vi[k], vj[k], df->ilower, df->jlower, + df->nce1, df->nce2, var->vector_mode, c[nk]); df_simple_writesub_3d(dumpdata, df, vid, start, count, c, df->klower); d_free_3d(c); @@ -4164,6 +4367,7 @@ static void df_simple_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, { df_simple_data_t *data = (df_simple_data_t *)df->private_data; df_simple_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; /*UR-FIX for icc */ int nd = 0; unsigned int offset = 0; /* Has a record dimension = 1, else 0 */ @@ -4174,6 +4378,12 @@ static void df_simple_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, int varid = ncw_var_id(fid, df->vars[vid]); unsigned int nzm1 = dumpdata->nz - 1; + /* Interpolate onto the (i,j) location */ + if (df->flag & DF_STO) { + df_parray_var_t *varp = &data->varsp[vid]; + df_interp_writesub_2d(dumpdata, df, interp, get_data(varp)); + } + nc_inq_varndims(fid, varid, &nd); offset = (nd > 2); nvals = d_alloc_2d(count[offset + 1], count[offset]); @@ -4193,7 +4403,16 @@ static void df_simple_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, df->landfill == locate_landfill_function("default")) nvals[j][i] = SIMPLE_MISSING_VALUE; else { - nvals[j][i] = values[oj][oi]; + /* Interpolate onto the (i,j) location */ + if (df->flag & DF_STO) { + df_parray_var_t *varp = &data->varsp[vid]; + int c, id, k = dumpdata->nz-1; + c = dumpdata->ij2c[j][i]; + id = interp->ids[c]; + nvals[j][i] = get_var_value(df, varp, interp, id, c, k); + } + else + nvals[j][i] = values[oj][oi]; } } } @@ -4215,6 +4434,7 @@ static void df_simple_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, { df_simple_data_t *data = (df_simple_data_t *)df->private_data; df_simple_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; /*UR-FIX for icc */ int nd = 0; unsigned int offset = 0; /* Has a record dimension = 1, else 0 */ @@ -4224,6 +4444,11 @@ static void df_simple_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, unsigned int varid = ncw_var_id(fid, df->vars[vid]); unsigned int i, j, k; + /* Interpolate onto the (i,j) location */ + if (df->flag & DF_STO) { + df_parray_var_t *varp = &data->varsp[vid]; + df_interp_writesub_3d(dumpdata, df, interp, get_data(varp)); + } nc_inq_varndims(fid, varid, &nd); offset = (nd > 3); @@ -4247,7 +4472,16 @@ static void df_simple_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, df->landfill == locate_landfill_function("default")) nvals[k][j][i] = SIMPLE_MISSING_VALUE; else - nvals[k][j][i] = values[ok][oj][oi]; + /* Interpolate onto the (i,j) location */ + if (df->flag & DF_STO) { + df_parray_var_t *varp = &data->varsp[vid]; + int c, id; + c = dumpdata->ij2c[j][i]; + id = interp->ids[c]; + nvals[k][j][i] = get_var_value(df, varp, interp, id, c, ok); + } + else + nvals[k][j][i] = values[ok][oj][oi]; } } } @@ -4630,10 +4864,8 @@ static int contains_string(char *p, char *tag) /* Prototypes for routines below */ static void df_parray_writegeom(dump_data_t *dumpdata, dump_file_t *df); -static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, - int fid); static void df_parray_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, - int n, double *values); + int n, double *values); static void df_parray_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, int n, double *values); static void df_parray_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, @@ -4669,62 +4901,14 @@ typedef struct { int sediment; /* Flag; = 1 if variable is tracer */ } df_parray_var_old_t; -typedef struct { - void **v; /* Pointer to values */ - int ndims; /* Number of spatial dimensions */ - char name[MAXSTRLEN]; /* df_variable_t name */ - char units[MAXSTRLEN]; /* df_variable_t units */ - char long_name[MAXSTRLEN]; /* Descriptive name */ - int vector_mode; /* NONE, east, north, mag, dirn */ - nc_type fptype; /* Data type. */ - double valid_range[2]; /* df_variable_t valid range */ - double scale; /* Scale factor */ - double offset; /* Scale factor */ - int xylocation[2]; - int zlocation[2]; - int sediment; /* Sediment flag */ - void **data[2]; /* Pointer to values (scalar or vector) */ - -} df_parray_var_t; - -/* Structure to describe each dump file. Also used for memory output */ -typedef struct { - int type; /* DF_PARRAY or DF_MEMORY */ - int fid; /* Netcdf file id */ - int nextrec; /* Netcdf record number */ - df_parray_var_t *vars; /* List of point array dump file variables */ - df_mempack_t *data; /* Output data structure for memory output */ - - GRID_SPECS **gs; /* Grid spec for parray interpolation */ - delaunay **d; /* Delaunay data structure for centre interpolation */ - int *cells; /* Cell locations used in the interpolation */ - int *ids; /* Delaunay indices used in the interpolation */ - int *c2k; /* Interpolation cells to layer map */ - int *botk; /* Bottom layer number for cell centres */ - int ncells; /* Number of cells used in the interpolation */ - int nz; /* Surface layer number */ - int ncs; /* Cell centres in surface layer */ - int nc; /* Total cell centres */ - - GRID_SPECS **ge; /* Grid spec for parray interpolation */ - delaunay **de; /* Delaunay data structure for edge interpolation */ - int *eells; /* Edge locations used in the interpolation */ - int *eds; /* Delaunay indices used in the interpolation */ - int *e2k; /* Interpolation edge to layer map */ - int *botek; /* Bottom layer number for edges */ - int neells; /* Number of edgess used in the interpolation */ - int nes; /* Edges in surface layer */ - int ec; /* Total edge centres */ - -} df_parray_data_t; /* Cell corners */ static double xcorner[4] = { 0, 1, 1, 0 }; static double ycorner[4] = { 0, 0, 1, 1 }; /*UR declare here to use in write_geom */ -static double get_var_value_2d(dump_file_t *df, df_parray_var_t *var, int id, int k); -static double get_var_value(dump_file_t *df, df_parray_var_t *var, int id, int i, int k); +static double get_var_value_2d(dump_file_t *df, df_parray_var_t *var, + df_interp_t *interp, int id, int k); static double get_missing(df_parray_var_t *var); @@ -4742,13 +4926,13 @@ INLINE void set_data_vector(df_parray_var_t *var, int vm, void *data_i, var->data[0] = data_i; var->data[1] = data_j; } - +/* INLINE double *get_data(df_parray_var_t *var) { void *p = var->v; return (*(double **)p); } - +*/ INLINE double **get_data_2d(df_parray_var_t *var) { return (*(double ***)var->data[0]); @@ -5219,21 +5403,22 @@ static void df_parray_writegeom(dump_data_t *dumpdata, dump_file_t *df) /*UR-ADDED botz - write this here and not as a var */ if(params->parray_inter_botz) { - delaunay **d = data->d; + df_interp_t *interp = data->interp; + delaunay **d = interp->d; /* Fill the Delaunay structure with values */ - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; if (c == geom->m2d[c]) { - id = data->ids[i]; - k = data->c2k[i]; + id = interp->ids[i]; + k = interp->c2k[i]; d[k]->points[id].v[0] = geom->botz[c]; } } /*UR interpolate in the same fashion as other variables */ for (i = 0; i < df->npoints; ++i) { - id = data->ids[i]; - v[i] = get_var_value(df, NULL, id, i, geom->nz-1); + id = interp->ids[i]; + v[i] = get_var_value(df, NULL, data->interp, id, i, geom->nz-1); } } else { /*UR don't interpolate, just output the value of the column the location falls into*/ @@ -5503,8 +5688,9 @@ int df_parray_get_varinfo(dump_data_t *dumpdata, dump_file_t *df, static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, int fid) { - int i; + int i, n; df_parray_data_t *data = NULL; + df_interp_t *interp = NULL; df_parse_vars(dumpdata,df,PARRAY_EXCLUDE_VARS,PARRAY_ALL_VARS); @@ -5514,7 +5700,7 @@ static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, free(((df_parray_data_t *)df->private_data)->vars); free(df->private_data); } - + data = (df_parray_data_t *)malloc(sizeof(df_parray_data_t)); memset(data, 0, sizeof(df_parray_data_t)); df->private_data = data; @@ -5535,15 +5721,15 @@ static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, data->fid = fid; - /* Allocate and initialize the grid_spec structures for */ - /* interpolation. Note: two of these exist; one for cell centered */ - /* variables (which can be interpolated onto a geographic */ - /* location using the i_rule) or edge variables (which are dumped */ - /* as the nearest neighbour edge to the geographic location). */ - /* The var->vector_mode distinguished between centred and edge */ - /* variables. */ - parray_grid_init(dumpdata, df); - parray_gride_init(dumpdata, df); + /* Initialise the interpolation structures */ + interp = (df_interp_t *)malloc(sizeof(df_interp_t)); + memset(interp, 0, sizeof(df_interp_t)); + /* Check for edge vectors requiring output */ + i = 1; + for (n = 0; n < df->nvars; n++) + if (data->vars[n].vector_mode & (VM_NOR|VM_TAN)) i = 0; + df_interp_init_data(dumpdata, df, interp, i); + data->interp = interp; /* Set next record to zero */ data->nextrec = 0; @@ -5554,10 +5740,30 @@ static void df_parray_init_data(dump_data_t *dumpdata, dump_file_t *df, df->name, df->tinc); } -static double get_var_value(dump_file_t *df, df_parray_var_t *var, int id, int i, int k) +/* Allocate and initialize the grid_spec structures for */ +/* interpolation. Note: two of these exist; one for cell centered */ +/* variables (which can be interpolated onto a geographic */ +/* location using the i_rule) or edge variables (which are dumped */ +/* as the nearest neighbour edge to the geographic location). */ +/* The var->vector_mode distinguished between centred and edge */ +/* variables. */ +static void df_interp_init_data(dump_data_t *dumpdata, + dump_file_t *df, + df_interp_t *interp, + int af) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; - GRID_SPECS **gs = (var->vector_mode & (VM_NOR|VM_TAN)) ? data->ge : data->gs; + parray_grid_init(dumpdata, df, interp); + if (!af) parray_gride_init(dumpdata, df, interp); +} + +static double get_var_value(dump_file_t *df, + df_parray_var_t *var, + df_interp_t *interp, + int id, + int i, + int k) +{ + GRID_SPECS **gs = (var->vector_mode & (VM_NOR|VM_TAN)) ? interp->ge : interp->gs; double x, y; double v = 0.0; @@ -5580,30 +5786,33 @@ static double get_var_value(dump_file_t *df, df_parray_var_t *var, int id, int i return v; } -static double get_var_value_2d(dump_file_t *df, df_parray_var_t *var, int id, int k) +static double get_var_value_2d(dump_file_t *df, + df_parray_var_t *var, + df_interp_t *interp, + int id, + int k) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; GRID_SPECS **gs; double x, y; double v = 0.0; if (var == NULL) - gs = data->gs; + gs = interp->gs; else - gs = (var->vector_mode & (VM_NOR|VM_TAN)) ? data->ge : data->gs; + gs = (var->vector_mode & (VM_NOR|VM_TAN)) ? interp->ge : interp->gs; if (df->osl & (L_BAYLIN|L_BILIN)) { x = (int)id; y = (int)id; } else { - x = data->d[k]->points[id].x; - y = data->d[k]->points[id].y; + x = interp->d[k]->points[id].x; + y = interp->d[k]->points[id].y; } v = grid_interp_on_point(gs[k], x, y); if (df->osl & (L_BAYLIN|L_BILIN)) { - x = data->d[k]->points[id].x; - y = data->d[k]->points[id].y; + x = interp->d[k]->points[id].x; + y = interp->d[k]->points[id].y; } if (var != NULL && var->fptype == NC_SHORT) @@ -5612,11 +5821,15 @@ static double get_var_value_2d(dump_file_t *df, df_parray_var_t *var, int id, in return v; } -static double get_var_value_3d(dump_file_t *df, df_parray_var_t *var, int id, int k) +static double get_var_value_3d(dump_file_t *df, + df_parray_var_t *var, + df_interp_t *interp, + int id, + int k) { double v; - v = get_var_value_2d(df, var, id, k); + v = get_var_value_2d(df, var, interp, id, k); return(v); } @@ -5633,24 +5846,54 @@ static void df_parray_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *data = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &data->vars[vid]; - master_t *master = dumpdata->master; - geometry_t *geom = master->geom; - delaunay **d = data->d; - GRID_SPECS **gs = data->gs; + df_interp_t *interp = data->interp; int fid = data->fid; int varid = ncw_var_id(fid, df->vars[vid]); - int i, k, c, id; - int fi, fj; + int i, id; + int k = dumpdata->nz-1; double *v = d_alloc_1d(df->npoints); size_t start[2]; size_t count[2]; + df_interp_writesub_2d(dumpdata, df, interp, values); + + for(i = 0; i < df->npoints; i++) { + v[i] = get_missing(var); + } + + for (i = 0; i < df->npoints; ++i) { + id = interp->ids[i]; + v[i] = get_var_value(df, var, interp, id, i, k); + } + + start[0] = data->nextrec; + start[1] = 0; + count[0] = 1; + count[1] = df->npoints; + + nc_put_vara_double(fid, varid, start, count, v); + + d_free_1d(v); + +} + +static void df_interp_writesub_2d(dump_data_t *dumpdata, + dump_file_t *df, + df_interp_t *interp, + double *values) +{ + master_t *master = dumpdata->master; + geometry_t *geom = master->geom; + delaunay **d = interp->d; + GRID_SPECS **gs = interp->gs; + int i, k, c, id; + /* Fill the Delaunay structure with values */ - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; if (c == geom->m2d[c]) { - id = data->ids[i]; - k = data->c2k[i]; + id = interp->ids[i]; + k = interp->c2k[i]; d[k]->vid = 0; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, c); @@ -5672,11 +5915,11 @@ static void df_parray_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, else gs[k]->rebuild(gs[k]->interpolator, d[k]->points); if(df->osl & (L_BILIN|L_BAYLIN)) { - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; if (c == geom->m2d[c]) { - id = data->ids[i]; - k = data->c2k[i]; + id = interp->ids[i]; + k = interp->c2k[i]; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, c); else @@ -5684,50 +5927,67 @@ static void df_parray_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, } } } +} - for(i = 0; i < df->npoints; i++) { - v[i] = get_missing(var); - } +static void df_parray_writesub_3d(dump_data_t *dumpdata, + dump_file_t *df, + int vid, + double *values, + int klower, + int nz) +{ + df_parray_data_t *data = (df_parray_data_t *)df->private_data; + df_parray_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; + int fid = data->fid; + int varid = ncw_var_id(fid, df->vars[vid]); + int i, k, id; + double **v = d_alloc_2d(df->npoints, nz); + size_t start[3]; + size_t count[3]; + + df_interp_writesub_3d(dumpdata, df, interp, values); + for(i = 0; i < df->npoints; i++) + for (k = 0; k < nz; ++k) + v[k][i] = 1.0; + /*v[k][i] = get_missing(var);*/ for (i = 0; i < df->npoints; ++i) { - id = data->ids[i]; - v[i] = get_var_value(df, var, id, i, k); + for (k = 0; k < nz; ++k){ + if (interp->d[k] == NULL) continue; + id = interp->ids[i]; + v[k][i] = get_var_value(df, var, interp, id, i, k); + } } start[0] = data->nextrec; start[1] = 0; + start[2] = 0; count[0] = 1; - count[1] = df->npoints; - - nc_put_vara_double(fid, varid, start, count, v); + count[1] = nz; + count[2] = df->npoints; - d_free_1d(v); + nc_put_vara_double(fid, varid, start, count, v[0]); + d_free_2d(v); } -static void df_parray_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, - int vid, double *values, int klower, - int nz) +static void df_interp_writesub_3d(dump_data_t *dumpdata, + dump_file_t *df, + df_interp_t *interp, + double *values) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; - df_parray_var_t *var = &data->vars[vid]; master_t *master = dumpdata->master; geometry_t *geom = master->geom; - delaunay **d = data->d; - GRID_SPECS **gs = data->gs; - int fid = data->fid; - int varid = ncw_var_id(fid, df->vars[vid]); + delaunay **d = interp->d; + GRID_SPECS **gs = interp->gs; int i, k, c, id; - int fi, fj; - double **v = d_alloc_2d(df->npoints, nz); - size_t start[3]; - size_t count[3]; /* Fill the Delaunay structure with values */ - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; - id = data->ids[i]; - k = data->c2k[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; + id = interp->ids[i]; + k = interp->c2k[i]; d[k]->vid = 0; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, c); @@ -5745,31 +6005,52 @@ static void df_parray_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, if (d[k] == NULL) continue; if (df->osl & (L_SIB|L_NONSIB)) for (i = 0; i < d[k]->npoints; i++) - data->gs[k]->rebuild(data->gs[k]->interpolator, &d[k]->points[i]); + interp->gs[k]->rebuild(interp->gs[k]->interpolator, &d[k]->points[i]); else - data->gs[k]->rebuild(data->gs[k]->interpolator, d[k]->points); + interp->gs[k]->rebuild(interp->gs[k]->interpolator, d[k]->points); } if(df->osl & (L_BILIN|L_BAYLIN)) { - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; - id = data->ids[i]; - k = data->c2k[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; + id = interp->ids[i]; + k = interp->c2k[i]; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, c); else d[k]->points[id].v[0] = values[c]; } } +} + +static void df_parray_writevec_3d(dump_data_t *dumpdata, + dump_file_t *df, + int vid, + double *values, + int klower, + int nz) +{ + df_parray_data_t *data = (df_parray_data_t *)df->private_data; + df_parray_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; + int fid = data->fid; + int varid = ncw_var_id(fid, df->vars[vid]); + int i, k, id; + double **v = d_alloc_2d(df->npoints, nz); + size_t start[3]; + size_t count[3]; + + df_interp_writevec_2d(dumpdata, df, interp, values); for(i = 0; i < df->npoints; i++) for (k = 0; k < nz; ++k) v[k][i] = 1.0; /*v[k][i] = get_missing(var);*/ + for (i = 0; i < df->npoints; ++i) { for (k = 0; k < nz; ++k){ - if (d[k] == NULL) continue; - id = data->ids[i]; - v[k][i] = get_var_value(df, var, id, i, k); + if (interp->d[k] == NULL) continue; + id = interp->eds[i]; + v[k][i] = get_var_value(df, var, interp, id, i, k); } } @@ -5785,29 +6066,20 @@ static void df_parray_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, d_free_2d(v); } -static void df_parray_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, - int vid, double *values, int klower, - int nz) +static void df_interp_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, + df_interp_t *interp, double *values) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; - df_parray_var_t *var = &data->vars[vid]; master_t *master = dumpdata->master; geometry_t *geom = master->geom; - delaunay **d = data->de; - GRID_SPECS **gs = data->ge; - int fid = data->fid; - int varid = ncw_var_id(fid, df->vars[vid]); + delaunay **d = interp->de; + GRID_SPECS **gs = interp->ge; int i, k, e, id; - int fi, fj; - double **v = d_alloc_2d(df->npoints, nz); - size_t start[3]; - size_t count[3]; /* Fill the Delaunay structure with values */ - for (i = 0; i < data->neells; i++) { - e = data->eells[i]; - id = data->eds[i]; - k = data->e2k[i]; + for (i = 0; i < interp->neells; i++) { + e = interp->eells[i]; + id = interp->eds[i]; + k = interp->e2k[i]; d[k]->vid = 0; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, e); @@ -5819,57 +6091,63 @@ static void df_parray_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, /* Rebuild the weights. */ for (k = 0; k < geom->nz; k++) { if (d[k] == NULL) continue; - data->gs[k]->rebuild(data->gs[k]->interpolator, d[k]->points); + interp->gs[k]->rebuild(interp->gs[k]->interpolator, d[k]->points); } +} - for(i = 0; i < df->npoints; i++) - for (k = 0; k < nz; ++k) - v[k][i] = 1.0; - /*v[k][i] = get_missing(var);*/ + +static void df_parray_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, + int vid, double *values) +{ + df_parray_data_t *data = (df_parray_data_t *)df->private_data; + df_parray_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; + int fid = data->fid; + int varid = ncw_var_id(fid, df->vars[vid]); + int i, id; + int k = dumpdata->nz-1; + double *v = d_alloc_1d(df->npoints); + size_t start[2]; + size_t count[2]; + + df_interp_writevec_2d(dumpdata, df, interp, values); + + for(i = 0; i < df->npoints; i++) { + v[i] = get_missing(var); + } for (i = 0; i < df->npoints; ++i) { - for (k = 0; k < nz; ++k){ - if (d[k] == NULL) continue; - id = data->eds[i]; - v[k][i] = get_var_value(df, var, id, i, k); - } + id = interp->eds[i]; + v[i] = get_var_value(df, var, interp, id, i, k); } start[0] = data->nextrec; start[1] = 0; - start[2] = 0; count[0] = 1; - count[1] = nz; - count[2] = df->npoints; + count[1] = df->npoints; - nc_put_vara_double(fid, varid, start, count, v[0]); + nc_put_vara_double(fid, varid, start, count, v); - d_free_2d(v); + d_free_1d(v); } -static void df_parray_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, - int vid, double *values) +static void df_interp_writevec_2d(dump_data_t *dumpdata, + dump_file_t *df, + df_interp_t *interp, + double *values) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; - df_parray_var_t *var = &data->vars[vid]; master_t *master = dumpdata->master; geometry_t *geom = master->geom; - delaunay **d = data->de; - GRID_SPECS **gs = data->ge; - int fid = data->fid; - int varid = ncw_var_id(fid, df->vars[vid]); + delaunay **d = interp->de; + GRID_SPECS **gs = interp->ge; int i, k, e, id; - int fi, fj; - double *v = d_alloc_1d(df->npoints); - size_t start[2]; - size_t count[2]; /* Fill the Delaunay structure with values */ - for (i = 0; i < data->neells; i++) { - e = data->eells[i]; + for (i = 0; i < interp->neells; i++) { + e = interp->eells[i]; if (e == geom->m2de[e]) { - id = data->eds[i]; - k = data->e2k[i]; + id = interp->eds[i]; + k = interp->e2k[i]; d[k]->vid = 0; if (df->filter) d[k]->points[id].v[0] = df_filter(geom, df, values, e); @@ -5882,13 +6160,43 @@ static void df_parray_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, /* Rebuild the weights. */ k = geom->nz-1; gs[k]->rebuild(gs[k]->interpolator, d[k]->points); +} - for(i = 0; i < df->npoints; i++) { + +static void df_parray_writevec_cen_2d(dump_data_t *dumpdata, dump_file_t *df, + int vid, double *values_i, + double *values_j) +{ + df_parray_data_t *data = (df_parray_data_t *)df->private_data; + df_parray_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; + int fid = data->fid; + int varid = ncw_var_id(fid, df->vars[vid]); + int i, k, c, id; + double *v = d_alloc_1d(df->npoints); + size_t start[2]; + size_t count[2]; + + + df_interp_writevec_cen_2d(dumpdata, df, interp, values_i, values_j); + + k = geom->nz-1; + for(i = 0; i < df->npoints; i++) v[i] = get_missing(var); - } + for (i = 0; i < df->npoints; ++i) { - id = data->eds[i]; - v[i] = get_var_value(df, var, id, i, k); + double vi, vj; + id = interp->ids[i]; + c = interp->cells[i]; + if (c == geom->m2d[c]) { + interp->d[k]->vid = 0; + vi = get_var_value(df, var, interp, id, i, k); + interp->d[k]->vid = 1; + vj = get_var_value(df, var, interp, id, i, k); + v[i] = + get_vector_component(dumpdata, vi, vj, c, + var->vector_mode); + } } start[0] = data->nextrec; @@ -5902,31 +6210,25 @@ static void df_parray_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, } -static void df_parray_writevec_cen_2d(dump_data_t *dumpdata, dump_file_t *df, - int vid, double *values_i, +static void df_interp_writevec_cen_2d(dump_data_t *dumpdata, + dump_file_t *df, + df_interp_t *interp, + double *values_i, double *values_j) { - df_parray_data_t *data = (df_parray_data_t *)df->private_data; - df_parray_var_t *var = &data->vars[vid]; master_t *master = dumpdata->master; geometry_t *geom = master->geom; - delaunay **d = data->d; - int fid = data->fid; - int varid = ncw_var_id(fid, df->vars[vid]); + delaunay **d = interp->d; int i, k, c, id; int ee, e, es; - int fi, fj; double nu, nv, a; - double *v = d_alloc_1d(df->npoints); - size_t start[2]; - size_t count[2]; /* Fill the Delaunay structure with values */ - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; if (c == geom->m2d[c]) { - id = data->ids[i]; - k = data->c2k[i]; + id = interp->ids[i]; + k = interp->c2k[i]; d[k]->points[id].v[0] = 0.0; d[k]->points[id].v[1] = 0.0; nu = nv = 0.0; @@ -5943,34 +6245,6 @@ static void df_parray_writevec_cen_2d(dump_data_t *dumpdata, dump_file_t *df, d[k]->points[id].v[1] = (nv) ? d[k]->points[id].v[1] / nv : 0.0; } } - - k = geom->nz-1; - for(i = 0; i < df->npoints; i++) - v[i] = get_missing(var); - - for (i = 0; i < df->npoints; ++i) { - double vi, vj; - id = data->ids[i]; - c = data->cells[i]; - if (c == geom->m2d[c]) { - d[k]->vid = 0; - vi = get_var_value(df, var, id, i, k); - d[k]->vid = 1; - vj = get_var_value(df, var, id, i, k); - v[i] = - get_vector_component(dumpdata, vi, vj, c, - var->vector_mode); - } - } - - start[0] = data->nextrec; - start[1] = 0; - count[0] = 1; - count[1] = df->npoints; - - nc_put_vara_double(fid, varid, start, count, v); - - d_free_1d(v); } static void df_parray_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, @@ -5979,41 +6253,19 @@ static void df_parray_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *data = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &data->vars[vid]; + df_interp_t *interp = data->interp; master_t *master = dumpdata->master; geometry_t *geom = master->geom; - delaunay **d = data->d; int fid = data->fid; int varid = ncw_var_id(fid, df->vars[vid]); int i, j, k, c, c2, id; int ee, e, es; - int fi, fj; double nu, nv, a; double **v = d_alloc_2d(df->npoints, nz); size_t start[3]; size_t count[3]; - /* Fill the Delaunay structure with values */ - for (i = 0; i < data->ncells; i++) { - c = data->cells[i]; - c2 = geom->m2d[c]; - id = data->ids[i]; - k = data->c2k[i]; - d[k]->points[id].v[0] = 0.0; - d[k]->points[id].v[1] = 0.0; - nu = nv = 0.0; - for (ee = 1; ee <= geom->npe[c2]; ee++) { - e = geom->c2e[ee][c]; - es = geom->m2de[e]; - /* Get the cell centered east and north velocity */ - a = 0.5 * geom->h1au1[es] * geom->h2au1[es]; - d[k]->points[id].v[0] += a * (values_i[e] * geom->costhu1[es] + values_j[e] * geom->costhu2[es]); - nu += a; - d[k]->points[id].v[1] += a * (values_i[e] * geom->sinthu1[es] + values_j[e] * geom->sinthu2[es]); - nv += a; - } - d[k]->points[id].v[0] = (nu) ? d[k]->points[id].v[0] / nu : 0.0; - d[k]->points[id].v[1] = (nv) ? d[k]->points[id].v[1] / nv : 0.0; - } + df_interp_writevec_cen_3d(dumpdata, df, interp, values_i, values_j); for(i = 0; i < df->npoints; i++) for (k = 0; k < nz; ++k) @@ -6021,13 +6273,13 @@ static void df_parray_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, for (i = 0; i < df->npoints; ++i) { for (k = 0; k < nz; ++k){ double vi, vj; - if (d[k] == NULL) continue; - id = data->ids[i]; - c = data->cells[i]; - d[k]->vid = 0; - vi = get_var_value(df, var, id, i, klower + k); - d[k]->vid = 1; - vj = get_var_value(df, var, id, i, klower + k); + if (interp->d[k] == NULL) continue; + id = interp->ids[i]; + c = interp->cells[i]; + interp->d[k]->vid = 0; + vi = get_var_value(df, var, interp, id, i, klower + k); + interp->d[k]->vid = 1; + vj = get_var_value(df, var, interp, id, i, klower + k); v[k][i] = get_vector_component(dumpdata, vi, vj, c, var->vector_mode); } @@ -6045,6 +6297,42 @@ static void df_parray_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, d_free_2d(v); } + +static void df_interp_writevec_cen_3d(dump_data_t *dumpdata, dump_file_t *df, + df_interp_t *interp, double *values_i, + double *values_j) +{ + master_t *master = dumpdata->master; + geometry_t *geom = master->geom; + delaunay **d = interp->d; + int i, j, k, c, c2, id; + int ee, e, es; + double nu, nv, a; + + /* Fill the Delaunay structure with values */ + for (i = 0; i < interp->ncells; i++) { + c = interp->cells[i]; + c2 = geom->m2d[c]; + id = interp->ids[i]; + k = interp->c2k[i]; + d[k]->points[id].v[0] = 0.0; + d[k]->points[id].v[1] = 0.0; + nu = nv = 0.0; + for (ee = 1; ee <= geom->npe[c2]; ee++) { + e = geom->c2e[ee][c]; + es = geom->m2de[e]; + /* Get the cell centered east and north velocity */ + a = 0.5 * geom->h1au1[es] * geom->h2au1[es]; + d[k]->points[id].v[0] += a * (values_i[e] * geom->costhu1[es] + values_j[e] * geom->costhu2[es]); + nu += a; + d[k]->points[id].v[1] += a * (values_i[e] * geom->sinthu1[es] + values_j[e] * geom->sinthu2[es]); + nv += a; + } + d[k]->points[id].v[0] = (nu) ? d[k]->points[id].v[0] / nu : 0.0; + d[k]->points[id].v[1] = (nv) ? d[k]->points[id].v[1] / nv : 0.0; + } +} + static double get_vector_component(dump_data_t *dumpdata, double vi, double vj, int c, int mode) { @@ -6138,11 +6426,10 @@ int find_next_restart_record(dump_file_t *df, int cdfid, /* Creates a GRID_SPEC structure and Delaunay triangulation for */ /* triangular linear interpolation of output. */ /*-------------------------------------------------------------------*/ -void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) +void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df, df_interp_t *interp) { master_t *master = dumpdata->master; geometry_t *geom = master->geom; - df_parray_data_t *data = (df_parray_data_t *)df->private_data; char *uvrule = df->irule; int cc, c, c2, cn, ci, k, kk; int i, j, id, fi, fj; @@ -6158,25 +6445,26 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) int isalloc; int usegst = 1; - isalloc = (data->gs && data->d) ? 1 : 0; + if (df->flag & DF_STO) usegst = 0; + isalloc = (interp->gs && interp->d) ? 1 : 0; if (!isalloc) { /* Get the number of points for the triangulation */ np = 0; - data->ncells = 0; - data->nz = geom->nz - 1; - data->botk = i_alloc_1d(df->npoints); + interp->ncells = 0; + interp->nz = geom->nz - 1; + interp->botk = i_alloc_1d(df->npoints); mask = i_alloc_1d(geom->szc); memset(mask, 0, geom->szc * sizeof(int)); for (i = 0; i < df->npoints; ++i) { c = hd_grid_xytoij(master, df->x[i], df->y[i], &fi, &fj); - data->botk[i] = data->nz; + interp->botk[i] = interp->nz; if (c > 0 && c < geom->szcS) { c2 = geom->m2d[c]; while (c != geom->zm1[c]) { if (!mask[c]) { if (c == geom->m2d[c]) np++; - data->ncells ++; + interp->ncells ++; mask[c] = 1; } @@ -6185,24 +6473,24 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) if (!usegst && geom->wgst[cn]) continue; if (!mask[cn]) { if (cn == geom->m2d[cn]) np++; - data->ncells ++; + interp->ncells ++; mask[cn] = 1; } } - data->nz = min(data->nz, geom->s2k[c]); - data->botk[i] = geom->s2k[c]; + interp->nz = min(interp->nz, geom->s2k[c]); + interp->botk[i] = geom->s2k[c]; c = geom->zm1[c]; } } } - if (!data->ncells) hd_quit("parray_grid_init: Can't find any cells for file %s Delaunay interpolation.\n", df->name); + if (!interp->ncells) hd_quit("parray_grid_init: Can't find any cells for file %s Delaunay interpolation.\n", df->name); /* Allocate */ - data->cells = i_alloc_1d(data->ncells); - data->ids = i_alloc_1d(data->ncells); - data->c2k = i_alloc_1d(data->ncells); - n2i = i_alloc_1d(data->ncells); + interp->cells = i_alloc_1d(interp->ncells); + interp->ids = i_alloc_1d(interp->ncells); + interp->c2k = i_alloc_1d(interp->ncells); + n2i = i_alloc_1d(interp->ncells); p = (point **)alloc_2d(np, nz, sizeof(point)); nk = i_alloc_1d(nz); memset(nk, 0, nz * sizeof(int)); @@ -6217,33 +6505,35 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) k = geom->s2k[c]; if (!mask[c]) { n2i[n] = i; - data->ids[n] = nk[k]; - data->c2k[n] = k; - data->cells[n++] = c; + interp->ids[n] = nk[k]; + interp->c2k[n] = k; + interp->cells[n++] = c; mask[c] = nk[k] + 1; p[k][nk[k]].x = geom->cellx[c2]; p[k][nk[k]++].y = geom->celly[c2]; /* if(strcmp(df->name,"/home/her127/work/meco/compas/est/tile0/bdry0-1_uv_nor.mpk.nc")==0) if(c==c2)printf("%f %f\n",p[k][nk[k]-1].x,p[k][nk[k]-1].y); + printf("%f %f p%d\n",p[k][nk[k]-1].x,p[k][nk[k]-1].y,nk[k]-1); */ } } } - data->ncs = n; + + interp->ncs = n; /* Set the sub-surface cell centres for the triangulation */ - for (i = 0; i < data->ncs; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncs; i++) { + c = interp->cells[i]; c2 = geom->m2d[c]; c = geom->zm1[c]; - id = data->ids[i]; + id = interp->ids[i]; while (c != geom->zm1[c]) { k = geom->s2k[c]; if (!mask[c]) { n2i[n] = i; - data->ids[n] = nk[k]; - data->c2k[n] = k; - data->cells[n++] = c; + interp->ids[n] = nk[k]; + interp->c2k[n] = k; + interp->cells[n++] = c; mask[c] = nk[k] + 1; p[k][nk[k]].x = geom->cellx[c2]; p[k][nk[k]++].y = geom->celly[c2]; @@ -6251,21 +6541,22 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) c = geom->zm1[c]; } } - data->nc = n; + interp->nc = n; /* Set the cells surrounding the cell centres */ - for (i = 0; i < data->ncs; i++) { - c = data->cells[i]; + for (i = 0; i < interp->ncs; i++) { + c = interp->cells[i]; c2 = geom->m2d[c]; while (c != geom->zm1[c]) { k = geom->s2k[c]; + /*if(k==0)printf("a1 %d %d %d\n",c2,c,geom->npe[c2]);*/ for (j = 1; j <= geom->npe[c2]; j++) { cn = geom->c2c[j][c]; if (!usegst && geom->wgst[cn]) continue; if (!mask[cn]) { - data->ids[n] = nk[k]; - data->c2k[n] = k; - data->cells[n++] = cn; + interp->ids[n] = nk[k]; + interp->c2k[n] = k; + interp->cells[n++] = cn; mask[cn] = nk[k] + 1; /* The geographic location of ghost cells is set to the */ /* cell edge so as to account for multiple ghost cells */ @@ -6281,6 +6572,7 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) /* if(strcmp(df->name,"/home/her127/work/meco/compas/est/tile0/bdry0-1_uv_nor.mpk.nc")==0) if(c==c2)printf("%f %f\n",p[k][nk[k]].x,p[k][nk[k]].y); + if(c==c2)printf("%f %f p%d\n",p[k][nk[k]].x,p[k][nk[k]].y,nk[k]); */ nk[k]++; } @@ -6293,17 +6585,18 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) /* Fill the points array and create the triangulation for every */ /* layer. Note this is designed to be used with COMPAS arrays, */ /* which start at index 1, hence use [c+1]. */ - data->d = (delaunay **)calloc(nz, sizeof(delaunay *)); + interp->d = (delaunay **)calloc(nz, sizeof(delaunay *)); for (k = 0; k < nz; k++) { delaunay *dk; - data->d[k] = NULL; + interp->d[k] = NULL; if (nk[k] == 0) continue; /* if(strcmp(df->name,"/home/her127/work/meco/compas/est/tile0/bdry0-1_uv_nor.mpk.nc")==0) - if(k==nz-1)for(i=0;id[k] = delaunay_build(nk[k], p[k], 0, NULL, 0, NULL); - dk = data->d[k]; + interp->d[k] = delaunay_build(nk[k], p[k], 0, NULL, 0, NULL); + dk = interp->d[k]; dk->vid = 0; dk->ptf = 1; @@ -6321,16 +6614,16 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) } /* Set the new Delaunay point triangles for the interpolation */ - for (i = 0; i < data->ncells; i++) { + for (i = 0; i < interp->ncells; i++) { delaunay *dk; - c = data->cells[i]; + c = interp->cells[i]; c2 = geom->m2d[c]; k = geom->s2k[c]; - id = data->ids[i]; - dk = data->d[k]; + id = interp->ids[i]; + dk = interp->d[k]; /* Cell centres in the triangulation */ - if (i < data->nc) { + if (i < interp->nc) { dk->n_point_triangles[id] = geom->npe[c2] + 1; dk->point_triangles[id] = malloc(dk->n_point_triangles[id] * sizeof(int)); dk->point_triangles[id][0] = id; @@ -6352,36 +6645,36 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) for (k = 0; k < nz; k++) { gs[k] = grid_spec_create(); - if (data->d[k] == NULL) continue; + if (interp->d[k] == NULL) continue; - grid_interp_init_t(gs[k], data->d[k], uvrule, 0); + grid_interp_init_t(gs[k], interp->d[k], uvrule, 0); } /*---------------------------------------------------------------*/ /* Set the grid_spec structure */ - data->gs = gs; + interp->gs = gs; } /*-----------------------------------------------------------------*/ /* Fill the points array in the delaunay structure. */ - for (i = 0; i < data->nc; i++) { - c = data->cells[i]; + for (i = 0; i < interp->nc; i++) { + c = interp->cells[i]; k = geom->s2k[c]; - id = data->ids[i]; + id = interp->ids[i]; /* - data->d[k]->points[id].x = df->x[n2i[i]]; - data->d[k]->points[id].y = df->y[n2i[i]]; + interp->d[k]->points[id].x = df->x[n2i[i]]; + interp->d[k]->points[id].y = df->y[n2i[i]]; */ - data->d[k]->points[id].z = (double)id; + interp->d[k]->points[id].z = (double)id; } /*-----------------------------------------------------------------*/ /* Rebuild the weights. Note: this is cumulative (i.e. weights are */ /* added to new cells) for each parray point. */ for (k = 0; k < nz; k++) { - if (data->d[k] == NULL) continue; - data->gs[k]->rebuild(data->gs[k]->interpolator, data->d[k]->points); + if (interp->d[k] == NULL) continue; + interp->gs[k]->rebuild(interp->gs[k]->interpolator, interp->d[k]->points); } i_free_1d(mask); i_free_1d(n2i); @@ -6397,11 +6690,10 @@ void parray_grid_init(dump_data_t *dumpdata, dump_file_t *df) /* are the nearest edge to a dump location, and all edges */ /* surrounding vertices at either end of that edge. */ /*-------------------------------------------------------------------*/ -void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) +void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df, df_interp_t *interp) { master_t *master = dumpdata->master; geometry_t *geom = master->geom; - df_parray_data_t *data = (df_parray_data_t *)df->private_data; char *uvrule = "nearest"; int c, c2, ee, e, e2, en, ei, k, kk; int i, j, id, fi, fj, n; @@ -6417,28 +6709,20 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) int isalloc; double x, y, dist, dm; - isalloc = (data->ge && data->de) ? 1 : 0; - /* Check for edge vectors requiring output */ - if (!isalloc) { - isalloc = 1; - for (n = 0; n < df->nvars; n++) { - if (data->vars[n].vector_mode & (VM_NOR|VM_TAN)) - isalloc = 0; - } - } + isalloc = (interp->ge && interp->de) ? 1 : 0; if (isalloc) return; /* Get the number of points for the triangulation */ np = 0; - data->neells = 0; - data->nz = geom->nz - 1; - data->botek = i_alloc_1d(df->npoints); + interp->neells = 0; + interp->nz = geom->nz - 1; + interp->botek = i_alloc_1d(df->npoints); mask = i_alloc_1d(geom->sze); memset(mask, 0, geom->sze * sizeof(int)); eloc = i_alloc_1d(df->npoints); for (i = 0; i < df->npoints; ++i) { c = hd_grid_xytoij(master, df->x[i], df->y[i], &fi, &fj); - data->botek[i] = data->nz; + interp->botek[i] = interp->nz; if (c > 0 && c < geom->szcS) { /* Find the edge closest to the dump location */ c2 = geom->m2d[c]; @@ -6458,7 +6742,7 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) while (e != geom->zm1e[e]) { if (!mask[e]) { if (e == geom->m2de[e]) np++; - data->neells ++; + interp->neells ++; mask[e] = 1; } for (j = 0; j < 2; j++) { @@ -6468,23 +6752,23 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) en = geom->v2e[v][vv]; if (!mask[en]) { if (en == geom->m2de[en]) np++; - data->neells ++; + interp->neells ++; mask[en] = 1; } } } - data->nz = min(data->nz, geom->e2k[e]); - data->botek[i] = geom->e2k[e]; + interp->nz = min(interp->nz, geom->e2k[e]); + interp->botek[i] = geom->e2k[e]; e = geom->zm1e[e]; } } } /* Allocate */ - data->eells = i_alloc_1d(data->neells); - data->eds = i_alloc_1d(data->neells); - data->e2k = i_alloc_1d(data->neells); - n2i = i_alloc_1d(data->neells); + interp->eells = i_alloc_1d(interp->neells); + interp->eds = i_alloc_1d(interp->neells); + interp->e2k = i_alloc_1d(interp->neells); + n2i = i_alloc_1d(interp->neells); p = (point **)alloc_2d(np, nz, sizeof(point)); nk = i_alloc_1d(nz); memset(nk, 0, nz * sizeof(int)); @@ -6498,27 +6782,27 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) k = geom->e2k[e]; if (!mask[e]) { n2i[n] = i; - data->eds[n] = nk[k]; - data->e2k[n] = k; - data->eells[n++] = e; + interp->eds[n] = nk[k]; + interp->e2k[n] = k; + interp->eells[n++] = e; mask[e] = nk[k] + 1; p[k][nk[k]].x = geom->u1x[e2]; p[k][nk[k]++].y = geom->u1y[e2]; } } - data->nes = n; + interp->nes = n; /* Set the sub-surface cell centres for the triangulation */ - for (i = 0; i < data->nes; i++) { - e = data->eells[i]; + for (i = 0; i < interp->nes; i++) { + e = interp->eells[i]; e2 = geom->m2de[e]; e = geom->zm1e[e]; while (e != geom->zm1e[e]) { k = geom->e2k[e]; if (!mask[e]) { n2i[n] = i; - data->eds[n] = nk[k]; - data->e2k[n] = k; - data->eells[n++] = e; + interp->eds[n] = nk[k]; + interp->e2k[n] = k; + interp->eells[n++] = e; mask[e] = nk[k] + 1; p[k][nk[k]].x = geom->u1x[e2]; p[k][nk[k]++].y = geom->u1y[e2]; @@ -6526,10 +6810,10 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) e = geom->zm1e[e]; } } - data->ec = n; + interp->ec = n; /* Set the edges surrounding the edge vertices */ - for (i = 0; i < data->nes; i++) { - e = data->eells[i]; + for (i = 0; i < interp->nes; i++) { + e = interp->eells[i]; e2 = geom->m2de[e]; while (e != geom->zm1e[e]) { k = geom->e2k[e]; @@ -6540,9 +6824,9 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) en = geom->v2e[v][vv]; if (!mask[en]) { e2 = geom->m2de[en]; - data->eds[n] = nk[k]; - data->e2k[n] = k; - data->eells[n++] = en; + interp->eds[n] = nk[k]; + interp->e2k[n] = k; + interp->eells[n++] = en; mask[en] = nk[k] + 1; p[k][nk[k]].x = geom->u1x[e2]; p[k][nk[k]].y = geom->u1y[e2]; @@ -6558,14 +6842,14 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) /* Fill the points array and create the triangulation for every */ /* layer. Note this is designed to be used with COMPAS arrays, */ /* which start at index 1, hence use [c+1]. */ - data->de = (delaunay **)calloc(nz, sizeof(delaunay *)); + interp->de = (delaunay **)calloc(nz, sizeof(delaunay *)); for (k = 0; k < nz; k++) { delaunay *dk; - data->de[k] = NULL; + interp->de[k] = NULL; if (nk[k] == 0) continue; - data->de[k] = delaunay_build(nk[k], p[k], 0, NULL, 0, NULL); - dk = data->de[k]; + interp->de[k] = delaunay_build(nk[k], p[k], 0, NULL, 0, NULL); + dk = interp->de[k]; dk->vid = 0; dk->ptf = 1; @@ -6584,19 +6868,19 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) } /* Set the new Delaunay point triangles for the interpolation */ - for (i = 0; i < data->neells; i++) { + for (i = 0; i < interp->neells; i++) { delaunay *dk; - e = data->eells[i]; + e = interp->eells[i]; e2 = geom->m2de[e]; k = geom->e2k[e]; - id = data->eds[i]; - dk = data->de[k]; + id = interp->eds[i]; + dk = interp->de[k]; /* Edges in the triangulation; these point_trianges are the */ /* indices corresponding to all vertices in the triangulation */ /* (i.e. edge locations) whose triangles share a common edge */ /* index. */ - if (i < data->ec) { + if (i < interp->ec) { n = 0; dk->n_point_triangles[id] = 4 + 1; dk->point_triangles[id] = malloc(dk->n_point_triangles[id] * sizeof(int)); @@ -6635,36 +6919,36 @@ void parray_gride_init(dump_data_t *dumpdata, dump_file_t *df) ge = (GRID_SPECS **)calloc(nz, sizeof(GRID_SPECS *)); for (k = 0; k < nz; k++) { ge[k] = grid_spec_create(); - if (data->de[k] == NULL) continue; + if (interp->de[k] == NULL) continue; - grid_interp_init_t(ge[k], data->de[k], uvrule, 0); + grid_interp_init_t(ge[k], interp->de[k], uvrule, 0); } /*---------------------------------------------------------------*/ /* Set the grid_spec structure */ - data->ge = ge; + interp->ge = ge; /*-----------------------------------------------------------------*/ /* Fill the points array in the delaunay structure. */ - for (i = 0; i < data->ec; i++) { - e = data->eells[i]; + for (i = 0; i < interp->ec; i++) { + e = interp->eells[i]; k = geom->e2k[e]; - id = data->eds[i]; + id = interp->eds[i]; /* - data->de[k]->points[id].x = df->x[n2i[i]]; - data->de[k]->points[id].y = df->y[n2i[i]]; + interp->de[k]->points[id].x = df->x[n2i[i]]; + interp->de[k]->points[id].y = df->y[n2i[i]]; */ - data->de[k]->points[id].z = (double)id; + interp->de[k]->points[id].z = (double)id; } /*-----------------------------------------------------------------*/ /* Rebuild the weights. Note: this is cumulative (i.e. weights are */ /* added to new cells) for each parray point. */ for (k = 0; k < nz; k++) { - if (data->de[k] == NULL) continue; - data->ge[k]->rebuild(data->ge[k]->interpolator, data->de[k]->points); + if (interp->de[k] == NULL) continue; + interp->ge[k]->rebuild(interp->ge[k]->interpolator, interp->de[k]->points); } i_free_1d(mask); @@ -6720,8 +7004,9 @@ typedef struct { static void df_memory_init_data(dump_data_t *dumpdata, dump_file_t *df) { - int i; + int i, n; df_parray_data_t *mem = NULL; + df_interp_t *interp; df_parse_vars(dumpdata,df,PARRAY_EXCLUDE_VARS,PARRAY_ALL_VARS); @@ -6743,10 +7028,15 @@ static void df_memory_init_data(dump_data_t *dumpdata, dump_file_t *df) hd_quit("dumpfile:df_memory_init_data: Unknown variable '%s'.", df->vars[i]); } - /* Allocate and initialize the grid_spec structures for */ - /* interpolation. */ - parray_grid_init(dumpdata, df); - parray_gride_init(dumpdata, df); + /* Initialise the interpolation structures */ + interp = (df_interp_t *)malloc(sizeof(df_interp_t)); + memset(interp, 0, sizeof(df_interp_t)); + /* Check for edge vectors requiring output */ + i = 1; + for (n = 0; n < df->nvars; n++) + if (mem->vars[n].vector_mode & (VM_NOR|VM_TAN)) i = 0; + df_interp_init_data(dumpdata, df, interp, i); + mem->interp = interp; } @@ -6925,56 +7215,15 @@ static void df_memory_writesub_2d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *mem = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &mem->vars[vid]; - master_t *master = dumpdata->master; - geometry_t *geom = master->geom; - delaunay **d = mem->d; - GRID_SPECS **gs = mem->gs; - int i, k, c, id; - int fi, fj; + df_interp_t *interp = mem->interp; + int i, id; + int k = dumpdata->nz-1; - /* Fill the Delaunay structure with values */ - for (i = 0; i < mem->ncells; i++) { - c = mem->cells[i]; - if (c == geom->m2d[c]) { - id = mem->ids[i]; - k = mem->c2k[i]; - d[k]->vid = 0; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom,df, values, c); - else - d[k]->points[id].v[0] = values[c]; - d[k]->points[id].z = d[k]->points[id].v[0]; - if (df->osl & (L_BAYLIN|L_BILIN)) { - d[k]->points[id].z = (double)id; - d[k]->points[id].v[0] = (double)id; - } - } - } - - /* Rebuild the weights. */ - k = geom->nz-1; - if (df->osl & (L_SIB|L_NONSIB)) - for (i = 0; i < d[k]->npoints; i++) - gs[k]->rebuild(gs[k]->interpolator, &d[k]->points[i]); - else - gs[k]->rebuild(gs[k]->interpolator, d[k]->points); - if(df->osl & (L_BILIN|L_BAYLIN)) { - for (i = 0; i < mem->ncells; i++) { - c = mem->cells[i]; - if (c == geom->m2d[c]) { - id = mem->ids[i]; - k = mem->c2k[i]; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom, df, values, c); - else - d[k]->points[id].v[0] = values[c]; - } - } - } + df_interp_writesub_2d(dumpdata, df, interp, v2d); for (i = 0; i < df->npoints; ++i) { - id = mem->ids[i]; - v2d[i] = get_var_value(df, var, id, i, k); + id = interp->ids[i]; + v2d[i] = get_var_value(df, var, interp, id, i, k); } } @@ -6986,50 +7235,10 @@ static void df_memory_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *mem = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &mem->vars[vid]; - master_t *master = dumpdata->master; - geometry_t *geom = master->geom; - delaunay **d = mem->d; - GRID_SPECS **gs = mem->gs; + df_interp_t *interp = mem->interp; int i, k, c, id; - int fi, fj; - - /* Fill the Delaunay structure with values */ - for (i = 0; i < mem->ncells; i++) { - c = mem->cells[i]; - id = mem->ids[i]; - k = mem->c2k[i]; - d[k]->vid = 0; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom, df, values, c); - else - d[k]->points[id].v[0] = values[c]; - d[k]->points[id].z = d[k]->points[id].v[0]; - if (df->osl & (L_BAYLIN|L_BILIN)) { - d[k]->points[id].z = (double)id; - d[k]->points[id].v[0] = (double)id; - } - } - /* Rebuild the weights. */ - for (k = 0; k < geom->nz; k++) { - if (d[k] == NULL) continue; - if (df->osl & (L_SIB|L_NONSIB)) - for (i = 0; i < d[k]->npoints; i++) - mem->gs[k]->rebuild(mem->gs[k]->interpolator, &d[k]->points[i]); - else - mem->gs[k]->rebuild(mem->gs[k]->interpolator, d[k]->points); - } - if(df->osl & (L_BILIN|L_BAYLIN)) { - for (i = 0; i < mem->ncells; i++) { - c = mem->cells[i]; - id = mem->ids[i]; - k = mem->c2k[i]; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom, df, values, c); - else - d[k]->points[id].v[0] = values[c]; - } - } + df_interp_writesub_3d(dumpdata, df, interp, v3d); /* c = 0; for (i = 0; i < df->npoints; ++i) { @@ -7043,16 +7252,16 @@ static void df_memory_writesub_3d(dump_data_t *dumpdata, dump_file_t *df, for (i = 0; i < df->npoints; ++i) { double vb; for (k = 0; k < nz; k++) { - if (d[k] != NULL && k >= mem->botk[i]) { - vb = get_var_value(df, var, id, i, k); + if (interp->d[k] != NULL && k >= interp->botk[i]) { + vb = get_var_value(df, var, interp, id, i, k); break; } } for (k = 0; k < nz; k++) { v3d[c] = 0.0; - if (d[k] != NULL && k >= mem->botk[i]) { - id = mem->ids[i]; - v3d[c] = get_var_value(df, var, id, i, k); + if (interp->d[k] != NULL && k >= interp->botk[i]) { + id = interp->ids[i]; + v3d[c] = get_var_value(df, var, interp, id, i, k); } /* Set a no-gradient below the sea bed. d[k] may != NULL, but */ /* have no points surrounding (df->x,df->y) because this df */ @@ -7073,30 +7282,11 @@ static void df_memory_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *mem = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &mem->vars[vid]; - master_t *master = dumpdata->master; - geometry_t *geom = master->geom; - delaunay **d = mem->de; - GRID_SPECS **gs = mem->ge; - int i, k, e, id; - - /* Fill the Delaunay structure with values */ - for (i = 0; i < mem->neells; i++) { - e = mem->eells[i]; - if (e == geom->m2de[e]) { - id = mem->eds[i]; - k = mem->e2k[i]; - d[k]->vid = 0; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom, df, values, e); - else - d[k]->points[id].v[0] = values[e]; - d[k]->points[id].z = d[k]->points[id].v[0]; - } - } + df_interp_t *interp = mem->interp; + int i, id; + int k = dumpdata->nz-1; - /* Rebuild the weights. */ - k = geom->nz-1; - gs[k]->rebuild(gs[k]->interpolator, d[k]->points); + df_interp_writevec_2d(dumpdata, df, interp, v2d); /* for(i = 0; i < df->npoints; i++) { @@ -7104,8 +7294,8 @@ static void df_memory_writevec_2d(dump_data_t *dumpdata, dump_file_t *df, } */ for (i = 0; i < df->npoints; ++i) { - id = mem->eds[i]; - v2d[i] = get_var_value(df, var, id, i, k); + id = interp->eds[i]; + v2d[i] = get_var_value(df, var, interp, id, i, k); } } @@ -7115,46 +7305,26 @@ static void df_memory_writevec_3d(dump_data_t *dumpdata, dump_file_t *df, { df_parray_data_t *mem = (df_parray_data_t *)df->private_data; df_parray_var_t *var = &mem->vars[vid]; - master_t *master = dumpdata->master; - geometry_t *geom = master->geom; - delaunay **d = mem->de; - GRID_SPECS **gs = mem->ge; + df_interp_t *interp = mem->interp; int i, k, e, id; - /* Fill the Delaunay structure with values */ - for (i = 0; i < mem->neells; i++) { - e = mem->eells[i]; - id = mem->eds[i]; - k = mem->e2k[i]; - d[k]->vid = 0; - if (df->filter) - d[k]->points[id].v[0] = df_filter(geom, df, values, e); - else - d[k]->points[id].v[0] = values[e]; - d[k]->points[id].z = d[k]->points[id].v[0]; - } - - /* Rebuild the weights. */ - for (k = 0; k < geom->nz; k++) { - if (d[k] == NULL) continue; - gs[k]->rebuild(gs[k]->interpolator, d[k]->points); - } + df_interp_writevec_2d(dumpdata, df, interp, v3d); e = 0; for (i = 0; i < df->npoints; ++i) { double vb; for (k = 0; k < nz; k++) { - if (d[k] != NULL && k >= mem->botek[i]) { - vb = get_var_value(df, var, id, i, k); + if (interp->d[k] != NULL && k >= interp->botek[i]) { + vb = get_var_value(df, var, interp, id, i, k); break; } } for (k = 0; k < nz; ++k){ v3d[e] = get_missing(var); v3d[e] = 0.0; - if (d[k] != NULL && k >= mem->botek[i]) { - id = mem->eds[i]; - v3d[e] = get_var_value(df, var, id, i, k); + if (interp->d[k] != NULL && k >= interp->botek[i]) { + id = interp->eds[i]; + v3d[e] = get_var_value(df, var, interp, id, i, k); } else { v3d[e] = vb; } diff --git a/model/hd-us/particles/pt.c b/model/hd-us/particles/pt.c index cfaa0ee..164a2c2 100644 --- a/model/hd-us/particles/pt.c +++ b/model/hd-us/particles/pt.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: pt.c 7409 2023-10-05 02:08:32Z her127 $ + * $Id: pt.c 7511 2024-03-11 22:40:17Z her127 $ * */ @@ -1782,15 +1782,17 @@ int get_pos_m(master_t *master, /* Window geometry */ isghost = 1; } cns = geom->m2d[cn]; + if (cn <= 0) + hd_warn("Can't find horizontal streamline position: destination = %d[%f %f]->[%f %f]. (i,j)=(%d,%d)]\n", + c, geom->cellx[cs], geom->celly[cs], slon, slat, i, j); } else { /* Unstructured meshes: walk through the Voronoi mesh */ cn = found = find_cell(geom, c, slon, slat, &nx, &ny); cns = geom->m2d[cn]; - - } - if (cn <= 0) - hd_warn("Can't find horizontal streamline position: destination = %d[%f %f]->[%f %f]. Intersection=[%f %f]\n", + if (cn <= 0) + hd_warn("Can't find horizontal streamline position: destination = %d[%f %f]->[%f %f]. Intersection=[%f %f]\n", c, geom->cellx[cs], geom->celly[cs], slon, slat, nx, ny); + } /* Get the vertical layer of the source cell */ if (cn == geom->zm1[cn]) cn = geom->zp1[cn]; diff --git a/model/hd-us/slaves/windows.c b/model/hd-us/slaves/windows.c index dccbc67..490edf3 100644 --- a/model/hd-us/slaves/windows.c +++ b/model/hd-us/slaves/windows.c @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: windows.c 7468 2023-12-13 03:54:09Z her127 $ + * $Id: windows.c 7564 2024-05-27 05:01:47Z her127 $ * */ @@ -327,9 +327,10 @@ void window_build(geometry_t *geom, /* Global geometry */ window_cells_linear_e1(geom, nwindows, ws2, wsz2D); else if (params->win_type & WIN_REG) window_cells_region(geom, nwindows, ws2, wsz2D, params->win_file); - else if (params->win_type & WIN_METIS) + else if (params->win_type & WIN_METIS) { + if (params->mrf & MR_READM) params->metis_opts |= METIS_REORDER; window_cells_metis(geom, nwindows, ws2, wsz2D, params->metis_opts); - else + } else window_cells_grouped(geom, nwindows, ws2, wsz2D); /* Get the global to local maps */ @@ -357,7 +358,10 @@ void window_build(geometry_t *geom, /* Global geometry */ window[n]->sednz = geom->sednz; /* Get the 3D local cells in window n */ - get_window_cells_h(geom, n, wsa, wsz, ws2[n], wsz2D[n]); + if (params->mrf & MR_VERT) + get_window_cells_v(geom, n, wsa, wsz, ws2[n], wsz2D[n]); + else + get_window_cells_h(geom, n, wsa, wsz, ws2[n], wsz2D[n]); /* Set the local wet array and local maps for window n */ get_local_maps(geom, window[n], n, wsa, wsz[n], ws2[n], wsz2D[n], cellf); @@ -620,8 +624,16 @@ void window_build(geometry_t *geom, /* Global geometry */ if(geom->sm_e2) window[n]->sm_e2 = win_vector_build(geom, window[n], n, geom->sm_e2, 2); - /*reorder_cells(window[n], window[n]->w3_t, window[n]->w2_t, - window[n]->b3_t, window[n]->b2_t, window[n]->bot_t, 1);*/ + /* Reorder the work arrays to be vertical stacks */ + if (params->mrf & MR_VERT) { + reorder_cells(window[n], window[n]->w3_t, window[n]->w2_t, + window[n]->b3_t, window[n]->b2_t, window[n]->zm1, 1); + reorder_cells(window[n], window[n]->w3_e1, window[n]->w2_e1, + window[n]->b3_e1, window[n]->b2_e1, window[n]->zm1e, 1); + reorder_cells(window[n], window[n]->w3_e2, window[n]->w2_e2, + window[n]->b3_e2, window[n]->b2_e2, window[n]->zm1v, 1); + } + get_inner_exmap(geom, window[n]); get_process_exclude(params, geom, window[n]); set_mask(window[n]); @@ -1052,7 +1064,7 @@ void window_cells_metis(geometry_t *geom, /* Global geometery */ } /* Reorder the cells */ - reorder_metis(geom, nwindows, ws2, wsizeS); + if (opts & METIS_REORDER) reorder_metis(geom, nwindows, ws2, wsizeS); /* Cleanup */ free(eptr); @@ -1846,6 +1858,59 @@ void get_window_cells_h(geometry_t *geom, /* Global geometery */ /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Routine to get the 3D local cells in window n given the surface */ +/* window partitioning. Fills the loacl sparse array in vertical */ +/* stacks first. */ +/*-------------------------------------------------------------------*/ +void get_window_cells_v(geometry_t *geom, /* Global geometery */ + int wn, /* Window number */ + int *wsa, /* 3D wet cells in window wn */ + int *wsize, /* Size of wsa */ + int *ws2, /* 2D wet cells in window wn */ + int wsizeS /* Size of ws2 */ + ) +{ + int c, cc, c1, zm1; /* Cell indices / counters */ + int *layer; /* Vertical layer map */ + + /* Initialise the vertical layer map */ + layer = i_alloc_1d(wsizeS + 1); + memcpy(layer, ws2, (wsizeS + 1) * sizeof(int)); + layer[0] = 0; + + /* Initialise auxiliary maps for this window */ + for (c = 1; c <= geom->sgnum; c++) + geom->fm[c].ac = 0; + + /* Get the global to local surface maps for the sub-surface layers */ + /* filling the array in the (k,i,j) direction. */ + c1 = 1; /* 3D cell counter */ + wsize[wn] = 0; /* Initialise number of 3D cells */ + while (layer[0] < wsizeS) { + for (cc = 1; cc <= wsizeS; cc++) { + c = layer[cc]; /* Global coordinate in window wn */ + zm1 = geom->zm1[c]; + while (c != zm1) { + wsa[c1] = c; + c1++; + wsize[wn]++; + layer[cc] = zm1; + c = zm1; + zm1 = geom->zm1[c]; + } + if (layer[cc]) + layer[0]++; + layer[cc] = 0; + } + } + i_free_1d(layer); +} + +/* END get_window_cells_v() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Routine to generate the local maps in the window structure */ /*-------------------------------------------------------------------*/ @@ -6039,6 +6104,11 @@ window_t **win_data_build(master_t *master, /* Master data */ } } + if(master->trasc & FFSL && master->ultimate) { + windat[n]->trmin = d_alloc_1d(window[n]->szc); + windat[n]->trmax = d_alloc_1d(window[n]->szc); + } + /* Initialise all 3D and 2D variables required by the window */ for (cc = 1; cc <= window[n]->enon; cc++) { c = window[n]->wsa[cc]; @@ -6102,6 +6172,15 @@ window_t **win_data_build(master_t *master, /* Master data */ master->shwin[c] = windat[n]->shwin[cs] = (double)n; master->shinx[c] = windat[n]->shinx[cs] = (double)cs; } + /* + for (n = 1; n <= nwindows; n++) { + for (cc = 1; cc <= window[n]->b2_t; cc++) { + cs = window[n]->w2_t[cc]; + c = window[n]->wsa[cs]; + master->shinx[c] = windat[n]->shinx[cs] = (double)cc; + } + } + */ } return (windat); @@ -6508,6 +6587,8 @@ window_t *win_data_init(master_t *master, /* Master data structure */ windat->tran_mean = windat->tr_wcS[m]; if (strcmp("flow", master->trinfo_2d[m].name) == 0) windat->riverflow = windat->tr_wcS[m]; + if (strcmp("iflow", master->trinfo_2d[m].name) == 0) + windat->iriverflow = windat->tr_wcS[m]; if (strcmp("flow_depth", master->trinfo_2d[m].name) == 0) windat->riverdepth = windat->tr_wcS[m]; if (strcmp("equitide", master->trinfo_2d[m].name) == 0) @@ -6730,6 +6811,7 @@ window_t *win_data_init(master_t *master, /* Master data structure */ windat->attn_mean = master->attn_mean; windat->tran_mean = master->tran_mean; windat->riverflow = master->riverflow; + windat->iriverflow = master->iriverflow; windat->riverdepth = master->riverdepth; windat->equitide = master->equitide; windat->tpxotide = master->tpxotide; @@ -6830,6 +6912,8 @@ window_t *win_data_init(master_t *master, /* Master data structure */ memset(windat->tr_hdif, 0, winsize * sizeof(double)); if (windat->tr_vdif) memset(windat->tr_vdif, 0, winsize * sizeof(double)); + if (windat->iriverflow) + memset(windat->iriverflow, 0, window->szcS * sizeof(double)); windat->sur_e1 = i_alloc_1d(window->szeS); winsize = window->enonS + 1; if (windat->sederr) @@ -7077,10 +7161,11 @@ win_priv_t **win_consts_init(master_t *master, /* Master data */ wincon[n]->crfxf = d_alloc_1d(szm); wincon[n]->crfyf = d_alloc_1d(szm); wincon[n]->Fzh = d_alloc_1d(window[n]->szc); - if (master->means & TRANSPORT) + if (master->means & TRANSPORT) { wincon[n]->dzo = d_alloc_1d(szm); - wincon[n]->etao = d_alloc_1d(window[n]->szcS); + wincon[n]->etao = d_alloc_1d(window[n]->szcS); wincon[n]->suro = i_alloc_1d(window[n]->szcS); + } } if(master->trasc & FCT) { wincon[n]->crfxc = d_alloc_1d(szm); @@ -7308,6 +7393,7 @@ win_priv_t **win_consts_init(master_t *master, /* Master data */ wincon[n]->visc_method = master->visc_method; wincon[n]->visc_fact = master->visc_fact; wincon[n]->stab = master->stab; + wincon[n]->trsf = master->trsf; wincon[n]->cfl = master->cfl; wincon[n]->cfl_dt = master->cfl_dt; wincon[n]->lnm = master->lnm; @@ -8327,9 +8413,11 @@ void pre_run_setup(master_t *master, /* Master data structure */ windat[n]->etab[c] = wincon[n]->oldeta[c] = windat[n]->eta[c]; } if (wincon[n]->numbers1 & CENTI) { + int cg; for (cc = 1; cc <= window[n]->b3_t; cc ++) { c = window[n]->w3_t[cc]; - windat[n]->centi[c] = (double)c; + cg = window[n]->wsa[c]; + master->centi[cg] = windat[n]->centi[c] = (double)c; } } /* Set the lateral boundary conditions for velocity. */ @@ -9841,23 +9929,32 @@ void reorder_cells(geometry_t *window, /* Processing window */ int *cells, /* Cells to process (ctp) */ int vc, /* Number of ctp */ int vcs, /* Surface number of ctp */ - int *bot, /* Bottom vector */ + int *map, /* Vertical map */ int mode /* Re-ordering method */ ) { int c, cc, cs, cb, nc; + int zm1, zm2; nc = 1; - /* Order from the surface to the bottom, for all i then j */ if(mode == 1) { for (cc = 1; cc <= vcs; cc++) { - c = cells[cc]; - cb = bot[cc]; + c = cb = cells[cc]; + /* Find the bottom cell (centre and edges store this; vertices */ + /* dont. */ + zm1 = map[cb]; + zm2 = map[zm1]; + while (zm1 != zm2) { + cb = zm1; + zm1 = zm2; + zm2 = map[zm2]; + } + /* Re-order in a vertical stack */ while (c != cb) { ncells[nc] = c; nc++; - c = window->zm1[c]; + c = map[c]; } ncells[nc] = c; nc++; @@ -9866,12 +9963,19 @@ void reorder_cells(geometry_t *window, /* Processing window */ /* Order from the bottom to the surface, for all i then j */ else if(mode == 2) { for (cc = 1; cc <= vcs; cc++) { - c = bot[cc]; + c = cells[cc]; + zm1 = map[cb]; + zm2 = map[zm1]; + while (zm1 != zm2) { + c = zm1; + zm1 = zm2; + zm2 = map[zm2]; + } cs = cells[cc]; while (c != cs) { ncells[nc] = c; nc++; - c = window->zp1[c]; + c = map[c]; } ncells[nc] = c; nc++; diff --git a/model/hd-us/tracers/autotracer.c b/model/hd-us/tracers/autotracer.c index 34a8e98..45a10f5 100644 --- a/model/hd-us/tracers/autotracer.c +++ b/model/hd-us/tracers/autotracer.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: load_tracer.c 7413 2023-10-05 02:10:10Z her127 $ + * $Id: load_tracer.c 7517 2024-03-18 00:35:51Z her127 $ * */ @@ -22,7 +22,6 @@ #include "hd.h" #include "tracer.h" -int NAUTOTR = 221; tracer_info_t autotracerlist[] = { { .name = "salt", @@ -1254,7 +1253,7 @@ tracer_info_t autotracerlist[] = { .type = WATER|HYDRO|DIAGNOSTIC, .inwc = 1, .dissol = 1, - .advect = 1, + .advect = 0, .diffuse = 0, .diagn = 0, .m = -1, @@ -3156,7 +3155,7 @@ tracer_info_t autotracerlist[] = { .diffuse = 0, .diagn = 0, .m = -1, - .groupkey = "botstress" + .groupkey = "NONE" }, { .name = "swr_attenuation", @@ -3172,7 +3171,7 @@ tracer_info_t autotracerlist[] = { .diffuse = 0, .diagn = 0, .m = -1, - .groupkey = "botstress" + .groupkey = "NONE" }, { .name = "swr_deep_attenuation", @@ -3284,7 +3283,23 @@ tracer_info_t autotracerlist[] = { .diffuse = 0, .diagn = 0, .m = -1, - .groupkey = "NONE" + .groupkey = "riverflow" + }, + { + .name = "iflow", + .long_name = "Accumulated river flow", + .units = "m3", + .valid_range_wc[0] = 0.00e+00, + .valid_range_wc[1] = 1.00e+10, + .fill_value_wc = 0.00, + .type = INTER|HYDRO|DIAGNOSTIC, + .inwc = 1, + .dissol = 1, + .advect = 0, + .diffuse = 0, + .diagn = 0, + .m = -1, + .groupkey = "riverflow" }, { .name = "flow_depth", @@ -3611,3 +3626,5 @@ tracer_info_t autotracerlist[] = { .groupkey = "NONE" } }; + +int NAUTOTR = (int)(sizeof(autotracerlist) / sizeof(tracer_info_t)); diff --git a/model/hd-us/tracers/load_tracer.c b/model/hd-us/tracers/load_tracer.c index c69339d..6732ed2 100644 --- a/model/hd-us/tracers/load_tracer.c +++ b/model/hd-us/tracers/load_tracer.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: load_tracer.c 7470 2023-12-13 04:02:25Z her127 $ + * $Id: load_tracer.c 7530 2024-04-03 21:47:01Z her127 $ * */ @@ -63,15 +63,16 @@ void interp_data_s(master_t *master, char *fname, char *vname, double *ret, int int *mask, double t); void filter_tracer(tracer_info_t *trinfo, geometry_t *window, double **tr, int n); void glider_scaling(parameters_t *params, master_t *master, char *mapname, int tm); -int find_autotracer_by_name(char *name); -void copy_autotracer_by_name(char *name, tracer_info_t tr[], int ntr, int *n, +int find_autotracer_by_name(int type, char *name); +void copy_autotracer_by_name(int type, char *name, tracer_info_t tr[], int ntr, int *n, double **tra, double **trp); -int set_autotracer_by_name(char *name, tracer_info_t tr[], int ntr, int *n, +int set_autotracer_by_name(int type, char *name, tracer_info_t tr[], int ntr, int *n, double **tra, double **trp, char *buf); -void find_autotracer_by_groupkey(char *name, int *tra, int *n); -int copy_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, int *n); -int set_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, - int *n, char *key); +void find_autotracer_by_groupkey(int type, char *name, int *tra, int *n); +int copy_autotracer_by_groupkey(int type, char *name, tracer_info_t tr[], + int ntr, int *n); +int set_autotracer_by_groupkey(int type, char *name, tracer_info_t tr[], + int ntr, int *n, char *key); void update_autotracer(tracer_info_t tr[], int ntr, int *atr, int *mtr); void typename(int code, char *name); int set_tracer_3d(parameters_t *params, master_t *master, int ntr, @@ -635,7 +636,8 @@ void init_tracer_2d(parameters_t *params, /* Input parameters data */ master->u1_rad = master->u2_rad = master->sonic = master->wetcell = NULL; master->surfz = master->slope_x = NULL; master->tau_be1 = master->tau_be2 = master->tau_bm = NULL; - master->sederr = master->ecoerr = master->riverflow = master->riverdepth = NULL; + master->sederr = master->ecoerr = master->riverflow = NULL; + master->riverdepth = master->iriverflow = NULL; master->bathy_range_max = master->bathy_range_min = NULL; master->bathy_grad_max = master->bathy_grad_min = master->eta_tc = master->eta_inc = NULL; master->cellres = master->equitide = master->tpxotide = master->vhreg = NULL; @@ -1271,11 +1273,11 @@ int set_tracer_3d(parameters_t *params, if (master != NULL) geom= master->geom; - copy_autotracer_by_name("salt", trinfo, ntr, &tn, tr, &master->sal); - copy_autotracer_by_name("temp", trinfo, ntr, &tn, tr, &master->temp); + copy_autotracer_by_name(WATER, "salt", trinfo, ntr, &tn, tr, &master->sal); + copy_autotracer_by_name(WATER, "temp", trinfo, ntr, &tn, tr, &master->temp); if (params->means & VEL3D) { - n = copy_autotracer_by_groupkey("vel3d_mean", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "vel3d_mean", trinfo, ntr, &tn); if (tr != NULL) { master->u1m = tr[n++]; master->u2m = tr[n++]; @@ -1286,12 +1288,12 @@ int set_tracer_3d(parameters_t *params, } } if (params->means & KZ_M) { - copy_autotracer_by_name("Kzmean", trinfo, ntr, &tn, tr, &master->Kzm); + copy_autotracer_by_name(WATER, "Kzmean", trinfo, ntr, &tn, tr, &master->Kzm); if (tr != NULL) memset(master->Kzm, 0, geom->sgsiz * sizeof(double)); } if (params->means & TS) { - n = copy_autotracer_by_groupkey("ts_mean", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "ts_mean", trinfo, ntr, &tn); if (tr != NULL) { master->tempm = tr[n++]; master->saltm = tr[n++]; @@ -1301,14 +1303,14 @@ int set_tracer_3d(parameters_t *params, } if (params->means & MTRA3D) { n = tn; - copy_autotracer_by_name("tracer_mean", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "tracer_mean", trinfo, ntr, &tn, tr, &master->tram); sprintf(buf, "Mean %s", params->means_tra); strcpy(master->trinfo_3d[n].long_name, buf); memset(master->tram, 0, geom->sgsiz * sizeof(double)); } if (strcmp(params->trflux, "NONE") != 0) { - n = m = copy_autotracer_by_groupkey("tr_flux", trinfo, ntr, &tn); + n = m = copy_autotracer_by_groupkey(WATER, "tr_flux", trinfo, ntr, &tn); if (tr != NULL) { master->fluxe1 = tr[n++]; master->fluxe2 = tr[n++]; @@ -1321,42 +1323,42 @@ int set_tracer_3d(parameters_t *params, params->trfd2); } if (strlen(params->regions)) { - n = copy_autotracer_by_groupkey("regions", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "regions", trinfo, ntr, &tn); if (tr != NULL) { master->regionid = tr[n++]; master->regres = tr[n++]; } } if (params->trflsh) { - copy_autotracer_by_name("flush", trinfo, ntr, &tn, tr, &master->fltr); + copy_autotracer_by_name(WATER, "flush", trinfo, ntr, &tn, tr, &master->fltr); } if (strlen(params->trage)) { - copy_autotracer_by_name("age", trinfo, ntr, &tn, tr, &master->agetr); + copy_autotracer_by_name(WATER, "age", trinfo, ntr, &tn, tr, &master->agetr); } if (strcmp(params->trperc, "NONE") != 0) { n = tn; sprintf(buf, "percentile_%s", params->trperc); - copy_autotracer_by_name(buf, trinfo, ntr, &tn, tr, &master->perc); + copy_autotracer_by_name(WATER, buf, trinfo, ntr, &tn, tr, &master->perc); strcpy(master->trinfo_3d[n].name, buf); sprintf(buf, "Percentile for %s", params->trperc); strcpy(trinfo[n].long_name, buf); } if (strcmp(params->mixsc, "k-e") == 0) { - n = copy_autotracer_by_groupkey("k-e", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "k-e", trinfo, ntr, &tn); if (tr != NULL) { master->tke = tr[n++]; master->diss = tr[n++]; } } if (strcmp(params->mixsc, "k-w") == 0) { - n = copy_autotracer_by_groupkey("k-w", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "k-w", trinfo, ntr, &tn); if (tr != NULL) { master->tke = tr[n++]; master->omega = tr[n++]; } } if (strcmp(params->mixsc, "W88") == 0) { - n = copy_autotracer_by_groupkey("k-w", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "k-w", trinfo, ntr, &tn); if (tr != NULL) { master->tke = tr[n++]; master->omega = tr[n++]; @@ -1364,14 +1366,14 @@ int set_tracer_3d(parameters_t *params, } if (strcmp(params->mixsc, "mellor_yamada_2_0") == 0 || strcmp(params->mixsc, "mellor_yamada_2_0_estuarine") == 0) { - n = copy_autotracer_by_groupkey("my2.0", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "my2.0", trinfo, ntr, &tn); if (tr != NULL) { master->L = tr[n++]; } } if (strcmp(params->mixsc, "mellor_yamada_2_5") == 0 || strcmp(params->mixsc, "harcourt") == 0) { - n = copy_autotracer_by_groupkey("my2.5", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "my2.5", trinfo, ntr, &tn); if (tr != NULL) { master->Q2 = tr[n++]; master->Q2L = tr[n++]; @@ -1380,15 +1382,15 @@ int set_tracer_3d(parameters_t *params, } } if (params->smagorinsky > 0.0) { - copy_autotracer_by_name("smagorinsky", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "smagorinsky", trinfo, ntr, &tn, tr, &master->sdc); } if (params->show_layers) { - copy_autotracer_by_name("layer_thick", trinfo, ntr, &tn, tr, &master->layth); + copy_autotracer_by_name(WATER, "layer_thick", trinfo, ntr, &tn, tr, &master->layth); } if (params->save_force & OTEMP) { n = tn; - copy_autotracer_by_name("otemp", trinfo, ntr, &tn, tr, &master->otemp); + copy_autotracer_by_name(WATER, "otemp", trinfo, ntr, &tn, tr, &master->otemp); if (strlen(params->odata)) { if (params->save_force & ROAM) sprintf(buf, "%s(otemp=temp)", params->tdata); @@ -1400,7 +1402,7 @@ int set_tracer_3d(parameters_t *params, } if (params->save_force & OSALT) { n = tn; - copy_autotracer_by_name("osalt", trinfo, ntr, &tn, tr, &master->osalt); + copy_autotracer_by_name(WATER, "osalt", trinfo, ntr, &tn, tr, &master->osalt); if (strlen(params->odata)) { char buf[MAXSTRLEN]; if (params->save_force & ROAM) @@ -1413,7 +1415,7 @@ int set_tracer_3d(parameters_t *params, } if (params->rtemp) { n = tn; - copy_autotracer_by_name("rtemp", trinfo, ntr, &tn, tr, &master->rtemp); + copy_autotracer_by_name(WATER, "rtemp", trinfo, ntr, &tn, tr, &master->rtemp); if (strlen(params->tdata)) { char buf1[MAXSTRLEN]; if (params->save_force & ROAM) { @@ -1430,7 +1432,7 @@ int set_tracer_3d(parameters_t *params, } if (params->rsalt) { n = tn; - copy_autotracer_by_name("rsalt", trinfo, ntr, &tn, tr, &master->rsalt); + copy_autotracer_by_name(WATER, "rsalt", trinfo, ntr, &tn, tr, &master->rsalt); if (strlen(params->sdata)) { char buf[MAXSTRLEN]; char buf1[MAXSTRLEN]; @@ -1448,7 +1450,7 @@ int set_tracer_3d(parameters_t *params, } if (params->save_force & FTEMP) { n = tn; - copy_autotracer_by_name("temp_force", trinfo, ntr, &tn, tr, &master->ftemp); + copy_autotracer_by_name(WATER, "temp_force", trinfo, ntr, &tn, tr, &master->ftemp); sprintf(buf, "%s(temp_force=temp)", params->tdata); strcpy(trinfo[n].reset_file, buf); strcpy(trinfo[n].reset_dt, params->ftemp_input_dt); @@ -1456,7 +1458,7 @@ int set_tracer_3d(parameters_t *params, } if (params->save_force & FSALT) { n = tn; - copy_autotracer_by_name("salt_force", trinfo, ntr, &tn, tr, &master->fsalt); + copy_autotracer_by_name(WATER, "salt_force", trinfo, ntr, &tn, tr, &master->fsalt); sprintf(buf, "%s(salt_force=salt)", params->sdata); strcpy(trinfo[n].reset_file, buf); strcpy(trinfo[n].reset_dt, params->fsalt_input_dt); @@ -1464,7 +1466,7 @@ int set_tracer_3d(parameters_t *params, } if (params->save_force & FVELU) { n = tn; - copy_autotracer_by_name("velu_force", trinfo, ntr, &tn, tr, &master->fvelu); + copy_autotracer_by_name(WATER, "velu_force", trinfo, ntr, &tn, tr, &master->fvelu); sprintf(buf, "%s(velu_force=u)", params->vdata); strcpy(trinfo[n].reset_file, buf); strcpy(trinfo[n].reset_dt, params->fvelu_input_dt); @@ -1472,20 +1474,20 @@ int set_tracer_3d(parameters_t *params, } if (params->save_force & FVELV) { n = tn; - copy_autotracer_by_name("velv_force", trinfo, ntr, &tn, tr, &master->fvelv); + copy_autotracer_by_name(WATER, "velv_force", trinfo, ntr, &tn, tr, &master->fvelv); sprintf(buf, "%s(velv_force=v)", params->vdata); strcpy(trinfo[n].reset_file, buf); strcpy(trinfo[n].reset_dt, params->fvelv_input_dt); strcpy(trinfo[n].reset_interp, params->fvelv_interp); } if (params->rtemp & (RLX_ADPT|RLX_REG|RLX_OBC)) { - copy_autotracer_by_name("temp_tc", trinfo, ntr, &tn, tr, &master->temp_tc); + copy_autotracer_by_name(WATER, "temp_tc", trinfo, ntr, &tn, tr, &master->temp_tc); } if (params->rsalt & (RLX_ADPT|RLX_REG|RLX_OBC)) { - copy_autotracer_by_name("salt_tc", trinfo, ntr, &tn, tr, &master->salt_tc); + copy_autotracer_by_name(WATER, "salt_tc", trinfo, ntr, &tn, tr, &master->salt_tc); } if (params->save_force & OVELU) { - copy_autotracer_by_name("ovelu", trinfo, ntr, &tn, tr, NULL); + copy_autotracer_by_name(WATER, "ovelu", trinfo, ntr, &tn, tr, NULL); if (strlen(params->vdata)) { char buf[MAXSTRLEN]; if (params->save_force & ROAM) @@ -1497,7 +1499,7 @@ int set_tracer_3d(parameters_t *params, } } if (params->save_force & OVELV) { - copy_autotracer_by_name("ovelv", trinfo, ntr, &tn, tr, NULL); + copy_autotracer_by_name(WATER, "ovelv", trinfo, ntr, &tn, tr, NULL); if (strlen(params->vdata)) { char buf[MAXSTRLEN]; if (params->save_force & ROAM) @@ -1512,95 +1514,95 @@ int set_tracer_3d(parameters_t *params, if (params->tendf) { - copy_autotracer_by_groupkey("tend", trinfo, ntr, &tn); + copy_autotracer_by_groupkey(WATER, "tend", trinfo, ntr, &tn); if (params->waves & STOKES_DRIFT) { - copy_autotracer_by_groupkey("tend_wave", trinfo, ntr, &tn); + copy_autotracer_by_groupkey(WATER, "tend_wave", trinfo, ntr, &tn); } } if (strlen(params->trtend)) { - copy_autotracer_by_groupkey("tr_tend", trinfo, ntr, &tn); + copy_autotracer_by_groupkey(WATER, "tr_tend", trinfo, ntr, &tn); } if (params->waves & SPECTRAL) { - n = copy_autotracer_by_groupkey("spec_wave", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "spec_wave", trinfo, ntr, &tn); if (tr != NULL) { master->wave_stke1 = tr[n++]; master->wave_stke2 = tr[n++]; } } if (params->numbers & BRUNT) { - copy_autotracer_by_name("brunt", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "brunt", trinfo, ntr, &tn, tr, &master->brunt); } if (params->numbers & INT_WAVE) { - copy_autotracer_by_name("int_wave_speed", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "int_wave_speed", trinfo, ntr, &tn, tr, &master->int_wave); } if (params->numbers & RICHARD_GR) { - copy_autotracer_by_name("richardson_gr", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "richardson_gr", trinfo, ntr, &tn, tr, &master->rich_gr); } if (params->numbers & RICHARD_FL) { - copy_autotracer_by_name("richardson_fl", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "richardson_fl", trinfo, ntr, &tn, tr, &master->rich_fl); } if (params->numbers & REYNOLDS) { - copy_autotracer_by_name("reynolds", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "reynolds", trinfo, ntr, &tn, tr, &master->reynolds); } if (params->numbers & FROUDE) { - copy_autotracer_by_name("froude", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "froude", trinfo, ntr, &tn, tr, &master->froude); } if (params->numbers & SIGMA_T) { - copy_autotracer_by_name("sigma_t", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "sigma_t", trinfo, ntr, &tn, tr, &master->sigma_t); } if (params->numbers & ENERGY) { - copy_autotracer_by_name("energy", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "energy", trinfo, ntr, &tn, tr, &master->energy); } if (params->numbers & KINETIC) { - copy_autotracer_by_name("kenergy", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "kenergy", trinfo, ntr, &tn, tr, &master->kenergy); } if (params->do_pt) { - copy_autotracer_by_name("ptconc", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "ptconc", trinfo, ntr, &tn, tr, &master->ptconc); } if (params->numbers & SOUND) { - n = copy_autotracer_by_groupkey("sound", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "sound", trinfo, ntr, &tn); if (tr != NULL) { master->sound = tr[n++]; master->schan = tr[n++]; } } if (params->numbers & ROSSBY_IN) { - copy_autotracer_by_name("rossby_internal", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "rossby_internal", trinfo, ntr, &tn, tr, &master->rossby_in); } if (params->numbers & SPEED_3D) { - copy_autotracer_by_name("current_speed_3d", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "current_speed_3d", trinfo, ntr, &tn, tr, &master->speed_3d); } if (params->numbers & SHEAR_V) { - copy_autotracer_by_name("shear_vert", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "shear_vert", trinfo, ntr, &tn, tr, &master->shear_v); } if (params->numbers & BUOY_PROD) { - copy_autotracer_by_name("buoy_prod", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "buoy_prod", trinfo, ntr, &tn, tr, &master->b_prod); } if (params->numbers & SHEAR_PROD) { - copy_autotracer_by_name("shear_prod", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "shear_prod", trinfo, ntr, &tn, tr, &master->s_prod); } if (!(params->decf & (NONE|DEC_ETA))) { - copy_autotracer_by_name("decorr_e1", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "decorr_e1", trinfo, ntr, &tn, tr, &master->decv1); } if (strlen(params->imp3df)) { n = tn; - copy_autotracer_by_name("imp3df", trinfo, ntr, &tn, tr, NULL); + copy_autotracer_by_name(WATER, "imp3df", trinfo, ntr, &tn, tr, NULL); strcpy(trinfo[n].name, params->imp3dn); strcpy(trinfo[n].long_name, params->imp3dn); strcpy(trinfo[n].units, params->imp3du); @@ -1613,7 +1615,7 @@ int set_tracer_3d(parameters_t *params, sprintf(trinfo[n].data, "[data=%s]", params->imp3df); } if (params->numbers & DUMMIES) { - n = copy_autotracer_by_groupkey("dummies", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "dummies", trinfo, ntr, &tn); if (tr != NULL) { master->dum1 = tr[n++]; master->dum2 = tr[n++]; @@ -1621,52 +1623,52 @@ int set_tracer_3d(parameters_t *params, } } if (params->numbers & UNIT) { - copy_autotracer_by_name("unit", trinfo, ntr, &tn, tr, &master->unit); + copy_autotracer_by_name(WATER, "unit", trinfo, ntr, &tn, tr, &master->unit); } if (params->numbers & PASS) { - copy_autotracer_by_name("passive", trinfo, ntr, &tn, tr, NULL); + copy_autotracer_by_name(WATER, "passive", trinfo, ntr, &tn, tr, NULL); } if (params->numbers & GLIDER) { - copy_autotracer_by_name("glider", trinfo, ntr, &tn, tr, &master->glider); + copy_autotracer_by_name(WATER, "glider", trinfo, ntr, &tn, tr, &master->glider); } if (params->numbers1 & U1VHC) { - copy_autotracer_by_name("u1vhc", trinfo, ntr, &tn, tr, &master->u1vhc); + copy_autotracer_by_name(WATER, "u1vhc", trinfo, ntr, &tn, tr, &master->u1vhc); } if (params->numbers1 & VOLCONT) { - copy_autotracer_by_name("vol_cont", trinfo, ntr, &tn, tr, &master->volcont); + copy_autotracer_by_name(WATER, "vol_cont", trinfo, ntr, &tn, tr, &master->volcont); } if (params->numbers1 & CENTI) { - copy_autotracer_by_name("cell_index", trinfo, ntr, &tn, tr, &master->centi); + copy_autotracer_by_name(WATER, "cell_index", trinfo, ntr, &tn, tr, &master->centi); } if (strlen(params->nprof)) { - copy_autotracer_by_name("nprof", trinfo, ntr, &tn, tr, &master->nprof); + copy_autotracer_by_name(WATER, "nprof", trinfo, ntr, &tn, tr, &master->nprof); } if (params->closf & VZ_R) { - copy_autotracer_by_name("VZ0", trinfo, ntr, &tn, tr, &master->vz0b); + copy_autotracer_by_name(WATER, "VZ0", trinfo, ntr, &tn, tr, &master->vz0b); } if (params->closf & KZ_R) { - copy_autotracer_by_name("KZ0", trinfo, ntr, &tn, tr, &master->kz0b); + copy_autotracer_by_name(WATER, "KZ0", trinfo, ntr, &tn, tr, &master->kz0b); } if (strlen(params->monotr)) { n = tn; - copy_autotracer_by_name("mono", trinfo, ntr, &tn, tr, &master->mono); + copy_autotracer_by_name(WATER, "mono", trinfo, ntr, &tn, tr, &master->mono); sprintf(trinfo[n].long_name, "Monotinicity of %s", params->monotr); } if (params->porusplate) { - n = copy_autotracer_by_groupkey("porusplate", trinfo, ntr, &tn); + n = copy_autotracer_by_groupkey(WATER, "porusplate", trinfo, ntr, &tn); if (tr != NULL) { master->reefe1 = tr[n++]; master->reefe2 = tr[n++]; } } if (params->riverflow == 2) { - copy_autotracer_by_name("flow_salt", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "flow_salt", trinfo, ntr, &tn, tr, &master->riversalt); } if (params->swr_type & SWR_3D && strlen(params->swr_attn)) { n = tn; if (tracer_find_index("swr_attenuation", ntr, trinfo) == -1) { - copy_autotracer_by_name("swr_attenuation", trinfo, ntr, &tn, + copy_autotracer_by_name(WATER, "swr_attenuation", trinfo, ntr, &tn, tr, &master->swr_attn); if (tr != NULL) trn_dataset(params->swr_attn, trinfo, n, master->ntr, @@ -1793,7 +1795,7 @@ int set_tracer_2d(parameters_t *params, sprintf(buf, "%c", '\0'); if (!(params->cfl & NONE)) { - n = set_autotracer_by_groupkey("cfl", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "cfl", trinfo, ntr, &tn, buf); master->cfl2d = tr[n++]; master->cfl3d = tr[n++]; master->cour = tr[n++]; @@ -1802,17 +1804,17 @@ int set_tracer_2d(parameters_t *params, master->courn = tr[n++]; } if (!(params->mixlayer & NONE)) - set_autotracer_by_name("mixed_layer", trinfo, ntr, &tn, tr, &master->mixl, buf); + set_autotracer_by_name(INTER, "mixed_layer", trinfo, ntr, &tn, tr, &master->mixl, buf); if (params->lnm != 0.0) - set_autotracer_by_name("steric", trinfo, ntr, &tn, tr, &master->steric, buf); + set_autotracer_by_name(INTER, "steric", trinfo, ntr, &tn, tr, &master->steric, buf); if (params->vorticity & ABSOLUTE) - set_autotracer_by_name("abs_vor", trinfo, ntr, &tn, tr, &master->av, buf); + set_autotracer_by_name(INTER, "abs_vor", trinfo, ntr, &tn, tr, &master->av, buf); if (params->vorticity & RELATIVE) - set_autotracer_by_name("rel_vor", trinfo, ntr, &tn, tr, &master->rv, buf); + set_autotracer_by_name(INTER, "rel_vor", trinfo, ntr, &tn, tr, &master->rv, buf); if (params->vorticity & POTENTIAL) - set_autotracer_by_name("pot_vor", trinfo, ntr, &tn, tr, &master->pv, buf); + set_autotracer_by_name(INTER, "pot_vor", trinfo, ntr, &tn, tr, &master->pv, buf); if (params->vorticity & TENDENCY) { - n = set_autotracer_by_groupkey("vorticity", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "vorticity", trinfo, ntr, &tn, buf); master->rv_drvdt = tr[n++]; master->rv_nonlin = tr[n++]; master->rv_beta = tr[n++]; @@ -1822,64 +1824,64 @@ int set_tracer_2d(parameters_t *params, master->rv_bsc = tr[n++]; } if (params->diff_scale & VH_REG) - set_autotracer_by_name("u1vh_region", trinfo, ntr, &tn, tr, &master->vhreg, buf); + set_autotracer_by_name(INTER, "u1vh_region", trinfo, ntr, &tn, tr, &master->vhreg, buf); if (params->numbers & ROSSBY_EX) - set_autotracer_by_name("rossby_external", trinfo, ntr, &tn, tr, &master->rossby_ex, buf); + set_autotracer_by_name(INTER, "rossby_external", trinfo, ntr, &tn, tr, &master->rossby_ex, buf); if (params->numbers & SPEED_2D) - set_autotracer_by_name("current_speed_2d", trinfo, ntr, &tn, tr, &master->speed_2d, buf); + set_autotracer_by_name(INTER, "current_speed_2d", trinfo, ntr, &tn, tr, &master->speed_2d, buf); if (params->numbers & SPEED_SQ) - set_autotracer_by_name("speed_sq", trinfo, ntr, &tn, tr, &master->speed_sq, buf); + set_autotracer_by_name(INTER, "speed_sq", trinfo, ntr, &tn, tr, &master->speed_sq, buf); if (params->numbers & OBC_PHASE) - set_autotracer_by_name("obc_phase", trinfo, ntr, &tn, tr, &master->obc_phase, buf); + set_autotracer_by_name(INTER, "obc_phase", trinfo, ntr, &tn, tr, &master->obc_phase, buf); if (params->numbers & WIND_CD) - set_autotracer_by_name("wind_Cd", trinfo, ntr, &tn, tr, &master->wind_Cd, buf); + set_autotracer_by_name(INTER, "wind_Cd", trinfo, ntr, &tn, tr, &master->wind_Cd, buf); if (params->numbers & CELLRES) { - n = set_autotracer_by_groupkey("resolution", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "resolution", trinfo, ntr, &tn, buf); master->cellres = tr[n++]; master->sarea = tr[n++]; master->searea = tr[n++]; } if (params->numbers1 & CELLAREA) { - n = set_autotracer_by_groupkey("area", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "area", trinfo, ntr, &tn, buf); master->carea = tr[n++]; master->earea = tr[n++]; } if (params->numbers1 & MESHUN) - set_autotracer_by_name("mesh_uniformity", trinfo, ntr, &tn, tr, &master->meshun, buf); + set_autotracer_by_name(INTER, "mesh_uniformity", trinfo, ntr, &tn, tr, &master->meshun, buf); if (params->waves & (TAN_RAD|WAVE_FOR) && params->tendf) { - n = set_autotracer_by_groupkey("rad_stress", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "rad_stress", trinfo, ntr, &tn, buf); master->u1_rad = tr[n++]; master->u2_rad = tr[n++]; } if (params->means & ETA_M) - set_autotracer_by_name("eta_mean", trinfo, ntr, &tn, tr, &master->etam, buf); + set_autotracer_by_name(INTER, "eta_mean", trinfo, ntr, &tn, tr, &master->etam, buf); if (params->save_force & FETA) { - n = set_autotracer_by_name("eta_force", trinfo, ntr, &tn, tr, &master->feta, buf); + n = set_autotracer_by_name(INTER, "eta_force", trinfo, ntr, &tn, tr, &master->feta, buf); sprintf(buf, "%s(eta_force=eta)", params->edata); strcpy(master->trinfo_2d[n].reset_file, buf); strcpy(master->trinfo_2d[n].reset_dt, params->feta_input_dt); strcpy(master->trinfo_2d[n].reset_interp, params->feta_interp); } if (params->means & WIND) { - n = set_autotracer_by_groupkey("wind_mean", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wind_mean", trinfo, ntr, &tn, buf); master->w1m = tr[n++]; master->w2m = tr[n++]; } if (params->means & VEL2D) { - n = set_autotracer_by_groupkey("vel2d_mean", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "vel2d_mean", trinfo, ntr, &tn, buf); master->u1am = tr[n++]; master->u2am = tr[n++]; } if (params->means & MTRA2D) { char key[MAXSTRLEN]; - n = set_autotracer_by_name("tracer_mean_2d", trinfo, ntr, &tn, tr, &master->tram, buf); + n = set_autotracer_by_name(INTER, "tracer_mean_2d", trinfo, ntr, &tn, tr, &master->tram, buf); strcpy(master->trinfo_2d[n].name, "tracer_mean"); sprintf(key, "Mean %s", params->means_tra); strcpy(master->trinfo_2d[n].long_name, key); memset(master->tram, 0, geom->sgsizS * sizeof(double)); } if (params->heatflux & ADVANCED) { - n = set_autotracer_by_groupkey("heatflux", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "heatflux", trinfo, ntr, &tn, buf); master->nhfd = tr[n++]; master->swrd = tr[n++]; master->lwrd = tr[n++]; @@ -1887,9 +1889,9 @@ int set_tracer_2d(parameters_t *params, master->shfd = tr[n++]; } if (params->heatflux & (INVERSE|COMP_HEAT|COMP_HEAT_MOM)) - set_autotracer_by_name("nhf", trinfo, ntr, &tn, tr, &master->nhfd, buf); + set_autotracer_by_name(INTER, "nhf", trinfo, ntr, &tn, tr, &master->nhfd, buf); if (params->heatflux & (COMP_HEAT | COMP_HEAT_MOM | COMP_HEAT_NONE)) { - n = set_autotracer_by_groupkey("heatcomp", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "heatcomp", trinfo, ntr, &tn, buf); if (params->heatflux & (COMP_HEAT_MOM | COMP_HEAT_NONE)) { /* See logic in heatflux.c:comp_heat_mom */ master->swr = tr[n++]; @@ -1900,48 +1902,48 @@ int set_tracer_2d(parameters_t *params, master->shfn = n++; if (params->heatflux & COMP_HEAT_NONE) { if (strlen(params->precip)) { - n = set_autotracer_by_name("precip", trinfo, ntr, &tn, tr, NULL, buf); + n = set_autotracer_by_name(INTER, "precip", trinfo, ntr, &tn, tr, NULL, buf); master->precipn = n; } if (strlen(params->evap)) { - n = set_autotracer_by_name("evap", trinfo, ntr, &tn, tr, NULL, buf); + n = set_autotracer_by_name(INTER, "evap", trinfo, ntr, &tn, tr, NULL, buf); master->evapn = n; } } } if (params->heatflux & NET_HEAT) { - n = set_autotracer_by_groupkey("netheat", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "netheat", trinfo, ntr, &tn, buf); master->nhfd = tr[n++]; master->swrd = tr[n++]; } if (params->saltflux & (ADVANCED | BULK | ORIGINAL)) - set_autotracer_by_name("nsf", trinfo, ntr, &tn, tr, &master->nsfd, buf); + set_autotracer_by_name(INTER, "nsf", trinfo, ntr, &tn, tr, &master->nsfd, buf); if (params->saltflux & (ADVANCED | ORIGINAL)) { - n = set_autotracer_by_groupkey("saltflux", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "saltflux", trinfo, ntr, &tn, buf); master->precipn = n++; master->evapn = n++; } if (params->waves & BOT_STR) - set_autotracer_by_name("wave_Cd", trinfo, ntr, &tn, tr, &master->wave_Cd, buf); + set_autotracer_by_name(INTER, "wave_Cd", trinfo, ntr, &tn, tr, &master->wave_Cd, buf); if (params->waves & TAN_RAD) { - n = set_autotracer_by_groupkey("rad_force", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "rad_force", trinfo, ntr, &tn, buf); master->wave_Sxy = tr[n++]; master->wave_Syx = tr[n++]; } if (params->waves & WAVE_FOR) { - n = set_autotracer_by_groupkey("wave_force", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wave_force", trinfo, ntr, &tn, buf); master->wave_Fx = tr[n++]; master->wave_Fy = tr[n++]; } if (params->waves & (NEARSHORE|STOKES|SPECTRAL)) { - set_autotracer_by_name("wave_k", trinfo, ntr, &tn, tr, &master->wave_k, buf); + set_autotracer_by_name(INTER, "wave_k", trinfo, ntr, &tn, tr, &master->wave_k, buf); } if (params->waves & (STOKES|SPECTRAL)) { - n = set_autotracer_by_groupkey("wave_stokes", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wave_stokes", trinfo, ntr, &tn, buf); master->wave_ste1 = tr[n++]; master->wave_ste2 = tr[n++]; if (params->waves & STOKES_DRIFT) { - n = set_autotracer_by_groupkey("wave_stress", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wave_stress", trinfo, ntr, &tn, buf); master->tau_w1 = tr[n++]; master->tau_w2 = tr[n++]; master->tau_diss1 = tr[n++]; @@ -1949,7 +1951,7 @@ int set_tracer_2d(parameters_t *params, } } if (params->waves & NEARSHORE) { - n = set_autotracer_by_groupkey("wave_nearshore", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wave_nearshore", trinfo, ntr, &tn, buf); master->wave_Kb = tr[n++]; /*master->wave_k = tr[n++];*/ master->wave_P = tr[n++]; @@ -1969,7 +1971,7 @@ int set_tracer_2d(parameters_t *params, master->wave_froly = tr[n++]; } if (!(params->do_wave & NONE)) { - n = set_autotracer_by_groupkey("waves", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "waves", trinfo, ntr, &tn, buf); master->ustrcw = tr[n++]; master->wave_ub = tr[n++]; master->wave_period = tr[n++]; @@ -1977,15 +1979,15 @@ int set_tracer_2d(parameters_t *params, master->wave_amp = tr[n++]; } if (params->etarlx & (RELAX|ALERT|BOUNDARY)) - set_autotracer_by_name("oeta", trinfo, ntr, &tn, tr, NULL, buf); + set_autotracer_by_name(INTER, "oeta", trinfo, ntr, &tn, tr, NULL, buf); if (params->etarlx & ETA_TPXO || params->etarlx & ETA_ADPT) - set_autotracer_by_name("eta_tc", trinfo, ntr, &tn, tr, &master->eta_tc, buf); + set_autotracer_by_name(INTER, "eta_tc", trinfo, ntr, &tn, tr, &master->eta_tc, buf); if (params->etarlx & ETA_ADPT) - set_autotracer_by_name("eta_inc", trinfo, ntr, &tn, tr, &master->eta_inc, buf); + set_autotracer_by_name(INTER, "eta_inc", trinfo, ntr, &tn, tr, &master->eta_inc, buf); if (params->avhrr) - set_autotracer_by_name("AVHRR", trinfo, ntr, &tn, tr, &master->avhrr, buf); + set_autotracer_by_name(INTER, "AVHRR", trinfo, ntr, &tn, tr, &master->avhrr, buf); if (params->ghrsst) { - n = set_autotracer_by_groupkey("ghrsst", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "ghrsst", trinfo, ntr, &tn, buf); strcpy(master->trinfo_2d[n].reset_file, params->ghrsst_path); strcpy(master->trinfo_2d[n].reset_dt, params->ghrsst_dt); if (strlen(params->ghrsst_irule)) @@ -1997,61 +1999,61 @@ int set_tracer_2d(parameters_t *params, /* Window index tracer currently not used, as it's the same as */ /* cell_index. In the surface layer cc and w2_t[cc] are the */ /* same for windows. */ - n = set_autotracer_by_groupkey("windiag", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "windiag", trinfo, ntr, &tn, buf); master->shwin = tr[n++]; master->shinx= tr[n++]; - /*set_autotracer_by_name("windows", trinfo, ntr, &tn, tr, &master->shwin, buf);*/ + /*set_autotracer_by_name(INTER, "windows", trinfo, ntr, &tn, tr, &master->shwin, buf);*/ } if (strlen(params->bathystats)) { - n = set_autotracer_by_groupkey("bathystat", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "bathystat", trinfo, ntr, &tn, buf); master->bathy_range_min = tr[n++]; master->bathy_range_max = tr[n++]; master->bathy_grad_min = tr[n++]; master->bathy_grad_max = tr[n++]; } if (contains_token(params->alert, "ACTIVE") != NULL) { - n = set_autotracer_by_groupkey("alerts", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "alerts", trinfo, ntr, &tn, buf); master->alert_a = tr[n++]; master->alert_c = tr[n++]; master->u1vhin = tr[n++]; master->u2vhin = tr[n++]; } if (params->fillf & (WEIGHTED|MONOTONIC) || (params->tmode & SP_FFSL)) - set_autotracer_by_name("vol_cons", trinfo, ntr, &tn, tr, &master->vol_cons, buf); + set_autotracer_by_name(INTER, "vol_cons", trinfo, ntr, &tn, tr, &master->vol_cons, buf); if (params->numbers & SOUND) - set_autotracer_by_name("sonic_depth", trinfo, ntr, &tn, tr, &master->sonic, buf); + set_autotracer_by_name(INTER, "sonic_depth", trinfo, ntr, &tn, tr, &master->sonic, buf); if (params->numbers & EKPUMP) { - n = set_autotracer_by_groupkey("ekman", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "ekman", trinfo, ntr, &tn, buf); master->sep = tr[n++]; master->bep = tr[n++]; } if (params->numbers & TIDEFR) - set_autotracer_by_name("SH_tide_front", trinfo, ntr, &tn, tr, &master->tfront, buf); + set_autotracer_by_name(INTER, "SH_tide_front", trinfo, ntr, &tn, tr, &master->tfront, buf); if (params->numbers1 & WINDSPDI) { - n = set_autotracer_by_groupkey("wind", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "wind", trinfo, ntr, &tn, buf); master->windcs = tr[n++]; master->windcd = tr[n++]; } if (params->numbers & WET_CELLS) - set_autotracer_by_name("wet_cells", trinfo, ntr, &tn, tr, &master->wetcell, buf); + set_autotracer_by_name(INTER, "wet_cells", trinfo, ntr, &tn, tr, &master->wetcell, buf); if (params->numbers & SURF_LAYER) - set_autotracer_by_name("surf_layer", trinfo, ntr, &tn, tr, &master->surfz, buf); + set_autotracer_by_name(INTER, "surf_layer", trinfo, ntr, &tn, tr, &master->surfz, buf); if (params->numbers & SLOPE) - set_autotracer_by_name("surf_slope", trinfo, ntr, &tn, tr, &master->slope_x, buf); + set_autotracer_by_name(INTER, "surf_slope", trinfo, ntr, &tn, tr, &master->slope_x, buf); if (params->numbers & BOTSTRESS) { - n = set_autotracer_by_groupkey("botstress", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "botstress", trinfo, ntr, &tn, buf); master->tau_be1 = tr[n++]; master->tau_be2 = tr[n++]; master->tau_bm = tr[n++]; } if (strlen(params->swr_babs)) { n = tn; - copy_autotracer_by_name("swr_bot_absorb", trinfo, ntr, &tn, tr, &master->swr_babs); + copy_autotracer_by_name(INTER, "swr_bot_absorb", trinfo, ntr, &tn, tr, &master->swr_babs); trn_dataset(params->swr_babs, trinfo, n, params->ntrS, params->atrS, tr, 1.0); } if (params->swr_type & SWR_2D && strlen(params->swr_attn)) { n = tn; - copy_autotracer_by_name("swr_attenuation", trinfo, ntr, &tn, tr, &master->swr_attn); + copy_autotracer_by_name(INTER, "swr_attenuation", trinfo, ntr, &tn, tr, &master->swr_attn); if (strlen(params->swr_regions)) trf_dataset(params->swr_attn, trinfo, n, params->ntrS, params->atrS, tr, 0.073); else @@ -2059,53 +2061,56 @@ int set_tracer_2d(parameters_t *params, } if (strlen(params->swr_attn1)) { n = tn; - copy_autotracer_by_name("swr_deep_attenuation", trinfo, ntr, &tn, tr, &master->swr_attn1); + copy_autotracer_by_name(INTER, "swr_deep_attenuation", trinfo, ntr, &tn, tr, &master->swr_attn1); tr_dataset(params->swr_attn1, &trinfo[n], 0.073); } if (strlen(params->swr_tran)) { n = tn; - copy_autotracer_by_name("swr_transmission", trinfo, ntr, &tn, tr, &master->swr_tran); + copy_autotracer_by_name(INTER, "swr_transmission", trinfo, ntr, &tn, tr, &master->swr_tran); if (strlen(params->swr_regions)) trf_dataset(params->swr_tran, trinfo, n, params->ntrS, params->atrS, tr, 0.26); else trn_dataset(params->swr_tran, trinfo, n, params->ntrS, params->atrS, tr, 0.26); } if (strlen(params->swr_regions)) { - n = set_autotracer_by_groupkey("swr_regions", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "swr_regions", trinfo, ntr, &tn, buf); master->swreg = tr[n++]; master->swrms = tr[n++]; master->attn_mean = tr[n++]; master->tran_mean = tr[n++]; } if (params->riverflow) { - set_autotracer_by_name("flow", trinfo, ntr, &tn, tr, &master->riverflow, buf); + n = set_autotracer_by_groupkey(INTER, "riverflow", trinfo, ntr, &tn, buf); + master->riverflow = tr[n++]; + master->iriverflow = tr[n++]; + /*set_autotracer_by_name(INTER, "flow", trinfo, ntr, &tn, tr, &master->riverflow, buf);*/ if (params->riverflow == 2) - set_autotracer_by_name("flow_depth", trinfo, ntr, &tn, tr, &master->riverdepth, buf); + set_autotracer_by_name(INTER, "flow_depth", trinfo, ntr, &tn, tr, &master->riverdepth, buf); } if (params->tidep) - set_autotracer_by_name("equitide", trinfo, ntr, &tn, tr, &master->equitide, buf); + set_autotracer_by_name(INTER, "equitide", trinfo, ntr, &tn, tr, &master->equitide, buf); if (params->numbers1 & TPXO) - set_autotracer_by_name("tpxotide", trinfo, ntr, &tn, tr, &master->tpxotide, buf); + set_autotracer_by_name(INTER, "tpxotide", trinfo, ntr, &tn, tr, &master->tpxotide, buf); if (params->numbers1 & TPXOV) { - n = set_autotracer_by_groupkey("tpxo_vel", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "tpxo_vel", trinfo, ntr, &tn, buf); master->tpxovelu = tr[n++]; master->tpxovelv = tr[n++]; } if (params->numbers1 & TPXOT) { - n = set_autotracer_by_groupkey("tpxo_tran", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "tpxo_tran", trinfo, ntr, &tn, buf); master->tpxotranu = tr[n++]; master->tpxotranv = tr[n++]; } if (params->numbers1 & TRAN2D) { - n = set_autotracer_by_groupkey("transport_2d", trinfo, ntr, &tn, buf); + n = set_autotracer_by_groupkey(INTER, "transport_2d", trinfo, ntr, &tn, buf); master->uat = tr[n++]; master->vat = tr[n++]; } if (params->decf & DEC_ETA) - set_autotracer_by_name("decorr_e1", trinfo, ntr, &tn, tr, &master->decv1, buf); + set_autotracer_by_name(INTER, "decorr_e1", trinfo, ntr, &tn, tr, &master->decv1, buf); if (strlen(params->imp2df)) { n = tn; - set_autotracer_by_name("imp2df", trinfo, ntr, &tn, tr, NULL, buf); + set_autotracer_by_name(INTER, "imp2df", trinfo, ntr, &tn, tr, NULL, buf); strcpy(trinfo[n].name, params->imp2dn); strcpy(trinfo[n].long_name, params->imp2dn); strcpy(trinfo[n].units, params->imp2du); @@ -2231,7 +2236,7 @@ void calc_scaling(parameters_t *params, /* Input parameters data */ int kbof = params->nz; /* Bottom k level */ int plotif = 1; /* Print individual files */ int daf = 0; /* Depth average scaling */ - geometry_t *geom = master->sgrid; + geometry_t *geom = master->geom; /*-----------------------------------------------------------------*/ /* Open the input scaling data file */ @@ -2407,9 +2412,11 @@ void calc_scaling(parameters_t *params, /* Input parameters data */ fof = i_alloc_1d(npoints); fv = d_alloc_1d(params->nz + 1); sc = d_alloc_1d(params->nz + 1); - if (daf) scda = d_alloc_1d(npoints); - memset(scda, 0, sizeof(double) * npoints); memset(fof, 0, sizeof(int) * npoints); + if (daf) { + scda = d_alloc_1d(npoints); + memset(scda, 0, sizeof(double) * npoints); + } /*-----------------------------------------------------------------*/ /* Assign the point lat, lon and depth */ @@ -2425,7 +2432,7 @@ void calc_scaling(parameters_t *params, /* Input parameters data */ lon[j] = x[i]; lat[j] = y[i]; /* Get the bottom k value */ - hd_xyztoindex_m(geom, lon[j], lat[j], 0.0, &kk, &cs[j], &cb[j]); + hd_xyztoindex_m(master, lon[j], lat[j], 0.0, &kk, &cs[j], &cb[j]); kbot[j] = geom->s2k[cb[j]]; for (m = 0; m < ntsfiles; m++) { if (x[m] != lon[j] && y[m] != lat[j]) continue; @@ -5876,11 +5883,12 @@ void value_init_sed(master_t *master, /* Master data */ /*-------------------------------------------------------------------*/ /* Finds an autotracer in the global list by its name */ /*-------------------------------------------------------------------*/ -int find_autotracer_by_name(char *name) +int find_autotracer_by_name(int type, char *name) { int i; for (i = 0; i < NAUTOTR; i++) - if (strcmp(autotracerlist[i].name, name) == 0) break; + if (autotracerlist[i].type & type && + strcmp(autotracerlist[i].name, name) == 0) break; return(i); } @@ -5892,7 +5900,8 @@ int find_autotracer_by_name(char *name) /* Copies an autotracer attributes to a tracer info structure given */ /* a tracer name. */ /*-------------------------------------------------------------------*/ -void copy_autotracer_by_name(char *name, +void copy_autotracer_by_name(int type, + char *name, tracer_info_t tr[], int ntr, int *n, @@ -5904,7 +5913,8 @@ void copy_autotracer_by_name(char *name, if ((tn = tracer_find_index(name, ntr, tr)) < 0) { int i; for (i = 0; i < NAUTOTR; i++) - if (strcmp(autotracerlist[i].name, name) == 0) break; + if (autotracerlist[i].type & type && + strcmp(autotracerlist[i].name, name) == 0) break; tracer_copy(&tr[*n], &autotracerlist[i]); tr[*n].n = tr[*n].m = *n; if (tra != NULL && trp != NULL) *trp = tra[*n]; @@ -5921,14 +5931,15 @@ void copy_autotracer_by_name(char *name, /* Same as copy_autotracer_by_name() except includes tracer */ /* initialisation. */ /*-------------------------------------------------------------------*/ -int set_autotracer_by_name(char *name, tracer_info_t tr[], int ntr, +int set_autotracer_by_name(int type, char *name, tracer_info_t tr[], int ntr, int *n, double **tra, double **trp, char *buf) { int tn, sn = *n; if ((tn = tracer_find_index(name, ntr, tr) < 0)) { int i; for (i = 0; i < NAUTOTR; i++) - if (strcmp(autotracerlist[i].name, name) == 0) break; + if (autotracerlist[i].type & type && + strcmp(autotracerlist[i].name, name) == 0) break; tracer_copy(&tr[*n], &autotracerlist[i]); tr[*n].n = tr[*n].m = *n; tr_dataset(buf, &tr[*n], 0.0); @@ -5946,13 +5957,14 @@ int set_autotracer_by_name(char *name, tracer_info_t tr[], int ntr, /*-------------------------------------------------------------------*/ /* Finds an autotracer in the global list by its group key */ /*-------------------------------------------------------------------*/ -void find_autotracer_by_groupkey(char *name, int *tra, int *n) +void find_autotracer_by_groupkey(int type, char *name, int *tra, int *n) { int i; *n = 0; for (i = 0; i < NAUTOTR; i++) { - if (strcmp(autotracerlist[i].groupkey, name) == 0) { + if (autotracerlist[i].type & type && + strcmp(autotracerlist[i].groupkey, name) == 0) { tra[*n] = i; *n += 1; } @@ -5967,7 +5979,7 @@ void find_autotracer_by_groupkey(char *name, int *tra, int *n) /* Copies an autotracer attributes to a tracer info structure given */ /* a tracer groupkey. */ /*-------------------------------------------------------------------*/ -int copy_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, int *n) +int copy_autotracer_by_groupkey(int type, char *name, tracer_info_t tr[], int ntr, int *n) { int i, j; int nf, nt = 0; @@ -5981,7 +5993,8 @@ int copy_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, int *n) strcpy(buf, autotracerlist[i].groupkey); nf = parseline(buf, fields, MAXNUMARGS); for (j = 0; j < nf; j++) { - if (strcmp(fields[j], name) == 0) { + if (autotracerlist[i].type & type && + strcmp(fields[j], name) == 0) { if ((tn = tracer_find_index(autotracerlist[i].name, ntr, tr)) < 0) { tracer_copy(&tr[*n], &autotracerlist[i]); tr[*n].n = tr[*n].m = *n; @@ -5994,8 +6007,8 @@ int copy_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, int *n) return(sn); } -int set_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, - int *n, char *key) +int set_autotracer_by_groupkey(int type, char *name, tracer_info_t tr[], + int ntr, int *n, char *key) { int i, j; int nf, nt = 0; @@ -6008,7 +6021,8 @@ int set_autotracer_by_groupkey(char *name, tracer_info_t tr[], int ntr, strcpy(buf, autotracerlist[i].groupkey); nf = parseline(buf, fields, MAXNUMARGS); for (j = 0; j < nf; j++) { - if (strcmp(fields[j], name) == 0) { + if (autotracerlist[i].type & type && + strcmp(fields[j], name) == 0) { if ((tn = tracer_find_index(autotracerlist[i].name, ntr, tr)) < 0) { tracer_copy(&tr[*n], &autotracerlist[i]); tr_dataset(key, &tr[*n], tr[*n].fill_value_wc); @@ -6277,7 +6291,7 @@ void write_autotracer(master_t *master) fprintf(fp," * reserved. See the license file for disclaimer and full\n"); fprintf(fp," * use/redistribution conditions.\n"); fprintf(fp," * \n"); - fprintf(fp," * $Id: load_tracer.c 7470 2023-12-13 04:02:25Z her127 $\n", version, ctime(&t)); + fprintf(fp," * $Id: load_tracer.c 7530 2024-04-03 21:47:01Z her127 $\n", version, ctime(&t)); fprintf(fp," *\n"); fprintf(fp," */\n\n"); @@ -6286,7 +6300,7 @@ void write_autotracer(master_t *master) fprintf(fp,"#include \"hd.h\"\n"); fprintf(fp,"#include \"tracer.h\"\n\n"); - fprintf(fp, "int NAUTOTR = %d;\n", trt); + /*fprintf(fp, "int NAUTOTR = %d;\n", trt);*/ fprintf(fp, "tracer_info_t autotracerlist[] = {\n"); tc = 1; for (n = 0; n < ntr; n++) { @@ -6310,6 +6324,7 @@ void write_autotracer(master_t *master) } } fprintf(fp, "};\n"); + fprintf(fp, "\nint NAUTOTR = (int)(sizeof(autotracerlist) / sizeof(tracer_info_t));\n"); i_free_1d(atr); i_free_1d(m3d); i_free_1d(m2d); diff --git a/model/hd-us/tracers/reset.c b/model/hd-us/tracers/reset.c index abfff0d..f38a1fb 100644 --- a/model/hd-us/tracers/reset.c +++ b/model/hd-us/tracers/reset.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: reset.c 7471 2023-12-13 04:02:46Z her127 $ + * $Id: reset.c 7571 2024-05-28 05:20:55Z riz008 $ * */ @@ -733,6 +733,18 @@ static double trans_reset_event(sched_event_t *event, double t) tsync = master->dt; tsin = reset->tnext + master->grid_dt; } + + /* + * The above block can cause tsin to be set beyond the end of + * simulation. This can cause the eval routines below issuing a + * FATAL error. The following early return is to prevent this early + * exit. This function returns to sched_set_time which returns to + * the while loop in main. As we'll be at the last time point, the + * while loop in main will exit anyway + */ + if (tsin > tsout) + return(tsout); + if (master->tmode & SP_STRUCT) { ve3 = geom->s2c; ve2 = geom->s2c; diff --git a/model/hd-us/tracers/tracers.c b/model/hd-us/tracers/tracers.c index fc16a51..159048f 100644 --- a/model/hd-us/tracers/tracers.c +++ b/model/hd-us/tracers/tracers.c @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: tracers.c 7472 2023-12-13 04:03:07Z her127 $ + * $Id: tracers.c 7565 2024-05-27 05:02:17Z her127 $ * */ @@ -431,8 +431,8 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ ) { int nn; /* Tracer index */ - int c, cc; /* Sparse indices */ - int c2; /* 2D cell corresponding to 3D cell */ + int c, cc, c1, c2; /* Sparse indices */ + int cs; /* 2D cell corresponding to 3D cell */ int cb; /* Bottom sparse coordinate (cell centre) */ int vc; /* Tracer cells to process counter */ int vcs; /* Surface tracer cells to process counter */ @@ -458,6 +458,7 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ double minval = 1e-10; /* Minimum value for velocity */ int slf = 1; /* Set to zero on the first sub-step */ int itermax = 20; /* Maximum number of substeps */ + int ssuf = 1; /* Use one sided Lipschitz condition */ int courf; /* Flag to calculate Courant numbers */ int ii = 0, jj = 0, kk = 0; /* (i,j,k) location of sub-step violation */ double vm = 0.0, vs = 0.0; /* Sub-step Courant number & grid spacing */ @@ -565,6 +566,7 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ } else if (wincon->trasc & FFSL) { itermax = 200; + sf = wincon->trsf; memset(wincon->nw, 0, window->sgsiz * sizeof(double)); /* Get the grid spacing. */ for (cc = 1; cc <= window->a3_t; cc++) { @@ -610,45 +612,24 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ dtm = dtu = windat->dt; if (wincon->means & TRANSPORT) dtm = dtu = windat->dttr; if (wincon->stab & (SUB_STEP | SUB_STEP_NOSURF | SUB_STEP_TRACER)) { - for (cc = 1; cc <= vc; cc++) { c = wincon->s1[cc]; /* Wet cell to process */ - c2 = window->m2d[c]; /* 2D cell corresponding to 3D cell */ + cs = window->m2d[c]; /* 2D cell corresponding to 3D cell */ zp1 = window->zp1[c]; - /* - for (j = 1; j <= window->npe[c2] / 2; j++) { - e1 = window->c2e[j][c]; - j1 = jo(j, window->npe[c2]); - e2 = window->c2e[j1][c]; - d4 = 0.5 * (u1[e1] + u1[e2]); - if (wincon->trasc == FFSL) - d4 = max(fabs(window->eSc[j][c2] * u1[e1] + - window->eSc[j1][c2] * u1[e2]), - wincon->u1kh[c]/ (2.0 * window->hacell[j][c2])); - - if (fabs(d4) < minval) - d4 = minval; - d1 = sf * fabs(2.0 * window->hacell[j][c2] / d4); - if (d1 < dtm) { - dtm = d1; - sprintf(vt, "u"); - vm = d4; - vs = 2.0 * window->hacell[j][c2]; - ii = c2; - jj = window->m2de[e1]; - kk = window->s2k[c]; - } - } - */ - for (j = 1; j <= window->npe[c2]; j++) { - e1 = window->c2e[j][c]; - e2 = window->m2de[e1]; + for (j = 1; j <= window->npe[cs]; j++) { + e = window->c2e[j][c]; + e2 = window->m2de[e]; + c1 = window->e2c[e][0]; + c2 = window->e2c[e][1]; if (wincon->trasc & FFSL) { - int c1 = window->c2c[j][c]; - d4 = (u[c] * window->costhu1[e2] + v[c] * window->sinthu1[e2]) - - (u[c1] * window->costhu1[e2] + v[c1] * window->sinthu1[e2]); + /* Rotate the centered velocities to be normal to edge j */ + d4 = (u[c1] * window->costhu1[e2] + v[c1] * window->sinthu1[e2]) - + (u[c2] * window->costhu1[e2] + v[c2] * window->sinthu1[e2]); + /* Streamlines only cross if the downstream velocity is */ + /* faster. */ + if (ssuf) d4 = (d4 < 0.0) ? -d4 : 0.0; } else - d4 = u1[e1]; + d4 = u1[e]; if (fabs(d4) < minval) d4 = minval; @@ -658,8 +639,8 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ sprintf(vt, "u"); vm = d4; vs = window->h2au1[e2]; - ii = c2; - jj = window->m2de[e1]; + ii = cs; + jj = window->m2de[e]; kk = window->s2k[c]; } } @@ -668,9 +649,11 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ /* Note : surface tracer values are calculated on the basis of */ /* mass conservation, hence Courant violations need not be */ /* considered. */ - if (wincon->trasc & FFSL) - d4 = (cc <= vcs) ? 0.0 : fabs(w[zp1] - w[c]); - else + if (wincon->trasc & FFSL) { + d4 = (cc <= vcs) ? 0.0 : w[zp1] - w[c]; + if (window->cask[c] & W_BOT) d4 = 0.0; + if (ssuf) d4 = (d4 < 0.0) ? -d4 : 0.0; + } else d4 = (cc <= vcs) ? 0.0 : 0.5 * (w[c] + w[zp1]); if (fabs(d4) < minval) d4 = minval; @@ -682,7 +665,7 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ vm = d4; vs = wincon->dz[c] * wincon->Ds[c2]; ii = c; - jj = window->m2de[e1]; + jj = window->m2de[e]; kk = window->s2k[c]; } } @@ -1008,7 +991,6 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ tr[c] *= wincon->Ds[c2]; else tr[c] *= wincon->Hn1[c2]; - wincon->tr_gr[tc][c] = -((dtracer[c] / window->cellarea[c2] + (Fz[zp1] - Fz[c])) / wincon->dz[c]); tr[c] = runge_kutta(window, windat, wincon, 1.0, 1.0, tc, c, slf); @@ -1107,8 +1089,10 @@ int advect_diffuse(geometry_t *window, /* Window geometry */ if (bgzf) save_OBC_tr(window, windat, wincon, Fx, tr, n, 2); if (cdb) printf("end %f\n",tr[cdb]); /* Clip the tracer for FFSL schemes */ - if (wincon->trasc & FFSL && (wincon->fillf & (MONOTONIC|CLIP))) - clip_ffsl(window, windat, wincon, tr); + if (wincon->trasc & FFSL) { + if (wincon->fillf & (MONOTONIC|CLIP)) clip_ffsl(window, windat, wincon, tr); + if (wincon->ultimate & UL_CLIP) clip_ffsl_ultimate(window, windat, wincon, tr); + } if (n == wincon->monon) get_source_minmax(window, windat, wincon, tr, dtu, 1); } /* rkstage loop end */ diff --git a/model/hd-us/tracers/transport.c b/model/hd-us/tracers/transport.c index 19a31ed..5865031 100644 --- a/model/hd-us/tracers/transport.c +++ b/model/hd-us/tracers/transport.c @@ -12,7 +12,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: transport.c 7473 2023-12-13 04:03:27Z her127 $ + * $Id: transport.c 7566 2024-05-27 05:02:47Z her127 $ * */ @@ -100,13 +100,19 @@ double get_dist(double x1, double y1, double x2, double y2); void ffsl_doc(geometry_t *window, window_t *windat, win_priv_t *wincon, double *ntr, double dtu, int mode); void check_monotone(geometry_t *window, window_t *windat, win_priv_t *wincon, double tr, int c); -void universal_limit(geometry_t *window, window_t *windat, win_priv_t *wincon, - int cin, int co, int cs, int cb, double dt, double *tr, double *Fx, double *Fz, int *mask); +int universal_limit(geometry_t *window, window_t *windat, win_priv_t *wincon, + int cin, int co, int cs, int cb, double dt, double *tr, + double *Fx, double *Fz, int *mask); +void universal_limith(geometry_t *window, window_t *windat, win_priv_t *wincon, + int cin, int co, int cs, int cb, double dt, double *tr, double *Fx, double *Fz, + int *mask); void ffsl_van_leer(double *F, double *tr, double *vel, double *crf, int *cl, int c, int *fmap, int *bmap, double *sminz, double *smaxz); void ffsl_quickest(geometry_t *window, double *F, double *tr, double *vel, double *crf, int *cl, int c, int *fmap, int *bmap, double *sminz, double *smaxz); +double check_tr_value(geometry_t *window, window_t *windat, win_priv_t *wincon, double *tr, + double *Fx, double *Fz, double dt, int c); /*-------------------------------------------------------------------*/ /* Transport step */ @@ -2008,7 +2014,7 @@ void ffsl_don(geometry_t *window, /* Window geometry */ int sodanos = 0; /* Compute distances on spheriod */ int tranf; /* Transverse term flag */ int doint = 1; /* Integrate tracers along streamline */ - int momo2 = 1; /* Second order momentum estimation */ + int momo2 = 0; /* Second order momentum estimation */ int vscheme = V_VC; /* Vertical scheme */ int verbose = 0; @@ -2137,6 +2143,13 @@ void ffsl_don(geometry_t *window, /* Window geometry */ windat->monox[e] = -HUGE; } } + if (windat->trmin && windat->trmax) { + for (cc = 1; cc <= window->n3_t; cc++) { + c = window->w3_t[ee]; + windat->trmin[c] = HUGE; + windat->trmax[c] = -HUGE; + } + } /* for (ee = 1; ee <= window->n3_e1; ee++) { e = window->w3_e1[ee]; @@ -2192,7 +2205,10 @@ void ffsl_don(geometry_t *window, /* Window geometry */ ei = e; if (wincon->fillf & CLIP) smin[e] = smax[e] = tr[cl[ee]]; - if (wincon->ultimate) smin[e] = smax[e] = tr_mod[cl[ee]]; + /* Get the initial min/max in the cell (Thuburn, Eq. 32 & 33) */ + if (wincon->ultimate & UL_IPROJ) smin[e] = smax[e] = tr_mod[cl[ee]]; + if (wincon->ultimate & UL_ITR) smin[e] = smax[e] = tr[cl[ee]]; + /*if (tmode) first = 1;*/ while (cl[ee] > 0) { double uu = u, vv = v; @@ -2280,8 +2296,16 @@ void ffsl_don(geometry_t *window, /* Window geometry */ /* Get the minimum and maximum values encountered in the */ /* stencil along the streamline. */ if (wincon->ultimate) { - get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); - get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); + if (wincon->ultimate & UL_SPROJ) { + /* Get the upstream min/max bounds ((Thuburn, Eq. 32 & 33) */ + get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); + get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); + } + /* Widen the range for inflow bounds (Thuburn, Eq. 46 & 47) */ + if (wincon->ultimate & UL_SSTCL) { + get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); + get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); + } } else if (wincon->fillf & CLIP) { get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); @@ -2303,8 +2327,8 @@ void ffsl_don(geometry_t *window, /* Window geometry */ /* Get the number of sub-timesteps used */ n++; - if(n > 10) { - printf("stuck in loop wn=%d e=%d [%f %f]\n",window->wn,e,window->u1x[window->m2de[e]],window->u1y[window->m2de[e]]); + if(n > 100) { + printf("stuck in loop wn=%d e=%d [%f %f] dt=%f\n",window->wn,e,window->u1x[window->m2de[e]],window->u1y[window->m2de[e]], dtu); exit(0); } /* Get the contributions to the horizontal transverse terms */ @@ -2388,7 +2412,7 @@ void ffsl_don(geometry_t *window, /* Window geometry */ if (tranf & T_HORZ) { for (cc = 1; cc <= window->b3_t; cc++) { c = window->w3_t[cc]; - tr_moc[c] /= (double)mask[c]; + if(mask[c]) tr_moc[c] /= (double)mask[c]; tr_moc[c] = tf * tr_moc[c] + (1.0 - tf) * tr[c]; } set_tr_nograd(window, tr_moc); @@ -2412,554 +2436,38 @@ void ffsl_don(geometry_t *window, /* Window geometry */ window->zp1, window->zm1, mn, mx); else if (vscheme == V_QK) ff_sl_quickest(window, Fz, tr_moc, w, crfzf, clzf, 1, wincon->vc, - wincon->s1, window->zp1, window->zm1, mn, mx); - - for (cc = 1; cc <= wincon->vc; cc++) { - c = wincon->s1[cc]; - ci = (w[c] > 0.0) ? c : window->zm1[c]; - c2 = (w[c] > 0.0) ? window->zm1[c] : c; - zm1 = window->zm1[c]; - /* If the maximum Courant number is < 1 (horizontally and */ - /* vertically) then do not use projected values for computation */ - /* of the vertical flux. */ - if (vscheme == V_VC) { - if (cour[c] < 1.0) - ffsl_van_leer(Fz, tr, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); - else - ffsl_van_leer(Fz, tr_moc, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); - } - if (vscheme == V_QC) { - if (cour[c] < 1.0) - ffsl_quickest(window, Fz, tr, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); - else - ffsl_quickest(window, Fz, tr_moc, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); - } - if (wincon->fillf & CLIP) sminz[c] = smaxz[c] = tr[c2]; - if (wincon->ultimate) sminz[c] = smaxz[c] = tr_moc[c2]; - - if (w[c] == 0.0 || zm1 == window->zm1[zm1]) { - Fz[c] = 0.0; - continue; - } - ci = clzf[c]; - if (c == verbose) printf(" vint %f %f %f\n",crfzf[c],wincon->dz[ci],Fz[c]); - Fz[c] *= (crfzf[c] * wincon->dz[ci]); - - /* Integrate over the "integer" component of the trajectory */ - ci = (w[c] > 0.0) ? window->zm1[c] : wincon->s1[cc]; - dz = wincon->dz[ci]; - dist = fabs(w[c] * dtu); - while (dz < dist) { - /* Get the minimum and maximum values encountered in the */ - /* stencil along the streamline. */ - if (wincon->ultimate) { - sminz[c] = min(sminz[c], tr_moc[ci]); - smaxz[c] = max(smaxz[c], tr_moc[ci]); - } else if (wincon->fillf & CLIP) { - sminz[c] = min(sminz[c], tr[ci]); - smaxz[c] = max(smaxz[c], tr[ci]); - } - /* - if (wincon->monon >= 0 && tr == windat->tr_wc[wincon->monon]) { - windat->monon[c] = min(windat->monon[c], tr_moc[ci]); - windat->monox[c] = max(windat->monox[c], tr_moc[ci]); - } - */ - Fz[c] += (dz * tr_moc[ci]); - dist -= dz; - ci = (w[c] < 0.0) ? window->zp1[ci] : window->zm1[ci]; - mask[c] = mask[ci]; - if (wincon->dz[ci] > 0) - dz = wincon->dz[ci]; - } - - /* Ensure Fz has correct sign */ - if (w[c] < 0) - Fz[c] *= -1.0; - if (c == verbose) printf(" vert %d w=%f : %f\n", clzf[c], w[c], Fz[c]); - - /* Universal flux limiter Eq 34 & 35 of Thuburn (1995) */ - if (wincon->ultimate) { - Fz[c] = min(Fz[c], smaxz[c] * w[c] * dtu); - Fz[c] = max(Fz[c], sminz[c] * w[c] * dtu); - } - } - - /* Limit the mean tracer value if rquired */ - if (wincon->ultimate) { - memset(mask, 0, window->sze * sizeof(int)); - /* Limit the horizontal fluxes in the order of the sparse */ - /* vector. Some edges may be missed doing this if a cell is */ - /* limited and the incoming flux is an outgoing flux for a */ - /* subsequent cell. */ - /* - for (cc = 1; cc <= wincon->vcs1; cc++) { - int co, cs, cb; - c = wincon->s1[cc]; - co = wincon->i3[cc]; - cs = wincon->i2[cc]; - cb = wincon->i1[cc]; - while (c != window->zm1[c]) { - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); - c = window->zm1[c]; - } - } - */ - /* Limit the horizontal fluxes following outgoing flow from */ - /* cells. */ - /* - for (cc = 1; cc <= window->b2_t; cc++) { - int cco, co, cs, cb; - c = wincon->i6[cc]; - if (!(window->cask[c] & (W_WET|W_AUX))) continue; - cco = window->c2cc[c]; - co = window->sur_t[cco]; - cs = window->nsur_t[cco]; - cb = window->bot_t[cco]; - while (c != window->zm1[c]) { - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); - c = window->zm1[c]; - } - } - */ - for (cc = 1; cc <= wincon->s6[0]; cc++) { - int cco, co, cs, cb, c2; - c = wincon->s6[cc]; - c2 = window->m2d[c]; - /* Note: do not limit on auxiliary cells */ - if (!(window->cask[c] & (W_WET))) continue; - cco = wincon->s7[c2]; - co = wincon->i3[cco]; - cs = max(co, wincon->i2[cco]); - cb = wincon->i1[cco]; - /* - co = window->sur_t[cco]; - cs = window->nsur_t[cco]; - cb = window->bot_t[cco]; - */ - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); - } - } -} - -/* END ffsl_don() */ -/*-------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------*/ -/* Flux Form semi-Lagrange: tracks streamlines from cell edges and */ -/* piecewise linearly integrates tracer values to get a streamline */ -/* mean value, which it then applies to that edge. Also invokes a */ -/* limiter (using streamline mean cell centered values) to keep the */ -/* solution monotonic. */ -/* Duplicate routine of ffsl_don() to parallel process. */ -/*-------------------------------------------------------------------*/ -void ffsl_donc(geometry_t *window, /* Window geometry */ - window_t *windat, /* Window data */ - win_priv_t *wincon, /* Window constants */ - double *tr, /* Tracer array */ - double *Fx, /* Horizontal flux */ - double *Fz, /* Vertical flux */ - double dtu /* Timestep */ - ) -{ - double dt; /* Sub-time step for Euler velocity */ - double time_left; /* Number of sub-timesteps per dt */ - double *u1, *u2; /* Horizontal edge velocity */ - double *w; /* Vertical velocity */ - double *u1v; /* Horizontal volume flux */ - double *nu, *nv; /* Cell centered horizontal velocity */ - double *nw = wincon->nw; /* Cell centered vertical velocity */ - double *cx = wincon->tr_mod_x; /* x position of streamline */ - double *cy = wincon->tr_mod_y; /* y position of streamline */ - double *cz = wincon->tr_mod_z; /* z poition of streamline */ - double *tr_mod = wincon->tr_mod; /* z transverse tracer */ - double *crfzf; /* Fractional factors of face trajectories */ - double *crfzc; /* Fractional factors of centre trajectories */ - int *clzf; /* Cell counter at face source cell */ - int *clzc; /* Cell counter cell centre source */ - double *ntr = wincon->w5; /* New tracer value */ - double *dint = wincon->w3; /* Streamline integrated tracer value */ - double *smin = wincon->crfxc; /* Minimum streamline value */ - double *smax = wincon->crfyc; /* Maximum streamline value */ - double *sminz = wincon->crfxf; /* Minimum vertical streamline value*/ - double *smaxz = wincon->crfyf; /* Maximum vertical streamline value*/ - double *tr_moc = wincon->Fzh; - int *cl = wincon->s2; /* Cell location of streamline */ - int *e2ee = wincon->s4; /* Mapping from edge to index */ - int *mask = wincon->s3; - double u, v; /* Velocities */ - double trs, tre; /* Tracer at start and end of segments */ - double dz; /* Cell thickness */ - double dist, d1; /* Lengths of segment */ - double sinth, costh; /* sin and cos of edge angle */ - double m2deg = 1.0 / (60.0 * 1852.0); /* Meter to degree */ - double px, py, pz; - int cc, c, cs, ci, kb, zm1, zp1; - int ee, e, e2, es, ei, pei; - int c1, c2; - int n; - int has_proj = (strlen(projection) > 0); - int is_geog = has_proj && (strcasecmp(projection, GEOGRAPHIC_TAG) == 0); - double tf = 0.55; /* Factor for implicitness of cross terms (0.5:1)*/ - int trconcf = 1; /* TRCONC on OBCs */ - int tmode = (wincon->means & TRANSPORT) ? 1 : 0; - int sodanos = 0; /* Compute distances on spheriod */ - int tranf; /* Transverse term flag */ - int doint = 1; /* Integrate tracers along streamline */ - int momo2 = 0; /* Second order momentum estimation */ - - int nmax = 0, emax; - double **cxc, **cyc; - int **clc; - - - /* Set which transverse terms are used */ - tranf = (T_VERT|T_HORZ|T_HV); - if (!is_geog) m2deg = 1.0; - if (tmode) { - u1 = windat->ume; - w = windat->wm; - u1v = windat->u1vm; - /* Get the u2 (tangential) velocity at the edge. */ - u2 = wincon->crfxc; - for (ee = 1; ee <= window->b3_e1; ee++) { - int eoe; - double fs; - e = window->w3_e1[ee]; - es = window->m2de[e]; - u2[e] = 0.0; - for (n = 1; n <= window->nee[es]; n++) { - eoe = window->eSe[n][e]; - fs = window->h1au1[window->m2de[eoe]] / window->h2au1[es]; - if (!eoe) continue; - u2[e] += fs * window->wAe[n][e] * u1[eoe]; - } - } - } else { - u1 = windat->u1; - u2 = windat->u2; - w = windat->w; - u1v = windat->u1flux3d; - } - - /*-----------------------------------------------------------------*/ - /* Initialise */ - memset(cx, 0, window->sze * sizeof(double)); - memset(cy, 0, window->sze * sizeof(double)); - memset(cz, 0, window->sze * sizeof(double)); - memset(ntr, 0.0, window->sze * sizeof(double)); - memset(dint, 0.0, window->sze * sizeof(double)); - memset(tr_moc, 0.0, window->szc * sizeof(double)); - memset(mask, 0, window->szc * sizeof(int)); - - /*-----------------------------------------------------------------*/ - /* Define a new tracer with z-direction transverse terms */ - clzf = wincon->clzf; - clzc = wincon->clzc; - crfzf = wincon->crfzf; - crfzc = wincon->crfzc; - memcpy(tr_mod, tr, window->szc * sizeof(double)); - if (tranf & T_VERT) { - for (cc = 1; cc <= window->a3_t; cc++) { - c = window->w3_t[cc]; - zp1 = (nw[c] < 0.0) ? clzc[c] : window->zm1[clzc[c]]; - ci = (nw[c] < 0.0) ? window->zm1[zp1] : clzc[c]; - tr_mod[c] = tf * tr[ci] + (1.0 - tf) * tr[c]; - /* Note: tf determines how much of the tracer at the forward */ - /* timestep is used (i.e. the 'implicitness' of the solution). */ - /* This should lie between 0.5 (centered) or 1 (implicit). The */ - /* implicit solutions tend to be more diffuse. */ - if (crfzc[c] > 0.) { - tr_mod[c] += tf * (crfzc[c] * (tr[zp1] - tr[ci])); - } - } - set_tr_nograd(window, tr_mod); - /* OBC ghost cells */ - for (n = 0; n < window->nobc; n++) { - open_bdrys_t *open = window->open[n]; - int trn = (int)tr[0]; - for (ee = 1; ee <= open->no3_e1; ee++) { - c = c2 = open->obc_e2[ee]; - zp1 = (nw[c] < 0.0) ? clzc[c] : window->zm1[clzc[c]]; - ci = (nw[c] < 0.0) ? window->zm1[zp1] : clzc[c]; - do { - c = open->omape[ee][c]; - zp1 = open->omape[ee][zp1]; - ci = open->omape[ee][ci]; - tr_mod[c] = tf * tr[ci] + (1.0 - tf) * tr[c]; - if (crfzc[c2] > 0.) - tr_mod[c] += tf * (crfzc[c2] * (tr[zp1] - tr[ci])); - } while (c != open->omape[ee][c]); - } - } - } - - /*-----------------------------------------------------------------*/ - /* Set the initial streamline location */ - for (ee = 1; ee <= window->a3_e1; ee++) { - e = window->w3_e1[ee]; - e2 = window->m2de[e]; - c1 = window->e2c[e][0]; - c2 = window->e2c[e][1]; - cx[e] = window->u1x[e2]; - cy[e] = window->u1y[e2]; - cz[e] = 0.5 * (wincon->cellz[c1] + wincon->cellz[c2]); - e2ee[e] = ee; - } - /* - for (ee = 1; ee <= window->n3_e1; ee++) { - e = window->w3_e1[ee]; - smin[e] = HUGE; - smax[e] = -HUGE; - } - */ - /* Get the initial cell centre; this is the cell upstream of the */ - /* edge. */ - for (cc = 1; cc <= window->b3_t; cc++) { - c = window->w3_t[cc]; - c2 = window->m2d[c]; - for (n = 1; n <= window->npe[c2]; n++) { - e = window->c2e[n][c]; - ee = e2ee[e]; - if (u1[e] > 0.0) - cl[ee] = (window->eSc[n][c2] < 0) ? window->c2c[n][c] : c; - else - cl[ee] = (window->eSc[n][c2] < 0) ? c : window->c2c[n][c]; - } - } - /* Use interior cells for normal open boundary edges */ - for (n = 0; n < window->nobc; n++) { - open_bdrys_t *open = window->open[n]; - int trn = (int)tr[0]; - if (!(open->bcond_tra[trn] & TRCONC)) { - trconcf = 0; - for (ee = 1; ee <= open->no3_e1; ee++) { - e = open->obc_e1[ee]; - cl[e2ee[e]] = open->obc_e2[ee]; - } - } - } - - /*-----------------------------------------------------------------*/ - /* Limit least squares interpolation functions */ - get_linear_limit(window, windat, wincon, tr_mod); - - /*-----------------------------------------------------------------*/ - /* Trace the streamline back to the origin */ - for (ee = 1; ee <= window->a3_e1; ee++) { - int first = 0; - e = window->w3_e1[ee]; - if (!trconcf && window->eask[e] & (W_NOBC|W_TOBC)) continue; - es = window->m2de[e]; - c1 = window->e2c[e][0]; - c2 = window->e2c[e][1]; - time_left = dtu; - u = u1[e] * window->costhu1[es] + u2[e] * window->costhu2[es]; - v = u1[e] * window->sinthu1[es] + u2[e] * window->sinthu2[es]; - n = 0; - ei = e; - - if (wincon->fillf & CLIP) smin[e] = smax[e] = tr[cl[ee]]; - if (wincon->ultimate) smin[e] = smax[e] = tr_mod[cl[ee]]; - - /*if (tmode) first = 1;*/ - - while (cl[ee] > 0) { - double uu = u, vv = v; - /* Set the current cell the streamline resides in. Note; these */ - /* locations are cell centre locations, stored for every edge. */ - c = ci = cl[ee]; - pz = wincon->cellz[ci]; - - /* Get the velocties at the origin. For the first segment */ - /* sub-step use (rotated u1) velocities at the edge location. */ - if (first) { - u = hd_trans_interp(window, windat->gsx, cx[e], cy[e], pz, c, 0, 0); - v = hd_trans_interp(window, windat->gsy, cx[e], cy[e], pz, c, 0, 1); - } - first = 1; - - /* If the streamline crosses an open boundary (i.e. the source */ - /* edge is a normal boundary edge) then compute the remaining */ - /* distance using the normal open boundary velocity, and set */ - /* the segment end concentration to that in the boundary ghost */ - /* cell. Then terminate the streamline tracing. */ - if (window->eask[ei] & W_NOBC && window->cask[abs(cl[ee])] & W_GOBC) { - dist = time_left * fabs(windat->u1[ei]); - cl[ee] = abs(cl[ee]); - trs = tre; - tre = tr[cl[ee]]; - dint[e] += dist; - if (doint) { - ntr[e] += 0.5 * dist * (trs + tre); - } else - ntr[e] += (tr_mod[ci] * dist); - if (tranf & T_HV) { - tr_moc[c1] += tre; - tr_moc[c2] += tre; - mask[c1]++; - mask[c2]++; - } - cl[ee] = -cl[ee]; - break; - } - - /* Get the new location of the streamline origin */ - px = cx[e]; - py = cy[e]; - pei = ei; - cl[ee] = get_posc(window, &ei, c, u, v, &cx[e], &cy[e], time_left, 1, &dist); - - /* Use a second order approximation for the velocity used to */ - /* track the streamline. */ - if (momo2) { - double u2 = hd_trans_interp(window, windat->gsx, cx[e], cy[e], pz, c, 0, 0); - double v2 = hd_trans_interp(window, windat->gsy, cx[e], cy[e], pz, c, 0, 1); - u = 0.5 * (u + u2); - v = 0.5 * (v + v2); - cx[e] = px; - cy[e] = py; - ei = pei; - cl[ee] = get_posc(window, &ei, c, u, v, &cx[e], &cy[e], time_left, 1, &dist); - } - - /* If the source lies in a wet cell, then get the time it's */ - /* taken to cross the cell. */ - if (cl[ee] > 0) { - d1 = sqrt(u * u + v * v); - dt = dist / d1; - time_left -= dt; - } - /* - if (window->wgst[cl[ee]] && window->zp1[cl[ee]] != window->wgst[cl[ee]] && - ee <= window->v3_e1) - printf("Streamline in ghost @ %f %d : %f %f\n",windat->days, e, window->cellx[window->m2d[cl[ee]]], - window->celly[window->m2d[cl[ee]]]); - - if (cl[ee] <=0 || cl[ee] >= window->szc) - printf("Streamline out of bounds @ %f %d %f %f\n", windat->days, e, window->cellx[window->m2d[cl[ee]]], - window->celly[window->m2d[cl[ee]]]); - */ - trs = get_linear_value(window, windat, wincon, tr_mod, ci, px, py, pz); - tre = get_linear_value(window, windat, wincon, tr_mod, ci, cx[e], cy[e], pz); - - /* Get the minimum and maximum values encountered in the */ - /* stencil along the streamline. */ - if (wincon->ultimate) { - get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); - get_local_bounds(window, windat, wincon, tr_mod[cl[cc]], NULL, e, abs(cl[ee]), smin, smax, 0); - } else if (wincon->fillf & CLIP) { - get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); - get_local_bounds(window, windat, wincon, tre, tr, e, abs(cl[ee]), smin, smax, 2); - } - - /* Integrate (linearly) the tracer along the streamline */ - /* segment. */ - dint[e] += dist; - - if (doint) { - ntr[e] += 0.5 * dist * (trs + tre); - } else - ntr[e] += (tr_mod[ci] * dist); - - /* Get the number of sub-timesteps used */ - n++; - if(n > 100) { - char name[MAXSTRLEN]; - int tn = find_tracer(window, windat, wincon, tr, name); - printf("Tracer %d (%s) stuck in loop e=%d [%f %f]\n",tn, name, e, - window->u1x[window->m2de[e]], window->u1y[window->m2de[e]]); - exit(0); - } - /* Get the contributions to the horizontal transverse terms */ - /* for the vertical advection, this is the tracer value at the */ - /* current source location. */ - if (tranf & T_HV) { - if (cl[ee] < 0) { - tre = get_linear_value(window, windat, wincon, tr, ci, cx[e], cy[e], pz); - tr_moc[c1] += tre; - tr_moc[c2] += tre; - mask[c1]++; - mask[c2]++; - } - } - } - if (n > nmax) { - nmax = n; - emax = e; - } - - /* Get the mean tracer value along the streamline */ - Fx[e] = (dint[e]) ? ntr[e] / dint[e] : Fx[e]; - - /* Universal flux limiter Eq 34 & 35 of Thuburn (1995) */ - if (wincon->ultimate) { - Fx[e] = min(Fx[e], smax[e]); - Fx[e] = max(Fx[e], smin[e]); - } - } - - debug_c(window, D_TS, D_STRML); - - /* Multiply the tracer by the volume flux */ - for (ee = 1; ee <= window->n3_e1; ee++) { - e = window->w3_e1[ee]; - Fx[e] *= u1v[e]; - } - - /* Get the horizontal transverse terms by interpolating tr at the */ - /* source location. Note: the weights and slope limiter for the */ - /* least squares interpolation must be reset using tr (rather than */ - /* tr_mod). */ - if (!(tranf & T_HV)) { - get_linear_limit(window, windat, wincon, tr); - for (ee = 1; ee <= window->a3_e1; ee++) { - e = window->w3_e1[ee]; - c = -cl[ee]; - c1 = window->e2c[e][0]; - c2 = window->e2c[e][1]; - pz = wincon->cellz[c]; - tre = get_linear_value(window, windat, wincon, tr, c, cx[e], cy[e], pz); - tr_moc[c1] += tre; - tr_moc[c2] += tre; - mask[c1]++; - mask[c2]++; - } - } - - /*-----------------------------------------------------------------*/ - /* Define a new tracer with horizontal-direction transverse terms */ - /* (note: we use the interpolated value at the streamline end */ - /* directly in tr_moc). */ - if (tranf & T_HORZ) { - for (cc = 1; cc <= window->b3_t; cc++) { - c = window->w3_t[cc]; - tr_moc[c] /= (double)mask[c]; - tr_moc[c] = tf * tr_moc[c] + (1.0 - tf) * tr[c]; - } - set_tr_nograd(window, tr_moc); - } else - memcpy(tr_moc, tr, window->szc * sizeof(double)); - - /*-----------------------------------------------------------------*/ - /* Vertical fluxes */ - /* Calculate the fractional part of the volume flux, using the */ - /* Van Leer algorithm to get the tracer value on the face. */ - ff_sl_van_leer(Fz, tr_moc, w, crfzf, clzf, 1, wincon->vc, wincon->s1, window->zp1, window->zm1, - sminz, smaxz); + wincon->s1, window->zp1, window->zm1, mn, mx); for (cc = 1; cc <= wincon->vc; cc++) { c = wincon->s1[cc]; ci = (w[c] > 0.0) ? c : window->zm1[c]; c2 = (w[c] > 0.0) ? window->zm1[c] : c; + zm1 = window->zm1[c]; + /* If the maximum Courant number is < 1 (horizontally and */ + /* vertically) then do not use projected values for computation */ + /* of the vertical flux. */ + if (vscheme == V_VC) { + if (cour[c] < 1.0) + ffsl_van_leer(Fz, tr, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); + else + ffsl_van_leer(Fz, tr_moc, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); + } + if (vscheme == V_QC) { + if (cour[c] < 1.0) + ffsl_quickest(window, Fz, tr, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); + else + ffsl_quickest(window, Fz, tr_moc, w, crfzf, clzf, c, window->zp1, window->zm1, mn, mx); + } if (wincon->fillf & CLIP) sminz[c] = smaxz[c] = tr[c2]; if (wincon->ultimate) sminz[c] = smaxz[c] = tr_moc[c2]; - if (w[c] == 0.0) { + /*if (wincon->ultimate & UL_ITR) sminz[c] = smaxz[c] = tr[c2];*/ + + if (w[c] == 0.0 || zm1 == window->zm1[zm1]) { Fz[c] = 0.0; continue; } ci = clzf[c]; + if (c == verbose) printf(" vint %f %f %f\n",crfzf[c],wincon->dz[ci],Fz[c]); Fz[c] *= (crfzf[c] * wincon->dz[ci]); /* Integrate over the "integer" component of the trajectory */ @@ -2969,16 +2477,27 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ while (dz < dist) { /* Get the minimum and maximum values encountered in the */ /* stencil along the streamline. */ - if (wincon->ultimate) { + if (wincon->ultimate) { sminz[c] = min(sminz[c], tr_moc[ci]); smaxz[c] = max(smaxz[c], tr_moc[ci]); + /* + sminz[c] = min(sminz[c], tr[ci]); + smaxz[c] = max(smaxz[c], tr[ci]); + */ } else if (wincon->fillf & CLIP) { sminz[c] = min(sminz[c], tr[ci]); smaxz[c] = max(smaxz[c], tr[ci]); } + /* + if (wincon->monon >= 0 && tr == windat->tr_wc[wincon->monon]) { + windat->monon[c] = min(windat->monon[c], tr_moc[ci]); + windat->monox[c] = max(windat->monox[c], tr_moc[ci]); + } + */ Fz[c] += (dz * tr_moc[ci]); dist -= dz; ci = (w[c] < 0.0) ? window->zp1[ci] : window->zm1[ci]; + mask[c] = mask[ci]; if (wincon->dz[ci] > 0) dz = wincon->dz[ci]; } @@ -2986,6 +2505,7 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ /* Ensure Fz has correct sign */ if (w[c] < 0) Fz[c] *= -1.0; + if (c == verbose) printf(" vert %d w=%f : %f\n", clzf[c], w[c], Fz[c]); /* Universal flux limiter Eq 34 & 35 of Thuburn (1995) */ if (wincon->ultimate) { @@ -2996,6 +2516,30 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ /* Limit the mean tracer value if rquired */ if (wincon->ultimate) { + + /* Make an array of non-monotonic values in order of decreasing */ + /* value, and expand the mesh around these points. + mask[0] = 0; + for (cc = 1; cc <= wincon->vc; cc++) { + c = wincon->s1[cc]; + if ((d1 = check_tr_value(window, windat, wincon, tr, Fx, Fz, dtu, c))) { + mask[0]++; + mask[mask[0]] = c; + dint[mask[0]] = d1; + n++; + } + } + if (mask[0]) { + quicksort(dint, mask, 1, mask[0]); + n = wincon->s7[0] = mask[0]; + for (cc = mask[0]; cc > 0; cc--) { + wincon->s7[cc] = mask[n--]; + } + mesh_expand_3d(window, u1, wincon->s7); + } else + return; + */ + memset(mask, 0, window->sze * sizeof(int)); /* Limit the horizontal fluxes in the order of the sparse */ /* vector. Some edges may be missed doing this if a cell is */ @@ -3009,11 +2553,11 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ cs = wincon->i2[cc]; cb = wincon->i1[cc]; while (c != window->zm1[c]) { - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); + universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask, 0); c = window->zm1[c]; } } - + */ /* Limit the horizontal fluxes following outgoing flow from */ /* cells. */ /* @@ -3026,16 +2570,19 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ cs = window->nsur_t[cco]; cb = window->bot_t[cco]; while (c != window->zm1[c]) { - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); + universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask, 0); c = window->zm1[c]; } } */ + + /* Limit fluxes with the universal limiter */ for (cc = 1; cc <= wincon->s6[0]; cc++) { int cco, co, cs, cb, c2; c = wincon->s6[cc]; c2 = window->m2d[c]; - if (!(window->cask[c] & (W_WET|W_AUX))) continue; + /* Note: do not limit on auxiliary cells */ + if (!(window->cask[c] & (W_WET))) continue; cco = wincon->s7[c2]; co = wincon->i3[cco]; cs = max(co, wincon->i2[cco]); @@ -3045,12 +2592,12 @@ void ffsl_donc(geometry_t *window, /* Window geometry */ cs = window->nsur_t[cco]; cb = window->bot_t[cco]; */ - universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); + universal_limit(window, windat, wincon, c, co, cs, cb, dtu, tr, Fx, Fz, mask); } } } -/* END ffsl_donc() */ +/* END ffsl_don() */ /*-------------------------------------------------------------------*/ @@ -3168,10 +2715,11 @@ void ffsl_init(geometry_t *window, /* Window geometry */ delaunay_reinit(window, windat->d, 2, nw); /* Make a contiguous mapping of cell locations */ + if (wincon->ultimate) - mesh_expand_3d(window, u1); + mesh_expand_3d(window, u1, NULL); - wincon->flag |= F_FFSL; + wincon->flag |= F_FFSL; } /* END ffsl_init() */ @@ -3292,7 +2840,7 @@ void universal_limith(geometry_t *window, /* Window geometry */ int dolimit = 1; int cd = 0; - if (checkf && window->s2i[c] == 47&& window->s2j[c] == 25 && window->s2k[c] == 22) cd = c; + /*if (checkf && window->s2i[c] == 47&& window->s2j[c] == 25 && window->s2k[c] == 22) cd = c;*/ /* Set the pointers */ if (wincon->means & TRANSPORT) { @@ -3525,7 +3073,7 @@ void universal_limith(geometry_t *window, /* Window geometry */ mask[e] = 1; d1 = window->eSc[ee][c2] * hflux[e]; flux = Fx[e]; - if (c == cd) printf("limit %f : ",Fx[e]); + if (c == cd) printf("limit %d e=%d %f : ", ee, e, Fx[e]); Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; if (c == cd) printf("%f : ",Fx[e]); Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; @@ -3561,28 +3109,73 @@ void universal_limith(geometry_t *window, /* Window geometry */ } } -/* END universal_limit() */ +/* END universal_limith() */ /*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/ -/* Universal flux limiter, see Thuburn (1996), JCP. */ +/* Universal flux limiter, see Thuburn (1996), Multidimensional flux */ +/* limited advection schemes. JCP, 123, 74-83. */ /* Assumes the upstream biased minimum and maximum values are stored */ /* in smin and smax. Limits horizontal and vertical fluxes. */ -/*-------------------------------------------------------------------*/ -void universal_limit(geometry_t *window, /* Window geometry */ - window_t *windat, /* Window data */ - win_priv_t *wincon, /* Window constants */ - int ci, - int co, - int cs, - int cb, - double dt, - double *tr, - double *Fx, - double *Fz, - int *mask - ) +/* The tracer update uses cell volumes rather than Courant numbers, */ +/* as in Thuburn, e.g. Eq. 9. Here we use: */ +/* t(n+1) = (t(n)*(old volume) - (dtracer + vflux)) / (new volume) */ +/* The steps used by Thuburn are: */ +/* (i) Define the inflow bounds, smin, smax, sminz, smaxz; Eq. 32 & */ +/* 33. This is carried out in ffsl_don(). Several options exist */ +/* to do this, using initial values of the tracer in the dest */ +/* cell (UL_ITR), projected tracer values in the dest cell */ +/* (UL_IPROJ), tracer values included in the interpolation */ +/* stencil along the streamline (UL_SSTCL) or projected tracer */ +/* values along the streamline (UL_SPROJ). UL_IPROJ and UL_SPROJ */ +/* are the default. */ +/* (ii) Limit the edge values used in Fx and Fz to the inflow bounds */ +/* Eq. 34 & 35. This is carried out in ffsl_don(). */ +/* (iii) Define the min and max values a tracer can achieve after */ +/* the update, Eq. 38 & 39. These are the min/max of (i) over */ +/* all faces (qn & qx). */ +/* (iv) Compute the outflow bounds each face must fall within, Eq. */ +/* 42 and 43 (qmin & qmax). Note that a maximum tracer value in */ +/* a cell is achieved with maximum inflow of mass and minimum */ +/* outflow of mass. */ +/* (v) Adjust the edge values to lie within the bounds from (iv), */ +/* Eq. 44 & 45. Note that these are adjusted in terms of fluxes */ +/* rather than tracer values, taking care of the sign of the */ +/* flux. */ +/* The refinement (Eq. 48 & 49) may be invoked prior to computing */ +/* the inflow fluxes, cn and cx. Take care of the sign when finding */ +/* min/max values of fluxes. */ +/* Things to remember: */ +/* : w is poitive upwards. */ +/* : eSc is poitive for an outward vector, negative inward. */ +/* : eSc * Fx is positive for outgoing flow. */ +/* : Fz is multiplied by dt, Fx isn't. */ +/* : qmax is the maximum edge value for outflow required to achive */ +/* the minimum tracer update (qn). If there is an undershoot, we */ +/* require the t(n+1) to be qn, hence edge values must be < qmax. */ +/* : qmin is the minimum edge value for outflow required to achive */ +/* the maximum tracer update (qx). If there is an overshoot, we */ +/* require the t(n+1) to be qx, hence edge values must be > qmin. */ +/* : whenever a cell is limited, ALL faces must be masked, so that */ +/* the divergence doesn't change from other cell's limiting. */ +/* Similar reasoning applies in the vertical, so we only limit */ +/* the bottom face of a cell, as the top face may alter the mass */ +/* balance in the cell above if it is limited. */ +/*-------------------------------------------------------------------*/ +int universal_limit(geometry_t *window, /* Window geometry */ + window_t *windat, /* Window data */ + win_priv_t *wincon, /* Window constants */ + int ci, + int co, + int cs, + int cb, + double dt, + double *tr, + double *Fx, + double *Fz, + int *mask + ) { int c, e, es, ee; int c2 = window->m2d[ci]; @@ -3606,14 +3199,19 @@ void universal_limit(geometry_t *window, /* Window geometry */ double *tr_mod = wincon->tr_mod; /* z transverse tracer */ double *tr_moc = wincon->Fzh; /* Horizontal transverse tracer */ double *w; /* Vertical velocity */ - double d1; /* Dummy */ - int checkf = 0; - int dolimit = 1; + double d1, d2, d3, d4; /* Dummy */ + int ismono = 0; /* 1 = overshoot, -1 = undershoot */ + int dolimit = 1; /* Perform limiting */ + int monof = 0; /* Check monotonicity */ + int checkf = 0; /* Report for cell cd */ int cd = 0; c = ci; - /*if (checkf && window->s2i[c] == 28 && window->s2j[c] == 26 && window->s2k[c] == 22) cd = c;*/ - /*if (checkf && c == 1) cd = c;*/ + /* Only report if cd is non-monotonic */ + /* + if (checkf && c == cd) + checkf = check_tr_value(window, windat, wincon, tr, Fx, Fz, dt, c); + */ /* Set the pointers */ if (wincon->means & TRANSPORT) { @@ -3624,6 +3222,7 @@ void universal_limit(geometry_t *window, /* Window geometry */ w = windat->w; } + /*-----------------------------------------------------------------*/ /* Get the cell volumes at time t and t-1 */ if (c == cs) { double watertop; @@ -3655,24 +3254,30 @@ void universal_limit(geometry_t *window, /* Window geometry */ tro = tr[c] * volo; } + /*-----------------------------------------------------------------*/ /* Diagnostics */ if(checkf && c == cd) { - printf("-----------------------------\n"); - printf("start %f %s %d %d[%d %d %d](%f %f) cs=%d co=%d tr=%f dt=%f\n",windat->days, - wincon->trinfo_3d[(int)tr[0]].name, + printf("\n-----------------------------\n"); + printf("start %f %d %s %d %d[%d %d %d](%f %f) cs=%d co=%d zp1=%d\n",windat->days, + master->nstep, wincon->trinfo_3d[(int)tr[0]].name, windat->nstep, c, window->s2i[c], window->s2j[c], window->s2k[c], - window->cellx[c2], window->celly[c2], cs, co, tr[c], dt); - printf("eta(t)=%f eta(t-1)=%f volo=%f vol=%f\n", subeta[c2], osubeta[c2], - volo/window->cellarea[c2], vol/window->cellarea[c2]); + window->cellx[c2], window->celly[c2], cs, co, window->zp1[c]); + printf(" tracer value=%f dt=%f\n",tr[c], dt); + printf(" eta(t)=%f eta(t-1)=%f volo=%f vol=%f\n", subeta[c2], osubeta[c2], + volo, vol); + printf(" ------------\n"); + printf("Fluxes\n"); } + /*-----------------------------------------------------------------*/ /* Get the sum of fluxes */ + c = ci; qn = min(tr_mod[c], tr_moc[c]); qx = max(tr_mod[c], tr_moc[c]); + cn = cx = cout = cin = 0.0; ni = no = 0; dtracer = 0.0; - c = ci; /* Increment the fluxes from the current layer to the layer below */ /* that containing the old surface. If elevation has risen through */ @@ -3685,17 +3290,23 @@ void universal_limit(geometry_t *window, /* Window geometry */ es = window->m2de[e]; flux = window->eSc[ee][c2] * hflux[e] * dt; dtracer += window->eSc[ee][c2] * Fx[e] * dt; - if(c==cd)printf("drop\n"); + if(checkf && c==cd)printf("drop\n"); if (flux > 0.0) { - cout += flux; + if (!mask[e]) + cout += flux; + else { + cn -= window->eSc[ee][c2] * Fx[e] * dt; + cx -= window->eSc[ee][c2] * Fx[e] * dt; + cin += flux; + } no++; } else if (flux < 0.0) { - mask[e] = 1; qn = min(qn, smin[e]); qx = max(qx, smax[e]); cn -= smin[e] * flux; cx -= smax[e] * flux; cin -= flux; + mask[e] = 1; ni++; } } @@ -3703,7 +3314,9 @@ void universal_limit(geometry_t *window, /* Window geometry */ } } - /* Increment the fluxes for the layer containing the old surface */ + /* Increment the fluxes for the layer containing the old surface. */ + /* Cells in the main water column, with constant volume, fall */ + /* within this loop. */ for (ee = 1; ee <= window->npe[c2]; ee++) { e = window->c2e[ee][c]; es = window->m2de[e]; @@ -3713,21 +3326,43 @@ void universal_limit(geometry_t *window, /* Window geometry */ dtracer += window->eSc[ee][c2] * Fx[e] * dt; if (flux > 0.0) { /* Outflow */ sf[ee] = 1; - cout += flux; - if(c==cd)printf("aout %d e=%d min=%f Fx=%f\n",ee, e, - smin[e] * flux, Fx[e]); - no++; - } else if (flux < 0.0) { + if (!mask[e]) { + cout += flux; + if(checkf && c==cd)printf("outflow face%d e=%d eSc=%d vol flux=%f Fx=%f mass out=%f\n", + ee, e, window->eSc[ee][c2], flux, + Fx[e], window->eSc[ee][c2] * Fx[e] * dt); + no++; + } else { + /* For masked outgoing fluxes, subtract outgoing mass from */ + /* the accumulated incoming mass, cn and cx. */ + cn -= window->eSc[ee][c2] * Fx[e] * dt; + cx -= window->eSc[ee][c2] * Fx[e] * dt; + cin -= flux; + if(checkf && c==cd)printf("outflow masked face%d e=%d eSc=%d vol flux=%f Fx=%f mass out=%f\n", + ee, e, window->eSc[ee][c2], flux, + Fx[e], window->eSc[ee][c2] * Fx[e] * dt); + } + } else if (flux < 0.0) { /* Add incoming mass: Note: eSc.Fx < 0 */ sf[ee] = -1; - mask[e] = 1; + /* Thuburn (1996) Eq. 38 & 39 */ qn = min(qn, smin[e]); qx = max(qx, smax[e]); - /* Thuburn (1996) Eq. 48 & 49 */ - cn -= min(window->eSc[ee][c2] * Fx[e]*dt, smin[e] * flux); - cx -= max(window->eSc[ee][c2] * Fx[e]*dt, smax[e] * flux); + /* Thuburn (1996) Eq. 48 & 49 */ + if (wincon->ultimate & UL_REFINE) { + cn -= max(window->eSc[ee][c2] * Fx[e]*dt, smin[e] * flux); + cx -= min(window->eSc[ee][c2] * Fx[e]*dt, smax[e] * flux); + } else { + cn -= window->eSc[ee][c2] * Fx[e]*dt; + cx -= window->eSc[ee][c2] * Fx[e]*dt; + } cin -= flux; - if(c==cd)printf("ain %d e=%d min=%f max=%f Fx=%f\n",ee,e, - cn, cx, Fx[e]*dt); + if(checkf && c==cd) { + printf("inflow face%d e=%d eSc=%d vol flux=%f Fx=%f mass out=%f\n", + ee, e, window->eSc[ee][c2], flux, Fx[e], window->eSc[ee][c2] * Fx[e] * dt); + printf(" cn=%f cx=%f smin mass=%f smax mass=%f\n", + cn, cx, smin[e] * flux, smax[e] * flux); + } + mask[e] = 1; ni++; } } @@ -3745,17 +3380,23 @@ void universal_limit(geometry_t *window, /* Window geometry */ es = window->m2de[e]; flux = window->eSc[ee][c2] * hflux[e] * dt; dtracer += window->eSc[ee][c2] * Fx[e] * dt; - if(c==cd)printf("rise\n"); + if(checkf && c==cd)printf("rise\n"); if (flux > 0.0) { - cout += flux; + if (!mask[e]) + cout += flux; + else { + cn -= window->eSc[ee][c2] * Fx[e] * dt; + cx -= window->eSc[ee][c2] * Fx[e] * dt; + cin += flux; + } no++; } else if (flux < 0.0) { - mask[e] = 1; qn = min(qn, smin[e]); qx = max(qx, smax[e]); cn -= smin[e] * flux; cx -= smax[e] * flux; cin -= flux; + mask[e] = 1; ni++; } } @@ -3764,92 +3405,182 @@ void universal_limit(geometry_t *window, /* Window geometry */ /* Vertical fluxes into the cell */ c = ci; + /* Bottom face. This face is limited for outflow. */ flux = w[c] * window->cellarea[c2] * dt; if (flux > 0.0) { qn = min(qn, sminz[c]); qx = max(qx, smaxz[c]); - /* Thuburn (1996) Eq. 48 & 49 */ - cn += min(Fz[c] * window->cellarea[c2], sminz[c] * flux); - cx += max(Fz[c] * window->cellarea[c2], smaxz[c] * flux); + /* Thuburn (1996) Eq. 48 & 49 */ + if (wincon->ultimate & UL_REFINE) { + cn += min(Fz[c] * window->cellarea[c2], sminz[c] * flux); + cx += max(Fz[c] * window->cellarea[c2], smaxz[c] * flux); + } else { + cn += Fz[c] * window->cellarea[c2]; + cx += Fz[c] * window->cellarea[c2]; + } + if(checkf && c==cd) { + printf("zin c=%d cn=%f cx=%f Fz=%f vol flux=%f mass in=%f\n", + c, cn, cx, Fz[c], flux, Fz[c] * window->cellarea[c2]); + printf(" tracer face value=%f sminz mass=%f smaxz mass=%f\n", + Fz[c]/flux, sminz[c] * flux, smaxz[c] * flux); + } cin += flux; ni++; } else if (flux < 0.0) { + if(checkf && c==cd)printf("zout c=%d Fz=%f vol flux=%f mass out=%f \n", + c, Fz[c], flux, Fz[c] * window->cellarea[c2]); cout -= flux; no++; } + + /* Top face. This face is never limited, since if it is we may */ + /* alter the mass balance in the cell above to be non-monotonic. */ + /* Subtract the outgoing fluxes from cn, cx, and do not increment */ + /* cout. */ if (c != cs) { zp1 = window->zp1[c]; flux = w[zp1] * window->cellarea[c2] * dt; if (flux < 0.0) { qn = min(qn, sminz[zp1]); qx = max(qx, smaxz[zp1]); - cn -= min(Fz[zp1] * window->cellarea[c2], sminz[zp1] * flux); - cx -= max(Fz[zp1] * window->cellarea[c2], smaxz[zp1] * flux); + /* Note: flux is < 0 for ingoing fluxes, so the minimum flux */ + /* in is max(Fzp1, smin.flux) and the maximum flux out is */ + /* max(Fzp1, smax.flux). */ + if (wincon->ultimate & UL_REFINE) { + cn -= max(Fz[zp1] * window->cellarea[c2], sminz[zp1] * flux); + cx -= min(Fz[zp1] * window->cellarea[c2], smaxz[zp1] * flux); + } else { + cn -= Fz[zp1] * window->cellarea[c2]; + cx -= Fz[zp1] * window->cellarea[c2]; + } + if(checkf && c==cd) { + printf("zpin c=%d cn=%f cx=%f Fzp=%f vol flux=%f zp mass in=%f\n", + c, cn, cx, Fz[zp1], flux, Fz[zp1] * window->cellarea[c2]); + printf(" tracer face value=%f sminz mass=%f smaxz mass=%f\n", + Fz[zp1]/flux, sminz[zp1] * flux, smaxz[zp1] * flux); + } cin -= flux; ni++; } else if (flux > 0.0) { - cout += flux; + if(checkf && c==cd)printf("zpout c=%d Fzp=%f vol flux=%f mass out=%f\n", + c, Fz[zp1], flux, Fz[zp1] * window->cellarea[c2]); + cn -= Fz[zp1] * window->cellarea[c2]; + cx -= Fz[zp1] * window->cellarea[c2]; + cin += flux; + /*cout += flux;*/ no++; } } - if (!cout) return; + c = ci; + /* No inflow, commentry in Thuburn post Eq. 39. */ if (!ni) { qn = qx = tr[c]; + if(checkf && c==cd) printf("No inflow edges in this cell : qn=qx=tr, cn=cx=0\n"); + } + windat->trmin[c] = qn; + windat->trmax[c] = qx; + if (!cout) { + if(checkf && c==cd) printf("No unmasked outflow edges in this cell to limit.\n"); + return(0); } - /* Get the horizontal outflow bounding values */ - qmin = (tro - vol * qx + cx) / (cout); - qmax = (tro - vol * qn + cn) / (cout); + /*-----------------------------------------------------------------*/ + /* Get the unlimited updated tracer */ + vflux = - Fz[c] * window->cellarea[c2] ; + if (c != cs) vflux += Fz[zp1] * window->cellarea[c2]; + d1 = (volo * tr[c] - (dtracer + vflux)) / vol; + if (d1 < qn) ismono = -1; + if (d1 > qx) ismono = 1; + if(!ismono) return(0); + if(checkf && c==cd) { + printf(" ------------\n"); + printf("Un-limited new tracer value = %f (should lie between %f %f). ismono=%d\n", + d1, qn, qx, ismono); + } - /* Check */ - if (c == cd) { - double d2, d3; - dtracer = 0.0; + /*-----------------------------------------------------------------*/ + /* Monotonicity check using original fluxes */ + if (monof == 2) { + double eps = -1e-6; + if (d1 - qn < eps) { + printf("\n-----------------------------\nStart\n"); + printf("Monotonicity min pre error %f %d tr=%f, min=%f, max=%f\n", windat->days, c, d1, qn, qx); + cd = c; + checkf = 1; + } + if (qx - d1 < eps) { + printf("\n-----------------------------\nStart\n"); + printf("Monotonicity max pre error %f %d tr=%f, min=%f, max=%f\n", windat->days, c, d1, qn, qx); + cd = c; + checkf = 1; + } + } + + /*-----------------------------------------------------------------*/ + /* Get the horizontal outflow bounding values (Eq. 42 & 43) */ + qmin = max(0.0, (tro - vol * qx + cx) / (cout)); + qmax = max(0.0, (tro - vol * qn + cn) / (cout)); + if(checkf && c==cd) { + printf(" ------------\n"); + printf("Outflow bounding values qmin=%f qmax=%f\n", qmin,qmax); + printf("Initial mass=%f, volume out=%f\n", tro, cout); + printf("min mass in (cn)=%f max mass in (cx)=%f\n", cn, cx); + } + + /*-----------------------------------------------------------------*/ + /* Checks */ + if (checkf && c == cd) { + printf(" ------------\n"); + printf("Checks:\n"); /* Get the continuity (volume) balance. This is independent of */ /* tracer value and checks volume fluxes and sea levels are */ /* correct. Should be ~1e-10. */ d1 = (volo - vol) - ((cout-cin)); - printf("cnt=%e flux=%f\n", d1, cout-cin); - /* Print the vertical mass fluxes */ + printf("Volume continuity=%e (should be ~1e-10) volume flux divergence=%f\n", d1, cout-cin); - vflux = - Fz[c] * window->cellarea[c2] ; - flux = w[c] * dt; - if (Fz[c] >= 0.0) - printf(" vin Fz=%f vol=%f min=%f max=%f tr=%f\n", Fz[c], flux, - flux * sminz[c], flux * smaxz[c], Fz[c]/flux); - else - printf(" vout Fz=%f vol=%f min=%f max=%f tr=%f\n", Fz[c], flux, - flux * sminz[c], flux * smaxz[c], Fz[c]/flux); - if (c != cs) { - vflux += Fz[zp1] * window->cellarea[c2]; - flux = w[zp1] * window->cellarea[c2] * dt; + /* Get the tracer values using qmin and qmax */ + d2 = (tro - qmin * cout + cx) / vol; + d3 = (tro - qmax * cout + cn) / vol; + printf("New tracer using qmax/qmin face values = %f %f\n", d3, d2); - if (Fz[zp1] < 0.0) - printf(" vp1in Fzp1=%f vol=%f min=%f max=%f tr=%f\n", Fz[zp1], flux, - flux * sminz[zp1], flux * smaxz[zp1], Fz[zp1]/flux); - else - printf(" vp1out Fzp1=%f vol=%f min=%f max=%f tr=%f\n", Fz[zp1], flux, - flux * sminz[zp1], flux * smaxz[zp1], Fz[zp1]/flux); - } /* Print the horizontal mass fluxes */ for (ee = 1; ee <= window->npe[c2]; ee++) { e = window->c2e[ee][c]; flux = window->eSc[ee][c2] * hflux[e] * dt; - dtracer += (window->eSc[ee][c2] * Fx[e] * dt); - if (sf[ee] == 1)printf(" out%d Fx=%f vol=%f min=%f max=%f tr=%f\n",ee, - window->eSc[ee][c2] * Fx[e], hflux[e], hflux[e] * smin[e], - hflux[e] * smax[e], Fx[e]/hflux[e]); - if (sf[ee] == -1)printf(" in%d Fx=%f vol=%f min=%f max=%f tr=%f\n",ee, - window->eSc[ee][c2] * Fx[e], hflux[e], hflux[e] * smin[e], - hflux[e] * smax[e], Fx[e]/hflux[e]); + if (sf[ee] == 1) + printf(" out%d mass limits: actual=%f min=%f max=%f\n", + ee, window->eSc[ee][c2] * Fx[e] * dt, flux * qmin, flux * qmax); + if (sf[ee] == -1) + printf(" out%d mass limits: actual=%f min=%f max=%f\n", + ee, window->eSc[ee][c2] * Fx[e] * dt, flux * qmin, flux * qmax); + } + /* Print the vertical mass fluxes */ + flux = w[c] * window->cellarea[c2] * dt; + if (Fz[c] >= 0.0) + printf(" vin mass limits: actual=%f min=%f max=%f\n", + -Fz[c] * window->cellarea[c2], -flux * qmin, -flux * qmax); + else { + printf(" vout mass limits: actual=%f min=%f max=%f\n", + -Fz[c] * window->cellarea[c2], -flux * qmin, -flux * qmax); + } + if (c != cs) { + flux = w[zp1] * window->cellarea[c2] * dt; + if (Fz[zp1] < 0.0) + printf(" vpin mass limits: actual=%f min=%f max=%f\n", + Fz[zp1] * window->cellarea[c2], flux * qmin, flux * qmax); + else + printf(" vpout mass limits: actual=%f min=%f max=%f\n", + Fz[zp1] * window->cellarea[c2], flux * qmin, flux * qmax); } - - d1 = (volo * tr[c] - (dtracer + vflux)) / vol; - printf(" cn=%f cx=%f min=%f max=%f tr=%f qmin=%f qmax=%f\n",cn, cx, qn, qx, d1, qmin, qmax); } - /* Limit the outflow edges */ + /*-----------------------------------------------------------------*/ + /* Limit the outflow edges (Eq. 44 & 45) */ if (dolimit) { + if(checkf && c==cd) { + printf(" ------------\n"); + printf("Limits\n"); + } dtracer = 0.0; /* Limit the fluxes from the current layer to the layer below */ /* that containing the old surface. */ @@ -3861,15 +3592,14 @@ void universal_limit(geometry_t *window, /* Window geometry */ if (!mask[e] && window->eSc[ee][c2] * hflux[e] > 0) { mask[e] = 1; d1 = window->eSc[ee][c2] * hflux[e]; - Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; - Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; + if (ismono == 1) Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; + if (ismono == -1) Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; } dtracer += (window->eSc[ee][c2] * Fx[e] * dt); } c = zp1; } } - /* Limit the fluxes for the layer containing the old surface */ for (ee = 1; ee <= window->npe[c2]; ee++) { e = window->c2e[ee][c]; @@ -3877,11 +3607,21 @@ void universal_limit(geometry_t *window, /* Window geometry */ mask[e] = 1; d1 = window->eSc[ee][c2] * hflux[e]; flux = Fx[e]; - if (c == cd) printf("limit %f : ",Fx[e]); - Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; - if (c == cd) printf("%f : ",Fx[e]); - Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; - if (c == cd) printf("%f\n",Fx[e]); + if (checkf && c == cd) printf("limit %d e=%d tr=%f Fx=%f : ", ee, e, fabs(Fx[e] / d1), Fx[e]); + /* Fx.eSc > 0 for outgoing fluxes. */ + + if (ismono == 1) Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; + if (ismono == -1) Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; + /* + if (ismono == 1) Fx[e] = qmin * hflux[e]; + if (ismono == -1) Fx[e] = qmax * hflux[e]; + */ + if (checkf && c == cd) { + printf("%f\n",Fx[e]); + printf(" eSc=%d flux=%f mass limits=%f %f\n",window->eSc[ee][c2], Fx[e] * window->eSc[ee][c2], + qmin * d1, qmax * d1); + printf(" new mass out%d = %f\n", ee, window->eSc[ee][c2] * Fx[e]*dt); + } /*if (fabs(flux - Fx[e]) > 1e-8) hd_quit("limit at c=%d d=%d %f %f\n",c, e, flux, Fx[e]);*/ } dtracer += (window->eSc[ee][c2] * Fx[e] * dt); @@ -3897,8 +3637,8 @@ void universal_limit(geometry_t *window, /* Window geometry */ if (!mask[e] && window->eSc[ee][c2] * hflux[e] > 0) { mask[e] = 1; d1 = window->eSc[ee][c2] * hflux[e]; - Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; - Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; + if (ismono == 1) Fx[e] = max(Fx[e] * window->eSc[ee][c2], qmin * d1) * window->eSc[ee][c2]; + if (ismono == -1) Fx[e] = min(Fx[e] * window->eSc[ee][c2], qmax * d1) * window->eSc[ee][c2]; } dtracer += (window->eSc[ee][c2] * Fx[e] * dt); } @@ -3907,34 +3647,176 @@ void universal_limit(geometry_t *window, /* Window geometry */ /* Limit the vertical outgoing fluxes */ c = ci; + /* Note: Fz[c] is < 0 for outgoing fluxes. */ if (Fz[c] < 0) { - if (c == cd) printf("limit lower %f : ",Fz[c]); - Fz[c] = max(Fz[c], qmax * w[c] * dt); - if (c == cd) printf("%f : ",Fz[c]); - Fz[c] = min(Fz[c], qmin * w[c] * dt); - if (c == cd) printf("%f\n",Fz[c]); + if (checkf && c == cd) printf("limit lower %f : ",Fz[c]); + + if (ismono == 1) Fz[c] = min(Fz[c], qmin * w[c] * dt); + if (ismono == -1) Fz[c] = max(Fz[c], qmax * w[c] * dt); + /* + if (ismono == 1) Fz[c] = qmin * w[c] * dt; + if (ismono == -1) Fz[c] = qmax * w[c] * dt; + */ + if (checkf && c == cd) { + printf("%f : limits=%f %f\n",Fz[c],qmin * w[c] * dt,qmax * w[c] * dt); + printf(" new mass out lower = %f\n", ee, -Fz[c] * window->cellarea[c2]); + } } + /* Outgoing fluxes are > 0 for zp1 are not limited. zp1 = window->zp1[c]; if (c != cs && Fz[zp1] > 0.0) { - if (c == cd) printf("limit upper %f : ",Fz[zp1]); - Fz[zp1] = min(Fz[zp1], qmax * w[zp1] * dt); - if (c == cd) printf("%f : ",Fz[zp1]); - Fz[zp1] = max(Fz[zp1], qmin * w[zp1] * dt); - if (c == cd) printf("%f\n",Fz[zp1]); + if (checkf && c == cd) printf("limit upper %f : ",Fz[zp1]); + + if (ismono == 1) Fz[zp1] = max(Fz[zp1], qmin * w[zp1] * dt); + if (ismono == -1) Fz[zp1] = min(Fz[zp1], qmax * w[zp1] * dt); + + if (ismono == 1) Fz[zp1] = qmin * w[zp1] * dt; + if (ismono == -1) Fz[zp1] = qmax * w[zp1] * dt; + if (checkf && c == cd) { + printf("%f\n",Fz[zp1]); + printf(" new mass out upper = %f\n", ee, Fz[c] * window->cellarea[c2]); + } } + */ } + c = ci; + /*-----------------------------------------------------------------*/ /* Check */ - if (checkf && c == cd) { - d1 = (tro - (dtracer + vflux)) / vol; - printf("new dtracer=%f tr=%f\n", dtracer, d1); + if (!monof && checkf && c == cd) { + d1 = (volo * tr[c] - (dtracer + vflux)) / vol; + printf("tr=%f min=%f max=%f\n", d1, windat->trmin[c], windat->trmax[c]); + } + /* Monotonicity check using limited fluxes */ + if (monof) { + double eps = -1e-6; + zp1 = window->zp1[c]; + vflux = - Fz[c] * window->cellarea[c2] ; + if (c != cs) vflux += Fz[zp1] * window->cellarea[c2]; + d1 = (volo * tr[c] - (dtracer + vflux)) / vol; + if(checkf && c == cd) { + printf("new tr=%f min=%f max=%f\n", d1, qn, qx); + if (d1 - qn >= eps && qx - d1 >= eps) + printf("New tracer monotonic using limited fluxes.\n"); + } + if (d1 - qn < eps) { + hd_quit("Monotonicity min post error %f %d k=%d tr=%f, qn=%f, qx=%f\n", + windat->days, c, window->s2k[c], d1, qn, qx); + if(checkf && c == cd) hd_quit("Quitting\n"); + return(1); + } + if (qx - d1 < eps) { + hd_quit("Monotonicity max post error %f %d k=%d tr=%f, qn=%f, qx=%f\n", + windat->days, c, window->s2k[c], d1, qn, qx); + if(checkf && c == cd) hd_quit("Quitting\n"); + return(1); + } } + return(0); } /* END universal_limit() */ /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Checks an updated tracer value against smin, smax */ +/*-------------------------------------------------------------------*/ +double check_tr_value(geometry_t *window, + window_t *windat, + win_priv_t *wincon, + double *tr, + double *Fx, + double *Fz, + double dt, + int c + ) +{ + int cc, cc1, c1, e, ee; + int c2 = window->m2d[c]; /* 2D cell corresponding to 3D cell*/ + int zp1 = window->zp1[c]; /* Cell above cell c */ + int scf = 0; /* Surface cell flag */ + double trn = tr[c]; /* New tracer */ + double *dtracer = wincon->w1; /* Horizontal fluxes */ + double *osubeta = wincon->d1; /* Old elevation */ + double *subeta = wincon->d2; /* Elevation */ + double *smin = wincon->crfxc; /* Minimum horz streamline value */ + double *smax = wincon->crfyc; /* Maximum horz streamline value */ + double *sminz = wincon->crfxf; /* Minimum vert streamline value */ + double *smaxz = wincon->crfyf; /* Maximum vert streamline value */ + double mn, mx; /* Min / max values at c */ + double eps = 1e-5; /* Tolerance for non-monotonicity */ + int verbose = 0; /* Print output */ + double ret = 0.0; + + /* Local bounds */ + mn = sminz[c]; + mx = smaxz[c]; + for (ee = 1; ee <= window->npe[c2]; ee++) { + e = window->c2e[ee][c]; + mn = min(mn, smin[e]); + mx = max(mx, smax[e]); + } + + /* Check if it's a surface cell */ + for (cc1 = 1; cc1 <= wincon->vcs1; cc1++) { + if (c == wincon->s1[cc1]) { + scf = 1; + break; + } + } + + /* Tracer horizontal fluxes */ + if (scf) { + c1 = c2; + while (c1 != window->zm1[c]) { + dtracer[c1] = 0.0; + for (ee = 1; ee <= window->npe[c2]; ee++) { + e = window->c2e[ee][c1]; + dtracer[c1] += (window->eSc[ee][c2] * Fx[e] * dt); + } + c1 = window->zm1[c1]; + } + } else { + for (ee = 1; ee <= window->npe[c2]; ee++) { + e = window->c2e[ee][c]; + dtracer[c] += (window->eSc[ee][c2] * Fx[e] * dt); + } + } + + /* Update */ + if (scf) { + double top = -Fz[c] * window->cellarea[c2]; + trn = surf_conc(window, windat, wincon, tr, osubeta[c2], + subeta[c2], c, c2, cc1, top, dtracer); + } else { + trn *= wincon->Ds[c2]; + trn -= ((dtracer[c] / window->cellarea[c2] + + (Fz[zp1] - Fz[c])) / wincon->dz[c]); + } + trn = max(trn, 0.0); + if (trn - mn < -eps) ret = fabs(trn - mn); + if (trn - mx > eps) ret = fabs(trn - mx); + if (verbose) { + if (verbose == 1) { + if (scf) + printf("%f : surface cell %d tr=%f, trn=%f (mn=%f, mx=%f)\n", windat->days, c, tr[c], trn, mn, mx); + else + printf("%f : cell %d tr=%f, trn=%f (mn=%f, mx=%f)\n", windat->days, c, tr[c], trn, mn, mx); + } else if (verbose == 2 && ret) { + if (scf) + printf("%f : surface cell %d tr=%f, trn=%f (mn=%f, mx=%f) r=%f\n", windat->days, c, tr[c], trn, mn, mx, ret); + else + printf("%f : cell %d tr=%f, trn=%f (mn=%f, mx=%f) r=%f\n", windat->days, c, tr[c], trn, mn, mx, ret); + } + } + return(ret); +} + +/* END check_tr_value() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Gets the local bounds for cell c, i.e. the minimum and maximum */ /* tracer values at all stencil points used to interpolate a value. */ @@ -4250,7 +4132,7 @@ void clip_ffsl(geometry_t *window, /* Window geometry */ double *smax = wincon->crfyc; /* Maximum streamline value */ double *sminz = wincon->crfxf; /* Minimum vertical streamline value*/ double *smaxz = wincon->crfyf; /* Maximum vertical streamline value*/ - return; + /* Get the min/max along all streamlines */ for (cc = 1; cc <= window->b3_t; cc++) { c = window->w3_t[cc]; @@ -4384,6 +4266,50 @@ void clip_ffsl(geometry_t *window, /* Window geometry */ /*-------------------------------------------------------------------*/ +/*-------------------------------------------------------------------*/ +/* Clips tracer values to the minimum and maximum values set in the */ +/* universal_limit() function. */ +/*-------------------------------------------------------------------*/ +void clip_ffsl_ultimate(geometry_t *window, /* Window geometry */ + window_t *windat, /* Window data */ + win_priv_t *wincon, /* Window constants */ + double *tr /* Tracer array */ + ) +{ + int cc, c; + double *mn = windat->trmin; /* Minimum value */ + double *mx = windat->trmax; /* Maximum value */ + double eps = -1e-6; + int checkf = 0; + + if (checkf) { + for (cc = 1; cc <= wincon->vc1; cc++) { + c = wincon->s1[cc]; + if (window->cask[c] & W_WET) { + if (mn[c] != HUGE && tr[c] - mn[c] < eps) { + hd_quit("Monotonicity tr min error %f %d k=%d tr=%f, min=%f, max=%f\n", + windat->days, c, window->s2k[c], tr[c], + windat->trmin[c], windat->trmin[c]); + } + if (mx[c] != -HUGE && mx[c] - tr[c] < eps) { + hd_quit("Monotonicity tr max error %f %d k=%d tr=%f, min=%f, max=%f\n", + windat->days, c, window->s2k[c], tr[c], + windat->trmin[c], windat->trmax[c]); + } + } + } + } + + for (cc = 1; cc <= window->v3_t; cc++) { + c = window->w3_t[cc]; + tr[c] = max(min(mx[c], tr[c]), mn[c]); + } +} + +/* END clip_ffsl_ultimate() */ +/*-------------------------------------------------------------------*/ + + /*-------------------------------------------------------------------*/ /* Sets up a grid_specs structure for variables using LAGRANGE that */ /* require interpolation. */ diff --git a/model/hd/control/globals.c b/model/hd/control/globals.c index cb6b886..483cf2c 100644 --- a/model/hd/control/globals.c +++ b/model/hd/control/globals.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: globals.c 5841 2018-06-28 06:51:55Z riz008 $ + * $Id: globals.c 7569 2024-05-27 07:14:59Z riz008 $ * */ @@ -47,15 +47,15 @@ double ambpress; /* Ambient atmospheric pressure */ double g; /* gravity */ double spec_heat; /* Specific heat of water */ -hd_data_t *hd_data; -geometry_t *geom; -geometry_t **window; -open_bdrys_t **Opens; -window_t **windat; -win_priv_t **wincon; -master_t *master; -parameters_t *params; -dump_data_t *dumpdata; +hd_data_t *hd_data = NULL; +geometry_t *geom = NULL; +geometry_t **window = NULL; +open_bdrys_t **Opens = NULL; +window_t **windat = NULL; +win_priv_t **wincon = NULL; +master_t *master = NULL; +parameters_t *params = NULL; +dump_data_t *dumpdata = NULL; ts_point_t tsflush; ts_point_t tsphist; ts_point_t tsalert; diff --git a/model/hd/control/main.c b/model/hd/control/main.c index afef7f8..e124e53 100644 --- a/model/hd/control/main.c +++ b/model/hd/control/main.c @@ -15,7 +15,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: main.c 6597 2020-09-03 05:29:26Z riz008 $ + * $Id: main.c 7569 2024-05-27 07:14:59Z riz008 $ * */ @@ -376,7 +376,10 @@ void print_trace (void) size = backtrace(array, 30); strings = backtrace_symbols(array, size); - hd_error("Segmentation violation detect (simulation time = %.4f days)\n", master->days); + if (master) + hd_error("Segmentation violation detect (simulation time = %.4f days)\n", master->days); + else + hd_error("Segmentation violation detected\n"); hd_error("Stack trace:\n", size); /* diff --git a/model/hd/diagnostics/monitor.c b/model/hd/diagnostics/monitor.c index 2df4261..2909fb7 100644 --- a/model/hd/diagnostics/monitor.c +++ b/model/hd/diagnostics/monitor.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: monitor.c 6709 2021-03-29 00:53:28Z her127 $ + * $Id: monitor.c 7574 2024-05-30 03:39:28Z riz008 $ * */ @@ -4542,7 +4542,7 @@ void mass_diag(geometry_t *window, /* Window geometry */ } /* Add sediment mass if this tracer has a sediment component */ - npor = tracer_find_index("porosity", wincon->ntr, wincon->trinfo_sed); + npor = tracer_find_index("porosity", wincon->nsed, wincon->trinfo_sed); for (n = 0; n < windat->ntot; n++) { if ((trn = windat->totid_sed[n]) >= 0) { for(cc = 1; cc <= window->b2_t; cc++) { diff --git a/model/hd/ecology/ecology.c b/model/hd/ecology/ecology.c index c0cee7c..4419bf8 100644 --- a/model/hd/ecology/ecology.c +++ b/model/hd/ecology/ecology.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ecology.c 7284 2023-01-09 23:03:51Z bai155 $ + * $Id: ecology.c 7568 2024-05-27 07:06:54Z riz008 $ * */ @@ -1340,7 +1340,8 @@ static int eco_set_autotracer(FILE *fp, int ntr, int tn, int necoclass, const char *ecoclass[][2], - int trinfo_type) + int trinfo_type, + int isauto) /* params->runmode & AUTO */ { char *vars[ECO_MAXNUMARGS], buf[MAXSTRLEN]; int m, n, i; @@ -1371,9 +1372,11 @@ static int eco_set_autotracer(FILE *fp, trinfo[tn].m = -1; // does not exist in the prm-file tracer_re_read(&trinfo[tn], fp, trinfo_type); /* - * Fill in the tracer number + * Fill in the tracer number, for AUTO model only */ - trinfo[tn].n = tn; + trinfo[tn].m = -1; + if (isauto) + trinfo[tn].m = tn; tn++; } } @@ -1388,11 +1391,12 @@ static int eco_set_autotracer(FILE *fp, /* Routine to initialise the 2D tracers in the master */ /*-------------------------------------------------------------------*/ int ecology_autotracer_3d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn) + void *e, tracer_info_t *trinfo, int ntr, int tn, + int isauto) { /* ECO 3D */ tn = eco_set_autotracer(fp, do_eco, eco_vars, eco_defs, e, trinfo, ntr, tn, - NUM_ECO_VARS_3D, ECONAME3D, WATER); + NUM_ECO_VARS_3D, ECONAME3D, WATER, isauto); return(tn); } @@ -1404,11 +1408,12 @@ int ecology_autotracer_3d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, /* Routine to initialise the 2D tracers in the master */ /*-------------------------------------------------------------------*/ int ecology_autotracer_2d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn) + void *e, tracer_info_t *trinfo, int ntr, int tn, + int isauto) { /* ECO 2D */ tn = eco_set_autotracer(fp, do_eco, eco_vars, eco_defs, e, trinfo, ntr, tn, - NUM_ECO_VARS_2D, ECONAME2D, INTER); + NUM_ECO_VARS_2D, ECONAME2D, INTER, isauto); return(tn); } @@ -1421,11 +1426,12 @@ int ecology_autotracer_2d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, /* Routine to initialise the 2D tracers in the master */ /*-------------------------------------------------------------------*/ int ecology_autotracer_sed(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn) + void *e, tracer_info_t *trinfo, int ntr, int tn, + int isauto) { /* ECO SED */ tn = eco_set_autotracer(fp, do_eco, eco_vars, eco_defs, e, trinfo, ntr, tn, - NUM_ECO_VARS_3D, ECONAME3D, SEDIM); + NUM_ECO_VARS_3D, ECONAME3D, SEDIM, isauto); return(tn); } diff --git a/model/hd/forcings/dhw.c b/model/hd/forcings/dhw.c index 6b30fb6..035f31a 100644 --- a/model/hd/forcings/dhw.c +++ b/model/hd/forcings/dhw.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dhw.c 6710 2021-03-29 00:54:11Z her127 $ + * $Id: dhw.c 7542 2024-05-09 03:27:40Z her127 $ * */ @@ -30,6 +30,7 @@ double thresh = 1.0; static int dhw_init(sched_event_t *event); double dhw_event(sched_event_t *event, double t); static void dhw_cleanup(sched_event_t *event, double t); +int ts_check_time(timeseries_t *ts, double r); typedef struct { master_t *master; /* Grid associated with */ @@ -161,6 +162,7 @@ double dhw_event(sched_event_t *event, double t) varid = ts_get_index(ts, fv_get_varname(dhw->tsnames[n], tname, buf)); if (varid < 0) found = 0; } + if (found && !dhw->checked) { prm_set_errfn(hd_silent_warn); hd_ts_multifile_check(dhw->ntsfiles, dhw->tsfiles, dhw->tsnames, tname, @@ -171,7 +173,7 @@ double dhw_event(sched_event_t *event, double t) /* Check if the offset dhd value is in the file */ tin = t - dhw->offset; for (n = 0; n < dhw->ntsfiles; n++) { - if (ts_has_time(dhw->tsfiles[n], tin) == 0) + if (ts_check_time(dhw->tsfiles[n], tin) == 0) found = 0; } @@ -206,7 +208,6 @@ double dhw_event(sched_event_t *event, double t) if (master->dhd[tn][c] > master->dhwc[tn][c] + thresh) master->dhw[tn][c] += fact * ((master->dhd[tn][c] - master->dhwc[tn][c]) - dhdo); } - /* Reinitialize the dhd value */ master->dhd[tn][c] = 0.0; } @@ -297,3 +298,40 @@ void calc_dhd(geometry_t *window, /* Window geometry */ /* END calc_dhd() */ /*-------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------*/ +/* Wrapper to check if a record exists in a file */ +/*-------------------------------------------------------------------*/ +int ts_check_time(timeseries_t *ts, double r) +{ + datafile_t *df = ts->df; + int ilow = 0; + int ihigh = df->nrecords - 1; + + if (df->records == NULL) { + return(0); + } + + /* If the time has not changed since the last function call, then + return the previously calculated bounds and fraction. */ + if (r == df->t0) { + return(1); + } + + if (r <= df->records[ilow]) { + return(0); + } + + if (r >= df->records[ihigh]) { + ts_has_time(ts, r); + if (r >= df->records[ihigh]) { + return(0); + } else + return(1); + } + return(1); +} + +/* END ts_check_time() */ +/*-------------------------------------------------------------------*/ diff --git a/model/hd/ginterface/ginterface.c b/model/hd/ginterface/ginterface.c index 0a1d071..a8b794a 100644 --- a/model/hd/ginterface/ginterface.c +++ b/model/hd/ginterface/ginterface.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ginterface.c 7475 2023-12-16 11:17:08Z bai155 $ + * $Id: ginterface.c 7578 2024-05-31 01:32:53Z riz008 $ * */ @@ -3475,3 +3475,15 @@ double ginterface_get_eta(void* hmodel, int b) double v = windat->eta[c2]; return v; } + +/* + * Handy utility for when singleton operations are needed in a + * multi-window environemnt. eg. ecology file operations + */ +int ginterface_is_window1(void *hmodel) +{ + if (hmodel == NULL) + return 1; + else + return (((geometry_t*)hmodel)->wn == 1); +} diff --git a/model/hd/include/proto.h b/model/hd/include/proto.h index c362e00..7cab030 100644 --- a/model/hd/include/proto.h +++ b/model/hd/include/proto.h @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: proto.h 7477 2023-12-22 01:31:36Z riz008 $ + * $Id: proto.h 7577 2024-05-31 00:36:37Z riz008 $ * */ @@ -26,7 +26,7 @@ /*------------------------------------------------------------------*/ #define SHOC_MAJOR_VERSION 1 #define SHOC_MINOR_VERSION 3 -#define SHOC_PATCH_VERSION 1 +#define SHOC_PATCH_VERSION 2 /*------------------------------------------------------------------*/ /* Parameter input routines */ @@ -1666,11 +1666,11 @@ void eco_set_tracer_defaults(tracer_info_t *tracer, char *trname, void eco_read_tr_atts(tracer_info_t *tr, FILE *fp, char *keyname); void read_ecology(parameters_t *params, FILE *fp, int *ntr); int ecology_autotracer_2d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn); + void *e, tracer_info_t *trinfo, int ntr, int tn, int isauto); int ecology_autotracer_3d(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn); + void *e, tracer_info_t *trinfo, int ntr, int tn, int isauto); int ecology_autotracer_sed(FILE *fp, int do_eco, char *eco_vars, char *eco_defs, - void *e, tracer_info_t *trinfo, int ntr, int tn); + void *e, tracer_info_t *trinfo, int ntr, int tn, int isauto); int count_eco_3d(char *eco_vars); int count_eco_2d(char *eco_vars); void eco_write_tr_atts(tracer_info_t *tr, FILE *fp, int n); @@ -1723,11 +1723,11 @@ int count_sed_classes(char *sed_vars); int count_sed_3d(); int count_sed_2d(); int sediment_autotracer_3d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn); + tracer_info_t *trinfo, int ntr, int tn, int isauto); int sediment_autotracer_2d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn); + tracer_info_t *trinfo, int ntr, int tn, int isauto); int sediment_autotracer_sed(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn); + tracer_info_t *trinfo, int ntr, int tn, int isauto); int sediment_autotracer_write(master_t *master, FILE *op, int tn); void sed_write_tr_atts(tracer_info_t *tr, FILE *fp, int n); void print_tr_sed_atts(tracer_info_t *tr); diff --git a/model/hd/outputs/dumpfile.c b/model/hd/outputs/dumpfile.c index 846cabb..b331adc 100644 --- a/model/hd/outputs/dumpfile.c +++ b/model/hd/outputs/dumpfile.c @@ -16,7 +16,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: dumpfile.c 7064 2022-03-16 01:58:29Z her127 $ + * $Id: dumpfile.c 7575 2024-05-30 03:44:13Z riz008 $ * */ @@ -193,7 +193,8 @@ double dump_event(sched_event_t *event, double t) master_t *master = (master_t *)schedGetPublicData(event); geometry_t *geom = master->geom; dump_data_t *dumpdata = master->dumpdata; - df_dispatch_data_t* dispatch_data = malloc(sizeof(df_dispatch_data_t)); + df_dispatch_data_t* dispatch_data = + (df_dispatch_data_t*)schedGetPrivateData(event); int debug = 0; /* Output dump and test point values if required */ @@ -240,11 +241,15 @@ double dump_event(sched_event_t *event, double t) } #endif + if (dispatch_data == NULL) { + dispatch_data = (df_dispatch_data_t*)calloc(1,sizeof(df_dispatch_data_t)); + schedSetPrivateData(event,dispatch_data); + } + TIMING_SET; dumpdata_fill(geom, master, dumpdata); TIMING_DUMP(1," dumpdata_fill"); dispatch_data->t = t; - schedSetPrivateData(event,dispatch_data); /* Run through the list of dumpfiles and find the the one that */ /* next fires off - after the current one diff --git a/model/hd/sediments/sediments.c b/model/hd/sediments/sediments.c index 408f58a..bfc5c83 100644 --- a/model/hd/sediments/sediments.c +++ b/model/hd/sediments/sediments.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: sediments.c 7484 2024-02-13 23:53:02Z mar644 $ + * $Id: sediments.c 7568 2024-05-27 07:06:54Z riz008 $ * */ @@ -2163,7 +2163,8 @@ static int sed_set_autotracer(FILE *fp, int ntr, int tn, int nsedclass, char *sedclass[], - int trinfo_type) + int trinfo_type, + int isauto) /* params->runmode & AUTO */ { char *vars[MAXSTRLEN * MAXNUMARGS], buf[MAXSTRLEN]; int m, n, i; @@ -2198,11 +2199,9 @@ static int sed_set_autotracer(FILE *fp, */ sed_read_tr_atts(&trinfo[tn], fp, sedclass[i]); trinfo[tn].m = -1; // does not exist in the prm-file + if (isauto) + trinfo[tn].m = tn; // Overwrite for AUTO mode tracer_re_read(&trinfo[tn], fp, trinfo_type); - /* - * Fill in the tracer number - */ - trinfo[tn].n = tn; tn++; } } @@ -2222,9 +2221,11 @@ static int sed_set_autotracer(FILE *fp, sed_read_tr_atts(&trinfo[tn], fp, sedclass[i]); tracer_re_read(&trinfo[tn], fp, trinfo_type); /* - * Fill in the tracer number + * Fill in the tracer number for AUTO mode only */ - trinfo[tn].n = tn; + trinfo[tn].m = -1; + if (isauto) + trinfo[tn].m = tn; tn++; } } @@ -2238,17 +2239,17 @@ static int sed_set_autotracer(FILE *fp, ./hd/tracers/load_tracer.c */ /*-------------------------------------------------------------------*/ int sediment_autotracer_3d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn) + tracer_info_t *trinfo, int ntr, int tn, int isauto) { int i; /* SED CLASSES */ if (do_sed && strlen(sed_vars)) { tn = sed_set_autotracer(fp, sed_vars, sed_defs, trinfo, ntr, tn, - NUM_SED_VARS, SEDCLASS, WATER); + NUM_SED_VARS, SEDCLASS, WATER, isauto); /* SED 3D - mandatory*/ tn = sed_set_autotracer(fp, NULL, sed_defs, trinfo, ntr, tn, - NUM_SED_VARS_3D, SEDNAME3D, WATER); + NUM_SED_VARS_3D, SEDNAME3D, WATER, isauto); } return(tn); } @@ -2262,12 +2263,12 @@ int sediment_autotracer_3d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, ./hd/tracers/load_tracer.c */ /*-------------------------------------------------------------------*/ int sediment_autotracer_2d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn) + tracer_info_t *trinfo, int ntr, int tn, int isauto) { /* SED 2D - mandatory */ if (do_sed && strlen(sed_vars)) { tn = sed_set_autotracer(fp, NULL, sed_defs, trinfo, ntr, tn, - NUM_SED_VARS_2D, SEDNAME2D, INTER); + NUM_SED_VARS_2D, SEDNAME2D, INTER, isauto); } return(tn); } @@ -2281,7 +2282,7 @@ int sediment_autotracer_2d(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, ./hd/tracers/load_tracer.c */ /*-------------------------------------------------------------------*/ int sediment_autotracer_sed(FILE *fp, int do_sed, char *sed_vars, char *sed_defs, - tracer_info_t *trinfo, int ntr, int tn) + tracer_info_t *trinfo, int ntr, int tn, int isauto) { /* Temperature and salinity */ @@ -2323,11 +2324,11 @@ int sediment_autotracer_sed(FILE *fp, int do_sed, char *sed_vars, char *sed_defs /* SED CLASSES */ if (do_sed && strlen(sed_vars)) { tn = sed_set_autotracer(fp, sed_vars, sed_defs, trinfo, ntr, tn, - NUM_SED_VARS, SEDCLASS, SEDIM); + NUM_SED_VARS, SEDCLASS, SEDIM, isauto); /* SED 3D - mandatory */ tn = sed_set_autotracer(fp, NULL, sed_defs, trinfo, ntr, tn, - NUM_SED_VARS_3D, SEDNAME3D, SEDIM); + NUM_SED_VARS_3D, SEDNAME3D, SEDIM, isauto); } return(tn); } diff --git a/model/hd/tracers/load_tracer.c b/model/hd/tracers/load_tracer.c index 6dcca11..3aa3f0f 100644 --- a/model/hd/tracers/load_tracer.c +++ b/model/hd/tracers/load_tracer.c @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: load_tracer.c 7453 2023-12-13 03:45:53Z her127 $ + * $Id: load_tracer.c 7568 2024-05-27 07:06:54Z riz008 $ * */ @@ -1833,7 +1833,7 @@ void init_tracer_2d(parameters_t *params, /* Input parameters data */ tn++; /* Set up sediment tracers if required */ tn = sediment_autotracer_2d(params->prmfd, params->do_sed, params->sed_vars, - params->sed_defs, master->trinfo_2d, master->ntrS, tn); + params->sed_defs, master->trinfo_2d, master->ntrS, tn, (params->runmode & AUTO)); } #endif /* Ecology error maps */ @@ -1852,7 +1852,8 @@ void init_tracer_2d(parameters_t *params, /* Input parameters data */ /* Set up sediment tracers if required */ tn = ecology_autotracer_2d(params->prmfd, params->do_eco, params->eco_vars, params->eco_defs, params->pre_eco, - master->trinfo_2d, master->ntrS, tn); + master->trinfo_2d, master->ntrS, tn, + (params->runmode & AUTO)); } #endif } @@ -1882,7 +1883,7 @@ void init_tracer_sed(parameters_t *params, /* Input parameters data */ if (params->do_sed) { /* Set up sediment tracers if required */ tn = sediment_autotracer_sed(params->prmfd, params->do_sed, params->sed_vars, - params->sed_defs, master->trinfo_sed, master->nsed, tn); + params->sed_defs, master->trinfo_sed, master->nsed, tn, (params->runmode & AUTO)); } #endif @@ -1892,7 +1893,8 @@ void init_tracer_sed(parameters_t *params, /* Input parameters data */ tn = ecology_autotracer_sed(params->prmfd, params->do_eco, params->eco_vars, params->eco_defs, params->pre_eco, - master->trinfo_sed, master->nsed, tn); + master->trinfo_sed, master->nsed, tn, + (params->runmode & AUTO)); } #endif @@ -4042,7 +4044,7 @@ void init_tracer_3d(parameters_t *params, /* Input parameters data */ if (params->do_sed) { /* Set up sediment tracers if required */ tn = sediment_autotracer_3d(params->prmfd, params->do_sed, params->sed_vars, - params->sed_defs, master->trinfo_3d, master->ntr, tn); + params->sed_defs, master->trinfo_3d, master->ntr, tn, (params->runmode & AUTO)); } #endif @@ -4051,7 +4053,8 @@ void init_tracer_3d(parameters_t *params, /* Input parameters data */ /* Set up ecology tracers if required */ tn = ecology_autotracer_3d(params->prmfd, params->do_eco, params->eco_vars, params->eco_defs, params->pre_eco, - master->trinfo_3d, master->ntr, tn); + master->trinfo_3d, master->ntr, tn, + (params->runmode & AUTO)); } #endif @@ -5874,7 +5877,8 @@ void create_tracer_3d(parameters_t *params) /* Input parameters */ if (params->do_sed) { /* Set up sediment tracers if required */ tn = sediment_autotracer_3d(params->prmfd, params->do_sed, params->sed_vars, - params->sed_defs, trinfo, params->ntr, tn); + params->sed_defs, trinfo, params->ntr, tn, + (params->runmode & AUTO)); } #endif @@ -5883,7 +5887,8 @@ void create_tracer_3d(parameters_t *params) /* Input parameters */ /* Set up ecology tracers if required */ tn = ecology_autotracer_3d(params->prmfd, params->do_eco, params->eco_vars, params->eco_defs, params->pre_eco, - trinfo, params->ntr, tn); + trinfo, params->ntr, tn, + (params->runmode & AUTO)); } #endif diff --git a/model/hd/tracers/reset.c b/model/hd/tracers/reset.c index 832fea9..a77ea81 100644 --- a/model/hd/tracers/reset.c +++ b/model/hd/tracers/reset.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: reset.c 7454 2023-12-13 03:46:19Z her127 $ + * $Id: reset.c 7571 2024-05-28 05:20:55Z riz008 $ * */ @@ -643,6 +643,17 @@ static double trans_reset_event(sched_event_t *event, double t) tsin = reset->tnext + master->grid_dt; } + /* + * The above block can cause tsin to be set beyond the end of + * simulation. This can cause the eval routines below issuing a + * FATAL error. The following early return is to prevent this early + * exit. This function returns to sched_set_time which returns to + * the while loop in main. As we'll be at the last time point, the + * while loop in main will exit anyway + */ + if (tsin > tsout) + return(tsout); + if ((t + reset->dt / 10.0) >= (reset->tnext - tsync)) { if (master->tmode & SP_ORIGIN) { hd_trans_multifile_eval(master, reset->ntsfiles, reset->tsfiles, diff --git a/model/lib/ecology/column.c b/model/lib/ecology/column.c index 5feea56..6fb2e5b 100644 --- a/model/lib/ecology/column.c +++ b/model/lib/ecology/column.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: column.c 6910 2021-09-27 05:57:34Z bai155 $ + * $Id: column.c 7539 2024-05-03 05:45:11Z riz008 $ * */ @@ -42,7 +42,7 @@ */ column* column_create(ecology* e, int b) { - column* col = malloc(sizeof(column)); + column* col = calloc(1, sizeof(column)); /* * k range, empty layers included @@ -81,7 +81,7 @@ column* column_create(ecology* e, int b) col->n_wc = 0; col->n_sed = 0; - col->cells = calloc(n_wc + n_sed - 1, sizeof(void*)); + col->cells = calloc(n_wc + n_sed, sizeof(void*)); /* * What is attempted here is to automatically exclude empty water or @@ -233,7 +233,7 @@ column* column_create(ecology* e, int b) /* Column based tracers */ col->y = d_alloc_2d(col->ncells, e->ntr); col->y_sed0 = d_alloc_1d(e->ntr); - col->y_epi = d_alloc_1d(e->nepi); + if (e->nepi) col->y_epi = d_alloc_1d(e->nepi); col->zc = d_alloc_1d(n_wc); col->dz = d_alloc_1d(n_wc); i = 0; diff --git a/model/lib/ecology/ecology.c b/model/lib/ecology/ecology.c index 2dc2484..44af2b6 100644 --- a/model/lib/ecology/ecology.c +++ b/model/lib/ecology/ecology.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ecology.c 7197 2022-09-13 10:41:09Z bai155 $ + * $Id: ecology.c 7576 2024-05-30 03:47:52Z riz008 $ * */ @@ -788,8 +788,8 @@ ecology* ecology_build(void* model, char* prmfname) * - Do this after the initial create_processes call to only * capture the events once (see call below) */ - e->eco_setup = e_fopen("ecology_setup.txt", "w"); - { + if (ginterface_is_window1(model)) { + e->eco_setup = e_fopen("ecology_setup.txt", "w"); /* Open file in the outputs area */ char *opath = ginterface_get_output_path(); if (opath) { @@ -797,6 +797,26 @@ ecology* ecology_build(void* model, char* prmfname) e->eco_osetup = e_fopen(buf, "w"); } } + + /* Remove ecology_parameter_setup.nc if it exists */ + // - replaced with CLOBBER below + // system("rm ecology_parameter_setup.nc"); + + /* + * Create file, clobber if it exists + * - it's closed towards the end of this function + */ + if (ginterface_is_window1(model)) { + int ncid; + ncw_create(ECO_PARAMS_SETUP, NC_CLOBBER, &ncid); + write_text_att(ncid, NC_GLOBAL, "title", "CSIRO Environmental Modelling Suite (EMS) biological parameter setup."); + write_text_att(ncid, NC_GLOBAL, "description", "All variables specified in the biological parameter file"); + write_date_created(ncid); + /* utils.c calls ncredef */ + nc_enddef(ncid); + /* cache netcdf file id */ + e->eco_params_ncid = ncid; + } /* * Initialise number of rsr wavelengths from the model @@ -967,6 +987,9 @@ ecology* ecology_build(void* model, char* prmfname) } #endif + if (ginterface_is_window1(model)) + ncw_close(ECO_PARAMS_SETUP, e->eco_params_ncid); + fclose(prmfile); /* Initialise bio optical properties, if needed */ @@ -1000,7 +1023,8 @@ ecology* ecology_build(void* model, char* prmfname) einterface_ecologyinit(model, e); /* Close setup file, use the runlog from now on */ - fclose(e->eco_setup); + if (e->eco_setup) + fclose(e->eco_setup); if (e->eco_osetup) fclose(e->eco_osetup); e->eco_setup = NULL; @@ -1571,16 +1595,18 @@ void eco_write_setup(ecology *e, const char *str, ...) /* Guard against pre_build */ if (e->eco_setup == NULL) return; - - va_start(args, str); - vfprintf(e->eco_setup, str, args); - va_end(args); - - /* Write into outputs as well */ - if (e->eco_osetup) { + + if (ginterface_is_window1(e->model)) { va_start(args, str); - vfprintf(e->eco_osetup, str, args); + vfprintf(e->eco_setup, str, args); va_end(args); + + /* Write into outputs as well */ + if (e->eco_osetup) { + va_start(args, str); + vfprintf(e->eco_osetup, str, args); + va_end(args); + } } } diff --git a/model/lib/ecology/include/ecology_internal.h b/model/lib/ecology/include/ecology_internal.h index d42e23d..b09d265 100644 --- a/model/lib/ecology/include/ecology_internal.h +++ b/model/lib/ecology/include/ecology_internal.h @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ecology_internal.h 6903 2021-09-07 06:55:37Z bai155 $ + * $Id: ecology_internal.h 7576 2024-05-30 03:47:52Z riz008 $ * */ @@ -58,6 +58,8 @@ #define OMP_NUM_THREADS_DEF 1 #endif +#define ECO_PARAMS_SETUP "ecology_parameter_setup.nc" + #define ISDEBUG() (is_log_enabled(LDEBUG)) typedef struct { @@ -278,6 +280,9 @@ struct ecology { FILE *eco_setup; FILE *eco_osetup; // output path copy + /* ECO_PARAMS_SETUP file */ + int eco_params_ncid; + #if (NCPU > 1) int multithreaded; /* flag */ diff --git a/model/lib/ecology/include/ecology_version.h b/model/lib/ecology/include/ecology_version.h index b580f87..240755f 100644 --- a/model/lib/ecology/include/ecology_version.h +++ b/model/lib/ecology/include/ecology_version.h @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: ecology_version.h 7477 2023-12-22 01:31:36Z riz008 $ + * $Id: ecology_version.h 7577 2024-05-31 00:36:37Z riz008 $ * */ @@ -22,7 +22,7 @@ /* Release verions and getters */ #define ECOLOGY_MAJOR_VERSION 1 #define ECOLOGY_MINOR_VERSION 3 -#define ECOLOGY_PATCH_VERSION 1 +#define ECOLOGY_PATCH_VERSION 2 int get_ecology_major_vers(void); int get_ecology_minor_vers(void); diff --git a/model/lib/ecology/include/einterface.h b/model/lib/ecology/include/einterface.h index a0ac0a8..d36010c 100644 --- a/model/lib/ecology/include/einterface.h +++ b/model/lib/ecology/include/einterface.h @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: einterface.h 7107 2022-04-20 11:44:57Z bai155 $ + * $Id: einterface.h 7576 2024-05-30 03:47:52Z riz008 $ * */ @@ -301,6 +301,7 @@ int einterface_get_eco_flag(void* model, char* name); /* Generic interface */ extern void i_set_error(void* hmodel, int col, int errorf, char *text); extern int i_get_error(void* hmodel, int col); +extern int ginterface_is_window1(void *hmodel); /* Optical functions */ extern int einterface_is_optical(void* model, char *name); diff --git a/model/lib/ecology/parameter_defaults.c b/model/lib/ecology/parameter_defaults.c index 923e598..3a87cdd 100644 --- a/model/lib/ecology/parameter_defaults.c +++ b/model/lib/ecology/parameter_defaults.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: parameter_defaults.c 7356 2023-05-09 04:03:42Z riz008 $ + * $Id: parameter_defaults.c 7570 2024-05-27 07:15:45Z riz008 $ * */ @@ -2138,7 +2138,7 @@ void eco_params_bgc3p1(parameter_info **params, int *nprm) // parameters[n].value[0] = 396.48; // parameters[n].ref = "Mean 2013 at Mauna Loa: http://co2now.org/current-co2/co2-now/"; // parameters[n].index = n; - n++; + // n++; parameters[n].name = "N2"; parameters[n].desc = "Concentration of dissolved N2"; diff --git a/model/lib/ecology/process_library/light_spectral_col.c b/model/lib/ecology/process_library/light_spectral_col.c index 634c22e..db55d2b 100755 --- a/model/lib/ecology/process_library/light_spectral_col.c +++ b/model/lib/ecology/process_library/light_spectral_col.c @@ -60,25 +60,26 @@ * 39. Colour_of_source_in_water (flu and bio) to add attribute to optical_setup.nc * 45. Add potassium decay as light source. * 47. Add passive fluorescence for all phytoplankton? Even Symbiodinium? - * 53. Create function to read SWR at t+dt, perhaps using a tracer. * 63. Pigments are still in "csiro_siop_library.nc" - should be in csiro_optical_parameters_library.nc. * 64. Restart works for column.nc, but it produces extra times between restart time and last old write out. * 65. Make solar_azimuth from zenith a function. * 66. Should fulldisk be PAR-integrated? * 67. values_common_epi is still hardwired with some plant types. * 69. Time ranges and frequency for column output file. - * 70. Code speed up: a. diffaa should do all wavelengths at one. + * 70. Code speed up: a. diffaa should do all wavelengths at once. * b. X x Omega(X) can be outside the wave loop. * c. m * red * MW_Nitr * 1000 could be done at initialisation. - * d. 3D sensor - demon outside layer loop. + * d. 3D sensor - denom outside layer loop. * e. Secchi pathlength outside layer loop. * f. moon-phase once for all columns. - * 71. Generalise fine sensor grid so it doesn't have to be 1 nm bandwidth. + * g. calculate clear water bb at 590 in init to save time later. + * h. re-order struct so largest are first (e.g. doubles before ints). + * i. avoid passing complete structures as parameters (moonlight) + * j. organize the algorithm so that your inner most loop iterates over the second index. * 72. We don't have a simulated satellite products for Secchi. * 73. Light_spectral_col doesn't work for KEYWORD specification of tracers. * 74. Likely to be problems with using col->b to identify output columns in fully-coupled version. * 75. Put in ems version into optical_setup.nc file attributes. - * 76. write_date_created(ncid1) only works for shoc, so commented out. */ #include @@ -370,6 +371,9 @@ typedef struct { /* wavelengths at which sensor response are integrated */ + double wave_sensor_finish; + double wave_sensor_start; + double wave_sensor_stride; int num_sensor_waves; double *sensor_waves; @@ -509,7 +513,7 @@ void optdatascalarread(ecology *e, workspace *ws, char *source_file, int ncid, c void optdataspectralread(ecology *e, workspace *ws, char *source_file, int ncid, const char *varname, const char *wavename, const char *codename, double* out); void optdataspectralread_fine(ecology *e, workspace *ws, char *source_file, int ncid, const char *varname, const char *wavename, const char *codename, double* out); void scale_sensor_response(ecology *e, workspace *ws, const char *sensorname, double* in, double* out, double *scale); // NOT TESTED PROPERLY. -void ModelRsestoBandRs(ecology *e, workspace *ws, double* response, double* modelR, double *obsR); +void ModelRsestoBandRs(double* bandwidth, int num_wave, double* response, double* modelR, double *obsR); void OC4Me(double band3, double band4, double band5, double band6, double *oc4me_out); void OC3M(double band2, double band4, double band6, double *oc3m_out); void OC3V(double band3, double band4, double band6, double *oc3v_out); @@ -517,7 +521,7 @@ void Hue3B(double band3, double band4, double band6, double band8, double band11 void nFLH_modis(double band9, double band10, double band11, double *nFLH_modis_out); void TSSM(double band1, double *tssm_out); void KD490M(double band4, double band6, double *kd490m_out); -void index_of_refraction(ecology *e, workspace *ws, double **y, double* n_stw); +void index_of_refraction(double S, double T, double* wave, int num_wave, double* n_stw); void colour_in_seawater(ecology *e, workspace *ws, const char *varname); void colour_of_bottom_type(ecology *e, workspace *ws, const char *varname); void passive_fluorescence_per_cell(ecology *e,workspace *ws, double kI, double Iquota, double *fluorescence); @@ -607,13 +611,34 @@ void light_spectral_col_init(eprocess* p) /* Set-up sensor response grid with Rrs_fine - only works for 1 nm at the moment. */ + // add parameters for start, stride, count of sensor waves. + + ws->wave_sensor_start = try_parameter_value(e, "wave_sensor_start"); + if (isnan(ws->wave_sensor_start)){ + ws->wave_sensor_start = 300.0; + eco_write_setup(e,"Code default of = wave_sensor_start %e \n",ws->wave_sensor_start); + } + + ws->wave_sensor_stride = try_parameter_value(e, "wave_sensor_stride"); + if (isnan(ws->wave_sensor_stride)){ + ws->wave_sensor_stride = 1.0; + eco_write_setup(e,"Code default of = wave_sensor_stride %e \n",ws->wave_sensor_stride); + } + + ws->wave_sensor_finish = try_parameter_value(e, "wave_sensor_finish"); + if (isnan(ws->wave_sensor_finish)){ + ws->wave_sensor_finish = 800.0; + eco_write_setup(e,"Code default of = wave_sensor_count %e \n",ws->wave_sensor_finish); + } + int ii; - ws->num_sensor_waves = 501; + ws->num_sensor_waves = (int) (ws->wave_sensor_finish - ws->wave_sensor_start)/ws->wave_sensor_stride; + ws->num_sensor_waves += 1; ws->sensor_waves = d_alloc_1d(ws->num_sensor_waves); - ws->sensor_waves[0] = 300.0; + ws->sensor_waves[0] = ws->wave_sensor_start; for (ii = 1; iinum_sensor_waves; ii++) { - ws->sensor_waves[ii] = ws->sensor_waves[0]+ii; + ws->sensor_waves[ii] = ws->sensor_waves[ii-1]+ws->wave_sensor_stride; } /***********************************************************************************************/ @@ -2275,35 +2300,22 @@ void light_spectral_col_init(eprocess* p) nc_put_att_text(ncid, varid, "description", 17,"Relative humidity"); nc_put_att_text(ncid, varid, "units",1,"%"); - if (ws->Sentinel_3B_Band3_i){ - nc_def_var(ncid,"Sentinel_3B_Band3",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band3"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); - } - if (ws->Sentinel_3B_Band4_i){ - nc_def_var(ncid,"Sentinel_3B_Band4",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band4"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); - } - if (ws->Sentinel_3B_Band5_i){ - nc_def_var(ncid,"Sentinel_3B_Band5",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band5"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); - } - if (ws->Sentinel_3B_Band6_i){ - nc_def_var(ncid,"Sentinel_3B_Band6",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band6"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); - } - if (ws->Sentinel_3B_Band8_i){ - nc_def_var(ncid,"Sentinel_3B_Band8",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band8"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); - } - if (ws->Sentinel_3B_Band11_i){ - nc_def_var(ncid,"Sentinel_3B_Band11",NC_DOUBLE,1,dims3,&varid); - nc_put_att_text(ncid, varid, "description", 47,"Remote-sensing reflectance on Sentinel_3B_Band11"); - nc_put_att_text(ncid, varid, "units", 4,"sr-1"); + // loop through epi tracers with "OPITCAL FLAG" + + char specresp2d_name1[MAXSTRLEN]; + + for (i = 0; i < e->nepi; i++) { + strcpy(epiname, e->epinames[i]); + if (einterface_is_optical2d(e->model,epiname)) { + if (((epiname[0] == 'M') && (epiname[1] == 'A'))||((epiname[0] == 'S') && (epiname[1] == 'G'))){ // not a sensor + }else{ + einterface_get_specresp2d_name(e->model, epiname, specresp2d_name1); + sprintf(specresp2d_name1, "Remote-sensing reflectance on %s",epiname); + nc_def_var(ncid,epiname,NC_DOUBLE,1,dims3,&varid); + nc_put_att_text(ncid, varid, "description",strlen(specresp2d_name1),specresp2d_name1); + nc_put_att_text(ncid, varid, "units", 4,"sr-1"); + } + } } if (ws->Moonlight_i > -1){ @@ -3346,7 +3358,7 @@ void light_spectral_col_precalc(eprocess* p, void* pp) ems_lunar_zenith = (fabs(ems_lunar_zenith)<1.0e-15)? 1.0e-15:ems_lunar_zenith; ems_lunar_zenith = ((ems_lunar_zenith-M_PI/2.0)-fabs(ems_lunar_zenith-M_PI/2.0))/2.0+M_PI/2.0; - index_of_refraction(e,ws,y,n_stw); + index_of_refraction(y[ws->salt_i][0],y[ws->temp_i][0],ws->wave, ws->num_wave, n_stw); for (w = 0; wnum_wave; w++) { thetaw = asin(sin(zenith)/n_stw[w]); // old code was: asin(sin(zenith)/1.33); @@ -4492,30 +4504,22 @@ void light_spectral_col_precalc(eprocess* p, void* pp) varid = ncw_var_id(ncid,"hyd_solar_azimuth"); nc_put_var1_double(ncid,varid,&reclen,&hyd_azimuth); - - if (ws->Sentinel_3B_Band3_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band3"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band3_i]); - } - if (ws->Sentinel_3B_Band4_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band4"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band4_i]); - } - if (ws->Sentinel_3B_Band5_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band5"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band5_i]); - } - if (ws->Sentinel_3B_Band6_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band6"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band6_i]); - } - if (ws->Sentinel_3B_Band8_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band8"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band8_i]); - } - if (ws->Sentinel_3B_Band11_i){ - varid = ncw_var_id(ncid,"Sentinel_3B_Band11"); - nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->Sentinel_3B_Band11_i]); + + // Careful here - assumes search in init finds hits in the same order here. + int bbb = -1; + char epiname[MAXSTRLEN] = ""; + char specresp2d_name[MAXSTRLEN]; + for (i = 0; i < e->nepi; i++) { + strcpy(epiname, e->epinames[i]); + if (einterface_is_optical2d(e->model,epiname)) { + if (((epiname[0] == 'M') && (epiname[1] == 'A'))||((epiname[0] == 'S') && (epiname[1] == 'G'))){ // not a sensor + }else{ + bbb = bbb+1; + einterface_get_specresp2d_name(e->model, epiname, specresp2d_name); + varid = ncw_var_id(ncid,epiname); + nc_put_var1_double(ncid,varid,&reclen,&y_epi[ws->ind_sp2[bbb]]); + } + } } if (ws->Moonlight_i > -1){ @@ -5399,7 +5403,7 @@ void meb_swr_airtrans_and_albedo(double tcld, double lunar_dec, double lunar_ang *swr_albedo = albedo2; } -void ModelRsestoBandRs(ecology *e,workspace *ws, double* response, double* modelR, double *obsR) +void ModelRsestoBandRs(double* bandwidth, int num_wave, double* response, double* modelR, double *obsR) { /* This function takes model reflectances and spectral response curves and returns band reflectance */ @@ -5407,9 +5411,9 @@ void ModelRsestoBandRs(ecology *e,workspace *ws, double* response, double* model double sum = 0.0; double denom = 0.0; - for (w=0; wnum_wave; w++){ - sum += modelR[w]*response[w]*ws->bandwidth[w]; - denom += response[w]*ws->bandwidth[w]; + for (w=0; wsalt_i][0]; - double T = y[ws->temp_i][0]; - double n0 = 1.31405; double n1 = 1.779e-4; double n2 = -1.05e-6; @@ -5539,8 +5540,8 @@ void index_of_refraction(ecology *e,workspace *ws,double **y, double* n_stw) double W; int w; - for (w=0; wnum_wave; w++){ - W = ws->wave[w]; + for (w=0; wprms, s, e); + + // Output parameter values to a netcdf file. + if (!e->pre_build && ginterface_is_window1(e->model)) { + int var_exists = 0; + int varid; + int ncid = e->eco_params_ncid; + + var_exists = nc_inq_varid(ncid,s,&varid); + + if (var_exists != 0){ + ncredef(ncid); + nc_def_var(ncid,s,NC_DOUBLE,0,0,&varid); + nc_put_att_text(ncid, varid, "name",strlen(s),s); + char* ttz = e->pinfo[index].desc; + nc_put_att_text(ncid, varid, "description",strlen(ttz),ttz); + char* ttt = e->pinfo[index].units; + nc_put_att_text(ncid, varid, "units",strlen(ttt),ttt); + nc_put_att_text(ncid, varid, "function_call",13,"get_parameter"); + nc_enddef(ncid); + ncw_put_var_double(ECO_PARAMS_SETUP,ncid,varid,ptr); + } + } + return(*ptr); } @@ -417,17 +441,35 @@ int string_exists(stringtable* st, char* s, ecology* e) double try_parameter_value(ecology* e, char* s) { int index = try_index(e->prms, s, e); + int var_exists = 0; if (index < 0){ eco_write_setup(e,"Ecol. parameter tried to read %s, but not in parameter file \n",s); return NaN; }else{ eco_write_setup(e,"Ecol. parameter tried to read %s and found %e \n",s,e->pinfo[index].value[0]); + if (!e->pre_build && ginterface_is_window1(e->model)) { + int ncid = e->eco_params_ncid,varid; + var_exists = nc_inq_varid(ncid,s,&varid); + + if (var_exists != 0){ + ncredef(ncid); + nc_def_var(ncid,s,NC_DOUBLE,0,0,&varid); + char* tty = e->pinfo[index].name; + nc_put_att_text(ncid, varid, "name",strlen(tty),tty); + char* ttz = e->pinfo[index].desc; + nc_put_att_text(ncid, varid, "description",strlen(ttz),ttz); + char* ttt = e->pinfo[index].units; + nc_put_att_text(ncid, varid, "units",strlen(ttt),ttt); + nc_put_att_text(ncid, varid, "function_call",13,"try_parameter"); + nc_enddef(ncid); + ncw_put_var_double(ECO_PARAMS_SETUP,ncid,varid,&e->pinfo[index].value[0]); + } + } + return e->pinfo[index].value[0]; } - return e->pinfo[index].value[0]; } - /** Gets parameter value for a parameter. Unlike get_parameter_value(), * goes on if the parameter is not found. * @param e Pointer to ecology diff --git a/model/lib/sediments/include/sediments.h b/model/lib/sediments/include/sediments.h index 0856ff9..70611a7 100644 --- a/model/lib/sediments/include/sediments.h +++ b/model/lib/sediments/include/sediments.h @@ -14,7 +14,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: sediments.h 7484 2024-02-13 23:53:02Z mar644 $ + * $Id: sediments.h 7577 2024-05-31 00:36:37Z riz008 $ * */ @@ -41,7 +41,7 @@ extern sedlogtag sedtag; /* Release verions and getters */ #define SEDIMENTS_MAJOR_VERSION 1 #define SEDIMENTS_MINOR_VERSION 1 -#define SEDIMENTS_PATCH_VERSION 3 +#define SEDIMENTS_PATCH_VERSION 4 int get_sediments_major_vers(void); int get_sediments_minor_vers(void); diff --git a/model/lib/sediments/sed2hd.c b/model/lib/sediments/sed2hd.c index 495bfe5..782eb30 100644 --- a/model/lib/sediments/sed2hd.c +++ b/model/lib/sediments/sed2hd.c @@ -13,7 +13,7 @@ * reserved. See the license file for disclaimer and full * use/redistribution conditions. * - * $Id: sed2hd.c 5975 2018-09-26 00:09:12Z mar644 $ + * $Id: sed2hd.c 7572 2024-05-30 03:34:39Z riz008 $ * */ @@ -57,7 +57,6 @@ void sed2hd(sediment_t *sediment, sed_column_t *sm, int c) void *hmodel = sediment->hmodel; int k, n, m; int tk,bk; - double *point = malloc(sizeof *point); sed_params_t *param = sediment->msparam; int col_index = sm->col_number-1; /* Copy internal spatial sediment variables */ @@ -184,6 +183,7 @@ void sed2hd(sediment_t *sediment, sed_column_t *sm, int c) //NMY 2018 // material fluxes across water and sediments for(n=0; n < param->ntrB; n++) { + double *point = NULL; if (param->fluxsedimap_inst[n] > 0) { point = sinterface_getpointerBtracer(hmodel, n, c); //get pointer to 2D diag tracer m = param->fluxsedimap_inst[n]; // get the number of the corresponding 3D tracer (i.e. erdepflux[m])