diff --git a/.gitignore b/.gitignore index 4befed3..08302ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store .idea + diff --git a/advection/.gitignore b/advection/.gitignore new file mode 100644 index 0000000..34ddd4b --- /dev/null +++ b/advection/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +src/.DS_Store +src/.cache +src/build +.idea +src/cmake-build-debug +src/cmake-build-release diff --git a/advection/README.md b/advection/README.md new file mode 100644 index 0000000..0abcf14 --- /dev/null +++ b/advection/README.md @@ -0,0 +1,46 @@ +## What is new? +There is one new added component: `AdvectionKernel`s which is an "interface" (i.e an abstract class). +There are two implementations simple Euler integration called `EulerIntegrationKernel` and +Runge Kutta integration called `RK4AdvectionKernel`. + +Main function gives a good example of how to use the library. Especially the following function which prints the +position of the particle at every time step. +```Cpp +template +void advectForSomeTime(const UVGrid &uvGrid, const AdvectionKernelImpl &kernel, double latstart, double lonstart) { + + // Require at compile time that kernel derives from the abstract class AdvectionKernel + static_assert(std::is_base_of::value, NotAKernelError); + + double lat1 = latstart, lon1 = lonstart; + for(int time = 100; time <= 10000; time += AdvectionKernel::DT) { + cout << "lat = " << lat1 << " lon = " << lon1 << endl; + auto [templat, templon] = kernel.advect(time, lat1, lon1); + lat1 = templat; + lon1 = templon; + } +} +``` + + +## Location of data +The data path is hardcoded such that the following tree structure is assumed: +The current assumption is that the name of the `u`s and `v`s are flipped since this is the way the data was given to us. +``` +data/ + grid.h5 + hydrodynamic_U.h5 + hydrodynamic_V.h5 +interactive-track-and-trace/ + opening-hdf5/ + ... +``` + +## Compiling +Let the current directory be the `src` directory. Run: +```shell +mkdir build +cd build +cmake .. +make +``` \ No newline at end of file diff --git a/advection/src/AdvectionKernel.h b/advection/src/AdvectionKernel.h new file mode 100644 index 0000000..0170c79 --- /dev/null +++ b/advection/src/AdvectionKernel.h @@ -0,0 +1,32 @@ +#ifndef ADVECTION_ADVECTIONKERNEL_H +#define ADVECTION_ADVECTIONKERNEL_H + +#include + +#include "Vel.h" + + +/* + * Implement this class for every integration method. + */ +class AdvectionKernel { +public: + const static int DT = 60 * 15; // 60 sec/min * 15 mins + /** + * This function must take a time, latitude and longitude of a particle and must output + * a new latitude and longitude after being advected once for AdvectionKernel::DT time as defined above. + * @param time Time since the beginning of the data + * @param latitude Latitude of particle + * @param longitude Longitude of particle + * @return A pair of latitude and longitude of particle. + */ + virtual std::pair advect(int time, double latitude, double longitude) const = 0; + + // Taken from Parcels https://github.com/OceanParcels/parcels/blob/daa4b062ed8ae0b2be3d87367d6b45599d6f95db/parcels/tools/converters.py#L155 + const static double metreToDegrees(double metre) { + return metre / 1000. / 1.852 / 60.; + } + +}; + +#endif //ADVECTION_ADVECTIONKERNEL_H diff --git a/advection/src/CMakeLists.txt b/advection/src/CMakeLists.txt new file mode 100644 index 0000000..b8c8758 --- /dev/null +++ b/advection/src/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required (VERSION 3.28) +project (Advection) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(netCDF REQUIRED) + +add_executable(Advection main.cpp + readdata.cpp + readdata.h + interpolate.cpp + interpolate.h + UVGrid.cpp + UVGrid.h + Vel.h + Vel.cpp + AdvectionKernel.h + EulerAdvectionKernel.cpp + EulerAdvectionKernel.h + RK4AdvectionKernel.cpp + RK4AdvectionKernel.h +) + +execute_process( + COMMAND nc-config --includedir + OUTPUT_VARIABLE NETCDF_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND ncxx4-config --libdir + OUTPUT_VARIABLE NETCDFCXX_LIB_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_include_directories(Advection PUBLIC ${netCDF_INCLUDE_DIR}) + +find_library(NETCDF_LIB NAMES netcdf-cxx4 netcdf_c++4 PATHS ${NETCDFCXX_LIB_DIR} NO_DEFAULT_PATH) +target_link_libraries(Advection ${NETCDF_LIB}) diff --git a/advection/src/EulerAdvectionKernel.cpp b/advection/src/EulerAdvectionKernel.cpp new file mode 100644 index 0000000..1a38944 --- /dev/null +++ b/advection/src/EulerAdvectionKernel.cpp @@ -0,0 +1,13 @@ + +#include "EulerAdvectionKernel.h" +#include "interpolate.h" + +using namespace std; + +EulerAdvectionKernel::EulerAdvectionKernel(std::shared_ptr grid): grid(grid) { } + +std::pair EulerAdvectionKernel::advect(int time, double latitude, double longitude) const { + auto [u, v] = bilinearinterpolate(*grid, time, latitude, longitude); + + return {latitude+metreToDegrees(v*DT), longitude+metreToDegrees(u*DT)}; +} diff --git a/advection/src/EulerAdvectionKernel.h b/advection/src/EulerAdvectionKernel.h new file mode 100644 index 0000000..d9893cb --- /dev/null +++ b/advection/src/EulerAdvectionKernel.h @@ -0,0 +1,25 @@ +#ifndef ADVECTION_EULERADVECTIONKERNEL_H +#define ADVECTION_EULERADVECTIONKERNEL_H + +#include "AdvectionKernel.h" +#include "UVGrid.h" + +/** + * Implementation of AdvectionKernel. + * The basic equation is: + * new_latitude = latitude + v*DT + * new_longitude = longitude + u*DT + * + * Uses bilinear interpolation as implemented in interpolate.h + */ +class EulerAdvectionKernel: public AdvectionKernel { +private: + std::shared_ptr grid; +public: + explicit EulerAdvectionKernel(std::shared_ptr grid); + std::pair advect(int time, double latitude, double longitude) const override; + +}; + + +#endif //ADVECTION_EULERADVECTIONKERNEL_H diff --git a/advection/src/RK4AdvectionKernel.cpp b/advection/src/RK4AdvectionKernel.cpp new file mode 100644 index 0000000..1edc9f9 --- /dev/null +++ b/advection/src/RK4AdvectionKernel.cpp @@ -0,0 +1,35 @@ +#include "RK4AdvectionKernel.h" +#include "interpolate.h" + +using namespace std; + +RK4AdvectionKernel::RK4AdvectionKernel(std::shared_ptr grid): grid(grid) { } + +std::pair RK4AdvectionKernel::advect(int time, double latitude, double longitude) const { + auto [u1, v1] = bilinearinterpolate(*grid, time, latitude, longitude); +// lon1, lat1 = (particle.lon + u1*.5*particle.dt, particle.lat + v1*.5*particle.dt); + double lon1 = longitude + metreToDegrees(u1 * 0.5*DT); + double lat1 = latitude + metreToDegrees(v1 * 0.5*DT); + +// (u2, v2) = fieldset.UV[time + .5 * particle.dt, particle.depth, lat1, lon1, particle] + auto [u2, v2] = bilinearinterpolate(*grid, time + 0.5 * DT, lat1, lon1); + +// lon2, lat2 = (particle.lon + u2*.5*particle.dt, particle.lat + v2*.5*particle.dt) + double lon2 = longitude + metreToDegrees(u2 * 0.5 * DT); + double lat2 = latitude + metreToDegrees(v2 * 0.5 * DT); + +// (u3, v3) = fieldset.UV[time + .5 * particle.dt, particle.depth, lat2, lon2, particle] + auto [u3, v3] = bilinearinterpolate(*grid, time + 0.5 * DT, lat2, lon2); + +// lon3, lat3 = (particle.lon + u3*particle.dt, particle.lat + v3*particle.dt) + double lon3 = longitude + metreToDegrees(u3 * DT); + double lat3 = latitude + metreToDegrees(v3 * DT); + +// (u4, v4) = fieldset.UV[time + particle.dt, particle.depth, lat3, lon3, particle] + auto [u4, v4] = bilinearinterpolate(*grid, time + DT, lat3, lon3); + + double lonFinal = longitude + metreToDegrees((u1 + 2 * u2 + 2 * u3 + u4) / 6.0 * DT); + double latFinal = latitude + metreToDegrees((v1 + 2 * v2 + 2 * v3 + v4) / 6.0 * DT); + + return {latFinal, lonFinal}; +} diff --git a/advection/src/RK4AdvectionKernel.h b/advection/src/RK4AdvectionKernel.h new file mode 100644 index 0000000..6b6c88d --- /dev/null +++ b/advection/src/RK4AdvectionKernel.h @@ -0,0 +1,22 @@ +#ifndef ADVECTION_RK4ADVECTIONKERNEL_H +#define ADVECTION_RK4ADVECTIONKERNEL_H + +#include "AdvectionKernel.h" +#include "UVGrid.h" + +/** + * Implementation of Advection kernel using RK4 integration + * See https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods for more details. + * Uses bilinear interpolation as implemented in interpolate.h + */ +class RK4AdvectionKernel: public AdvectionKernel { +private: + std::shared_ptr grid; +public: + explicit RK4AdvectionKernel(std::shared_ptr grid); + std::pair advect(int time, double latitude, double longitude) const override; + +}; + + +#endif //ADVECTION_RK4ADVECTIONKERNEL_H diff --git a/advection/src/UVGrid.cpp b/advection/src/UVGrid.cpp new file mode 100644 index 0000000..1febbfe --- /dev/null +++ b/advection/src/UVGrid.cpp @@ -0,0 +1,66 @@ +#include + +#include "UVGrid.h" +#include "readdata.h" + +#define sizeError2 "The sizes of the hydrodynamic data files are different" +#define sizeError "The sizes of the hydrodynamicU or -V files does not correspond with the sizes of the grid file" + +using namespace std; + +UVGrid::UVGrid() { + auto us = readHydrodynamicU(); + auto vs = readHydrodynamicV(); + if (us.size() != vs.size()) { + throw domain_error(sizeError2); + } + + tie(times, lats, lons) = readGrid(); + + timeSize = times.size(); + latSize = lats.size(); + lonSize = lons.size(); + + size_t gridSize = timeSize * latSize * lonSize; + if (gridSize != us.size()) { + throw domain_error(sizeError); + } + + uvData.reserve(gridSize); + + for (auto vel: views::zip(us, vs)) { + uvData.push_back(Vel(vel)); + } +} + +const Vel &UVGrid::operator[](size_t timeIndex, size_t latIndex, size_t lonIndex) const { + if(timeIndex < 0 or timeIndex >= timeSize + or latIndex < 0 or latIndex >= latSize + or lonIndex < 0 or lonIndex >= lonSize) { + throw std::out_of_range("Index out of bounds"); + } + size_t index = timeIndex * (latSize * lonSize) + latIndex * lonSize + lonIndex; + return uvData[index]; +} + +double UVGrid::lonStep() const { + return lons[1] - lons[0]; +} + +double UVGrid::latStep() const { + return lats[1] - lats[0]; +} + +int UVGrid::timeStep() const { + return times[1] - times[0]; +} + +void UVGrid::streamSlice(ostream &os, size_t t) { + for (int x = 0; x < latSize; x++) { + for (int y = 0; y < lonSize; y++) { + auto vel = (*this)[t,x,y]; + os << vel << " "; + } + os << endl; + } +} \ No newline at end of file diff --git a/advection/src/UVGrid.h b/advection/src/UVGrid.h new file mode 100644 index 0000000..f068ea9 --- /dev/null +++ b/advection/src/UVGrid.h @@ -0,0 +1,65 @@ +#ifndef ADVECTION_UVGRID_H +#define ADVECTION_UVGRID_H + +#include +#include "Vel.h" + +class UVGrid { +private: + /** + * 1D data vector of all the us and vs + */ + std::vector uvData; +public: + UVGrid(); + + /** + * The matrix has shape (timeSize, latSize, lonSize) + */ + size_t timeSize; + size_t latSize; + size_t lonSize; + + /** + * Assuming grid is a regular grid, gives the longitudinal spacing of grid. + * @return longitudinal spacing + */ + double lonStep() const; + + /** + * Assuming grid is a regular grid, gives the latitudinal spacing of grid. + * @return latitudinal spacing + */ + double latStep() const; + + /** + * Assuming grid is a regular grid, gives the time spacing of grid. + * @return time spacing + */ + int timeStep() const; + + /** + * times, lats, lons are vector of length timeSize, latSize, lonSize respectively. + * The maintain the following invariant: + * grid[timeIndex,latIndex,lonIndex] gives the u,v at the point with latitude at lats[latIndex], + * with longitude at lons[lonIndex], and with time at times[timeIndex]. + */ + std::vector times; + std::vector lats; + std::vector lons; + + /** + * The 3D index into the data. The array is sized by [8761][67][116] + * @return Velocity at that index + */ + const Vel& operator[](size_t timeIndex, size_t latIndex, size_t lonIndex) const; + + /** + * Streams a slice at timeIndex t of the matrix to the outstream given by os + * @param os outstream + * @param t index with which to slice matrix + */ + void streamSlice(std::ostream &os, size_t t); +}; + +#endif //ADVECTION_UVGRID_H diff --git a/advection/src/Vel.cpp b/advection/src/Vel.cpp new file mode 100644 index 0000000..6bb5990 --- /dev/null +++ b/advection/src/Vel.cpp @@ -0,0 +1,40 @@ +#include "Vel.h" +#include +#include + +using namespace std; + +Vel::Vel(double u, double v) : u(u), v(v) {} + +Vel::Vel(const std::pair& p) : u(p.first), v(p.second) {} + +Vel& Vel::operator=(const std::pair& p) { + u = p.first; + v = p.second; + return *this; +} + +Vel Vel::operator+(const Vel& other) const { + return Vel(u + other.u, v + other.v); +} + +Vel& Vel::operator+=(const Vel& other) { + u += other.u; + v += other.v; + return *this; +} + +template +Vel Vel::operator/(Scalar scalar) const { + if (scalar == 0) throw std::runtime_error("Division by zero"); + return Vel(u / scalar, v / scalar); +} + +std::ostream& operator<<(ostream& os, const Vel& vel) { + os << "("; + os << fixed << setprecision(2) << setw(5) << vel.u; + os << ", "; + os << fixed << setprecision(2) << setw(5) << vel.v; + os << ")"; + return os; +} \ No newline at end of file diff --git a/advection/src/Vel.h b/advection/src/Vel.h new file mode 100644 index 0000000..74d62cd --- /dev/null +++ b/advection/src/Vel.h @@ -0,0 +1,44 @@ +#ifndef ADVECTION_VEL_H +#define ADVECTION_VEL_H + +#include +#include +#include +#include + +class Vel { +public: + double u; // Eastward Current Velocity in the Water Column + double v; // Northward Current Velocity in the Water Column + + Vel(double u, double v); + Vel(const std::pair& p); // Conversion constructor + Vel& operator=(const std::pair& p); + + // Operator + to add two Vel objects + Vel operator+(const Vel& other) const; + + // Operator += to add another Vel object to this object + Vel& operator+=(const Vel& other); + + // Operator * to multiply Vel by a scalar, defined as a member template + template + Vel operator*(Scalar scalar) const { + return Vel(u * scalar, v * scalar); + } + + // Operator / to divide Vel by a scalar, defined as a member template + template + Vel operator/(Scalar scalar) const; + + // Friend declaration for the stream insertion operator + friend std::ostream& operator<<(std::ostream& os, const Vel& vel); +}; + +// Non-member function for scalar multiplication on the left +template +Vel operator*(Scalar scalar, const Vel& p) { + return Vel(p.u * scalar, p.v * scalar); +} + +#endif //ADVECTION_VEL_H diff --git a/advection/src/interpolate.cpp b/advection/src/interpolate.cpp new file mode 100644 index 0000000..7d3e0cc --- /dev/null +++ b/advection/src/interpolate.cpp @@ -0,0 +1,47 @@ +#include "interpolate.h" + +using namespace std; + +Vel bilinearinterpolate(const UVGrid &uvGrid, int time, double lat, double lon) { + double latStep = uvGrid.latStep(); + double lonStep = uvGrid.lonStep(); + int timeStep = uvGrid.timeStep(); + + int latIndex = (lat - uvGrid.lats[0]) / latStep; + int lonIndex = (lon - uvGrid.lons[0]) / lonStep; + int timeIndex = (time - uvGrid.times[0]) / timeStep; + + double timeRatio = (static_cast(time) - uvGrid.times[timeIndex]) / timeStep; + double latRatio = (lat - uvGrid.lats[latIndex]) / latStep; + double lonRatio = (lon - uvGrid.lons[lonIndex]) / lonStep; + + Vel point = {0, 0}; + for (int timeOffset = 0; timeOffset <= 1; timeOffset++) { + for (int latOffset = 0; latOffset <= 1; latOffset++) { + for (int lonOffset = 0; lonOffset <= 1; lonOffset++) { + auto vertex = uvGrid[ + timeIndex + 1 < uvGrid.timeSize ? timeIndex + timeOffset : timeIndex, + latIndex + 1 < uvGrid.latSize ? latIndex + latOffset : latIndex, + lonIndex + 1 < uvGrid.lonSize ? lonIndex + lonOffset : lonIndex + ]; + + double timeRation = (1 - timeOffset) * (1 - timeRatio) + timeOffset * timeRatio; + double latRation = (1 - latOffset) * (1 - latRatio) + latOffset * latRatio; + double lonRation = (1 - lonOffset) * (1 - lonRatio) + lonOffset * lonRatio; + point += timeRation * latRation * lonRation * vertex; + } + } + } + + return point; +} + +vector bilinearinterpolation(const UVGrid &uvGrid, vector> points) { + vector result; + result.reserve(points.size()); + for (auto [time, lat, lon]: points) { + result.push_back(bilinearinterpolate(uvGrid, time, lat, lon)); + } + + return result; +} diff --git a/advection/src/interpolate.h b/advection/src/interpolate.h new file mode 100644 index 0000000..80176d5 --- /dev/null +++ b/advection/src/interpolate.h @@ -0,0 +1,28 @@ +#ifndef ADVECTION_INTERPOLATE_H +#define ADVECTION_INTERPOLATE_H + +#include + +#include "UVGrid.h" + +/** + * Bilinearly interpolate the point (time, lat, lon) to produce the interpolated velocity. + * Since it is in 3D, this means that it interpolates against 8 points (excluding edges). + * As described in https://numerical.recipes/book.html Chapter 3.6 + * @param uvGrid velocity grid + * @param time time of point + * @param lat latitude of point + * @param lon longitude of point + * @return interpolated velocity + */ +Vel bilinearinterpolate(const UVGrid &uvGrid, int time, double lat, double lon); + +/** + * Helper function for bilnearly interpolating a vector of points + * @param uvGrid velocity grid + * @param points vector of points + * @return interpolated velocities + */ +std::vector bilinearinterpolation(const UVGrid &uvGrid, std::vector> points); + +#endif //ADVECTION_INTERPOLATE_H diff --git a/advection/src/main.cpp b/advection/src/main.cpp new file mode 100644 index 0000000..0e0fc01 --- /dev/null +++ b/advection/src/main.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "interpolate.h" +#include "Vel.h" +#include "EulerAdvectionKernel.h" +#include "RK4AdvectionKernel.h" +#include "interpolate.h" + +#define NotAKernelError "Template parameter T must derive from AdvectionKernel" + +using namespace std; + +template +void advectForSomeTime(const UVGrid &uvGrid, const AdvectionKernelImpl &kernel, double latstart, double lonstart, int i, char colour[10]) { + + // Require at compile time that kernel derives from the abstract class AdvectionKernel + static_assert(std::is_base_of::value, NotAKernelError); + + double lat1 = latstart, lon1 = lonstart; + for(int time = 0; time <= 31536000.; time += AdvectionKernel::DT) { +// cout << setprecision(8) << lat1 << "," << setprecision(8) << lon1 << ",end" << i << "," << colour << endl; + try { + auto [templat, templon] = kernel.advect(time, lat1, lon1); + lat1 = templat; + lon1 = templon; + } catch (const out_of_range& e) { + cerr << "broke out of loop!" << endl; + time = 31536001; + } + } + cout << setprecision(8) << latstart << "," << setprecision(8) << lonstart << ",begin" << i << "," << colour << endl; + cout << setprecision(8) << lat1 << "," << setprecision(8) << lon1 << ",end" << i << "," << colour << endl; +} + +void testGridIndexing(const UVGrid *uvGrid) { + int time = 20000; + cout << "=== land === (should all give 0)" << endl; + cout << bilinearinterpolate(*uvGrid, time, 53.80956379699079, -1.6496306344654406) << endl; + cout << bilinearinterpolate(*uvGrid, time, 55.31428895563707, -2.851581041325997) << endl; + cout << bilinearinterpolate(*uvGrid, time, 47.71548983067583, -1.8704054037408626) << endl; + cout << bilinearinterpolate(*uvGrid, time, 56.23521060314398, 8.505979324950573) << endl; + cout << bilinearinterpolate(*uvGrid, time, 53.135645440244375, 8.505979324950573) << endl; + cout << bilinearinterpolate(*uvGrid, time, 56.44761278775708, -4.140629303756164) << endl; + cout << bilinearinterpolate(*uvGrid, time, 52.67625153110339, 0.8978569759455872) << endl; + cout << bilinearinterpolate(*uvGrid, time, 52.07154079279377, 4.627951041411331) << endl; + + cout << "=== ocean === (should give not 0)" << endl; + cout << bilinearinterpolate(*uvGrid, time, 47.43923166616274, -4.985451481829083) << endl; + cout << bilinearinterpolate(*uvGrid, time, 50.68943556852362, -9.306162999561733) << endl; + cout << bilinearinterpolate(*uvGrid, time, 53.70606799886677, -4.518347647034465) << endl; + cout << bilinearinterpolate(*uvGrid, time, 60.57987114267971, -12.208262973672621) << endl; + cout << bilinearinterpolate(*uvGrid, time, 46.532221548197285, -13.408189172582638) << endl; + cout << bilinearinterpolate(*uvGrid, time, 50.92725094937812, 1.3975824052375256) << endl; + cout << bilinearinterpolate(*uvGrid, time, 51.4028921682209, 2.4059571950925203) << endl; + cout << bilinearinterpolate(*uvGrid, time, 53.448445236769004, 0.7996966058017515) << endl; +// cout << bilinearinterpolate(*uvGrid, time, ) << endl; +} + +int main() { + std::shared_ptr uvGrid = std::make_shared(); + + uvGrid->streamSlice(cout, 900); + + auto kernelRK4 = RK4AdvectionKernel(uvGrid); + +// You can use https://maps.co/gis/ to visualise these points + cout << "======= RK4 Integration =======" << endl; + advectForSomeTime(*uvGrid, kernelRK4, 53.53407391652826, 6.274975037862238, 0, "#ADD8E6"); + advectForSomeTime(*uvGrid, kernelRK4, 53.494053820479365, 5.673454142386921, 1, "#DC143C"); + advectForSomeTime(*uvGrid, kernelRK4, 53.49321966653616, 5.681867022043919, 2, "#50C878"); + advectForSomeTime(*uvGrid, kernelRK4, 53.581548701694324, 6.552600066543153, 3, "#FFEA00"); + advectForSomeTime(*uvGrid, kernelRK4, 53.431446729744124, 5.241592961691523, 4, "#663399"); + advectForSomeTime(*uvGrid, kernelRK4, 53.27913608324572, 4.82094897884165, 5, "#FFA500"); + advectForSomeTime(*uvGrid, kernelRK4, 53.18597595482688, 4.767667388308705, 6, "#008080"); + advectForSomeTime(*uvGrid, kernelRK4, 53.01592078792383, 4.6064205160882, 7, "#FFB6C1"); + advectForSomeTime(*uvGrid, kernelRK4, 52.72816940158886, 4.5853883152993635, 8, "#36454F"); // on land + advectForSomeTime(*uvGrid, kernelRK4, 52.56142091881038, 4.502661662924255, 9, "#1E90FF"); // Dodger Blue + advectForSomeTime(*uvGrid, kernelRK4, 52.23202593893584, 4.2825246383181845, 10, "#FFD700"); // Gold + advectForSomeTime(*uvGrid, kernelRK4, 52.08062567609582, 4.112864890830927, 11, "#6A5ACD"); // Slate Blue + advectForSomeTime(*uvGrid, kernelRK4, 51.89497719759734, 3.8114033568921686, 12, "#20B2AA"); // Light Sea Green + advectForSomeTime(*uvGrid, kernelRK4, 51.752848503723634, 3.664177951809339, 13, "#FF69B4"); // Hot Pink + advectForSomeTime(*uvGrid, kernelRK4, 51.64595756528835, 3.626319993352851, 14, "#800080"); // Purple + advectForSomeTime(*uvGrid, kernelRK4, 51.55140730645238, 3.4326152213887986, 15, "#FF4500"); // Orange Red + advectForSomeTime(*uvGrid, kernelRK4, 51.45679776223422, 3.4452813365018384, 16, "#A52A2A"); // Brown + advectForSomeTime(*uvGrid, kernelRK4, 51.41444662720727, 3.4648562416765363, 17, "#4682B4"); // Steel Blue + advectForSomeTime(*uvGrid, kernelRK4, 51.37421261203866, 3.2449264214689455, 18, "#FF6347"); // Tomato + advectForSomeTime(*uvGrid, kernelRK4, 51.29651848898365, 2.9547572241424773, 19, "#008000"); // Green + advectForSomeTime(*uvGrid, kernelRK4, 51.19705098468974, 2.7647654914530024, 20, "#B8860B"); // Dark Goldenrod + advectForSomeTime(*uvGrid, kernelRK4, 51.114719857442665, 2.577076679365129, 21, "#FFC0CB"); // Pink +// advectForSomeTime(*uvGrid, kernelRK4, ,0); + + return 0; +} diff --git a/advection/src/readdata.cpp b/advection/src/readdata.cpp new file mode 100644 index 0000000..3597c5b --- /dev/null +++ b/advection/src/readdata.cpp @@ -0,0 +1,50 @@ +#include + +#include + +#include "readdata.h" + +using namespace std; +using namespace netCDF; + +template +vector getVarVector(const NcVar &var) { + int length = 1; + for (NcDim dim : var.getDims()) { + length *= dim.getSize(); + } + + vector vec(length); + + var.getVar(vec.data()); + + return vec; +} + +vector readHydrodynamicU() { + // Vs and Us flipped cause the files are named incorrectly + netCDF::NcFile data("../../../../data/hydrodynamic_V.h5", netCDF::NcFile::read); + + multimap< string, NcVar > vars = data.getVars(); + + return getVarVector(vars.find("vo")->second); +} + +vector readHydrodynamicV() { + // Vs and Us flipped cause the files are named incorrectly + netCDF::NcFile data("../../../../data/hydrodynamic_U.h5", netCDF::NcFile::read); + + multimap< string, NcVar > vars = data.getVars(); + + return getVarVector(vars.find("uo")->second); +} + +tuple, vector, vector> readGrid() { + netCDF::NcFile data("../../../../data/grid.h5", netCDF::NcFile::read); + multimap< string, NcVar > vars = data.getVars(); + vector time = getVarVector(vars.find("times")->second); + vector longitude = getVarVector(vars.find("longitude")->second); + vector latitude = getVarVector(vars.find("latitude")->second); + + return {time, latitude, longitude}; +} diff --git a/advection/src/readdata.h b/advection/src/readdata.h new file mode 100644 index 0000000..56a3fee --- /dev/null +++ b/advection/src/readdata.h @@ -0,0 +1,22 @@ +#ifndef ADVECTION_READDATA_H +#define ADVECTION_READDATA_H + +/** + * reads the file hydrodynamic_U.h5 + * @return the data vector of us + */ +std::vector readHydrodynamicU(); + +/** + * reads the file hydrodynamic_V.h5 + * @return the data vector of vs + */ +std::vector readHydrodynamicV(); + +/** + * Reads the file grid.h5 + * @return a tuple of (times, latitude, longitude) + */ +std::tuple, std::vector, std::vector> readGrid(); + +#endif //ADVECTION_READDATA_H diff --git a/vtk/src/Program.cpp b/vtk/src/Program.cpp index d35aa6a..7d9220f 100644 --- a/vtk/src/Program.cpp +++ b/vtk/src/Program.cpp @@ -38,6 +38,7 @@ void Program::setupTimer() { auto callback = vtkSmartPointer::New(this); callback->SetClientData(this); this->interact->AddObserver(vtkCommand::TimerEvent, callback); + this->interact->AddObserver(vtkCommand::KeyPressEvent, callback); this->interact->CreateRepeatingTimer(17); // 60 fps == 1000 / 60 == 16.7 ms per frame } diff --git a/vtk/src/commands/SpawnPointCallback.cpp b/vtk/src/commands/SpawnPointCallback.cpp index 3f41acd..8bf6838 100644 --- a/vtk/src/commands/SpawnPointCallback.cpp +++ b/vtk/src/commands/SpawnPointCallback.cpp @@ -50,8 +50,7 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca vtkSmartPointer vertices = vtkSmartPointer::New(); vertices->InsertNextCell(vertex); data->SetVerts(vertices); -// data->Modified(); // These might be needed im not sure. -// ren->GetRenderWindow()->Render(); + ren->GetRenderWindow()->Render(); } diff --git a/vtk/src/commands/TimerCallbackCommand.cpp b/vtk/src/commands/TimerCallbackCommand.cpp index 6700ac5..2cad359 100644 --- a/vtk/src/commands/TimerCallbackCommand.cpp +++ b/vtk/src/commands/TimerCallbackCommand.cpp @@ -8,10 +8,16 @@ TimerCallbackCommand::TimerCallbackCommand() : dt(3600), maxTime(3600*24*365), t TimerCallbackCommand* TimerCallbackCommand::New(Program *program) { TimerCallbackCommand *cb = new TimerCallbackCommand(); cb->setProgram(program); + cb->setPaused(false); return cb; } void TimerCallbackCommand::Execute(vtkObject* caller, unsigned long eventId, void* vtkNotUsed(callData)) { + auto intr = reinterpret_cast(caller); + + if (eventId == vtkCommand::KeyPressEvent and not strcmp("space", intr->GetKeySym())) { + this->paused = ! this->paused; + } else if (eventId == vtkCommand::TimerEvent and not this->paused) { this->time += this->dt; if (this->time >= this->maxTime) { @@ -20,9 +26,15 @@ void TimerCallbackCommand::Execute(vtkObject* caller, unsigned long eventId, voi } this->program->updateData(this->time); -} + } +} void TimerCallbackCommand::setProgram(Program *program) { this->program = program; } + + +void TimerCallbackCommand::setPaused(const bool val) { + this->paused = val; +} diff --git a/vtk/src/commands/TimerCallbackCommand.h b/vtk/src/commands/TimerCallbackCommand.h index 618c7bf..2ff65ea 100644 --- a/vtk/src/commands/TimerCallbackCommand.h +++ b/vtk/src/commands/TimerCallbackCommand.h @@ -11,11 +11,13 @@ public: void Execute(vtkObject* caller, unsigned long eventId, void* vtkNotUsed(callData)) override; void setProgram(Program *program); + void setPaused(const bool val); private: int time; int dt; int maxTime; + bool paused; Program *program; };