Merge branch 'djairo-vtk-pause' into djairo-vtk-camera
This commit is contained in:
commit
f40c8ef2f7
|
|
@ -1,2 +1,3 @@
|
|||
.DS_Store
|
||||
.idea
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
.DS_Store
|
||||
src/.DS_Store
|
||||
src/.cache
|
||||
src/build
|
||||
.idea
|
||||
src/cmake-build-debug
|
||||
src/cmake-build-release
|
||||
|
|
@ -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 <typename AdvectionKernelImpl>
|
||||
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<AdvectionKernel, AdvectionKernelImpl>::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
|
||||
```
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef ADVECTION_ADVECTIONKERNEL_H
|
||||
#define ADVECTION_ADVECTIONKERNEL_H
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#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<double, double> 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
|
||||
|
|
@ -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})
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
#include "EulerAdvectionKernel.h"
|
||||
#include "interpolate.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
EulerAdvectionKernel::EulerAdvectionKernel(std::shared_ptr<UVGrid> grid): grid(grid) { }
|
||||
|
||||
std::pair<double, double> 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)};
|
||||
}
|
||||
|
|
@ -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<UVGrid> grid;
|
||||
public:
|
||||
explicit EulerAdvectionKernel(std::shared_ptr<UVGrid> grid);
|
||||
std::pair<double, double> advect(int time, double latitude, double longitude) const override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //ADVECTION_EULERADVECTIONKERNEL_H
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#include "RK4AdvectionKernel.h"
|
||||
#include "interpolate.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
RK4AdvectionKernel::RK4AdvectionKernel(std::shared_ptr<UVGrid> grid): grid(grid) { }
|
||||
|
||||
std::pair<double, double> 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};
|
||||
}
|
||||
|
|
@ -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<UVGrid> grid;
|
||||
public:
|
||||
explicit RK4AdvectionKernel(std::shared_ptr<UVGrid> grid);
|
||||
std::pair<double, double> advect(int time, double latitude, double longitude) const override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //ADVECTION_RK4ADVECTIONKERNEL_H
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#include <ranges>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef ADVECTION_UVGRID_H
|
||||
#define ADVECTION_UVGRID_H
|
||||
|
||||
#include <vector>
|
||||
#include "Vel.h"
|
||||
|
||||
class UVGrid {
|
||||
private:
|
||||
/**
|
||||
* 1D data vector of all the us and vs
|
||||
*/
|
||||
std::vector<Vel> 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<int> times;
|
||||
std::vector<double> lats;
|
||||
std::vector<double> 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
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "Vel.h"
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Vel::Vel(double u, double v) : u(u), v(v) {}
|
||||
|
||||
Vel::Vel(const std::pair<double, double>& p) : u(p.first), v(p.second) {}
|
||||
|
||||
Vel& Vel::operator=(const std::pair<double, double>& 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<typename Scalar>
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef ADVECTION_VEL_H
|
||||
#define ADVECTION_VEL_H
|
||||
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
|
||||
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<double, double>& p); // Conversion constructor
|
||||
Vel& operator=(const std::pair<double, double>& 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<typename Scalar>
|
||||
Vel operator*(Scalar scalar) const {
|
||||
return Vel(u * scalar, v * scalar);
|
||||
}
|
||||
|
||||
// Operator / to divide Vel by a scalar, defined as a member template
|
||||
template<typename Scalar>
|
||||
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<typename Scalar>
|
||||
Vel operator*(Scalar scalar, const Vel& p) {
|
||||
return Vel(p.u * scalar, p.v * scalar);
|
||||
}
|
||||
|
||||
#endif //ADVECTION_VEL_H
|
||||
|
|
@ -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<double>(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<Vel> bilinearinterpolation(const UVGrid &uvGrid, vector<tuple<int, double, double>> points) {
|
||||
vector<Vel> result;
|
||||
result.reserve(points.size());
|
||||
for (auto [time, lat, lon]: points) {
|
||||
result.push_back(bilinearinterpolate(uvGrid, time, lat, lon));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef ADVECTION_INTERPOLATE_H
|
||||
#define ADVECTION_INTERPOLATE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<Vel> bilinearinterpolation(const UVGrid &uvGrid, std::vector<std::tuple<int, double, double>> points);
|
||||
|
||||
#endif //ADVECTION_INTERPOLATE_H
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
#include <ranges>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
#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 <typename AdvectionKernelImpl>
|
||||
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<AdvectionKernel, AdvectionKernelImpl>::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> uvGrid = std::make_shared<UVGrid>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include <netcdf>
|
||||
|
||||
#include "readdata.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace netCDF;
|
||||
|
||||
template <typename T>
|
||||
vector<T> getVarVector(const NcVar &var) {
|
||||
int length = 1;
|
||||
for (NcDim dim : var.getDims()) {
|
||||
length *= dim.getSize();
|
||||
}
|
||||
|
||||
vector<T> vec(length);
|
||||
|
||||
var.getVar(vec.data());
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
vector<double> 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<double>(vars.find("vo")->second);
|
||||
}
|
||||
|
||||
vector<double> 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<double>(vars.find("uo")->second);
|
||||
}
|
||||
|
||||
tuple<vector<int>, vector<double>, vector<double>> readGrid() {
|
||||
netCDF::NcFile data("../../../../data/grid.h5", netCDF::NcFile::read);
|
||||
multimap< string, NcVar > vars = data.getVars();
|
||||
vector<int> time = getVarVector<int>(vars.find("times")->second);
|
||||
vector<double> longitude = getVarVector<double>(vars.find("longitude")->second);
|
||||
vector<double> latitude = getVarVector<double>(vars.find("latitude")->second);
|
||||
|
||||
return {time, latitude, longitude};
|
||||
}
|
||||
|
|
@ -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<double> readHydrodynamicU();
|
||||
|
||||
/**
|
||||
* reads the file hydrodynamic_V.h5
|
||||
* @return the data vector of vs
|
||||
*/
|
||||
std::vector<double> readHydrodynamicV();
|
||||
|
||||
/**
|
||||
* Reads the file grid.h5
|
||||
* @return a tuple of (times, latitude, longitude)
|
||||
*/
|
||||
std::tuple<std::vector<int>, std::vector<double>, std::vector<double>> readGrid();
|
||||
|
||||
#endif //ADVECTION_READDATA_H
|
||||
|
|
@ -38,6 +38,7 @@ void Program::setupTimer() {
|
|||
auto callback = vtkSmartPointer<TimerCallbackCommand>::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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
|
|||
vtkSmartPointer<vtkCellArray> vertices = vtkSmartPointer<vtkCellArray>::New();
|
||||
vertices->InsertNextCell(vertex);
|
||||
data->SetVerts(vertices);
|
||||
// data->Modified(); // These might be needed im not sure.
|
||||
// ren->GetRenderWindow()->Render();
|
||||
ren->GetRenderWindow()->Render();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<vtkRenderWindowInteractor *>(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) {
|
||||
|
|
@ -22,7 +28,13 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue