├── .github └── workflows │ ├── main.yml │ └── package.yml ├── .gitignore ├── CMakeLists.txt ├── COPYING ├── LICENSE ├── README.md ├── cmake ├── FindNetCDF.cmake ├── FindR.cmake └── Testing.cmake ├── docs ├── DoxygenLayout.xml ├── config.in └── image.jpg ├── include └── gridpp.h ├── src ├── api │ ├── .calc_gradient.cpp.swo │ ├── .gitignore │ ├── bilinear.cpp │ ├── calc_gradient.cpp │ ├── corr_points.cpp │ ├── count.cpp │ ├── curve.cpp │ ├── distance.cpp │ ├── distribution.cpp │ ├── doping.cpp │ ├── downscale_probability.cpp │ ├── downscaling.cpp │ ├── fill.cpp │ ├── gradient.cpp │ ├── grid.cpp │ ├── gridding.cpp │ ├── gridpp.cpp │ ├── humidity.cpp │ ├── kdtree.cpp │ ├── local_distribution_correction.cpp │ ├── mask_threshold_downscale_consensus.cpp │ ├── metric_optimizer.cpp │ ├── nearest.cpp │ ├── neighbourhood.cpp │ ├── neighbourhood_score.cpp │ ├── neighbourhood_search.cpp │ ├── oi.cpp │ ├── oi_ensi.cpp │ ├── oi_ensi_multi.cpp │ ├── point.cpp │ ├── points.cpp │ ├── pressure.cpp │ ├── qnh.cpp │ ├── quantile_mapping.cpp │ ├── simple_gradient.cpp │ ├── smart.cpp │ ├── structure.cpp │ ├── swig.cpp │ ├── transform.cpp │ ├── util.cpp │ ├── wind.cpp │ └── window.cpp └── client │ ├── CMakeLists.txt │ ├── Calibrator │ ├── Accumulate.cpp │ ├── Accumulate.h │ ├── Altitude.cpp │ ├── Altitude.h │ ├── Bct.cpp │ ├── Bct.h │ ├── Calibrator.cpp │ ├── Calibrator.h │ ├── Cloud.cpp │ ├── Cloud.h │ ├── Coastal.cpp │ ├── Coastal.h │ ├── Deaccumulate.cpp │ ├── Deaccumulate.h │ ├── DiagnoseHumidity.cpp │ ├── DiagnoseHumidity.h │ ├── DiagnoseWind.cpp │ ├── DiagnoseWind.h │ ├── Gaussian.cpp │ ├── Gaussian.h │ ├── Kriging.cpp │ ├── Kriging.h │ ├── Mask.cpp │ ├── Mask.h │ ├── Neighbourhood.cpp │ ├── Neighbourhood.h │ ├── Oi.cpp │ ├── Oi.h │ ├── Override.cpp │ ├── Override.h │ ├── Phase.cpp │ ├── Phase.h │ ├── Qc.cpp │ ├── Qc.h │ ├── Qnh.cpp │ ├── Qnh.h │ ├── Qq.cpp │ ├── Qq.h │ ├── Regression.cpp │ ├── Regression.h │ ├── Sort.cpp │ ├── Sort.h │ ├── Temperature.cpp │ ├── Temperature.h │ ├── Threshold.cpp │ ├── Threshold.h │ ├── WindDirection.cpp │ ├── WindDirection.h │ ├── Window.cpp │ ├── Window.h │ ├── Zaga.cpp │ ├── Zaga.h │ └── devel │ │ ├── Wind.cpp │ │ └── Wind.h │ ├── Downscaler │ ├── Bilinear.cpp │ ├── Bilinear.h │ ├── Bypass.cpp │ ├── Bypass.h │ ├── Coastal.h │ ├── Downscaler.cpp │ ├── Downscaler.h │ ├── Gradient.cpp │ ├── Gradient.h │ ├── NearestNeighbour.cpp │ ├── NearestNeighbour.h │ ├── Pressure.cpp │ ├── Pressure.h │ ├── Smart.cpp │ ├── Smart.h │ ├── Upscale.cpp │ └── Upscale.h │ ├── Driver │ ├── .vimrc │ ├── Custom.cpp │ └── Gridpp.cpp │ ├── Field.cpp │ ├── Field.h │ ├── File │ ├── Fake.cpp │ ├── Fake.h │ ├── File.cpp │ ├── File.h │ ├── Netcdf.cpp │ ├── Netcdf.h │ ├── NorcomQnh.cpp │ ├── NorcomQnh.h │ ├── Point.cpp │ ├── Point.h │ ├── Text.cpp │ └── Text.h │ ├── Grid.cpp │ ├── Grid.h │ ├── KDTree.cpp │ ├── KDTree.h │ ├── Location.cpp │ ├── Location.h │ ├── NetcdfUtil.cpp │ ├── NetcdfUtil.h │ ├── Options.cpp │ ├── Options.h │ ├── ParameterFile │ ├── .vimrc │ ├── Netcdf.cpp │ ├── Netcdf.h │ ├── ParameterFile.cpp │ ├── ParameterFile.h │ ├── Simple.cpp │ ├── Simple.h │ ├── Text.cpp │ └── Text.h │ ├── Parameters.cpp │ ├── Parameters.h │ ├── Scheme.cpp │ ├── Scheme.h │ ├── Setup.cpp │ ├── Setup.h │ ├── Testing │ ├── .vimrc │ ├── Calibrator.cpp │ ├── CalibratorAccumulate.cpp │ ├── CalibratorAltitude.cpp │ ├── CalibratorBct.cpp │ ├── CalibratorDeaccumulate.cpp │ ├── CalibratorDiagnoseHumidity.cpp │ ├── CalibratorDiagnoseWind.cpp │ ├── CalibratorKriging.cpp │ ├── CalibratorMask.cpp │ ├── CalibratorNeighbourhood.cpp │ ├── CalibratorPhase.cpp │ ├── CalibratorQc.cpp │ ├── CalibratorQnh.cpp │ ├── CalibratorQq.cpp │ ├── CalibratorRegression.cpp │ ├── CalibratorSort.cpp │ ├── CalibratorThreshold.cpp │ ├── CalibratorWindDirection.cpp │ ├── CalibratorWindow.cpp │ ├── CalibratorZaga.cpp │ ├── Downscaler.cpp │ ├── DownscalerBilinear.cpp │ ├── DownscalerGradient.cpp │ ├── DownscalerNearestNeighbour.cpp │ ├── DownscalerPressure.cpp │ ├── DownscalerSmart.cpp │ ├── Field.cpp │ ├── File.cpp │ ├── FileFake.cpp │ ├── FileNetcdf.cpp │ ├── FileNorcomQnh.cpp │ ├── FilePoint.cpp │ ├── FileText.cpp │ ├── KDTree.cpp │ ├── Location.cpp │ ├── NetcdfUtil.cpp │ ├── Options.cpp │ ├── ParameterFile.cpp │ ├── ParameterFileNetcdf.cpp │ ├── ParameterFileSimple.cpp │ ├── ParameterFileText.cpp │ ├── Parameters.cpp │ ├── Setup.cpp │ ├── Template │ ├── Util.cpp │ ├── Variable.cpp │ └── devel │ │ └── OperationalStatkraft.cpp │ ├── Util.cpp │ ├── Util.h │ ├── Uuid.h │ ├── Variable.cpp │ ├── Variable.h │ ├── Version.h │ └── run_all_tests.sh ├── swig ├── CMakeLists.txt ├── R │ └── CMakeLists.txt ├── gridpp.i ├── numpy.i ├── python-packaging │ ├── CMakeLists.txt │ ├── MANIFEST.in │ ├── pyproject.toml │ ├── setup.in.py │ └── setup.sh ├── python │ ├── CMakeLists.txt │ └── setup.in.py └── vector.i └── tests ├── .gitignore ├── __init__.py ├── benchmark.py ├── check_installation.py ├── files ├── .gitignore ├── 10x10.nc ├── 10x10.txt ├── 10x10_copy.nc ├── 10x10_ec.nc ├── 10x10_noPrecip.nc ├── 10x10_param.nc ├── 10x10_param_xy.nc ├── 10x10_param_zero_altitude.nc ├── 1x1.nc ├── 3x3.nc ├── invalidText1.txt ├── kalmanEmpty.txt ├── kalmanInvalid1.txt ├── kalmanInvalid2.txt ├── kalmanInvalid3.txt ├── kalmanInvalid4.txt ├── kalmanOutput.txt ├── kalmanOutputWithMissing.txt ├── mask0.txt ├── parameters.txt ├── parametersCycling.txt ├── parametersEmpty.txt ├── parametersInvalidEntries.txt ├── parametersInvalidTime.txt ├── parametersKriging.txt ├── parametersMultipleTime.txt ├── parametersNoTime.txt ├── parametersPhase.txt ├── parametersQq.txt ├── parametersSingleTime.txt ├── parametersUnevenRows.txt ├── parametersWindDirection.txt ├── regression0order.txt ├── regression1order.txt ├── regression2order.txt ├── regressionInvalid1.txt ├── regressionMissing.txt ├── sampleObsFcst.txt ├── training.txt ├── trainingDataFcst.txt ├── trainingDataObs.txt ├── trainingDataTemperature.txt ├── validArome1.nc ├── validArome2.nc ├── validEc1.nc ├── validEc2.nc ├── validEc3.nc ├── validEc_gph.nc ├── validEc_gph_no_x.nc ├── validEc_multidim_altitude.nc ├── validNetcdf1.nc ├── validNetcdf2.nc ├── validNetcdf3.nc ├── validNetcdf4.nc ├── validNetcdfAnalysis.nc ├── validNetcdfAnalysis2.nc ├── validNetcdfDimNames.nc ├── validNetcdfGeopotential.nc ├── validPoint1.txt ├── validPoint2.txt ├── validText1.txt └── validText2.txt ├── test_apply_curve.py ├── test_barnes_structure.py ├── test_bilinear.py ├── test_calc_gradient.py ├── test_count.py ├── test_distance.py ├── test_distribution.py ├── test_doping.py ├── test_downscale_probability.py ├── test_downscaling.py ├── test_fill.py ├── test_fill_missing.py ├── test_full_gradient.py ├── test_get_neighbourhood_thresholds.py ├── test_gradient.py ├── test_grid.py ├── test_gridding.py ├── test_humidity.py ├── test_init_vec.py ├── test_interpolate.py ├── test_kdtree.py ├── test_local_distribution_correction.py ├── test_mask_threshold_downscale_consensus.py ├── test_memory.py ├── test_metric_optimizer.py ├── test_monotonize.py ├── test_nearest.py ├── test_neighbourhood.py ├── test_neighbourhood_quantile.py ├── test_neighbourhood_quantile_fast.py ├── test_neighbourhood_search.py ├── test_optimal_interpolation.py ├── test_optimal_interpolation_ens.py ├── test_point.py ├── test_point_in_rectangle.py ├── test_points.py ├── test_pressure.py ├── test_qnh.py ├── test_quantile_mapping.py ├── test_sea_level_pressure.py ├── test_simple_gradient.py ├── test_structure.py ├── test_swig.py ├── test_transform.py ├── test_util.py ├── test_wind.py └── test_window.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push, pull_request] 4 | 5 | # push: 6 | # branches: [ master ] 7 | # pull_request: 8 | # branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-20.04 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.10' 19 | - name: dependencies 20 | run: | 21 | sudo apt update -qq 22 | sudo apt install swig libboost-dev libarmadillo9 libarmadillo-dev cmake python3-setuptools python3-numpy python3-pip lcov 23 | - name: configure 24 | run: mkdir build && cd build && cmake -DBUILD_R=no -DCMAKE_BUILD_TYPE=DEBUG -DENABLE_TESTS=ON .. 25 | - name: build 26 | run: cd build && VERBOSE=1 make develop-python-user 27 | - name: test 28 | run: | 29 | cd build 30 | /usr/bin/python3 -m pip install coverage nose 31 | make tests 32 | lcov --directory CMakeFiles/gridpp.dir/src/api --capture --output-file coverage.info 33 | lcov --remove coverage.info '/usr/*' --output-file coverage.info 34 | lcov --list coverage.info 35 | # - name: test 36 | # run: | 37 | # /usr/bin/python3 -c "import gridpp; print(gridpp.version())" 38 | # /usr/bin/python3 -m pip install --upgrade pip 39 | # /usr/bin/python3 -m pip install coverage nose 40 | # coverage run --omit */*-packages/*,*tests* -m nose 41 | # coverage report 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | gridpp 4 | gridpp_debug 5 | gridpp_train 6 | gridpp_train_debug 7 | gridpp_kf 8 | gridpp_kf_debug 9 | *.nc 10 | gmon.out 11 | tags 12 | temp/ 13 | */devel/ 14 | devel/ 15 | .*.swp 16 | data 17 | coverage.* 18 | debian/gridpp* 19 | build* 20 | env/ 21 | .vscode -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2014-2024 Norwegian Meteorological Institute 2 | All rights reserved. 3 | 4 | This program is free software: you can redistribute it and/or modify it under the terms of 5 | the GNU Lesser General Public License as published by the Free Software Foundation, either 6 | version 3 of the License, or (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 9 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | See the GNU Lesser General Public License for more details. 11 | 12 | You should have received a copy of the GNU Lesser General Public License along with this 13 | program. If not, see . 14 | -------------------------------------------------------------------------------- /docs/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/docs/image.jpg -------------------------------------------------------------------------------- /src/api/.calc_gradient.cpp.swo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/src/api/.calc_gradient.cpp.swo -------------------------------------------------------------------------------- /src/api/.gitignore: -------------------------------------------------------------------------------- 1 | convert.sh 2 | -------------------------------------------------------------------------------- /src/api/count.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | 4 | using namespace gridpp; 5 | 6 | vec gridpp::count(const Grid& grid, const Points& points, float radius) { 7 | int size = points.size(); 8 | vec output(size); 9 | vec olats = points.get_lats(); 10 | vec olons = points.get_lons(); 11 | #pragma omp parallel for 12 | for(int i = 0; i < size; i++) { 13 | int num = grid.get_num_neighbours(olats[i], olons[i], radius); 14 | output[i] = num; 15 | } 16 | return output; 17 | } 18 | 19 | vec2 gridpp::count(const Grid& igrid, const Grid& ogrid, float radius) { 20 | ivec size = ogrid.size(); 21 | vec2 output(size[0]); 22 | vec2 olats = ogrid.get_lats(); 23 | vec2 olons = ogrid.get_lons(); 24 | for(int i = 0; i < size[0]; i++) { 25 | output[i].resize(size[1], 0); 26 | } 27 | #pragma omp parallel for collapse(2) 28 | for(int i = 0; i < size[0]; i++) { 29 | for(int j = 0; j < size[1]; j++) { 30 | int num = igrid.get_num_neighbours(olats[i][j], olons[i][j], radius); 31 | output[i][j] = num; 32 | } 33 | } 34 | return output; 35 | } 36 | 37 | vec2 gridpp::count(const Points& points, const Grid& grid, float radius) { 38 | ivec size = grid.size(); 39 | vec2 output(size[0]); 40 | vec2 olats = grid.get_lats(); 41 | vec2 olons = grid.get_lons(); 42 | for(int i = 0; i < size[0]; i++) { 43 | output[i].resize(size[1], 0); 44 | } 45 | #pragma omp parallel for collapse(2) 46 | for(int i = 0; i < size[0]; i++) { 47 | for(int j = 0; j < size[1]; j++) { 48 | int num = points.get_num_neighbours(olats[i][j], olons[i][j], radius); 49 | output[i][j] = num; 50 | } 51 | } 52 | return output; 53 | } 54 | 55 | vec gridpp::count(const Points& ipoints, const Points& opoints, float radius) { 56 | int size = opoints.size(); 57 | vec output(size); 58 | vec olats = opoints.get_lats(); 59 | vec olons = opoints.get_lons(); 60 | #pragma omp parallel for 61 | for(int i = 0; i < size; i++) { 62 | int num = ipoints.get_num_neighbours(olats[i], olons[i], radius); 63 | output[i] = num; 64 | } 65 | return output; 66 | } 67 | -------------------------------------------------------------------------------- /src/api/distribution.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | 3 | using namespace gridpp; 4 | 5 | vec gridpp::gamma_inv(const vec& levels, const vec& shape, const vec& scale) { 6 | int N = shape.size(); 7 | for(int i = 0; i < N; i++) { 8 | if(levels[i] < 0 || levels[i] > 1 || !gridpp::is_valid(levels[i])) { 9 | std::stringstream ss; 10 | ss << "Invalid level '" << levels[i] << "'. Levels must be on the interval [0, 1]."; 11 | throw std::invalid_argument(ss.str()); 12 | } 13 | if(shape[i] <= 0 || !gridpp::is_valid(shape[i])) { 14 | std::stringstream ss; 15 | ss << "Invalid shape '" << shape[i] << "'. Shapes must be > 0."; 16 | throw std::invalid_argument(ss.str()); 17 | } 18 | if(scale[i] <= 0 || !gridpp::is_valid(scale[i])) { 19 | std::stringstream ss; 20 | ss << "Invalid scale '" << scale[i] << "'. Scale must be > 0."; 21 | throw std::invalid_argument(ss.str()); 22 | } 23 | } 24 | vec results(N); 25 | 26 | #pragma omp parallel for 27 | for(int i = 0; i < N; i++) { 28 | boost::math::gamma_distribution<> m_gamma_dist(shape[i], scale[i]); 29 | float value = boost::math::quantile(m_gamma_dist, levels[i]); 30 | results[i] = value; 31 | } 32 | return results; 33 | } 34 | -------------------------------------------------------------------------------- /src/api/downscale_probability.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | #include 4 | 5 | using namespace gridpp; 6 | 7 | vec2 gridpp::downscale_probability(const Grid& igrid, const Grid& ogrid, const vec3& ivalues, const vec2& threshold, const ComparisonOperator& comparison_operator) { 8 | vec2 iOutputLats = ogrid.get_lats(); 9 | vec2 iOutputLons = ogrid.get_lons(); 10 | 11 | int nLat = iOutputLats.size(); 12 | int nLon = iOutputLats[0].size(); 13 | int nEns = ivalues[0][0].size(); 14 | 15 | vec2 output(nLat); 16 | for(int i = 0; i < nLat; i++) 17 | output[i].resize(nLon); 18 | 19 | #pragma omp parallel for collapse(2) 20 | for(int i = 0; i < nLat; i++) { 21 | for(int j = 0; j < nLon; j++) { 22 | ivec indices = igrid.get_nearest_neighbour(iOutputLats[i][j], iOutputLons[i][j]); 23 | int I = indices[0]; 24 | int J = indices[1]; 25 | int count = 0; 26 | int total = 0; 27 | for(int k = 0; k < nEns; k++){ 28 | if (gridpp::is_valid(ivalues[I][J][k])) { 29 | count = count + 1; 30 | if (comparison_operator == gridpp::Leq) { 31 | if (ivalues[I][J][k] <= threshold[i][j]) { 32 | total = total + 1; 33 | } 34 | } else if (comparison_operator == gridpp::Lt) { 35 | if (ivalues[I][J][k] < threshold[i][j]) { 36 | total = total + 1; 37 | } 38 | } else if (comparison_operator == gridpp::Geq) { 39 | if (ivalues[I][J][k] >= threshold[i][j]) { 40 | total = total + 1; 41 | } 42 | } else if (comparison_operator == gridpp::Gt) { 43 | if (ivalues[I][J][k] > threshold[i][j]) { 44 | total = total + 1; 45 | } 46 | } 47 | } 48 | } 49 | // std::cout << "total[" << i <<"][" << j << "]:" << total << std::endl; 50 | // std::cout << "count[" << i <<"][" << j << "]:" << count << std::endl; 51 | 52 | float prob; 53 | if (count == 0) { 54 | prob = gridpp::MV; 55 | } 56 | else { 57 | prob = (float)total / (float)count; 58 | // std::cout << "prob[" << i <<"][" << j << "]:" << prob << std::endl; 59 | } 60 | 61 | output[i][j] = prob; 62 | } 63 | } 64 | return output; 65 | } -------------------------------------------------------------------------------- /src/api/downscaling.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | #include 4 | 5 | using namespace gridpp; 6 | 7 | vec gridpp::downscaling(const Grid& igrid, const Points& opoints, const vec2& ivalues, Downscaler downscaler) { 8 | if(!gridpp::compatible_size(igrid, ivalues)) 9 | throw std::invalid_argument("Grid size is not the same as values"); 10 | 11 | vec output; 12 | if(downscaler == gridpp::Nearest) 13 | output = gridpp::nearest(igrid, opoints, ivalues); 14 | else if(downscaler == gridpp::Bilinear) 15 | output = gridpp::bilinear(igrid, opoints, ivalues); 16 | else 17 | throw std::invalid_argument("Invalid downscaler"); 18 | return output; 19 | } 20 | 21 | vec2 gridpp::downscaling(const Grid& igrid, const Grid& ogrid, const vec2& ivalues, Downscaler downscaler) { 22 | if(!gridpp::compatible_size(igrid, ivalues)) 23 | throw std::invalid_argument("Grid size is not the same as values"); 24 | 25 | vec2 output; 26 | if(downscaler == gridpp::Nearest) 27 | output = gridpp::nearest(igrid, ogrid, ivalues); 28 | else if(downscaler == gridpp::Bilinear) 29 | output = gridpp::bilinear(igrid, ogrid, ivalues); 30 | else 31 | throw std::invalid_argument("Invalid downscaler"); 32 | return output; 33 | } 34 | 35 | vec2 gridpp::downscaling(const Grid& igrid, const Points& opoints, const vec3& ivalues, Downscaler downscaler) { 36 | if(!gridpp::compatible_size(igrid, ivalues)) 37 | throw std::invalid_argument("Grid size is not the same as values"); 38 | 39 | vec2 output; 40 | if(downscaler == gridpp::Nearest) 41 | output = gridpp::nearest(igrid, opoints, ivalues); 42 | else if(downscaler == gridpp::Bilinear) 43 | output = gridpp::bilinear(igrid, opoints, ivalues); 44 | else 45 | throw std::invalid_argument("Invalid downscaler"); 46 | return output; 47 | } 48 | 49 | vec3 gridpp::downscaling(const Grid& igrid, const Grid& ogrid, const vec3& ivalues, Downscaler downscaler) { 50 | if(!gridpp::compatible_size(igrid, ivalues)) 51 | throw std::invalid_argument("Grid size is not the same as values"); 52 | 53 | vec3 output; 54 | if(downscaler == gridpp::Nearest) 55 | output = gridpp::nearest(igrid, ogrid, ivalues); 56 | else if(downscaler == gridpp::Bilinear) 57 | output = gridpp::bilinear(igrid, ogrid, ivalues); 58 | else 59 | throw std::invalid_argument("Invalid downscaler"); 60 | return output; 61 | } 62 | -------------------------------------------------------------------------------- /src/api/gridpp.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace gridpp; 7 | 8 | std::string gridpp::version() { 9 | return __version__; 10 | } 11 | gridpp::Statistic gridpp::get_statistic(std::string name) { 12 | gridpp::Statistic type; 13 | if(name == "mean") { 14 | type = gridpp::Mean; 15 | } 16 | else if(name == "min") { 17 | type = gridpp::Min; 18 | } 19 | else if(name == "max") { 20 | type = gridpp::Max; 21 | } 22 | else if(name == "median") { 23 | type = gridpp::Median; 24 | } 25 | else if(name == "quantile") { 26 | type = gridpp::Quantile; 27 | } 28 | else if(name == "std") { 29 | type = gridpp::Std; 30 | } 31 | else if(name == "sum") { 32 | type = gridpp::Sum; 33 | } 34 | else if(name == "count") { 35 | type = gridpp::Count; 36 | } 37 | else if(name == "randomchoice") { 38 | type = gridpp::RandomChoice; 39 | } 40 | else { 41 | type = gridpp::Unknown; 42 | } 43 | return type; 44 | } 45 | void gridpp::initialize_omp() { 46 | #ifdef _OPENMP 47 | int num_threads = 1; 48 | const char* num_threads_char = std::getenv("OMP_NUM_THREADS"); 49 | if(num_threads_char != NULL) { 50 | std::istringstream(std::string(num_threads_char)) >> num_threads; 51 | if(num_threads <= 0) 52 | num_threads = 1; 53 | } 54 | gridpp::set_omp_threads(num_threads); 55 | #endif 56 | } 57 | void gridpp::set_omp_threads(int num) { 58 | #ifdef _OPENMP 59 | // omp_set_dynamic(0); 60 | omp_set_num_threads(num); 61 | #endif 62 | } 63 | int gridpp::get_omp_threads() { 64 | #ifdef _OPENMP 65 | return omp_get_max_threads(); 66 | #endif 67 | return 0; 68 | } 69 | 70 | void gridpp::set_debug_level(int level) { 71 | gridpp::debug_level = level; 72 | } 73 | 74 | int gridpp::get_debug_level() { 75 | return gridpp::debug_level; 76 | } 77 | gridpp::not_implemented_exception::not_implemented_exception() : 78 | std::logic_error("Function not yet implemented") { 79 | }; 80 | -------------------------------------------------------------------------------- /src/api/neighbourhood_score.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | 4 | using namespace gridpp; 5 | 6 | vec2 gridpp::neighbourhood_score(const Grid& grid, const Points& points, const vec2& fcst, const vec& ref, int half_width, gridpp::Metric metric, float threshold) { 7 | 8 | if(!gridpp::compatible_size(grid, fcst)) { 9 | throw std::invalid_argument("Grid size is not the same as forecast values"); 10 | } 11 | 12 | if(half_width <= 0) { 13 | throw std::invalid_argument("half_width must be greater than 0"); 14 | } 15 | 16 | int nY = fcst.size(); 17 | int nX = fcst[0].size(); 18 | 19 | vec2 a = gridpp::init_vec2(nY, nX, 0); 20 | vec2 b = gridpp::init_vec2(nY, nX, 0); 21 | vec2 c = gridpp::init_vec2(nY, nX, 0); 22 | vec2 d = gridpp::init_vec2(nY, nX, 0); 23 | 24 | // Gridding of observations on the the forecast grid 25 | vec2 ref_grid = gridpp::gridding_nearest(grid, points, ref, 1, gridpp::Mean); 26 | 27 | // Compute the 4 contingency values 28 | #pragma omp parallel for collapse(2) 29 | for(int y = 0; y < nY; y++) { 30 | for(int x = 0; x < nX; x++) { 31 | if(gridpp::is_valid(ref_grid[y][x]) && gridpp::is_valid(fcst[y][x])) { 32 | if(fcst[y][x] > threshold) { 33 | a[y][x] = ref_grid[y][x] > threshold; 34 | b[y][x] = ref_grid[y][x] <= threshold; 35 | } 36 | else { 37 | c[y][x] = ref_grid[y][x] > threshold; 38 | d[y][x] = ref_grid[y][x] <= threshold; 39 | } 40 | } 41 | } 42 | } 43 | 44 | // Apply neighbourhood method on contingency values 45 | vec2 a_hood = gridpp::neighbourhood(a, half_width, gridpp::Mean); 46 | vec2 b_hood = gridpp::neighbourhood(b, half_width, gridpp::Mean); 47 | vec2 c_hood = gridpp::neighbourhood(c, half_width, gridpp::Mean); 48 | vec2 d_hood = gridpp::neighbourhood(d, half_width, gridpp::Mean); 49 | 50 | // Compute score in neighbourhood 51 | vec2 output = gridpp::init_vec2(nY, nX, gridpp::Mean); 52 | #pragma omp parallel for collapse(2) 53 | for(int y = 0; y < nY; y++) { 54 | for(int x = 0; x < nX; x++) { 55 | output[y][x] = gridpp::calc_score(a_hood[y][x], b_hood[y][x], c_hood[y][x], d_hood[y][x], metric); 56 | // std::cout << y << " " << x << " " << a_hood[y][x] << std::endl; 57 | } 58 | } 59 | return output; 60 | } 61 | -------------------------------------------------------------------------------- /src/api/point.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | 3 | using namespace gridpp; 4 | 5 | gridpp::Point::Point(float lat, float lon, float elev, float laf, CoordinateType type) { 6 | this->lat = lat; 7 | this->lon = lon; 8 | this->elev = elev; 9 | this->laf = laf; 10 | this->type = type; 11 | 12 | if(type == gridpp::Geodetic) { 13 | float new_x = 0; 14 | float new_y = 0; 15 | float new_z = 0; 16 | gridpp::convert_coordinates(lat, lon, type, this->x, this->y, this->z); 17 | } 18 | else { 19 | this->x = lat; 20 | this->y = lon; 21 | this->z = 0; 22 | } 23 | } 24 | 25 | gridpp::Point::Point(float lat, float lon, float elev, float laf, CoordinateType type, float x, float y, float z) : lat(lat), lon(lon), elev(elev), laf(laf), type(type), x(x), y(y), z(z) { 26 | } 27 | -------------------------------------------------------------------------------- /src/api/qnh.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | 4 | using namespace gridpp; 5 | 6 | float gridpp::qnh(float pressure, float altitude) { 7 | if(pressure == 0) 8 | return 0; 9 | else if(gridpp::is_valid(altitude) && gridpp::is_valid(pressure)) { 10 | float g = 9.80665; // m/s2 11 | float T0 = 288.15; // K 12 | float L = 0.0065; // K/m 13 | // Method 1: 14 | // float dElev = 0 - altitude; 15 | // float M = 0.0289644; // kg/mol 16 | // float R = 8.31447; // J/(mol•K) 17 | // float cp = 1007; // J/(kg•K) 18 | // float constant = -g*M/R/T0; 19 | // float qnh = pressure * pow(1 - L*dElev/T0, g*M/R/L); 20 | 21 | // Method 2: http://www.hochwarth.com/misc/AviationCalculator.html 22 | float CRGas = 287.053; // [m^2/(s^2*K)] = [J/(kg*K)] 23 | float p0 = 101325; // pa 24 | float qnh = p0*pow(pow((pressure/p0), (CRGas*L)/g) + (altitude*L)/T0, g/(CRGas*L)); 25 | return qnh; 26 | } 27 | else { 28 | return MV; 29 | } 30 | } 31 | vec gridpp::qnh(const vec& pressure, const vec& altitude) { 32 | if(pressure.size() != altitude.size()) 33 | throw std::invalid_argument("Pressure and altitude vectors are not the same size"); 34 | 35 | vec ret(pressure.size()); 36 | #pragma omp parallel for 37 | for(int y = 0; y < pressure.size(); y++) { 38 | ret[y] = gridpp::qnh(pressure[y], altitude[y]); 39 | } 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /src/api/quantile_mapping.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | 3 | using namespace gridpp; 4 | 5 | vec gridpp::quantile_mapping_curve(const vec& ref, const vec& fcst, vec& output_fcst, vec quantiles) { 6 | if(ref.size() != fcst.size()) 7 | throw std::invalid_argument("ref and fcst must be of the same size"); 8 | 9 | if(quantiles.size() > 0) { 10 | for(int i = 0; i < quantiles.size(); i++) { 11 | float curr = quantiles[i]; 12 | if(!gridpp::is_valid(curr) || curr > 1 || curr < 0) 13 | throw std::invalid_argument("Quantiles must be >= 0 and <= 1"); 14 | } 15 | } 16 | output_fcst.clear(); 17 | if(ref.size() == 0) { 18 | output_fcst = fcst; 19 | return ref; 20 | } 21 | else if(ref.size() == 1) { 22 | output_fcst = fcst; 23 | return ref; 24 | } 25 | 26 | vec ref_sort = ref; 27 | std::sort(ref_sort.begin(), ref_sort.end()); 28 | vec fcst_sort = fcst; 29 | std::sort(fcst_sort.begin(), fcst_sort.end()); 30 | int N = quantiles.size(); 31 | int S = fcst_sort.size(); 32 | if(N == 0) { 33 | output_fcst = fcst_sort; 34 | return ref_sort; 35 | } 36 | else { 37 | output_fcst.resize(N); 38 | vec output_ref(N); 39 | for(int i = 0; i < N; i++) { 40 | int index = quantiles[i] * (S - 1); 41 | output_fcst[i] = fcst[index]; 42 | output_ref[i] = ref[index]; 43 | } 44 | return output_ref; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/api/wind.cpp: -------------------------------------------------------------------------------- 1 | #include "gridpp.h" 2 | #include 3 | 4 | using namespace gridpp; 5 | 6 | float gridpp::wind_speed(float xwind, float ywind) { 7 | return sqrt(xwind * xwind + ywind * ywind); 8 | } 9 | vec gridpp::wind_speed(const vec& xwind, const vec& ywind) { 10 | if(xwind.size() != ywind.size()) 11 | throw std::invalid_argument("xwind and ywind must be of the same size"); 12 | int N = xwind.size(); 13 | vec values(N, gridpp::MV); 14 | #pragma omp parallel for 15 | for(int n = 0; n < N; n++) { 16 | values[n] = wind_speed(xwind[n], ywind[n]); 17 | } 18 | return values; 19 | } 20 | float gridpp::wind_direction(float xwind, float ywind) { 21 | float dir = std::atan2(-xwind, -ywind) * 180 / gridpp::pi; 22 | if(dir < 0) 23 | dir += 360; 24 | 25 | return dir; 26 | } 27 | vec gridpp::wind_direction(const vec& xwind, const vec& ywind) { 28 | if(xwind.size() != ywind.size()) 29 | throw std::invalid_argument("xwind and ywind must be of the same size"); 30 | int N = xwind.size(); 31 | vec values(N, gridpp::MV); 32 | #pragma omp parallel for 33 | for(int n = 0; n < N; n++) { 34 | values[n] = wind_direction(xwind[n], ywind[n]); 35 | } 36 | return values; 37 | } 38 | -------------------------------------------------------------------------------- /src/client/Calibrator/Accumulate.cpp: -------------------------------------------------------------------------------- 1 | #include "Accumulate.h" 2 | #include "../Util.h" 3 | #include "../File/File.h" 4 | CalibratorAccumulate::CalibratorAccumulate(const Variable& iVariable, const Options& iOptions) : 5 | Calibrator(iVariable, iOptions) { 6 | iOptions.check(); 7 | } 8 | bool CalibratorAccumulate::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 9 | int nY = iFile.getNumY(); 10 | int nX = iFile.getNumX(); 11 | int nEns = iFile.getNumEns(); 12 | int nTime = iFile.getNumTime(); 13 | 14 | // Get all fields 15 | std::vector fields(nTime); 16 | std::vector fieldsAcc(nTime); 17 | for(int t = 0; t < nTime; t++) { 18 | fields[t] = iFile.getField(mVariable, t); 19 | fieldsAcc[t] = iFile.getEmptyField(); 20 | } 21 | 22 | for(int t = 0; t < nTime; t++) { 23 | #pragma omp parallel for 24 | for(int y = 0; y < nY; y++) { 25 | for(int x = 0; x < nX; x++) { 26 | for(int e = 0; e < nEns; e++) { 27 | if(t == 0) { 28 | (*fieldsAcc[t])(y, x, e) = 0; 29 | } 30 | else { 31 | float previous = (*fieldsAcc[t - 1])(y, x, e); 32 | float current = (*fields[t])(y, x, e); 33 | if(Util::isValid(current) && Util::isValid(previous)) { 34 | (*fieldsAcc[t])(y, x, e) = current + previous; 35 | } 36 | else { 37 | (*fieldsAcc[t])(y, x, e) = Util::MV; 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | iFile.addField(fieldsAcc[t], mVariable, t); 45 | } 46 | return true; 47 | } 48 | std::string CalibratorAccumulate::description(bool full) { 49 | std::stringstream ss; 50 | ss << Util::formatDescription("-c accumulate","Accumlates a variable over time") << std::endl; 51 | return ss.str(); 52 | } 53 | -------------------------------------------------------------------------------- /src/client/Calibrator/Accumulate.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_ACCUMULATE_H 2 | #define CALIBRATOR_ACCUMULATE_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | // Accumlates a certain variable 7 | class CalibratorAccumulate : public Calibrator { 8 | public: 9 | CalibratorAccumulate(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "accumulate";}; 12 | bool requiresParameterFile() const { return false;}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | }; 16 | #endif 17 | -------------------------------------------------------------------------------- /src/client/Calibrator/Altitude.cpp: -------------------------------------------------------------------------------- 1 | #include "Altitude.h" 2 | #include 3 | #include "../Util.h" 4 | #include "../File/File.h" 5 | #include "../ParameterFile/ParameterFile.h" 6 | CalibratorAltitude::CalibratorAltitude(const Variable& iVariable, const Options& iOptions) : 7 | Calibrator(iVariable, iOptions) { 8 | iOptions.check(); 9 | } 10 | bool CalibratorAltitude::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 11 | if(!iParameterFile->isLocationDependent()) { 12 | Util::error("Cannot use a location independent parameter file to update the altitudes"); 13 | } 14 | int nLat = iFile.getNumY(); 15 | int nLon = iFile.getNumX(); 16 | vec2 lats = iFile.getLats(); 17 | vec2 lons = iFile.getLons(); 18 | vec2 elevs = iFile.getElevs(); 19 | 20 | #pragma omp parallel for 21 | for(int i = 0; i < nLat; i++) { 22 | for(int j = 0; j < nLon; j++) { 23 | Location loc(Util::MV, Util::MV, Util::MV); 24 | iParameterFile->getNearestLocation(0, Location(lats[i][j], lons[i][j], elevs[i][j]), loc); 25 | elevs[i][j] = loc.elev(); 26 | } 27 | } 28 | iFile.setElevs(elevs); 29 | return true; 30 | } 31 | 32 | std::string CalibratorAltitude::description(bool full) { 33 | std::stringstream ss; 34 | if(full) 35 | ss << Util::formatDescription("-c altitude", "Changes the altitudes to the altitudes in the parameter file. If the file does not have an altitude field, then a new one is not created.") << std::endl; 36 | else 37 | ss << Util::formatDescription("-c altitude", "Changes the altitudes to the altitudes in the parameter file") << std::endl; 38 | return ss.str(); 39 | } 40 | -------------------------------------------------------------------------------- /src/client/Calibrator/Altitude.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_ALTITUDE_H 2 | #define CALIBRATOR_ALTITUDE_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Changes the altitudes in iFile to the altitudes in the parameter file 8 | class CalibratorAltitude : public Calibrator { 9 | public: 10 | CalibratorAltitude(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "altitude";}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | }; 16 | #endif 17 | -------------------------------------------------------------------------------- /src/client/Calibrator/Bct.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_BCT_H 2 | #define CALIBRATOR_BCT_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | class ParameterFile; 7 | class Parameters; 8 | 9 | //! Ensemble calibration using a Box-Cox t-distribution. Its predictors are: 10 | //! - ensemble mean 11 | //! - ensemble standard deviation 12 | //! Designed for windspeed 13 | class CalibratorBct : public Calibrator { 14 | public: 15 | CalibratorBct(const Variable& iMainPredictor, const Options& iOptions); 16 | static float getInvCdf(float iQuantile, float iEnsMean, float iEnsStd, const Parameters& iParameters); 17 | 18 | static std::string description(bool full=true); 19 | std::string name() const {return "bct";}; 20 | private: 21 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 22 | float mMaxEnsMean; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /src/client/Calibrator/Calibrator.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_H 2 | #define CALIBRATOR_H 3 | #include 4 | #include 5 | #include "../Scheme.h" 6 | #include "../Parameters.h" 7 | #include "../Field.h" 8 | #include "../Variable.h" 9 | 10 | typedef std::vector Ens; 11 | typedef std::pair ObsEns; 12 | typedef std::pair ObsEnsField; 13 | class File; 14 | class Options; 15 | class ParameterFile; 16 | class Grid; 17 | 18 | //! Abstract calibration class 19 | class Calibrator : public Scheme { 20 | public: 21 | //! The calibrator does not free the memory of iParameterFile 22 | Calibrator(const Variable& iVariable, const Options& iOptions); 23 | virtual ~Calibrator() {}; 24 | //! \brief Calibrate one or more fields in iFile 25 | //! @return true if calibration was successful, false otherwise 26 | bool calibrate(File& iFile, const ParameterFile* iParameterFile=NULL) const; 27 | 28 | //! Instantiates a calibrator with name iName 29 | static Calibrator* getScheme(std::string iName, Variable iVariable, const Options& iOptions); 30 | 31 | //! \brief Ensure that values in iAfter are in the same order as in iBefore. 32 | //! If missing values are encountered in iBefore or iAfter, then iAfter is left unchanged. 33 | //! If the sizes are different, then iAfter is left unchanged. 34 | static void shuffle(const std::vector& iBefore, std::vector& iAfter); 35 | 36 | //! Returns the name of this calibrator 37 | virtual std::string name() const = 0; 38 | 39 | static std::string getDescriptions(bool full=true); 40 | 41 | virtual Parameters train(const std::vector& iData) const; 42 | // Defaults to using the regular train method 43 | virtual Parameters train(const std::vector& iData, const Grid& iObsGrid, const Grid& iEnsGrid, int iIobs, int iJobs, int iIEns, int iJEns) const; 44 | 45 | // Does this calibrator require a parameter file? 46 | virtual bool requiresParameterFile() const { return true;}; 47 | Options getOptions() const; 48 | protected: 49 | virtual bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const = 0; 50 | Variable mVariable; 51 | private: 52 | Options mOptions; 53 | }; 54 | // #include "Wind.h" 55 | #include "Accumulate.h" 56 | #include "Altitude.h" 57 | #include "Bct.h" 58 | #include "Cloud.h" 59 | #include "Coastal.h" 60 | #include "Deaccumulate.h" 61 | #include "DiagnoseHumidity.h" 62 | #include "DiagnoseWind.h" 63 | #include "Gaussian.h" 64 | #include "Kriging.h" 65 | #include "Mask.h" 66 | #include "Neighbourhood.h" 67 | #include "Oi.h" 68 | #include "Override.h" 69 | #include "Phase.h" 70 | #include "Qc.h" 71 | #include "Qnh.h" 72 | #include "Qq.h" 73 | #include "Regression.h" 74 | #include "Sort.h" 75 | #include "Temperature.h" 76 | #include "Threshold.h" 77 | #include "WindDirection.h" 78 | #include "Window.h" 79 | #include "Zaga.h" 80 | #endif 81 | -------------------------------------------------------------------------------- /src/client/Calibrator/Cloud.cpp: -------------------------------------------------------------------------------- 1 | #include "Cloud.h" 2 | #include "../Util.h" 3 | #include "../File/File.h" 4 | CalibratorCloud::CalibratorCloud(const Variable& iVariable, const Options& iOptions) : 5 | Calibrator(iVariable, iOptions), 6 | mValue(1), 7 | mPrecipVariable("") { 8 | iOptions.getRequiredValue("precipVariable", mPrecipVariable); 9 | iOptions.getValue("value", mValue); 10 | iOptions.check(); 11 | } 12 | bool CalibratorCloud::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 13 | int nLat = iFile.getNumY(); 14 | int nLon = iFile.getNumX(); 15 | int nEns = iFile.getNumEns(); 16 | int nTime = iFile.getNumTime(); 17 | 18 | // Loop over offsets 19 | for(int t = 0; t < nTime; t++) { 20 | const Field& precip = *iFile.getField(mPrecipVariable, t); 21 | Field& cloud = *iFile.getField(mVariable, t); 22 | 23 | // TODO: Figure out which cloudless members to use. Ideally, if more members 24 | // need precip, we should pick members that already have clouds, so that we minimize 25 | // our effect on the cloud cover field. 26 | 27 | #pragma omp parallel for 28 | for(int i = 0; i < nLat; i++) { 29 | for(int j = 0; j < nLon; j++) { 30 | // Turn on clouds if needed, i.e don't allow a member to 31 | // have precip without cloud cover. 32 | for(int e = 0; e < nEns; e++) { 33 | float currPrecip = precip(i,j,e); 34 | float currCloud = cloud(i,j,e); 35 | if(Util::isValid(currPrecip) && Util::isValid(currCloud)) { 36 | cloud(i,j,e) = currCloud; 37 | if(currPrecip > 0 && currCloud < mValue) { 38 | cloud(i,j,e) = mValue; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | return true; 46 | } 47 | std::string CalibratorCloud::description(bool full) { 48 | std::stringstream ss; 49 | if(full) { 50 | ss << Util::formatDescription("-c cloud", "Ensure a minimum cloud cover value precipitation is present") << std::endl; 51 | ss << Util::formatDescription(" precipVariable=undef", "Name of precipitation variable") << std::endl; 52 | ss << Util::formatDescription(" value=1", "Minimum cloud cover value allowed") << std::endl; 53 | } 54 | else 55 | ss << Util::formatDescription("-c cloud", "Ensure clouds when precip is present") << std::endl; 56 | return ss.str(); 57 | } 58 | -------------------------------------------------------------------------------- /src/client/Calibrator/Cloud.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_CLOUD_H 2 | #define CALIBRATOR_CLOUD_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | //! Ensures that if a member has precip it also has full cloud cover 7 | class CalibratorCloud : public Calibrator { 8 | public: 9 | CalibratorCloud(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "cloud";}; 12 | bool requiresParameterFile() const { return false;}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | std::string mPrecipVariable; 16 | float mValue; 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/client/Calibrator/Coastal.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_COASTAL_H 2 | #define CALIBRATOR_COASTAL_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | class Grid; 7 | 8 | class CalibratorCoastal : public Calibrator { 9 | public: 10 | CalibratorCoastal(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "coastal";}; 13 | Parameters train(const std::vector& iData, const Grid& iObsGrid, const Grid& iEnsGrid, int iIobs, int iJobs, int iIEns, int iJEns) const; 14 | private: 15 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 16 | int mSearchRadius; 17 | float mMinLafDiff; 18 | bool mUseNN; 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /src/client/Calibrator/Deaccumulate.cpp: -------------------------------------------------------------------------------- 1 | #include "Deaccumulate.h" 2 | #include "../Util.h" 3 | #include "../File/File.h" 4 | CalibratorDeaccumulate::CalibratorDeaccumulate(const Variable& iVariable, const Options& iOptions) : 5 | mWindow(1), 6 | Calibrator(iVariable, iOptions) { 7 | iOptions.getValue("window", mWindow); 8 | iOptions.check(); 9 | } 10 | bool CalibratorDeaccumulate::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 11 | int nY = iFile.getNumY(); 12 | int nX = iFile.getNumX(); 13 | int nEns = iFile.getNumEns(); 14 | int nTime = iFile.getNumTime(); 15 | 16 | // Get all fields 17 | std::vector fields(nTime); 18 | std::vector fieldsAcc(nTime); 19 | for(int t = 0; t < nTime; t++) { 20 | fieldsAcc[t] = iFile.getField(mVariable, t); 21 | fields[t] = iFile.getEmptyField(); 22 | } 23 | 24 | for(int t = 0; t < nTime; t++) { 25 | #pragma omp parallel for 26 | for(int y = 0; y < nY; y++) { 27 | for(int x = 0; x < nX; x++) { 28 | for(int e = 0; e < nEns; e++) { 29 | if(t < mWindow) { 30 | (*fields[t])(y, x, e) = Util::MV; 31 | } 32 | else { 33 | float previous = (*fieldsAcc[t - mWindow])(y, x, e); 34 | float current = (*fieldsAcc[t])(y, x, e); 35 | if(Util::isValid(current) && Util::isValid(previous)) { 36 | (*fields[t])(y, x, e) = current - previous; 37 | } 38 | else { 39 | (*fields[t])(y, x, e) = Util::MV; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | iFile.addField(fields[t], mVariable, t); 47 | } 48 | return true; 49 | } 50 | std::string CalibratorDeaccumulate::description(bool full) { 51 | std::stringstream ss; 52 | ss << Util::formatDescription("-c deaccumulate","Deaccumlates a variable over time.") << std::endl; 53 | return ss.str(); 54 | } 55 | -------------------------------------------------------------------------------- /src/client/Calibrator/Deaccumulate.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_DEACCUMULATE_H 2 | #define CALIBRATOR_DEACCUMULATE_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | // Deaccumlates a certain variable 7 | class CalibratorDeaccumulate : public Calibrator { 8 | public: 9 | CalibratorDeaccumulate(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "deaccumulate";}; 12 | bool requiresParameterFile() const { return false;}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | int mWindow; 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /src/client/Calibrator/DiagnoseHumidity.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_DIAGNOSE_HUMIDITY_H 2 | #define CALIBRATOR_DIAGNOSE_HUMIDITY_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | class CalibratorDiagnoseHumidity : public Calibrator { 7 | public: 8 | CalibratorDiagnoseHumidity(const Variable& iVariable, const Options& iOptions); 9 | std::string name() const {return "diagnoseHumidity";}; 10 | bool requiresParameterFile() const { return false;}; 11 | static std::string description(bool full=true); 12 | // Temperature in K, RH in [0,1], Returns TD in K 13 | static float computeDewpoint(float iTemperature, float iRelativeHumidity); 14 | // Temperatures in K, Returns RH in [0,1] 15 | static float computeRh(float iTemperature, float iDewPointTemperature); 16 | //! Compute wetbulb temperature 17 | //! @param iTemperature Temperature in K 18 | //! @param iPressure Pressure in pa 19 | //! @param iRelativeHumidity Relative humidity (out of 1) 20 | //! @return Wetbulb temperature in K 21 | static float computeWetbulb(float iTemperature, float iPressure, float iRelativeHumidity); 22 | private: 23 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 24 | static float mEwt[41]; 25 | std::string mTemperature; 26 | std::string mRh; 27 | std::string mDewpoint; 28 | std::string mPressure; 29 | std::string mCompute; 30 | }; 31 | #endif 32 | -------------------------------------------------------------------------------- /src/client/Calibrator/DiagnoseWind.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_DIAGNOSE_WIND_H 2 | #define CALIBRATOR_DIAGNOSE_WIND_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | class CalibratorDiagnoseWind : public Calibrator { 7 | public: 8 | CalibratorDiagnoseWind(const Variable& iVariable, const Options& iOptions); 9 | std::string name() const {return "diagnoseWind";}; 10 | bool requiresParameterFile() const { return false;}; 11 | static std::string description(bool full=true); 12 | private: 13 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 14 | std::string mX; 15 | std::string mY; 16 | std::string mSpeed; 17 | std::string mDirection; 18 | std::string mCompute; 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /src/client/Calibrator/Gaussian.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_GAUSSIAN_H 2 | #define CALIBRATOR_GAUSSIAN_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | #include 6 | #include 7 | 8 | class ParameterFile; 9 | class Parameters; 10 | 11 | //! Ensemble calibration using zero-adjusted gamma distribution. Its predictors are: 12 | //! - ensemble mean 13 | //! - ensemble fraction 14 | //! Designed for precip 15 | class CalibratorGaussian : public Calibrator { 16 | public: 17 | CalibratorGaussian(const Variable& iVariable, const Options& iOptions); 18 | static float getInvCdf(float iQuantile, float iEnsMean, float iEnsSpread, const Parameters& iParameters); 19 | static float getCdf(float iThreshold, float iEnsMean, float iEnsSpread, const Parameters& iParameters); 20 | static float getPdf(float iThreshold, float iEnsMean, float iEnsSpread, const Parameters& iParameters); 21 | 22 | static std::string description(bool full=true); 23 | std::string name() const {return "gaussian";}; 24 | Parameters train(const std::vector& iData) const; 25 | private: 26 | static double my_f(const gsl_vector *v, void *params); 27 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 28 | int mNeighbourhoodSize; 29 | float mLogLikelihoodTolerance; 30 | static const int mNumParameters = 2; 31 | }; 32 | #endif 33 | -------------------------------------------------------------------------------- /src/client/Calibrator/Kriging.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_KRIGING_H 2 | #define CALIBRATOR_KRIGING_H 3 | #include "Calibrator.h" 4 | #include "../ParameterFile/ParameterFile.h" 5 | class Obs; 6 | class Forecast; 7 | class Parameters; 8 | 9 | class CalibratorKriging : public Calibrator { 10 | public: 11 | CalibratorKriging(const Variable& iVariable, const Options& iOptions); 12 | static std::string description(bool full=true); 13 | float calcCovar(const Location& loc1, const Location& loc2) const; 14 | enum Type { 15 | TypeCressman = 10, 16 | TypeBarnes = 20 17 | }; 18 | //! Compute the bias at the training point 19 | Parameters train(const std::vector& iData) const; 20 | private: 21 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 22 | 23 | float mRadius; 24 | float mMaxElevDiff; 25 | float mEfoldDist; 26 | std::string name() const {return "kriging";}; 27 | File* mPrevious; 28 | std::string mAuxVariable; 29 | float mLowerThreshold; 30 | float mUpperThreshold; 31 | Type mKrigingType; 32 | bool mUseApproxDistance; 33 | Util::Operator mOperator; 34 | bool mCrossValidate; 35 | int mWindow; 36 | }; 37 | #endif 38 | -------------------------------------------------------------------------------- /src/client/Calibrator/Mask.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_MASK_H 2 | #define CALIBRATOR_MASK_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Sets gridpoints that are far away from parameter points to missing 8 | class CalibratorMask : public Calibrator { 9 | public: 10 | CalibratorMask(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "mask";}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | bool mUseNearestOnly; 16 | bool mKeep; 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/client/Calibrator/Neighbourhood.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_NEIGHBOURHOOD_H 2 | #define CALIBRATOR_NEIGHBOURHOOD_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | 7 | class ParameterFile; 8 | class Parameters; 9 | 10 | //! Applies a statistical operator to a neighbourhood 11 | class CalibratorNeighbourhood : public Calibrator { 12 | public: 13 | CalibratorNeighbourhood(const Variable& iVariable, const Options& iOptions); 14 | static std::string description(bool full=true); 15 | std::string name() const {return "neighbourhood";}; 16 | bool requiresParameterFile() const { return false;}; 17 | void calibrateField(const Field& iInput, Field& iOutput, const Parameters* iParameters=NULL) const; 18 | private: 19 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 20 | int mRadius; 21 | Util::StatType mStatType; 22 | float mQuantile; 23 | bool mFast; 24 | bool mApprox; 25 | int numMissingValues(const Field& iField, int iEnsIndex) const; 26 | }; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/client/Calibrator/Oi.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_OI_H 2 | #define CALIBRATOR_OI_H 3 | #include 4 | #include "Calibrator.h" 5 | #include "../ParameterFile/ParameterFile.h" 6 | class Obs; 7 | class Forecast; 8 | class Parameters; 9 | 10 | class CalibratorOi : public Calibrator { 11 | public: 12 | CalibratorOi(Variable iVariable, const Options& iOptions); 13 | // Compute rho. A rho of 0 is returned if a vertical distance is missing when the 14 | // vertical scale is defined. 15 | enum RhoType {RhoTypeGaussian, RhoTypeSoar}; 16 | float calcRho(float iHdist, float iVdist, float iLdist, RhoType iType=RhoTypeGaussian) const; 17 | static std::string description(bool full=true); 18 | std::string name() const {return "oi";}; 19 | private: 20 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 21 | enum Type {TypeTemperature, TypePrecipitation}; 22 | enum TransformType {TransformTypeNone, TransformTypeBoxCox}; 23 | float mVLength; 24 | float mHLength; 25 | float mHLengthC; 26 | float mWLength; 27 | float mMu; 28 | float mGamma; 29 | std::string mBiasVariable; 30 | int mMaxLocations; 31 | float mSigma; 32 | float mSigmaC; 33 | float mDelta; 34 | std::string mDeltaVariable; 35 | std::string mNumVariable; 36 | float mC; 37 | float mEpsilonC; 38 | float mEpsilon; 39 | int mX; 40 | int mY; 41 | float mMinRho; 42 | float mMaxBytes; 43 | int mMinValidEns; 44 | bool mSaveDiff; 45 | float mElevGradient; 46 | bool mExtrapolate; 47 | float mWMin; 48 | float mNewDeltaVar; 49 | float mMaxElevDiff; 50 | bool mDiagnose; 51 | bool mLandOnly; 52 | std::string mDiaFile; 53 | bool mUseEns; 54 | typedef arma::mat mattype; 55 | typedef arma::vec vectype; 56 | typedef arma::cx_mat cxtype; 57 | float mLambda; 58 | bool mCrossValidate; 59 | Type mType; 60 | TransformType mTransformType; 61 | float calcDelta(float iOldDelta, const vec2& iY) const; 62 | float transform(float iValue) const; 63 | float invTransform(float iValue) const; 64 | RhoType mRhoType; 65 | float mBoxCoxThreshold; 66 | }; 67 | #endif 68 | -------------------------------------------------------------------------------- /src/client/Calibrator/Override.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_OVERRIDE_H 2 | #define CALIBRATOR_OVERRIDE_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Overrides values from parameter file into gridpoints 8 | class CalibratorOverride : public Calibrator { 9 | public: 10 | CalibratorOverride(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "override";}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | int mRadius; 16 | float mMaxElevDiff; 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/client/Calibrator/Phase.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_PHASE_H 2 | #define CALIBRATOR_PHASE_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | 6 | //! Creates a precipitation-phase field 7 | class CalibratorPhase : public Calibrator { 8 | public: 9 | CalibratorPhase(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "phase";}; 12 | 13 | //! Precipitation phase 14 | enum Phase { 15 | PhaseNone = 0, 16 | PhaseRain = 1, 17 | PhaseSleet = 2, 18 | PhaseSnow = 3 19 | }; 20 | private: 21 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 22 | float mMinPrecip; 23 | float mSnowThreshold; 24 | float mRainThreshold; 25 | std::string mTemperatureVariable; 26 | std::string mPrecipitationVariable; 27 | }; 28 | #endif 29 | -------------------------------------------------------------------------------- /src/client/Calibrator/Qc.cpp: -------------------------------------------------------------------------------- 1 | #include "Qc.h" 2 | #include 3 | #include 4 | #include 5 | #include "../Util.h" 6 | #include "../File/File.h" 7 | CalibratorQc::CalibratorQc(const Variable& iVariable, const Options& iOptions): 8 | Calibrator(iVariable, iOptions), 9 | mMin(Util::MV), 10 | mMax(Util::MV) { 11 | iOptions.getValue("min", mMin); 12 | iOptions.getValue("max", mMax); 13 | if(!Util::isValid(mMin) && !Util::isValid(mMax)) 14 | Util::warning("CalibratorQc: both 'min' and 'max' are missing, therefore no correction is applied."); 15 | iOptions.check(); 16 | } 17 | 18 | bool CalibratorQc::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 19 | int nLat = iFile.getNumY(); 20 | int nLon = iFile.getNumX(); 21 | int nEns = iFile.getNumEns(); 22 | int nTime = iFile.getNumTime(); 23 | 24 | // Loop over offsets 25 | for(int t = 0; t < nTime; t++) { 26 | Field& field = *iFile.getField(mVariable, t); 27 | 28 | #pragma omp parallel for 29 | for(int i = 0; i < nLat; i++) { 30 | for(int j = 0; j < nLon; j++) { 31 | 32 | for(int e = 0; e < nEns; e++) { 33 | float value = field(i,j,e); 34 | if(Util::isValid(value)) { 35 | if(Util::isValid(mMin) && value < mMin) 36 | value = mMin; 37 | else if(Util::isValid(mMax) && value > mMax) 38 | value = mMax; 39 | field(i,j,e) = value; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | return true; 46 | } 47 | 48 | std::string CalibratorQc::description(bool full) { 49 | std::stringstream ss; 50 | if(full) { 51 | ss << Util::formatDescription("-c qc", "Apply quality control, ensuring the values are within the bounds of the variable. If the original value is missing, no correction is applied.") << std::endl; 52 | ss << Util::formatDescription(" min=undef", "Force the minimum allowed value. If not provided, don't force a minimum.") << std::endl; 53 | ss << Util::formatDescription(" max=undef", "Force the maximum allowed value. If not provided, don't force a maximum.") << std::endl; 54 | } 55 | else 56 | ss << Util::formatDescription("-c qc", "Ensures values are within bounds") << std::endl; 57 | return ss.str(); 58 | } 59 | -------------------------------------------------------------------------------- /src/client/Calibrator/Qc.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_QC_H 2 | #define CALIBRATOR_QC_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | 7 | class ParameterFile; 8 | class Parameters; 9 | 10 | //! Applies a quality control adjustment. 11 | //! Ensures that all values are within an appropriate range. 12 | class CalibratorQc : public Calibrator { 13 | public: 14 | CalibratorQc(const Variable& iVariable, const Options& iOptions); 15 | static std::string description(bool full=true); 16 | std::string name() const {return "qc";}; 17 | bool requiresParameterFile() const { return false;}; 18 | private: 19 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 20 | float mMin; 21 | float mMax; 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /src/client/Calibrator/Qnh.cpp: -------------------------------------------------------------------------------- 1 | #include "Qnh.h" 2 | #include "../Util.h" 3 | #include "../File/File.h" 4 | #include 5 | CalibratorQnh::CalibratorQnh(const Variable& iVariable, const Options& iOptions) : 6 | Calibrator(iVariable, iOptions) { 7 | iOptions.getRequiredValue("pressureVariable", mPressureVariable); 8 | iOptions.check(); 9 | } 10 | bool CalibratorQnh::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 11 | int nLat = iFile.getNumY(); 12 | int nLon = iFile.getNumX(); 13 | int nEns = iFile.getNumEns(); 14 | int nTime = iFile.getNumTime(); 15 | 16 | vec2 lats = iFile.getLats(); 17 | vec2 lons = iFile.getLons(); 18 | vec2 elevs = iFile.getElevs(); 19 | 20 | // Loop over offsets 21 | for(int t = 0; t < nTime; t++) { 22 | const Field& input = *iFile.getField(mPressureVariable, t); 23 | Field& output = *iFile.getField(mVariable, t); 24 | 25 | #pragma omp parallel for 26 | for(int i = 0; i < nLat; i++) { 27 | for(int j = 0; j < nLon; j++) { 28 | float currElev = elevs[i][j]; 29 | for(int e = 0; e < nEns; e++) { 30 | float currPressure = input(i,j,e); 31 | if(Util::isValid(currPressure) && Util::isValid(currElev)) { 32 | output(i,j,e) = calcQnh(currElev, currPressure); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | return true; 39 | } 40 | std::string CalibratorQnh::description(bool full) { 41 | std::stringstream ss; 42 | if(full) 43 | ss << Util::formatDescription("-c qnh", "Adjusts the surface pressure down to sea-level based on a standard atmosphere (ICAO) producing the QNH variable.") << std::endl; 44 | else 45 | ss << Util::formatDescription("-c qnh", "Compute QNH") << std::endl; 46 | return ss.str(); 47 | } 48 | float CalibratorQnh::calcQnh(float iElev, float iPressure) { 49 | if(iPressure == 0) 50 | return 0; 51 | else if(Util::isValid(iElev) && Util::isValid(iPressure)) { 52 | float g = 9.80665; // m/s2 53 | float T0 = 288.15; // K 54 | float L = 0.0065; // K/m 55 | // Method 1: 56 | // float dElev = 0 - iElev; 57 | // float M = 0.0289644; // kg/mol 58 | // float R = 8.31447; // J/(mol•K) 59 | // float cp = 1007; // J/(kg•K) 60 | // float constant = -g*M/R/T0; 61 | // float qnh = iPressure * pow(1 - L*dElev/T0, g*M/R/L); 62 | 63 | // Method 2: http://www.hochwarth.com/misc/AviationCalculator.html 64 | float CRGas = 287.053; // [m^2/(s^2*K)] = [J/(kg*K)] 65 | float p0 = 101325; // pa 66 | float qnh = p0*pow(pow((iPressure/p0), (CRGas*L)/g) + (iElev*L)/T0, g/(CRGas*L)); 67 | return qnh; 68 | } 69 | else { 70 | return Util::MV; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/client/Calibrator/Qnh.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_QNH_H 2 | #define CALIBRATOR_QNH_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | //! Creates the QNH field by using surface pressure 7 | class CalibratorQnh : public Calibrator { 8 | public: 9 | CalibratorQnh(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "qnh";}; 12 | static float calcQnh(float iElev, float iPressure); 13 | bool requiresParameterFile() const { return false;}; 14 | private: 15 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 16 | std::string mPressureVariable; 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/client/Calibrator/Qq.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_QQ_H 2 | #define CALIBRATOR_QQ_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | 6 | //! Applies polynomial regression to forecasts 7 | class CalibratorQq : public Calibrator { 8 | public: 9 | CalibratorQq(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "qq";}; 12 | Parameters train(const std::vector& iData) const; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | float mLowerQuantile; 16 | float mUpperQuantile; 17 | std::vector mQuantiles; 18 | struct ExtrapolationPolicy { 19 | enum Policy { 20 | OneToOne = 0, 21 | MeanSlope = 10, 22 | NearestSlope = 20, 23 | Zero = 30, 24 | }; 25 | }; 26 | ExtrapolationPolicy::Policy mPolicy; 27 | // Separate the vector of obs,fcst,obs,fcst,... into separate vectors 28 | void separate(const Parameters& iParameters, std::vector& iObs, std::vector& iFcst) const; 29 | std::vector mExtraObs; 30 | std::vector mExtraFcst; 31 | int mX; 32 | int mY; 33 | }; 34 | #endif 35 | -------------------------------------------------------------------------------- /src/client/Calibrator/Regression.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_REGRESSION_H 2 | #define CALIBRATOR_REGRESSION_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Applies polynomial regression to forecasts 8 | class CalibratorRegression : public Calibrator { 9 | public: 10 | CalibratorRegression(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "regression";}; 13 | Parameters train(const std::vector& iData) const; 14 | private: 15 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 16 | int mOrder; 17 | bool mIntercept; 18 | std::vector mVariables; 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /src/client/Calibrator/Sort.cpp: -------------------------------------------------------------------------------- 1 | #include "Sort.h" 2 | #include 3 | #include "../Util.h" 4 | #include "../File/File.h" 5 | #include "../ParameterFile/ParameterFile.h" 6 | #include "../Downscaler/Pressure.h" 7 | CalibratorSort::CalibratorSort(const Variable& iVariable, const Options& iOptions) : 8 | Calibrator(iVariable, iOptions) { 9 | iOptions.check(); 10 | } 11 | bool CalibratorSort::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 12 | int nLat = iFile.getNumY(); 13 | int nLon = iFile.getNumX(); 14 | int nEns = iFile.getNumEns(); 15 | int nTime = iFile.getNumTime(); 16 | vec2 lats = iFile.getLats(); 17 | vec2 lons = iFile.getLons(); 18 | vec2 elevs = iFile.getElevs(); 19 | 20 | // Loop over offsets 21 | for(int t = 0; t < nTime; t++) { 22 | Field& field = *iFile.getField(mVariable, t); 23 | 24 | #pragma omp parallel for 25 | for(int i = 0; i < nLat; i++) { 26 | for(int j = 0; j < nLon; j++) { 27 | std::vector values = field(i,j); 28 | std::sort(values.begin(), values.end()); 29 | 30 | // Create a new array with all the missing values 31 | // at the end 32 | std::vector missingLast(nEns, Util::MV); 33 | int counter = 0; 34 | for(int e = 0; e < nEns; e++) { 35 | if(Util::isValid(values[e])) { 36 | missingLast[counter] = values[e]; 37 | counter++; 38 | } 39 | } 40 | for(int e = 0; e < nEns; e++) { 41 | field(i,j,e) = missingLast[e]; 42 | } 43 | } 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | std::string CalibratorSort::description(bool full) { 50 | std::stringstream ss; 51 | if(full) 52 | ss << Util::formatDescription("-c sort", "Sorts ensemble members from lowest to highest. Any missing members are placed last.") << std::endl; 53 | else 54 | ss << Util::formatDescription("-c sort", "Sorts ensemble members from lowest to highest") << std::endl; 55 | return ss.str(); 56 | } 57 | -------------------------------------------------------------------------------- /src/client/Calibrator/Sort.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_SORT_H 2 | #define CALIBRATOR_SORT_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Applies polynomial regression to forecasts 8 | class CalibratorSort : public Calibrator { 9 | public: 10 | CalibratorSort(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "sort";}; 13 | bool requiresParameterFile() const { return false;}; 14 | private: 15 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /src/client/Calibrator/Temperature.cpp: -------------------------------------------------------------------------------- 1 | #include "Calibrator.h" 2 | #include 3 | #include 4 | #include 5 | #include "../Util.h" 6 | -------------------------------------------------------------------------------- /src/client/Calibrator/Temperature.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_TEMPERATURE_H 2 | #define CALIBRATOR_TEMPERATURE_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | 6 | class CalibratorTemperature : public Calibrator { 7 | public: 8 | CalibratorTemperature(const Variable& iVariable, const Options& iOptions); 9 | static std::string description(bool full=true); 10 | std::string name() const {return "temperature";}; 11 | private: 12 | bool calibrateCore(const File& iFile, const ParameterFile* iParameterFile) const; 13 | }; 14 | #endif 15 | -------------------------------------------------------------------------------- /src/client/Calibrator/Threshold.cpp: -------------------------------------------------------------------------------- 1 | #include "Threshold.h" 2 | #include 3 | #include "../Util.h" 4 | #include "../File/File.h" 5 | #include "../ParameterFile/ParameterFile.h" 6 | #include "../Downscaler/Pressure.h" 7 | CalibratorThreshold::CalibratorThreshold(const Variable& iVariable, const Options& iOptions) : 8 | Calibrator(iVariable, iOptions) { 9 | iOptions.getRequiredValues("thresholds", mThresholds); 10 | iOptions.getRequiredValues("values", mValues); 11 | if(!iOptions.getValues("equals", mEquals)) { 12 | mEquals.resize(mThresholds.size(), 0); 13 | } 14 | if(mValues.size() != mThresholds.size() + 1) { 15 | std::stringstream ss; 16 | ss << "Length of 'values' must be one longer than the length of 'thresholds'"; 17 | Util::error(ss.str()); 18 | } 19 | if(mEquals.size() != mThresholds.size()) { 20 | std::stringstream ss; 21 | ss << "Length of 'equals' must be the same as the length of 'thresholds'"; 22 | Util::error(ss.str()); 23 | } 24 | iOptions.check(); 25 | } 26 | bool CalibratorThreshold::calibrateCore(File& iFile, const ParameterFile* iParameterFile) const { 27 | int nEns = iFile.getNumEns(); 28 | int nTime = iFile.getNumTime(); 29 | int nX = iFile.getNumX(); 30 | int nY = iFile.getNumY(); 31 | vec2 elevs = iFile.getElevs(); 32 | int nThresholds = mThresholds.size(); 33 | 34 | for(int t = 0; t < nTime; t++) { 35 | const FieldPtr field = iFile.getField(mVariable, t); 36 | for(int e = 0; e < nEns; e++) { 37 | for(int y = 0; y < nY; y++) { 38 | for(int x = 0; x < nX; x++) { 39 | float value = (*field)(y, x, e); 40 | if(Util::isValid(value)) { 41 | (*field)(y, x, e) = mValues[nThresholds]; 42 | for(int p = 0; p < nThresholds; p++) { 43 | if(value < mThresholds[p]) { 44 | (*field)(y, x, e) = mValues[p]; 45 | break; 46 | } 47 | else if(value == mThresholds[p] && mEquals[p] == 1) { 48 | (*field)(y, x, e) = mValues[p]; 49 | break; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | return true; 58 | } 59 | 60 | std::string CalibratorThreshold::description(bool full) { 61 | std::stringstream ss; 62 | ss << Util::formatDescription("-c threshold", "Apply thresholding to the field, converting ranges into specified values.") << std::endl; 63 | if(full) { 64 | ss << Util::formatDescription(" thresholds=required", "List of thresholds (x1, x2, x3, ..., xn). Must be sorted.") << std::endl; 65 | ss << Util::formatDescription(" values=required", "List of values to set the ranges to. The first value is used for field values below the first threshold. Must have length one longer than threhsolds") << std::endl; 66 | ss << Util::formatDescription(" equals=undef", "Should the intervals include the upper threshold? One number for each threshold. 1 means yes, 0 means no.") << std::endl; 67 | } 68 | return ss.str(); 69 | } 70 | -------------------------------------------------------------------------------- /src/client/Calibrator/Threshold.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_THRESHOLD_H 2 | #define CALIBRATOR_THRESHOLD_H 3 | #include "Calibrator.h" 4 | class ParameterFile; 5 | class Parameters; 6 | 7 | //! Thresholds values from parameter file into gridpoints 8 | class CalibratorThreshold : public Calibrator { 9 | public: 10 | CalibratorThreshold(const Variable& iVariable, const Options& iOptions); 11 | static std::string description(bool full=true); 12 | std::string name() const {return "threshold";}; 13 | bool requiresParameterFile() const { return false;}; 14 | private: 15 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 16 | std::vector mThresholds; 17 | std::vector mValues; 18 | std::vector mEquals; 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /src/client/Calibrator/WindDirection.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_WIND_DIRECTION_H 2 | #define CALIBRATOR_WIND_DIRECTION_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | class ParameterFile; 7 | class Parameters; 8 | 9 | //! Multiply a variable by a factor based on the wind-direction 10 | //! factor = a + b*sin(dir) + c*cos(dir) + d*sin(2*dir) + e*cos(2*dir) 11 | //! + f*sin(3*dir) + g*cos(3*dir) + h*sin(4*dir) + i*cos(4*dir) 12 | class CalibratorWindDirection : public Calibrator { 13 | public: 14 | CalibratorWindDirection(const Variable& iVariable, const Options& iOptions); 15 | static std::string description(bool full=true); 16 | std::string name() const {return "windDirection";}; 17 | //! Get multiplication factor for given wind direction 18 | //! @param iWindDirection in degrees, meteorological wind direction (0 degrees is from North) 19 | static float getFactor(float iWindDirection, const Parameters& iPar); 20 | private: 21 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 22 | std::string mDirectionVariable; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /src/client/Calibrator/Window.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_WINDOW_H 2 | #define CALIBRATOR_WINDOW_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | 6 | // Applies a statistical operator to values within a temporal window 7 | class CalibratorWindow : public Calibrator { 8 | public: 9 | CalibratorWindow(const Variable& iVariable, const Options& iOptions); 10 | static std::string description(bool full=true); 11 | std::string name() const {return "window";}; 12 | bool requiresParameterFile() const { return false;}; 13 | private: 14 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 15 | int mLength; 16 | Util::StatType mStatType; 17 | float mQuantile; 18 | bool mBefore; 19 | bool mKeepMissing; 20 | std::vector mWeights; 21 | enum EdgePolicy { 22 | EdgePolicyCompute = 0, 23 | EdgePolicyMissing = 10 24 | }; 25 | EdgePolicy mEdgePolicy; 26 | }; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/client/Calibrator/Zaga.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_ZAGA_H 2 | #define CALIBRATOR_ZAGA_H 3 | #include "Calibrator.h" 4 | #include "../Variable.h" 5 | #include 6 | #include 7 | 8 | class ParameterFile; 9 | class Parameters; 10 | 11 | //! Ensemble calibration using zero-adjusted gamma distribution. Its predictors are: 12 | //! - ensemble mean 13 | //! - ensemble fraction 14 | //! Designed for precip 15 | class CalibratorZaga : public Calibrator { 16 | public: 17 | CalibratorZaga(const Variable& iVariable, const Options& iOptions); 18 | //! Get probability mass at 0 mm (i.e probability of no precipitation) 19 | //! If any input has missing values, the end result is missing 20 | static float getP0(float iEnsMean, float iEnsFrac, const Parameters& iParameters); 21 | //! Get Precipitation amount corresponding to quantile 22 | //! If any input has missing values, the end result is missing 23 | static float getInvCdf(float iQuantile, float iEnsMean, float iEnsFrac, const Parameters& iParameters); 24 | //! Get Precipitation amount corresponding to quantile. If any input has missing values, or 25 | //! iEnsMean < 0 or iEnsFrac is not in [0,1], a missing value is returned. 26 | static float getCdf(float iThreshold, float iEnsMean, float iEnsFrac, const Parameters& iParameters); 27 | static float getPdf(float iThreshold, float iEnsMean, float iEnsFrac, const Parameters& iParameters); 28 | 29 | static std::string description(bool full=true); 30 | std::string name() const {return "zaga";}; 31 | Parameters train(const std::vector& iData) const; 32 | private: 33 | bool calibrateCore(File& iFile, const ParameterFile* iParameterFile) const; 34 | static float logLikelihood(float obs, float iEnsMean, float iEnsFrac, const Parameters& iParameters); 35 | static double my_f(const gsl_vector *v, void *params); 36 | // static void my_df(const gsl_vector *v, void *params, gsl_vector *df); 37 | // static void my_fdf(const gsl_vector *x, void *params, double *f, gsl_vector *df); 38 | //! What precip threshold should be used to count members with no precip? 39 | float mFracThreshold; 40 | int mNeighbourhoodSize; 41 | float mPopThreshold; 42 | float mPrecipLowQuantile; 43 | float mPrecipMiddleQuantile; 44 | float mPrecipHighQuantile; 45 | float mMaxEnsMean; 46 | bool m6h; 47 | float mLogLikelihoodTolerance; 48 | std::string mPopVariable; 49 | std::string mLowVariable; 50 | std::string mMiddleVariable; 51 | std::string mHighVariable; 52 | }; 53 | #endif 54 | -------------------------------------------------------------------------------- /src/client/Calibrator/devel/Wind.cpp: -------------------------------------------------------------------------------- 1 | #include "Wind.h" 2 | #include "../Util.h" 3 | #include "../File/File.h" 4 | 5 | CalibratorWind::CalibratorWind(const ParameterFileRegion& iParameterFile) : 6 | Calibrator(), 7 | mParameterFile(iParameterFile) { 8 | } 9 | void CalibratorWind::calibrateCore(File& iFile) const { 10 | int nLat = iFile.getNumLat(); 11 | int nLon = iFile.getNumLon(); 12 | int nEns = iFile.getNumEns(); 13 | int nTime = iFile.getNumTime(); 14 | 15 | // Initialize calibrated fields in the output file 16 | std::vector windCals(nTime); 17 | for(int t = 0; t < nTime; t++) { 18 | windCals[t] = iFile.getEmptyField(); 19 | } 20 | 21 | // Loop over offsets 22 | for(int t = 0; t < nTime; t++) { 23 | FieldPtr u = iFile.getField(Variable::U, t); 24 | FieldPtr v = iFile.getField(Variable::V, t); 25 | 26 | Field& windCal = *windCals[t]; 27 | 28 | // Parallelizable 29 | #pragma omp parallel for 30 | for(int i = 0; i < nLat; i++) { 31 | for(int j = 0; j < nLon; j++) { 32 | Parameters parameters = mParameterFile.getParameters(i, j, t); 33 | for(int e = 0; e < nEns; e++) { 34 | float currU = (*u)[i][j][e]; 35 | float currV = (*v)[i][j][e]; 36 | if(Util::isValid(currU) && Util::isValid(currV)) { 37 | float dir = getDir(currU, currV); 38 | float speed = getSpeed(currU, currV); 39 | float calSpeed = calibrateLocal(speed, dir, parameters); 40 | windCal[i][j][e] = calSpeed; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | float CalibratorWind::calibrateLocal(float iSpeed, float iDir, const Parameters& iParameters) const { 49 | return 16.4; 50 | } 51 | 52 | float CalibratorWind::getDir(float iU, float iV) { 53 | return 0; 54 | } 55 | float CalibratorWind::getSpeed(float iU, float iV) { 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /src/client/Calibrator/devel/Wind.h: -------------------------------------------------------------------------------- 1 | #ifndef CALIBRATOR_WIND_H 2 | #define CALIBRATOR_WIND_H 3 | #include "Calibrator.h" 4 | #include "../ParameterFile.h" 5 | 6 | class CalibratorWind : public Calibrator { 7 | public: 8 | CalibratorWind(const ParameterFileRegion& iParameterFileRegion); 9 | private: 10 | void calibrateCore(File& iFile) const; 11 | static float getDir(float iU, float iV); 12 | static float getSpeed(float iU, float iV); 13 | float calibrateLocal(float iSpeed, float iDir, const Parameters& iParameters) const; 14 | ParameterFileRegion mParameterFile; 15 | }; 16 | #endif 17 | -------------------------------------------------------------------------------- /src/client/Downscaler/Bilinear.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_BILINEAR_H 2 | #define DOWNSCALER_BILINEAR_H 3 | #include 4 | #include "Downscaler.h" 5 | #include "../Variable.h" 6 | #include "../Util.h" 7 | #include "../Field.h" 8 | class File; 9 | class DownscalerBilinear : public Downscaler { 10 | public: 11 | DownscalerBilinear(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 12 | 13 | //! Bilinearly interpolate four irregularly placed points: 14 | //! v1 v3 15 | //! v0 v2 16 | //! with corresponding x,y coordinates. 17 | //! @param x Interpolate to this x-coordinate 18 | //! @param y Interpolate to this y-coordinate 19 | //! Two or more points cannot be colocated 20 | static float bilinear(float x, float y, float x0, float x1, float x2, float x3, float y0, float y1, float y2, float y3, float v0, float v1, float v2, float v3); 21 | 22 | // Interpolate a whole field 23 | static void downscaleField(const Field& iInput, Field& iOutput, 24 | const vec2& iInputLats, const vec2& iInputLons, 25 | const vec2& iOutputLats, const vec2& iOutputLons, 26 | const vec2Int& nearestI, const vec2Int& nearestJ); 27 | 28 | // Interpolate a whole vec2 29 | static vec2 downscaleVec(const vec2& iInput, 30 | const vec2& iInputLats, const vec2& iInputLons, 31 | const vec2& iOutputLats, const vec2& iOutputLons, 32 | const vec2Int& nearestI, const vec2Int& nearestJ); 33 | 34 | //! Find which I/J coordinates surround a lookup point 35 | //! Returns false if the lookup point is outside the grid. In this case, I1, I2, J1, J2 values 36 | //! cannot be used. 37 | static bool findCoords(float iLat, float iLon, const vec2& iLats, const vec2& iLons, int I, int J, int& I1, int& J1, int& I2, int& J2); 38 | 39 | static std::string description(bool full=true); 40 | std::string name() const {return "bilinear";}; 41 | 42 | static bool calcParallelogram(float x, float y, float X1, float X2, float X3, float X4, float Y1, float Y2, float Y3, float Y4, float &t, float &s); 43 | static bool calcGeneral(float x, float y, float x0, float x1, float x2, float x3, float y0, float y1, float y2, float y3, float &t, float &s); 44 | static bool calcST(float x, float y, float x0, float x1, float x2, float x3, float y0, float y1, float y2, float y3, float &t, float &s); 45 | static bool getI(int I, bool isAbove, bool Iinc, int& I1, int& I2); 46 | static bool getJ(int J, bool isAbove, bool Jinc, int& J1, int& J2); 47 | private: 48 | void downscaleCore(const File& iInput, File& iOutput) const; 49 | static float bilinearLimit; 50 | }; 51 | #endif 52 | -------------------------------------------------------------------------------- /src/client/Downscaler/Bypass.cpp: -------------------------------------------------------------------------------- 1 | #include "Bypass.h" 2 | #include "../File/File.h" 3 | #include "../Util.h" 4 | #include 5 | 6 | DownscalerBypass::DownscalerBypass(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions) : 7 | Downscaler(iInputVariable, iOutputVariable, iOptions) { 8 | iOptions.check(); 9 | } 10 | 11 | void DownscalerBypass::downscaleCore(const File& iInput, File& iOutput) const { 12 | if(!iOutput.hasVariable(mOutputVariable)) { 13 | // int nTime = iInput.getNumTime(); 14 | // for(int t = 0; t < nTime; t++) { 15 | iOutput.initNewVariable(mOutputVariable); 16 | // } 17 | } 18 | } 19 | 20 | std::string DownscalerBypass::description(bool full) { 21 | std::stringstream ss; 22 | ss << Util::formatDescription("-d bypass", "Skip downscaling, used when diagnosed fields") << std::endl; 23 | return ss.str(); 24 | } 25 | -------------------------------------------------------------------------------- /src/client/Downscaler/Bypass.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_BYPASS_H 2 | #define DOWNSCALER_BYPASS_H 3 | #include "Downscaler.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | typedef std::vector > vec2Int; 7 | 8 | //! Skip the downscaling stage 9 | class DownscalerBypass : public Downscaler { 10 | public: 11 | DownscalerBypass(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 12 | static std::string description(bool full=true); 13 | std::string name() const {return "bypass";}; 14 | private: 15 | void downscaleCore(const File& iInput, File& iOutput) const; 16 | }; 17 | #endif 18 | -------------------------------------------------------------------------------- /src/client/Downscaler/Coastal.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_COASTAL_H 2 | #define DOWNSCALER_COASTAL_H 3 | #include "Downscaler.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | typedef std::vector > vec2Int; 7 | class DownscalerCoastal : public Downscaler { 8 | public: 9 | //! Downscale the specified variable 10 | DownscalerCoastal(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 11 | std::vector getSearchRadii() const; 12 | float getMinLafDiff() const; 13 | float getMinGradient() const; 14 | float getMaxGradient() const; 15 | static std::string description(bool full=true); 16 | std::string name() const {return "coastal";}; 17 | private: 18 | void downscaleCore(const File& iInput, File& iOutput) const; 19 | std::vector mSearchRadii; 20 | std::vector mWeights; 21 | int mLafRadius; 22 | float mMinLafDiff; // Minimum elevation difference within neighbourhood to use gradient 23 | float mMinGradient; 24 | float mMaxGradient; 25 | float mElevGradient; 26 | }; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/client/Downscaler/Downscaler.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_H 2 | #define DOWNSCALER_H 3 | #include 4 | #include 5 | #include "../Options.h" 6 | #include "../Variable.h" 7 | #include "../Scheme.h" 8 | #include "../Uuid.h" 9 | class File; 10 | typedef std::vector > vec2Int; 11 | 12 | //! Converts fields from one grid to another 13 | class Downscaler : public Scheme { 14 | public: 15 | Downscaler(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 16 | virtual ~Downscaler() {}; 17 | bool downscale(const File& iInput, File& iOutput) const; 18 | static Downscaler* getScheme(std::string iName, const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 19 | virtual std::string name() const = 0; 20 | 21 | //! Create a nearest-neighbour map. For each grid point in iTo, find the index into the grid 22 | //! in iFrom of the nearest neighbour. Uses a 2-d BST for search speedup. 23 | //! @param iI I-indices of nearest point. Set to Util::MV if no nearest neighbour. 24 | //! @param iJ J-indices of nearest point. Set to Util::MV if no nearest neighbour. 25 | static void getNearestNeighbour(const File& iFrom, const File& iTo, vec2Int& iI, vec2Int& iJ); 26 | 27 | //! Reference realization of getNearestNeighbour. Uses a brute force method by checking every 28 | //! neighbour. 29 | static void getNearestNeighbourBruteForce(const File& iFrom, const File& iTo, vec2Int& iI, vec2Int& iJ); 30 | 31 | //! Find the index into the lat/lon arrays in iFrom that is nearest to the point 32 | //! defined by iLon,iLat 33 | //! @param iI I-index of nearest point. Set to Util::MV if no nearest neighbour. 34 | //! @param iJ J-index of nearest point. Set to Util::MV if no nearest neighbour. 35 | static void getNearestNeighbourBruteForce(const File& iFrom, float iLon, float iLat, int& iI, int &iJ); 36 | 37 | // @param full Give full descriptions, including options 38 | static std::string getDescriptions(bool full=true); 39 | 40 | //! Clears nearest neighbour cache 41 | static void clearCache(); 42 | protected: 43 | virtual void downscaleCore(const File& iInput, File& iOutput) const = 0; 44 | Variable mInputVariable; 45 | Variable mOutputVariable; 46 | private: 47 | // Cache calls to nearest neighbour 48 | //! Is the nearest neighbours in @param iFrom for each point in @param iTo already computed? 49 | static bool isCached(const File& iFrom, const File& iTo); 50 | static void addToCache(const File& iFrom, const File& iTo, vec2Int iI, vec2Int iJ); 51 | static bool getFromCache(const File& iFrom, const File& iTo, vec2Int& iI, vec2Int& iJ); 52 | static std::map > > mNeighbourCache; 53 | }; 54 | #include "NearestNeighbour.h" 55 | #include "Gradient.h" 56 | #include "Bilinear.h" 57 | #include "Pressure.h" 58 | #include "Smart.h" 59 | #include "Upscale.h" 60 | #include "Bypass.h" 61 | #endif 62 | -------------------------------------------------------------------------------- /src/client/Downscaler/Gradient.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_GRADIENT2_H 2 | #define DOWNSCALER_GRADIENT2_H 3 | #include "Downscaler.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | #include "../Field.h" 7 | typedef std::vector > vec2Int; 8 | //! Adjust the value of the nearest neighbour (nn), based on the gradient in a neighbourhood 9 | //! surrounding the nearest neighbour, and the elevation difference to the lookup point (p): 10 | //! T(p) = T(nn) + gradient*(elev(p) - elev(nn)) 11 | //! If the variable is log transformed, then use: 12 | //! T(p) = T(nn) * exp(gradient*(elev(p) - elev(nn))) 13 | //! Uses nearest neighbour when the lookup location does not have an elevation 14 | class DownscalerGradient : public Downscaler { 15 | public: 16 | //! Downscale the specified variable 17 | DownscalerGradient(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 18 | ~DownscalerGradient(); 19 | static std::string description(bool full=true); 20 | std::string name() const {return "gradient";}; 21 | private: 22 | void downscaleCore(const File& iInput, File& iOutput) const; 23 | int mElevRadius; 24 | float mConstantElevGradient; 25 | float mMinElevDiff; // Minimum elevation difference within neighbourhood to use gradient 26 | bool mLogTransform; 27 | float mMinElevGradient; 28 | float mMaxElevGradient; 29 | float mMinLafGradient; 30 | float mMaxLafGradient; 31 | float mMinLafForElevGradient; 32 | int mMinNumPoints; 33 | float mMinFracSeaPoints; 34 | float mDefaultElevGradient; 35 | int mLafRadius; 36 | std::vector mLafSearchRadii; 37 | std::vector mLafWeights; 38 | float mMinLafDiff; // Minimum elevation difference within neighbourhood to use gradient 39 | bool mAverageNeighbourhood; 40 | Downscaler* mDownscaler; 41 | std::string mDownscalerName; 42 | std::string mSaveGradient; 43 | std::string mElevGradientVariableName; 44 | bool mLimit; 45 | float calcLafGradient(int i, int j, int e, int Icenter, int Jcenter, const Field& iField, const vec2& iLafs, const vec2& iElevs, float iElevGradient) const; 46 | float calcElevGradient(int i, int j, int e, int Icenter, int Jcenter, const Field& iField, const Field& iGfield, const vec2& iElevs, const vec2& iLafs) const; 47 | bool calcNeighbourhoodMean(const Field& iField, const vec2& iElevs, int i, int j, int e, int Icenter, int Jcenter, int iRadius, float& iValueMean, float& iElevMean) const; 48 | bool calcBaseValues(const Field& iField, const vec2& iElevs, const vec2& iLafs, int i, int j, int e, int Icenter, int Jcenter, int iRadius, float& iValueMean, float& iElevMean, float& iLafMean) const; 49 | // float calcLafGradient(float iLaf, int iIcenter, int iJcenter, int e, vec2 iLafs, vec2 iElevs, const Field& iField, const File& iInput) const; 50 | // float calcElevGradient(float iLaf, int iIcenter, int iJcenter, int e, vec2 iLafs, vec2 iElevs, const Field& iField, const File& iInput) const; 51 | }; 52 | #endif 53 | -------------------------------------------------------------------------------- /src/client/Downscaler/NearestNeighbour.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_NEAREST_NEIGHBOUR_H 2 | #define DOWNSCALER_NEAREST_NEIGHBOUR_H 3 | #include 4 | #include "Downscaler.h" 5 | #include "../Variable.h" 6 | #include "../Util.h" 7 | #include "../Field.h" 8 | class File; 9 | class DownscalerNearestNeighbour : public Downscaler { 10 | public: 11 | DownscalerNearestNeighbour(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 12 | 13 | // Interpolate a whole field 14 | static void downscaleField(const Field& iInput, Field& iOutput, 15 | const vec2& iInputLats, const vec2& iInputLons, 16 | const vec2& iOutputLats, const vec2& iOutputLons, 17 | const vec2Int& nearestI, const vec2Int& nearestJ); 18 | 19 | // Interpolate a whole vec2 20 | static vec2 downscaleVec(const vec2& iInput, 21 | const vec2& iInputLats, const vec2& iInputLons, 22 | const vec2& iOutputLats, const vec2& iOutputLons, 23 | const vec2Int& nearestI, const vec2Int& nearestJ); 24 | 25 | static std::string description(bool full=true); 26 | std::string name() const {return "nearestNeighbour";}; 27 | private: 28 | void downscaleCore(const File& iInput, File& iOutput) const; 29 | }; 30 | #endif 31 | -------------------------------------------------------------------------------- /src/client/Downscaler/Pressure.cpp: -------------------------------------------------------------------------------- 1 | #include "Pressure.h" 2 | #include "../File/File.h" 3 | #include "../Util.h" 4 | #include 5 | const float DownscalerPressure::mConstant = -1.21e-4; 6 | 7 | DownscalerPressure::DownscalerPressure(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions) : 8 | Downscaler(iInputVariable, iOutputVariable, iOptions) { 9 | iOptions.check(); 10 | } 11 | 12 | void DownscalerPressure::downscaleCore(const File& iInput, File& iOutput) const { 13 | int nLat = iOutput.getNumY(); 14 | int nLon = iOutput.getNumX(); 15 | int nEns = iOutput.getNumEns(); 16 | int nTime = iInput.getNumTime(); 17 | 18 | vec2 ilats = iInput.getLats(); 19 | vec2 ilons = iInput.getLons(); 20 | vec2 ielevs = iInput.getElevs(); 21 | vec2 olats = iOutput.getLats(); 22 | vec2 olons = iOutput.getLons(); 23 | vec2 oelevs = iOutput.getElevs(); 24 | 25 | // Get nearest neighbour 26 | vec2Int nearestI, nearestJ; 27 | getNearestNeighbour(iInput, iOutput, nearestI, nearestJ); 28 | 29 | for(int t = 0; t < nTime; t++) { 30 | Field& ifield = *iInput.getField(mInputVariable, t); 31 | Field& ofield = *iOutput.getField(mOutputVariable, t, true); 32 | 33 | #pragma omp parallel for 34 | for(int i = 0; i < nLat; i++) { 35 | for(int j = 0; j < nLon; j++) { 36 | int Icenter = nearestI[i][j]; 37 | int Jcenter = nearestJ[i][j]; 38 | assert(Icenter < ielevs.size()); 39 | assert(Jcenter < ielevs[Icenter].size()); 40 | for(int e = 0; e < nEns; e++) { 41 | float currElev = oelevs[i][j]; 42 | float nearestElev = ielevs[Icenter][Jcenter]; 43 | if(!Util::isValid(currElev) || !Util::isValid(nearestElev)) { 44 | // Can't adjust if we don't have an elevation, use nearest neighbour 45 | ofield(i,j,e) = ifield(Icenter,Jcenter,e); 46 | } 47 | else { 48 | float nearestPressure = ifield(Icenter,Jcenter,e); 49 | float currPressure = calcPressure(nearestElev, nearestPressure, currElev); 50 | ofield(i,j,e) = currPressure; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | std::string DownscalerPressure::description(bool full) { 58 | std::stringstream ss; 59 | if(full) 60 | ss << Util::formatDescription("-d pressure", "Adjusts the pressure of the nearest neighbour based on the elevation difference and a standard atmosphere.") << std::endl; 61 | else 62 | ss << Util::formatDescription("-d pressure", "Adjusts the pressure based on the elevation differences") << std::endl; 63 | return ss.str(); 64 | } 65 | 66 | float DownscalerPressure::calcPressure(float iElev0, float iPressure0, float iElev1) { 67 | if(Util::isValid(iElev0) && Util::isValid(iPressure0) && Util::isValid(iElev1)) { 68 | float dElev = iElev1 - iElev0; 69 | return iPressure0 * exp(mConstant * (dElev)); 70 | } 71 | else { 72 | return Util::MV; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/client/Downscaler/Pressure.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_PRESSURE_H 2 | #define DOWNSCALER_PRESSURE_H 3 | #include "Downscaler.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | typedef std::vector > vec2Int; 7 | //! Adjust the value of the nearest neighbour (nn), based on a standard atmosphere and the elevation 8 | //! difference to the lookup point (p): 9 | //! P(p) = P(nn) * exp(-1.21e-4*(elev(p) - elev(nn)) 10 | //! Uses the pressure at the nearest neighbour when the lookup location does not have an elevation 11 | class DownscalerPressure : public Downscaler { 12 | public: 13 | DownscalerPressure(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 14 | static std::string description(bool full=true); 15 | std::string name() const {return "pressure";}; 16 | static float calcPressure(float iElev0, float iPressure0, float iElev1); 17 | private: 18 | void downscaleCore(const File& iInput, File& iOutput) const; 19 | static const float mConstant; 20 | }; 21 | #endif 22 | -------------------------------------------------------------------------------- /src/client/Downscaler/Smart.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_SMART_H 2 | #define DOWNSCALER_SMART_H 3 | #include "Downscaler.h" 4 | #include "../Variable.h" 5 | #include "../Util.h" 6 | typedef std::vector > vec2Int; 7 | typedef std::vector > > vec3Int; // lat, lon, neighbour index 8 | 9 | //! Downscale using neighbours at similar elevation 10 | class DownscalerSmart : public Downscaler { 11 | public: 12 | //! Downscale the specified variable 13 | DownscalerSmart(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 14 | static std::string description(bool full=true); 15 | std::string name() const {return "smart";}; 16 | 17 | //! Method may return fewer than num smart neighbours 18 | void getSmartNeighbours(const File& iFrom, const File& iTo, vec3Int& iI, vec3Int& iJ) const; 19 | static int getNumSearchPoints(int iSearchRadius) ; 20 | int getNumSearchPoints() const; 21 | private: 22 | void downscaleCore(const File& iInput, File& iOutput) const; 23 | int mRadius; 24 | int mNum; 25 | float mMinElevDiff; 26 | }; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/client/Downscaler/Upscale.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNSCALER_UPSCALE_H 2 | #define DOWNSCALER_UPSCALE_H 3 | #include 4 | #include "Downscaler.h" 5 | #include "../Variable.h" 6 | #include "../Util.h" 7 | #include "../Field.h" 8 | class File; 9 | class DownscalerUpscale : public Downscaler { 10 | public: 11 | DownscalerUpscale(const Variable& iInputVariable, const Variable& iOutputVariable, const Options& iOptions); 12 | 13 | static std::string description(bool full=true); 14 | std::string name() const {return "upscale";}; 15 | private: 16 | void downscaleCore(const File& iInput, File& iOutput) const; 17 | Util::StatType mStatType; 18 | float mQuantile; 19 | }; 20 | #endif 21 | -------------------------------------------------------------------------------- /src/client/Driver/.vimrc: -------------------------------------------------------------------------------- 1 | ../.vimrc -------------------------------------------------------------------------------- /src/client/Field.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Field.h" 3 | Field::Field(int nY, int nX, int nEns, float iFillValue) : 4 | mNY(nY), mNX(nX), mNEns(nEns) { 5 | if(Util::isValid(nY) && Util::isValid(nX) && Util::isValid(nEns) 6 | && nY >= 0 && nX >= 0 && nEns >= 0) { 7 | mValues.resize(nY*nX*nEns, iFillValue); 8 | } 9 | else { 10 | std::stringstream ss; 11 | ss << "Cannot create field of size [" << nY << "," << nX << "," << nEns << "]"; 12 | Util::error(ss.str()); 13 | } 14 | } 15 | 16 | std::vector Field::operator()(unsigned int y, unsigned int x) const { 17 | std::vector values(mValues.begin()+getIndex(y, x, 0), mValues.begin()+getIndex(y, x, mNEns-1)+1); 18 | return values; 19 | } 20 | 21 | int Field::getNumY() const { 22 | return mNY; 23 | } 24 | int Field::getNumX() const { 25 | return mNX; 26 | } 27 | int Field::getNumEns() const { 28 | return mNEns; 29 | } 30 | 31 | bool Field::operator==(const Field& iField) const { 32 | return mValues == iField.mValues; 33 | } 34 | bool Field::operator!=(const Field& iField) const { 35 | return mValues != iField.mValues; 36 | } 37 | -------------------------------------------------------------------------------- /src/client/Field.h: -------------------------------------------------------------------------------- 1 | #ifndef FIELD_H 2 | #define FIELD_H 3 | #include 4 | #include 5 | #include "Util.h" 6 | 7 | //! Encapsulates gridded data in 3 dimensions: latitude, longitude, ensemble member. 8 | //! Latitude generally represents the north-south direction and longitude the east-west, but the 9 | //! grid does not necessarily need to follow a lat/lon grid. Any 2D grid will do. 10 | // TODO: Rename latitude to x and longitude to y, as this is more generally correct. 11 | class Field { 12 | public: 13 | //! Initialize 3D field 14 | //! @param nLat number of latitudes 15 | //! @param nLon number of longitudes 16 | //! @param nLon number of ensemble members 17 | //! @param iFillValue initialize all values in field with this 18 | Field(int nY, int nX, int nEns, float iFillValue=Util::MV); 19 | 20 | //! Access to data. Inlined for improved performance on some systems 21 | //! @param i latitude index 22 | //! @param j longitude index 23 | //! @param k ensemble index 24 | //! @return data at specified coordinate 25 | float & operator()(unsigned int y, unsigned int x, unsigned int e) { 26 | int index = getIndex(y, x, e); 27 | return mValues[index]; 28 | }; 29 | float const& operator()(unsigned int y, unsigned int x, unsigned int e) const { 30 | int index = getIndex(y, x, e); 31 | return mValues[index]; 32 | }; 33 | 34 | //! Access to an ensemble for a specific grid point 35 | //! @param y y-axis index 36 | //! @param x x-axis index 37 | //! @return ensemble of values 38 | std::vector operator()(unsigned int y, unsigned int x) const; 39 | 40 | //! Are all values (for all lat/lon/ens) in fields identical? 41 | bool operator==(const Field& iField) const; 42 | bool operator!=(const Field& iField) const; 43 | 44 | //! Number of gridpoints in the y direction 45 | int getNumY() const; 46 | 47 | //! Number of gridpoints in the x direction 48 | int getNumX() const; 49 | 50 | //! Number of ensemble members 51 | int getNumEns() const; 52 | private: 53 | //! Data values stored in a flat array. Index for ensemble changes fastest. 54 | std::vector mValues; 55 | int mNY; 56 | int mNX; 57 | int mNEns; 58 | //! Index into flat array that corresponds to coordinate. Inlined for performance reasons 59 | int getIndex(unsigned int y, unsigned int x, unsigned int e) const { 60 | // Don't use an if statement, since this seems to be slow 61 | assert(y < mNY && x < mNX && e < mNEns); 62 | int index = e + x*mNEns + y*mNX*mNEns; 63 | return index; 64 | }; 65 | }; 66 | typedef boost::shared_ptr FieldPtr; 67 | #endif 68 | -------------------------------------------------------------------------------- /src/client/File/Fake.cpp: -------------------------------------------------------------------------------- 1 | #include "Fake.h" 2 | FileFake::FileFake(const Options& iOptions) : 3 | File("", iOptions) { 4 | int nLat = 10; 5 | int nLon = 10; 6 | mNEns = 2; 7 | int nTime = 10; 8 | 9 | iOptions.getValue("nLat", nLat); 10 | iOptions.getValue("nLon", nLon); 11 | iOptions.getValue("nEns", mNEns); 12 | iOptions.getValue("nTime", nTime); 13 | if(!Util::isValid(nLat) || nLat <= 0) { 14 | Util::error("FileFake: Invalid number of latitudes"); 15 | } 16 | if(!Util::isValid(nLat) || nLon <= 0) { 17 | Util::error("FileFake: Invalid number of longitudes"); 18 | } 19 | if(!Util::isValid(mNEns) || mNEns <= 0) { 20 | Util::error("FileFake: Invalid number of ensemble members"); 21 | } 22 | if(!Util::isValid(nTime) || nTime <= 0) { 23 | Util::error("FileFake: Invalid number of times"); 24 | } 25 | vec2 lats; 26 | lats.resize(nLat); 27 | for(int i = 0; i < nLat; i++) { 28 | lats[i].resize(nLon); 29 | for(int j = 0; j < nLon; j++) { 30 | lats[i][j] = 50 + 10.0 * i / nLat; 31 | } 32 | } 33 | vec2 lons; 34 | lons.resize(nLat); 35 | for(int i = 0; i < nLat; i++) { 36 | lons[i].resize(nLon); 37 | for(int j = 0; j < nLon; j++) { 38 | lons[i][j] = 0 + 10.0 * j / nLon; 39 | } 40 | } 41 | bool successLats = setLats(lats); 42 | if(!successLats) { 43 | std::stringstream ss; 44 | ss << "Could not set latitudes in " << getFilename(); 45 | Util::error(ss.str()); 46 | } 47 | bool successLons = setLons(lons); 48 | if(!successLons) { 49 | std::stringstream ss; 50 | ss << "Could not set longitudes in " << getFilename(); 51 | Util::error(ss.str()); 52 | } 53 | 54 | vec2 elevs; 55 | elevs.resize(getNumY()); 56 | for(int i = 0; i < nLat; i++) { 57 | elevs[i].resize(nLon); 58 | for(int j = 0; j < nLon; j++) { 59 | elevs[i][j] = 0; 60 | } 61 | } 62 | setElevs(elevs); 63 | mLandFractions.resize(nLat); 64 | for(int i = 0; i < nLat; i++) { 65 | mLandFractions[i].resize(nLon); 66 | for(int j = 0; j < nLon; j++) { 67 | mLandFractions[i][j] = (float) i / nLat / 2 + (float) j / nLon; 68 | } 69 | } 70 | std::vector times(nTime, 0); 71 | for(int i = 0; i < nTime; i++) 72 | times[i] = i; 73 | setTimes(times); 74 | } 75 | 76 | FieldPtr FileFake::getFieldCore(const Variable& iVariable, int iTime) const { 77 | FieldPtr field = getEmptyField(); 78 | 79 | for(int i = 0; i < getNumY(); i++) { 80 | for(int j = 0; j < getNumX(); j++) { 81 | for(int e = 0; e < getNumEns(); e++) { 82 | (*field)(i,j,e) = i + j + e; 83 | } 84 | } 85 | } 86 | return field; 87 | } 88 | void FileFake::writeCore(std::vector iVariables, std::string iMessage) { 89 | Util::warning("Cannot write file using the 'Fake' format"); 90 | } 91 | 92 | bool FileFake::hasVariableCore(const Variable& iVariable) const { 93 | return true; 94 | } 95 | std::string FileFake::description() { 96 | std::stringstream ss; 97 | ss << " type=fake Fake file" << std::endl; 98 | return ss.str(); 99 | } 100 | -------------------------------------------------------------------------------- /src/client/File/Fake.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_FAKE_H 2 | #define FILE_FAKE_H 3 | #include "File.h" 4 | 5 | //! A file type giving fake data, useful for testing 6 | class FileFake : public File { 7 | public: 8 | FileFake(const Options& iOptions); 9 | static std::string description(); 10 | std::string name() const {return "fake";}; 11 | protected: 12 | void writeCore(std::vector iVariables, std::string iMessage=""); 13 | FieldPtr getFieldCore(const Variable& iVariable, int iTime) const; 14 | bool hasVariableCore(const Variable& iVariable) const; 15 | }; 16 | #endif 17 | -------------------------------------------------------------------------------- /src/client/File/NorcomQnh.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_NORCOM_QNH_H 2 | #define FILE_NORCOM_QNH_H 3 | #include 4 | #include 5 | #include "File.h" 6 | #include "../Variable.h" 7 | #include "../Options.h" 8 | 9 | //! Represents a point-based text file suitable for sending minimum QNH values to Norcom. 10 | //! The output file looks as follows: 11 | //! FBNO52 ENNC 290545 // day,hour,minute 12 | //! VALID 290600 - 290900 UTC. 13 | //! ST MIN QNH FANARAAKEN : 0987 HPA 14 | //! ST MIN QNH GAUSTATOPPEN: 0989 HPA 15 | class FileNorcomQnh : public File { 16 | public: 17 | FileNorcomQnh(std::string iFilename, const Options& iOptions); 18 | ~FileNorcomQnh(); 19 | static std::string description(); 20 | std::string name() const {return "norcom";}; 21 | protected: 22 | FieldPtr getFieldCore(const Variable& iVariable, int iTime) const; 23 | void writeCore(std::vector iVariables, std::string iMessage=""); 24 | bool hasVariableCore(const Variable& iVariable) const {return true;}; 25 | int mStartTime; 26 | int mEndTime; 27 | std::vector mNames; 28 | // Create a formated string with format DDHHMM 29 | std::string getNorcomTimeStamp(time_t iUnixTime) const; 30 | }; 31 | #endif 32 | -------------------------------------------------------------------------------- /src/client/File/Point.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_POINT_H 2 | #define FILE_POINT_H 3 | #include 4 | #include 5 | #include "File.h" 6 | #include "../Variable.h" 7 | #include "../Options.h" 8 | 9 | //! Represents a point-based text file. First column is the time (in seconds since 1970) 10 | //! and the second column is the forecast value. 11 | class FilePoint : public File { 12 | public: 13 | FilePoint(std::string iFilename, const Options& iOptions); 14 | ~FilePoint(); 15 | static std::string description(); 16 | std::string name() const {return "point";}; 17 | protected: 18 | FieldPtr getFieldCore(const Variable& iVariable, int iTime) const; 19 | void writeCore(std::vector iVariables, std::string iMessage=""); 20 | bool hasVariableCore(const Variable& iVariable) const {return true;}; 21 | }; 22 | #endif 23 | -------------------------------------------------------------------------------- /src/client/File/Text.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_TEXT_H 2 | #define FILE_TEXT_H 3 | #include 4 | #include 5 | #include "File.h" 6 | #include "../Variable.h" 7 | #include "../Options.h" 8 | 9 | class Location; 10 | 11 | //! Represents a point-based text file. First column is the time (in seconds since 1970) 12 | //! and the second column is the forecast value. 13 | class FileText : public File { 14 | public: 15 | FileText(std::string iFilename, const Options& iOptions); 16 | static std::string description(); 17 | std::string name() const {return "text";}; 18 | protected: 19 | FieldPtr getFieldCore(const Variable& iVariable, int iTime) const; 20 | void writeCore(std::vector iVariables, std::string iMessage=""); 21 | bool hasVariableCore(const Variable& iVariable) const {return true;}; 22 | std::vector mLocalFields; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /src/client/Grid.cpp: -------------------------------------------------------------------------------- 1 | #include "Grid.h" 2 | 3 | Grid::Grid() { 4 | 5 | } 6 | 7 | vec2 Grid::lats() const { 8 | return mLats; 9 | } 10 | vec2 Grid::lons() const { 11 | return mLons; 12 | } 13 | vec2 Grid::elevs() const { 14 | return mElevs; 15 | } 16 | vec2 Grid::landFractions() const { 17 | return mLandFractions; 18 | } 19 | void Grid::lats(vec2 iLats) { 20 | mLats = iLats; 21 | } 22 | void Grid::lons(vec2 iLons) { 23 | mLons = iLons; 24 | } 25 | void Grid::elevs(vec2 iElevs) { 26 | mElevs = iElevs; 27 | } 28 | void Grid::landFractions(vec2 iLandFractions) { 29 | mLandFractions = iLandFractions; 30 | } 31 | -------------------------------------------------------------------------------- /src/client/Grid.h: -------------------------------------------------------------------------------- 1 | #ifndef GRID_H 2 | #define GRID_H 3 | #include 4 | typedef std::vector > vec2; 5 | 6 | class Grid { 7 | public: 8 | Grid(); 9 | vec2 lats() const; 10 | vec2 lons() const; 11 | vec2 elevs() const; 12 | vec2 landFractions() const; 13 | void lats(vec2 iLats); 14 | void lons(vec2 iLons); 15 | void elevs(vec2 iElevs); 16 | void landFractions(vec2 iLandFractions); 17 | private: 18 | vec2 mLats; 19 | vec2 mLons; 20 | vec2 mElevs; 21 | vec2 mLandFractions; 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /src/client/KDTree.h: -------------------------------------------------------------------------------- 1 | #ifndef KDTREE_H 2 | #define KDTREE_H 3 | #include 4 | #include 5 | #include 6 | #include "Util.h" 7 | #include "File/File.h" 8 | typedef std::vector > vec2Int; 9 | 10 | class KDTree { 11 | public: 12 | KDTree(); 13 | void build(const vec2& iLats, const vec2& iLons); 14 | KDTree(const vec2& iLats, const vec2& iLons); 15 | KDTree& operator=(KDTree other); 16 | KDTree(const KDTree& other); 17 | 18 | void getNearestNeighbour(const File& iTo, vec2Int& iI, vec2Int& iJ) const; 19 | // I,J: The indices into the lat/lon grid with the nearest neighbour 20 | void getNearestNeighbour(float iLat, float iLon, int& iI, int& iJ) const; 21 | 22 | private: 23 | struct TreeNode { 24 | bool xsection; 25 | float lon; 26 | float lat; 27 | size_t ipos; 28 | size_t jpos; 29 | 30 | boost::scoped_ptr left; 31 | boost::scoped_ptr right; 32 | 33 | const TreeNode* parent; 34 | 35 | TreeNode(): xsection(false), left(NULL), right(NULL), parent(NULL) {} 36 | TreeNode(const TreeNode &other): 37 | xsection(other.xsection), 38 | lon(other.lon), 39 | lat(other.lat), 40 | ipos(other.ipos), 41 | jpos(other.jpos), 42 | left(NULL), 43 | right(NULL), 44 | parent(NULL) { 45 | 46 | if(other.left) { 47 | left.reset(new TreeNode(*other.left)); 48 | left->parent = this; 49 | } 50 | 51 | if(other.right) { 52 | right.reset(new TreeNode(*other.right)); 53 | right->parent = this; 54 | } 55 | } 56 | }; 57 | 58 | struct Indexed { 59 | float lon; 60 | float lat; 61 | size_t index1; 62 | size_t index2; 63 | 64 | Indexed() {} 65 | Indexed(const float lon_, 66 | const float lat_, 67 | const size_t index1_, 68 | const size_t index2_): 69 | lon(lon_), lat(lat_), index1(index1_), index2(index2_) {} 70 | }; 71 | 72 | typedef boost::scoped_ptr unode; 73 | unode mRoot; 74 | vec2 mLats; 75 | vec2 mLons; 76 | 77 | friend bool compareLons (const KDTree::Indexed& l, const KDTree::Indexed& r); 78 | friend bool compareLats (const KDTree::Indexed& l, const KDTree::Indexed& r); 79 | typedef std::vector indexdVec; 80 | typedef std::pair queryRes; 81 | 82 | 83 | static const queryRes nearestNeighbour(const unode& root, const float lon, const float lat, const float oldBest); 84 | static const TreeNode* firstGuess(const unode& root, const float lon, const float lat); 85 | static void subTree(indexdVec& iLonLat, 86 | const size_t from, 87 | const size_t to, 88 | const bool xsection, 89 | const TreeNode* parent, 90 | unode& root); 91 | 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/client/Location.cpp: -------------------------------------------------------------------------------- 1 | #include "Location.h" 2 | 3 | Location::Location(float iLat, float iLon, float iElev) : 4 | mLat(iLat), 5 | mLon(iLon), 6 | mElev(iElev) { 7 | 8 | } 9 | 10 | bool Location::operator<(const Location &right) const { 11 | if(mLat == right.lat()) { 12 | if(mLon == right.lon()) { 13 | return mElev < right.mElev; 14 | } 15 | else { 16 | return mLon < right.mLon; 17 | } 18 | } 19 | else { 20 | return mLat < right.mLat; 21 | } 22 | } 23 | 24 | bool Location::operator==(const Location &right) const { 25 | return (mLat == right.lat() && mLon == right.lon() && mElev == right.elev()); 26 | } 27 | float Location::lat() const { 28 | return mLat; 29 | } 30 | float Location::lon() const { 31 | return mLon; 32 | } 33 | float Location::elev() const { 34 | return mElev; 35 | } 36 | void Location::lat(float iLat) { 37 | mLat = iLat; 38 | } 39 | void Location::lon(float iLon) { 40 | mLon = iLon; 41 | } 42 | void Location::elev(float iElev) { 43 | mElev = iElev; 44 | } 45 | 46 | bool Location::CmpIgnoreElevation::operator()(const Location &right, const Location &left) const { 47 | if(left.lat() == right.lat()) { 48 | return right.lon() < left.lon(); 49 | } 50 | else { 51 | return right.lat() < left.lat(); 52 | } 53 | } 54 | 55 | float Location::getDistance(const Location& loc1) const { 56 | return Util::getDistance(mLat, mLon, loc1.lat(), loc1.lon()); 57 | } 58 | -------------------------------------------------------------------------------- /src/client/Location.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCATION_H 2 | #define LOCATION_H 3 | #include "Util.h" 4 | 5 | //! Represents a point in space 6 | class Location { 7 | public: 8 | Location(float iLat, float iLon, float iElev=Util::MV); 9 | //! Accessors 10 | float lat() const; 11 | float lon() const; 12 | float elev() const; 13 | //! Mutators 14 | void lat(float iLat); 15 | void lon(float iLon); 16 | void elev(float iElev); 17 | //! Used for sorting in std::map 18 | bool operator<(const Location &right) const; 19 | bool operator==(const Location &right) const; 20 | 21 | float getDistance(const Location& loc1) const; 22 | 23 | //! A comparator that only looks at lat/lon, and ignores elevation 24 | class CmpIgnoreElevation { 25 | public: 26 | bool operator()(const Location &right, const Location &left) const; 27 | }; 28 | private: 29 | float mLat; 30 | float mLon; 31 | float mElev; 32 | }; 33 | #endif 34 | -------------------------------------------------------------------------------- /src/client/NetcdfUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "NetcdfUtil.h" 2 | #include 3 | 4 | float NetcdfUtil::getMissingValue(int iFile, int iVar) { 5 | float fillValue; 6 | int status = nc_get_att_float(iFile, iVar, "_FillValue", &fillValue); 7 | if(status != NC_NOERR) 8 | fillValue = NC_FILL_FLOAT; 9 | return fillValue; 10 | } 11 | 12 | long NetcdfUtil::getTotalSize(int iFile, int iVar) { 13 | int ndims; 14 | int status = nc_inq_varndims(iFile, iVar, &ndims); 15 | handleNetcdfError(status, "Could not get number of dimensions"); 16 | 17 | int dims[ndims]; 18 | status = nc_inq_vardimid(iFile, iVar, dims); 19 | handleNetcdfError(status, "Could not get dimension ids"); 20 | 21 | long total = 1; 22 | for(int i = 0; i < ndims; i++) { 23 | size_t size; 24 | int status = nc_inq_dimlen(iFile, dims[i], &size); 25 | handleNetcdfError(status,"Could not get size of dimension"); 26 | total = total * size; 27 | } 28 | return total; 29 | 30 | } 31 | 32 | void NetcdfUtil::handleNetcdfError(int status, std::string message) { 33 | if(status != NC_NOERR) { 34 | std::stringstream ss; 35 | if(message == "") { 36 | ss << "Netcdf error code: " << status << "."; 37 | } 38 | else { 39 | ss << "Netcdf error: " << message << ". " 40 | << "Netcdf error code: " << status << "."; 41 | } 42 | Util::error(ss.str()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/client/NetcdfUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef NETCDF_UTIL_H 2 | #define NETCDF_UTIL_H 3 | #include "Util.h" 4 | #include 5 | #include 6 | 7 | class NetcdfUtil { 8 | public: 9 | static float getMissingValue(int iFile, int iVar); 10 | static long getTotalSize(int iFile, int iVar); 11 | static void handleNetcdfError(int status, std::string message=""); 12 | }; 13 | #endif 14 | -------------------------------------------------------------------------------- /src/client/ParameterFile/.vimrc: -------------------------------------------------------------------------------- 1 | ../.vimrc -------------------------------------------------------------------------------- /src/client/ParameterFile/Netcdf.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETER_FILE_NETCDF_H 2 | #define PARAMETER_FILE_NETCDF_H 3 | #include 4 | #include 5 | #include 6 | #include "ParameterFile.h" 7 | #include "../Parameters.h" 8 | #include "../Location.h" 9 | #include "../File/File.h" 10 | // TODO: if a location has missing parameters for some hours but not others, then 11 | // the missing values are returned, not the nearest neighbours values. 12 | class ParameterFileNetcdf : public ParameterFile { 13 | public: 14 | ParameterFileNetcdf(const Options& iOptions, bool iIsNew=false); 15 | ~ParameterFileNetcdf(); 16 | 17 | bool isFixedSize() const {return true;}; 18 | 19 | static bool isValid(std::string iFilename); 20 | bool isReadable() const; 21 | bool isLocationDependent() const { return true; }; 22 | 23 | static std::string description(bool full=true); 24 | std::string name() const {return "netcdf";}; 25 | 26 | void write() const; 27 | private: 28 | float mLocalMV; 29 | int getLatDim(int iFile) const; 30 | int getLonDim(int iFile) const; 31 | int getTimeDim(int iFile) const; 32 | int getCoefficientDim(int iFile) const; 33 | std::vector getDims(int iFile, int iVar) const; 34 | 35 | int getDim(int iFile, std::string iDim) const; 36 | int createDim(int iFile, std::string iDim, int iLength) const; 37 | int getVar(int iFile, std::string iVar) const; 38 | int getDimSize(int iFile, int iDim) const; 39 | static bool hasDim(int iFile, std::string iDim); 40 | static bool hasVar(int iFile, std::string iVar); 41 | std::string mDimName; 42 | std::string mVarName; 43 | std::string mXDimName; 44 | std::string mYDimName; 45 | void handleNetcdfError(int status, std::string message="") const; 46 | //! Convert linear index 'i' to vector 'iInidices'. 'iCount' specifies the size of the data 47 | //! Using row-major ordering (last index varies fastest) 48 | std::vector getIndices(int i, const std::vector& iCount) const; 49 | int getIndex(const std::vector& iIndices, const std::vector& iCount) const; 50 | 51 | // Read variable from file, convert missing values 52 | // User must release memory 53 | float* getNcFloats(int iFile, int iVar); 54 | 55 | // Reads lat/lon/elev values from the variable, regardless of the order of the x, y dimensions 56 | // For lat/lon, the variable can have one or two dimensions. If the former, then the values 57 | // are assumed to be the same across the other dimension. 58 | vec2 getGridValues(int iFile, int iVariable) const; 59 | 60 | int mFile; 61 | 62 | void startDefineMode() const; 63 | void endDefineMode() const; 64 | mutable bool mInDefineMode; 65 | }; 66 | #endif 67 | -------------------------------------------------------------------------------- /src/client/ParameterFile/Simple.cpp: -------------------------------------------------------------------------------- 1 | #include "Simple.h" 2 | #include 3 | #include 4 | #include "../Util.h" 5 | #include 6 | #include 7 | #include 8 | 9 | ParameterFileSimple::ParameterFileSimple(Parameters iParameters) : ParameterFile(Options()) { 10 | Location defaultLocation(Util::MV,Util::MV,Util::MV); 11 | setParameters(iParameters, 0, defaultLocation); 12 | 13 | recomputeTree(); 14 | } 15 | 16 | std::vector ParameterFileSimple::getTimes() const { 17 | std::vector times(1,0); 18 | return times; 19 | } 20 | 21 | 22 | bool ParameterFileSimple::isValid(std::string iFilename) { 23 | // TODO 24 | return true; 25 | } 26 | -------------------------------------------------------------------------------- /src/client/ParameterFile/Simple.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETER_FILE_SIMPLE_H 2 | #define PARAMETER_FILE_SIMPLE_H 3 | #include 4 | #include 5 | #include "ParameterFile.h" 6 | #include "../Parameters.h" 7 | #include "../Location.h" 8 | 9 | class ParameterFileSimple : public ParameterFile { 10 | public: 11 | //! Read parameters from this file 12 | ParameterFileSimple(Parameters iParameters); 13 | 14 | bool isFixedSize() const {return true;}; 15 | 16 | std::vector getTimes() const; 17 | static bool isValid(std::string iFilename); 18 | bool isReadable() const {return true;}; 19 | std::string name() const {return "simple";}; 20 | private: 21 | int mNumParameters; 22 | std::vector mTimes; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /src/client/ParameterFile/Text.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETER_FILE_TEXT_H 2 | #define PARAMETER_FILE_TEXT_H 3 | #include 4 | #include 5 | #include "ParameterFile.h" 6 | #include "../Parameters.h" 7 | #include "../Location.h" 8 | 9 | //! Represents a collection of parameters, one set for each forecast time 10 | //! Parameters are read from a specified text file with the following format: 11 | //! time lat lon elev param1 param2 param3 12 | //! 0 60 10 3.1 3.4 2.1 5.2 12 41 13 | //! 1 60 10 3.1 3.4 2.1 5.2 12 41 14 | //! 2 60 10 3.1 3.4 2.1 5.2 12 41 15 | //! Each line represents one forecast time. The first column is the forecast timestep (an index 16 | //! not the number of hours), starting at 0. The remaning columns are parameters that can be used 17 | //! in post-processing methods. The number of columns most be constant. If a file has only one 18 | //! line, then the parameters are used for all forecast hours. 19 | class ParameterFileText : public ParameterFile { 20 | public: 21 | //! Read parameters from this file 22 | ParameterFileText(const Options& iOptions, bool iIsNew=false); 23 | 24 | bool isFixedSize() const; 25 | 26 | std::vector getTimes() const; 27 | static bool isValid(std::string iFilename); 28 | bool isReadable() const; 29 | bool isLocationDependent() const; 30 | 31 | //! Write parameter file to disk 32 | //! @param iFilename Write to this filename. If empty, write to the file that was read from. 33 | void write(const std::string& iFilename) const; 34 | void write() const; 35 | static std::string description(bool full=true); 36 | std::string name() const {return "text";}; 37 | private: 38 | std::vector mTimes; 39 | }; 40 | #endif 41 | -------------------------------------------------------------------------------- /src/client/Parameters.cpp: -------------------------------------------------------------------------------- 1 | #include "Parameters.h" 2 | #include 3 | #include 4 | #include 5 | #include "Util.h" 6 | 7 | Parameters::Parameters(std::vector iValues) : mValues(iValues) { 8 | 9 | } 10 | Parameters::Parameters(float iValue) { 11 | mValues = std::vector(1, iValue); 12 | } 13 | Parameters::Parameters() { 14 | 15 | } 16 | 17 | std::vector Parameters::getValues() const { 18 | return mValues; 19 | } 20 | float & Parameters::operator[](unsigned int i) { 21 | if(i >= mValues.size() || i < 0) { 22 | std::stringstream ss; 23 | ss << "Attempted to access element " << i << " in parameter array of length " << mValues.size() << std::endl; 24 | Util::error(ss.str()); 25 | } 26 | 27 | return mValues[i]; 28 | } 29 | const float & Parameters::operator[](unsigned int i) const { 30 | if(i >= mValues.size() || i < 0) { 31 | std::stringstream ss; 32 | ss << "Attempted to access element " << i << " in parameter array of length " << mValues.size() << std::endl; 33 | Util::error(ss.str()); 34 | } 35 | 36 | return mValues[i]; 37 | } 38 | int Parameters::size() const { 39 | return mValues.size(); 40 | } 41 | 42 | bool Parameters::isValid() const { 43 | for(int i = 0; i < mValues.size(); i++) { 44 | if(!Util::isValid(mValues[i])) 45 | return false; 46 | } 47 | return true; 48 | } 49 | -------------------------------------------------------------------------------- /src/client/Parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef PARAMETERS_H 2 | #define PARAMETERS_H 3 | #include 4 | 5 | //! Represents a vector of parameter values 6 | class Parameters { 7 | public: 8 | Parameters(std::vector iValues); 9 | Parameters(float iValue); 10 | 11 | //! Initialize an empty set of size 0 12 | Parameters(); 13 | 14 | //! Returns the number of parameters 15 | int size() const; 16 | std::vector getValues() const; 17 | 18 | //! Are all parameters in set valid numbers? 19 | bool isValid() const; 20 | 21 | //! Access the i'th parameter 22 | float & operator[](unsigned int i); 23 | const float & operator[](unsigned int i) const; 24 | private: 25 | std::vector mValues; 26 | }; 27 | #endif 28 | 29 | -------------------------------------------------------------------------------- /src/client/Scheme.cpp: -------------------------------------------------------------------------------- 1 | #include "Scheme.h" 2 | #include "Options.h" 3 | 4 | Scheme::Scheme(const Options& iOptions) : mDebug(false) { 5 | iOptions.getValue("debug", mDebug); 6 | } 7 | bool Scheme::debug() const { 8 | return mDebug; 9 | } 10 | -------------------------------------------------------------------------------- /src/client/Scheme.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHEME_H 2 | #define SCHEME_H 3 | class Options; 4 | class ParameterFile; 5 | 6 | //! Base class for downscaler, calibrator 7 | class Scheme { 8 | public: 9 | Scheme(const Options& iOptions); 10 | // Is debug turned on? Schemes should display debug information if this is true 11 | bool debug() const; 12 | private: 13 | bool mDebug; 14 | }; 15 | #endif 16 | -------------------------------------------------------------------------------- /src/client/Setup.h: -------------------------------------------------------------------------------- 1 | #ifndef METCAL_SETUP_H 2 | #define METCAL_SETUP_H 3 | #include 4 | #include 5 | #include 6 | #include "Variable.h" 7 | #include "Options.h" 8 | class File; 9 | class Calibrator; 10 | class Downscaler; 11 | class ParameterFile; 12 | class Variable; 13 | 14 | //! Represents the post-processing of one variable 15 | struct VariableConfiguration { 16 | //! Which variable should be post-processed? 17 | Variable inputVariable; 18 | Variable outputVariable; 19 | //! Which downscaler should be used 20 | Downscaler* downscaler; 21 | //! Which calibrators should be use 22 | std::vector calibrators; 23 | Options inputVariableOptions; 24 | Options outputVariableOptions; 25 | std::vector parameterFileCalibrators; 26 | ParameterFile* parameterFileDownscaler; 27 | }; 28 | 29 | //! Represents what and how the post-processing should be done. Includes which input file to 30 | //! post-process, which output file to place the results in, which variables to post-process, 31 | //! and what post-processing methods to invoke on each variable. 32 | //! Aborts if one of the input files does not exits/cannot be parsed. 33 | class Setup { 34 | public: 35 | std::vector inputFiles; 36 | std::vector outputFiles; 37 | Options inputOptions; 38 | Options outputOptions; 39 | std::vector variableConfigurations; 40 | Setup(const std::vector& argv); 41 | ~Setup(); 42 | static std::string defaultDownscaler(); 43 | std::map variableAliases; 44 | private: 45 | // In some cases, it is not possible to open the same file first as readonly and then writeable 46 | // (for NetCDF). Therefore, use the same filehandle for both if the files are the same. Remember 47 | // to not free the memory of both files. 48 | std::map mFileMap; // filename, file handle 49 | bool hasFile(std::string iFilename) const; 50 | }; 51 | #endif 52 | -------------------------------------------------------------------------------- /src/client/Testing/.vimrc: -------------------------------------------------------------------------------- 1 | set makeprg=make\ -C../\ -j8\ testing/'%:t:r'.exe 2 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorAccumulate.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Accumulate.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorAccumulate : public ::testing::Test { 9 | protected: 10 | TestCalibratorAccumulate() { 11 | } 12 | virtual ~TestCalibratorAccumulate() { 13 | } 14 | virtual void SetUp() { 15 | mTemperature = Variable("air_temperature_2m"); 16 | mPrecipitation = Variable("precipitation_amount"); 17 | mMissing = Variable("qnh"); 18 | } 19 | virtual void TearDown() { 20 | } 21 | Variable mTemperature; 22 | Variable mPrecipitation; 23 | Variable mMissing; 24 | }; 25 | 26 | TEST_F(TestCalibratorAccumulate, 1x1) { 27 | FileNetcdf from("tests/files/1x1.nc"); 28 | CalibratorAccumulate cal = CalibratorAccumulate(mTemperature, Options()); 29 | cal.calibrate(from); 30 | 31 | EXPECT_FLOAT_EQ(0, (*from.getField(mTemperature, 0))(0,0,0)); 32 | EXPECT_FLOAT_EQ(20, (*from.getField(mTemperature, 1))(0,0,0)); 33 | EXPECT_FLOAT_EQ(35, (*from.getField(mTemperature, 2))(0,0,0)); 34 | EXPECT_FLOAT_EQ(56, (*from.getField(mTemperature, 3))(0,0,0)); 35 | EXPECT_FLOAT_EQ(70, (*from.getField(mTemperature, 4))(0,0,0)); 36 | EXPECT_FLOAT_EQ(100, (*from.getField(mTemperature, 5))(0,0,0)); 37 | EXPECT_FLOAT_EQ(121, (*from.getField(mTemperature, 6))(0,0,0)); 38 | EXPECT_FLOAT_EQ(140, (*from.getField(mTemperature, 7))(0,0,0)); 39 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mTemperature, 8))(0,0,0)); 40 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mTemperature, 9))(0,0,0)); 41 | } 42 | TEST_F(TestCalibratorAccumulate, 10x10) { 43 | FileNetcdf from("tests/files/10x10.nc"); 44 | CalibratorAccumulate cal = CalibratorAccumulate(mPrecipitation, Options()); 45 | cal.calibrate(from); 46 | 47 | EXPECT_FLOAT_EQ(0, (*from.getField(mPrecipitation, 0))(5,2,0)); 48 | EXPECT_FLOAT_EQ(0.539526, (*from.getField(mPrecipitation, 1))(5,2,0)); 49 | EXPECT_FLOAT_EQ(0, (*from.getField(mPrecipitation, 0))(5,9,0)); 50 | EXPECT_FLOAT_EQ(6.929162, (*from.getField(mPrecipitation, 1))(5,9,0)); 51 | EXPECT_FLOAT_EQ(0, (*from.getField(mPrecipitation, 0))(0,9,0)); 52 | EXPECT_FLOAT_EQ(5.442121, (*from.getField(mPrecipitation, 1))(0,9,0)); 53 | } 54 | TEST_F(TestCalibratorAccumulate, description) { 55 | CalibratorAccumulate::description(); 56 | } 57 | } 58 | int main(int argc, char **argv) { 59 | ::testing::InitGoogleTest(&argc, argv); 60 | return RUN_ALL_TESTS(); 61 | } 62 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorAltitude.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Altitude.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorAltitude : public ::testing::Test { 9 | protected: 10 | TestCalibratorAltitude() { 11 | } 12 | virtual ~TestCalibratorAltitude() { 13 | } 14 | virtual void SetUp() { 15 | mVariable = Variable("air_temperature_2m"); 16 | } 17 | virtual void TearDown() { 18 | } 19 | Variable mVariable; 20 | }; 21 | TEST_F(TestCalibratorAltitude, arome) { 22 | FileNetcdf from("tests/files/10x10.nc"); 23 | ParameterFileNetcdf par(Options("file=tests/files/10x10_param_zero_altitude.nc")); 24 | CalibratorAltitude cal = CalibratorAltitude(mVariable, Options()); 25 | 26 | cal.calibrate(from, &par); 27 | 28 | // Elevations should all be 0 29 | vec2 elevs = from.getElevs(); 30 | for(int i = 0; i < from.getNumY(); i++) { 31 | for(int j = 0; j < from.getNumX(); j++) { 32 | EXPECT_FLOAT_EQ(0, elevs[i][j]); 33 | } 34 | } 35 | // Shouldn't have changed anything else 36 | FieldPtr after = from.getField(mVariable, 0); 37 | EXPECT_FLOAT_EQ(301, (*after)(5,2,0)); 38 | EXPECT_FLOAT_EQ(304, (*after)(5,9,0)); 39 | EXPECT_FLOAT_EQ(320, (*after)(0,9,0)); 40 | } 41 | TEST_F(TestCalibratorAltitude, ec) { 42 | FileNetcdf from("tests/files/10x10_ec.nc"); 43 | ParameterFileNetcdf par(Options("file=tests/files/10x10_param_zero_altitude.nc")); 44 | CalibratorAltitude cal = CalibratorAltitude(mVariable, Options()); 45 | 46 | cal.calibrate(from, &par); 47 | 48 | // Elevations should all be 0 49 | vec2 elevs = from.getElevs(); 50 | for(int i = 0; i < from.getNumY(); i++) { 51 | for(int j = 0; j < from.getNumX(); j++) { 52 | EXPECT_FLOAT_EQ(0, elevs[i][j]); 53 | } 54 | } 55 | // Shouldn't have changed anything else 56 | FieldPtr after = from.getField(mVariable, 0); 57 | EXPECT_FLOAT_EQ(301, (*after)(5,2,0)); 58 | EXPECT_FLOAT_EQ(304, (*after)(5,9,0)); 59 | EXPECT_FLOAT_EQ(320, (*after)(0,9,0)); 60 | } 61 | // Should not calibrate when a parameter file with no locaions is used 62 | TEST_F(TestCalibratorAltitude, locationIndependent) { 63 | ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 64 | Util::setShowError(false); 65 | FileNetcdf from("tests/files/10x10.nc"); 66 | ParameterFileText par(Options("file=tests/files/parametersSingleTime.txt")); 67 | CalibratorAltitude cal = CalibratorAltitude(mVariable, Options()); 68 | EXPECT_DEATH(cal.calibrate(from, &par), ".*"); 69 | } 70 | TEST_F(TestCalibratorAltitude, description) { 71 | CalibratorAltitude::description(); 72 | } 73 | } 74 | int main(int argc, char **argv) { 75 | ::testing::InitGoogleTest(&argc, argv); 76 | return RUN_ALL_TESTS(); 77 | } 78 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorDeaccumulate.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Deaccumulate.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorDeaccumulate : public ::testing::Test { 9 | protected: 10 | TestCalibratorDeaccumulate() { 11 | } 12 | virtual ~TestCalibratorDeaccumulate() { 13 | } 14 | virtual void SetUp() { 15 | mTemperature = Variable("air_temperature_2m"); 16 | mPrecipitation = Variable("precipitation_amount_acc"); 17 | mMissing = Variable("qnh"); 18 | } 19 | virtual void TearDown() { 20 | } 21 | Variable mTemperature; 22 | Variable mPrecipitation; 23 | Variable mMissing; 24 | }; 25 | 26 | TEST_F(TestCalibratorDeaccumulate, 1x1) { 27 | FileNetcdf from("tests/files/1x1.nc"); 28 | CalibratorDeaccumulate cal = CalibratorDeaccumulate(mPrecipitation, Options("window=3")); 29 | cal.calibrate(from); 30 | 31 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mPrecipitation, 0))(0,0,0)); 32 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mPrecipitation, 1))(0,0,0)); 33 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mPrecipitation, 2))(0,0,0)); 34 | EXPECT_FLOAT_EQ(4, (*from.getField(mPrecipitation, 3))(0,0,0)); 35 | EXPECT_FLOAT_EQ(2.5, (*from.getField(mPrecipitation, 4))(0,0,0)); 36 | EXPECT_FLOAT_EQ(6, (*from.getField(mPrecipitation, 5))(0,0,0)); 37 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mPrecipitation, 6))(0,0,0)); 38 | EXPECT_FLOAT_EQ(6.5, (*from.getField(mPrecipitation, 7))(0,0,0)); 39 | EXPECT_FLOAT_EQ(2, (*from.getField(mPrecipitation, 8))(0,0,0)); 40 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mPrecipitation, 9))(0,0,0)); 41 | } 42 | TEST_F(TestCalibratorDeaccumulate, ) { 43 | FileNetcdf from("tests/files/1x1.nc"); 44 | CalibratorDeaccumulate cal = CalibratorDeaccumulate(mTemperature, Options()); 45 | cal.calibrate(from); 46 | 47 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mTemperature, 0))(0,0,0)); 48 | EXPECT_FLOAT_EQ(-3, (*from.getField(mTemperature, 1))(0,0,0)); 49 | EXPECT_FLOAT_EQ(-5, (*from.getField(mTemperature, 2))(0,0,0)); 50 | EXPECT_FLOAT_EQ(6, (*from.getField(mTemperature, 3))(0,0,0)); 51 | EXPECT_FLOAT_EQ(-7, (*from.getField(mTemperature, 4))(0,0,0)); 52 | EXPECT_FLOAT_EQ(16, (*from.getField(mTemperature, 5))(0,0,0)); 53 | EXPECT_FLOAT_EQ(-9, (*from.getField(mTemperature, 6))(0,0,0)); 54 | EXPECT_FLOAT_EQ(-2, (*from.getField(mTemperature, 7))(0,0,0)); 55 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mTemperature, 8))(0,0,0)); 56 | EXPECT_FLOAT_EQ(Util::MV, (*from.getField(mTemperature, 9))(0,0,0)); 57 | } 58 | TEST_F(TestCalibratorDeaccumulate, description) { 59 | CalibratorDeaccumulate::description(); 60 | } 61 | } 62 | int main(int argc, char **argv) { 63 | ::testing::InitGoogleTest(&argc, argv); 64 | return RUN_ALL_TESTS(); 65 | } 66 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorMask.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Mask.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorMask : public ::testing::Test { 9 | protected: 10 | TestCalibratorMask() { 11 | } 12 | virtual ~TestCalibratorMask() { 13 | } 14 | virtual void SetUp() { 15 | mVariable = Variable("air_temperature_2m"); 16 | } 17 | virtual void TearDown() { 18 | } 19 | Variable mVariable; 20 | std::vector getVector(float iArray[]) { 21 | return std::vector(iArray, iArray + sizeof(iArray)/sizeof(float)); 22 | } 23 | }; 24 | // Mask out values 25 | TEST_F(TestCalibratorMask, 10x10_mask_out) { 26 | FileNetcdf from("tests/files/10x10.nc"); 27 | // One point at 3,5 with 223 km radius and one point at 4,6 with 336 km radius 28 | ParameterFileText par(Options("file=tests/files/mask0.txt")); 29 | CalibratorMask cal = CalibratorMask(mVariable, Options("keep=0")); 30 | 31 | cal.calibrate(from, &par); 32 | FieldPtr after = from.getField(mVariable, 0); 33 | ASSERT_EQ(10, after->getNumY()); 34 | ASSERT_EQ(10, after->getNumX()); 35 | ASSERT_EQ(1, after->getNumEns()); 36 | 37 | EXPECT_FLOAT_EQ(301, (*after)(5,2,0)); 38 | EXPECT_FLOAT_EQ(Util::MV, (*after)(3,5,0)); 39 | EXPECT_FLOAT_EQ(Util::MV, (*after)(3,3,0)); 40 | EXPECT_FLOAT_EQ(Util::MV, (*after)(2,5,0)); 41 | EXPECT_FLOAT_EQ(Util::MV, (*after)(4,9,0)); 42 | EXPECT_FLOAT_EQ(302, (*after)(2,3,0)); 43 | EXPECT_FLOAT_EQ(310, (*after)(6,9,0)); 44 | } 45 | // Mask in values 46 | TEST_F(TestCalibratorMask, 10x10_mask_in) { 47 | FileNetcdf from("tests/files/10x10.nc"); 48 | ParameterFileText par(Options("file=tests/files/mask0.txt")); 49 | CalibratorMask cal = CalibratorMask(mVariable, Options("mask=1")); 50 | 51 | cal.calibrate(from, &par); 52 | FieldPtr after = from.getField(mVariable, 0); 53 | ASSERT_EQ(10, after->getNumY()); 54 | ASSERT_EQ(10, after->getNumX()); 55 | ASSERT_EQ(1, after->getNumEns()); 56 | 57 | EXPECT_FLOAT_EQ(Util::MV, (*after)(5,2,0)); 58 | EXPECT_FLOAT_EQ(302, (*after)(3,5,0)); 59 | EXPECT_FLOAT_EQ(302, (*after)(3,3,0)); 60 | EXPECT_FLOAT_EQ(302, (*after)(2,5,0)); 61 | EXPECT_FLOAT_EQ(302, (*after)(4,9,0)); 62 | EXPECT_FLOAT_EQ(Util::MV, (*after)(2,3,0)); 63 | EXPECT_FLOAT_EQ(Util::MV, (*after)(6,9,0)); 64 | } 65 | // Missing parameter file 66 | TEST_F(TestCalibratorMask, invalid2) { 67 | ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 68 | FileNetcdf from("tests/files/10x10.nc"); 69 | Util::setShowError(false); 70 | CalibratorMask calibrator(mVariable, Options()); 71 | EXPECT_DEATH(calibrator.calibrate(from, NULL), ".*"); 72 | } 73 | TEST_F(TestCalibratorMask, description) { 74 | CalibratorMask::description(); 75 | } 76 | } 77 | int main(int argc, char **argv) { 78 | ::testing::InitGoogleTest(&argc, argv); 79 | return RUN_ALL_TESTS(); 80 | } 81 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorQnh.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Qnh.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorQnh : public ::testing::Test { 9 | protected: 10 | TestCalibratorQnh() { 11 | } 12 | virtual ~TestCalibratorQnh() { 13 | } 14 | virtual void SetUp() { 15 | } 16 | virtual void TearDown() { 17 | } 18 | }; 19 | TEST_F(TestCalibratorQnh, 10x10) { 20 | Variable variableQnh = Variable("qnh"); 21 | Variable variableP = Variable("surface_air_pressure"); 22 | FileNetcdf from("tests/files/10x10.nc"); 23 | CalibratorQnh cal = CalibratorQnh(variableQnh, Options("pressureVariable=surface_air_pressure")); 24 | FieldPtr p = from.getField(variableP, 0); 25 | 26 | for(int i = 0; i < from.getNumTime(); i++) 27 | from.addField(from.getEmptyField(), variableQnh, i); 28 | cal.calibrate(from); 29 | EXPECT_FLOAT_EQ(98334.44, (*p)(5,2,0)); 30 | FieldPtr qnh = from.getField(variableQnh, 0); 31 | ASSERT_EQ(10, qnh->getNumY()); 32 | ASSERT_EQ(10, qnh->getNumX()); 33 | ASSERT_EQ(1, qnh->getNumEns()); 34 | 35 | // Altitude: 159.6324 Pressure: 98334.44 36 | EXPECT_FLOAT_EQ(100220.6455, (*qnh)(5,2,0)); 37 | } 38 | TEST_F(TestCalibratorQnh, calcQnh) { 39 | EXPECT_FLOAT_EQ(100000, CalibratorQnh::calcQnh(0,100000)); 40 | EXPECT_FLOAT_EQ(0, CalibratorQnh::calcQnh(0,0)); 41 | 42 | EXPECT_FLOAT_EQ(100184.6424, CalibratorQnh::calcQnh(100,99000)); 43 | EXPECT_FLOAT_EQ(97826.7259, CalibratorQnh::calcQnh(-100,99000)); 44 | EXPECT_FLOAT_EQ(0, CalibratorQnh::calcQnh(-100,0)); 45 | } 46 | TEST_F(TestCalibratorQnh, description) { 47 | CalibratorQnh::description(); 48 | } 49 | } 50 | int main(int argc, char **argv) { 51 | ::testing::InitGoogleTest(&argc, argv); 52 | return RUN_ALL_TESTS(); 53 | } 54 | -------------------------------------------------------------------------------- /src/client/Testing/CalibratorSort.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Fake.h" 2 | #include "../Util.h" 3 | #include "../ParameterFile/ParameterFile.h" 4 | #include "../Calibrator/Sort.h" 5 | #include 6 | 7 | namespace { 8 | class TestCalibratorSort : public ::testing::Test { 9 | protected: 10 | TestCalibratorSort() { 11 | } 12 | virtual ~TestCalibratorSort() { 13 | } 14 | virtual void SetUp() { 15 | mVariable = Variable("air_temperature_2m"); 16 | } 17 | virtual void TearDown() { 18 | } 19 | Variable mVariable; 20 | void test(CalibratorSort& cal, File& file, float i0, float i1, float i2, float e0, float e1, float e2) { 21 | FieldPtr field = file.getField(mVariable, 0); 22 | (*field)(0,0,0) = i0; 23 | (*field)(0,0,1) = i1; 24 | (*field)(0,0,2) = i2; 25 | cal.calibrate(file, NULL); 26 | const FieldPtr after = file.getField(mVariable, 0); 27 | 28 | EXPECT_FLOAT_EQ(e0, (*after)(0,0,0)); 29 | EXPECT_FLOAT_EQ(e1, (*after)(0,0,1)); 30 | EXPECT_FLOAT_EQ(e2, (*after)(0,0,2)); 31 | } 32 | }; 33 | TEST_F(TestCalibratorSort, simple) { 34 | CalibratorSort cal = CalibratorSort(mVariable, Options("")); 35 | FileFake file(Options("nLat=1 nLon=1 nEns=3 nTime=1")); 36 | // Before sort After sort 37 | test(cal, file, 3,1,2, 1,2,3); 38 | test(cal, file, 1,1,2, 1,1,2); 39 | test(cal, file, 3,1,1, 1,1,3); 40 | test(cal, file, 3,Util::MV,2, 2,3,Util::MV); 41 | test(cal, file, 2,Util::MV,2, 2,2,Util::MV); 42 | test(cal, file, Util::MV,Util::MV,Util::MV, Util::MV,Util::MV,Util::MV); 43 | test(cal, file, Util::MV,1,Util::MV, 1,Util::MV,Util::MV); 44 | } 45 | TEST_F(TestCalibratorSort, description) { 46 | CalibratorSort::description(); 47 | } 48 | } 49 | int main(int argc, char **argv) { 50 | ::testing::InitGoogleTest(&argc, argv); 51 | return RUN_ALL_TESTS(); 52 | } 53 | -------------------------------------------------------------------------------- /src/client/Testing/FileText.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Text.h" 2 | #include "../Util.h" 3 | #include "../Downscaler/Downscaler.h" 4 | #include 5 | 6 | namespace { 7 | class FileTextTest : public ::testing::Test { 8 | protected: 9 | virtual void SetUp() { 10 | mVariable = Variable("air_temperature_2m"); 11 | } 12 | virtual void TearDown() { 13 | } 14 | Variable mVariable; 15 | }; 16 | 17 | TEST_F(FileTextTest, asInput) { 18 | FileText file("tests/files/validText1.txt", Options()); 19 | FieldPtr field0 = file.getField(mVariable, 0); 20 | EXPECT_FLOAT_EQ(3.2, (*field0)(0,0,0)); 21 | FieldPtr field1 = file.getField(mVariable, 1); 22 | EXPECT_FLOAT_EQ(4.1, (*field1)(0,0,0)); 23 | } 24 | TEST_F(FileTextTest, asEnsemble) { 25 | FileText file("tests/files/validText2.txt", Options("lat=1 lon=2 elev=3")); 26 | FieldPtr field0 = file.getField(mVariable, 0); 27 | ASSERT_EQ(3, field0->getNumEns()); 28 | ASSERT_EQ(2, field0->getNumY()); 29 | ASSERT_EQ(1, field0->getNumX()); 30 | EXPECT_FLOAT_EQ(11, (*field0)(0,0,0)); 31 | EXPECT_FLOAT_EQ(21, (*field0)(0,0,1)); 32 | EXPECT_FLOAT_EQ(-1, (*field0)(0,0,2)); 33 | EXPECT_FLOAT_EQ(3.2, (*field0)(1,0,0)); 34 | EXPECT_FLOAT_EQ(1.5, (*field0)(1,0,1)); 35 | EXPECT_FLOAT_EQ(5.1, (*field0)(1,0,2)); 36 | FieldPtr field1 = file.getField(mVariable, 1); 37 | ASSERT_EQ(3, field1->getNumEns()); 38 | EXPECT_FLOAT_EQ(Util::MV, (*field1)(0,0,0)); 39 | EXPECT_FLOAT_EQ(Util::MV, (*field1)(0,0,1)); 40 | EXPECT_FLOAT_EQ(Util::MV, (*field1)(0,0,2)); 41 | EXPECT_FLOAT_EQ(4, (*field1)(1,0,0)); 42 | EXPECT_FLOAT_EQ(1, (*field1)(1,0,1)); 43 | EXPECT_FLOAT_EQ(2, (*field1)(1,0,2)); 44 | } 45 | TEST_F(FileTextTest, invalid) { 46 | ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 47 | Util::setShowError(false); 48 | EXPECT_DEATH(FileText("tests/files/invalidText1.txt", Options()), ".*"); 49 | } 50 | } 51 | int main(int argc, char **argv) { 52 | ::testing::InitGoogleTest(&argc, argv); 53 | return RUN_ALL_TESTS(); 54 | } 55 | -------------------------------------------------------------------------------- /src/client/Testing/Location.cpp: -------------------------------------------------------------------------------- 1 | #include "../Util.h" 2 | #include "../Location.h" 3 | #include 4 | 5 | namespace { 6 | class LocationTest : public ::testing::Test { 7 | protected: 8 | }; 9 | 10 | TEST_F(LocationTest, constructor) { 11 | Location loc(1,2,3); 12 | EXPECT_FLOAT_EQ(1, loc.lat()); 13 | EXPECT_FLOAT_EQ(2, loc.lon()); 14 | EXPECT_FLOAT_EQ(3, loc.elev()); 15 | } 16 | TEST_F(LocationTest, setters) { 17 | Location loc(1,2,3); 18 | loc.lat(3.2); 19 | EXPECT_FLOAT_EQ(3.2, loc.lat()); 20 | EXPECT_FLOAT_EQ(2, loc.lon()); 21 | EXPECT_FLOAT_EQ(3, loc.elev()); 22 | loc.lon(4); 23 | EXPECT_FLOAT_EQ(3.2, loc.lat()); 24 | EXPECT_FLOAT_EQ(4, loc.lon()); 25 | EXPECT_FLOAT_EQ(3, loc.elev()); 26 | loc.elev(-3.5); 27 | EXPECT_FLOAT_EQ(3.2, loc.lat()); 28 | EXPECT_FLOAT_EQ(4, loc.lon()); 29 | EXPECT_FLOAT_EQ(-3.5, loc.elev()); 30 | } 31 | TEST_F(LocationTest, order) { 32 | Location loc1(1,2,3); 33 | Location loc2(1,2,3); 34 | // < should be false in both cases when locations are equal 35 | EXPECT_FALSE(loc1 < loc2 || loc2 < loc1); 36 | // < should always order one high when locations are different 37 | loc2 = Location(1,2,4); 38 | EXPECT_TRUE(loc1 < loc2 || loc2 < loc1); 39 | loc2 = Location(1,3,2); 40 | EXPECT_TRUE(loc1 < loc2 || loc2 < loc1); 41 | loc2 = Location(2,2,3); 42 | EXPECT_TRUE(loc1 < loc2 || loc2 < loc1); 43 | } 44 | } 45 | int main(int argc, char **argv) { 46 | ::testing::InitGoogleTest(&argc, argv); 47 | return RUN_ALL_TESTS(); 48 | } 49 | -------------------------------------------------------------------------------- /src/client/Testing/NetcdfUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "../NetcdfUtil.h" 2 | #include "../Variable.h" 3 | #include "../Calibrator/Calibrator.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace { 12 | class NetcdfUtilTest : public ::testing::Test { 13 | protected: 14 | }; 15 | 16 | TEST_F(NetcdfUtilTest, get) { 17 | int file; 18 | int status = nc_open("tests/files/10x10.nc", NC_NOWRITE, &file); 19 | EXPECT_EQ(status, NC_NOERR); 20 | int var; 21 | status = nc_inq_varid(file, "air_temperature_2m", &var); 22 | EXPECT_EQ(status, NC_NOERR); 23 | EXPECT_EQ(NC_FILL_FLOAT, NetcdfUtil::getMissingValue(file, var)); 24 | EXPECT_EQ(200, NetcdfUtil::getTotalSize(file, var)); 25 | } 26 | TEST_F(NetcdfUtilTest, error) { 27 | ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 28 | Util::setShowError(false); 29 | EXPECT_DEATH(NetcdfUtil::handleNetcdfError(NC_EBADID, "error"), ".*"); 30 | EXPECT_DEATH(NetcdfUtil::handleNetcdfError(NC_EBADID, ""), ".*"); 31 | } 32 | } 33 | int main(int argc, char **argv) { 34 | ::testing::InitGoogleTest(&argc, argv); 35 | return RUN_ALL_TESTS(); 36 | } 37 | -------------------------------------------------------------------------------- /src/client/Testing/ParameterFileSimple.cpp: -------------------------------------------------------------------------------- 1 | #include "../ParameterFile/ParameterFile.h" 2 | #include "../Util.h" 3 | #include 4 | #include 5 | 6 | namespace { 7 | TEST(ParameterFileSimpleTest, valid) { 8 | std::vector values; 9 | values.push_back(2.1); 10 | values.push_back(3.4); 11 | values.push_back(-2.9); 12 | ParameterFileSimple file(values); 13 | ASSERT_EQ(1, file.getTimes().size()); 14 | Parameters par = file.getParameters(0); 15 | ASSERT_EQ(3, par.size()); 16 | EXPECT_FLOAT_EQ(2.1, par[0]); 17 | EXPECT_FLOAT_EQ(3.4, par[1]); 18 | EXPECT_FLOAT_EQ(-2.9, par[2]); 19 | } 20 | TEST(ParameterFileSimpleTest, empty) { 21 | std::vector values; 22 | ParameterFileSimple file(values); 23 | ASSERT_EQ(1, file.getTimes().size()); 24 | Parameters par = file.getParameters(0); 25 | ASSERT_EQ(0, par.size()); 26 | EXPECT_FALSE(file.isLocationDependent()); 27 | std::vector locations; 28 | locations = file.getLocations(); 29 | EXPECT_EQ(0, locations.size()); 30 | } 31 | 32 | } 33 | int main(int argc, char **argv) { 34 | ::testing::InitGoogleTest(&argc, argv); 35 | return RUN_ALL_TESTS(); 36 | } 37 | -------------------------------------------------------------------------------- /src/client/Testing/Template: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace { 4 | class BaseTest : public ::testing::Test { 5 | protected: 6 | BaseTest() { 7 | // You can do set-up work for each test here. 8 | } 9 | 10 | virtual ~BaseTest() { 11 | // You can do clean-up work that doesn't throw exceptions here. 12 | } 13 | virtual void SetUp() { 14 | // Code here will be called immediately after the constructor (right 15 | // before each test). 16 | } 17 | 18 | virtual void TearDown() { 19 | // Code here will be called immediately after each test (right 20 | // before the destructor). 21 | } 22 | 23 | }; 24 | 25 | TEST_F(BaseTest, locations) { 26 | } 27 | } 28 | int main(int argc, char **argv) { 29 | ::testing::InitGoogleTest(&argc, argv); 30 | return RUN_ALL_TESTS(); 31 | } 32 | -------------------------------------------------------------------------------- /src/client/Testing/Variable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../Variable.h" 3 | #include "../Util.h" 4 | 5 | namespace { 6 | 7 | TEST(VariableTest, test) { 8 | Variable variable("test", "units"); 9 | EXPECT_EQ("test", variable.name()); 10 | EXPECT_EQ("units", variable.units()); 11 | 12 | Variable variable2 = variable; 13 | EXPECT_EQ("test", variable2.name()); 14 | EXPECT_EQ("units", variable2.units()); 15 | EXPECT_EQ(variable, variable2); 16 | } 17 | } 18 | int main(int argc, char **argv) { 19 | ::testing::InitGoogleTest(&argc, argv); 20 | return RUN_ALL_TESTS(); 21 | } 22 | -------------------------------------------------------------------------------- /src/client/Testing/devel/OperationalStatkraft.cpp: -------------------------------------------------------------------------------- 1 | #include "../File/Arome.h" 2 | #include "../Util.h" 3 | #include 4 | 5 | namespace { 6 | class OperationalStatkraftTest : public ::testing::Test { 7 | protected: 8 | OperationalStatkraftTest() { 9 | } 10 | virtual ~OperationalStatkraftTest() { 11 | } 12 | virtual void SetUp() { 13 | } 14 | virtual void TearDown() { 15 | } 16 | std::string getOperationalAromeFile(int iDate, int iInit) { 17 | std::stringstream ss; 18 | ss << "/opdata/arome2_5/arome_metcoop_default2_5km_" << iDate << "_" 19 | << std::setfill('0') << std::setw(2) << iInit << ".nc"; 20 | return ss.str(); 21 | } 22 | }; 23 | 24 | // Check that the AROME input files look right 25 | TEST_F(OperationalStatkraftTest, input) { 26 | int today = Util::getCurrentDate(); 27 | int yesterday = Util::calcDate(today, -24); 28 | std::string filename = getOperationalAromeFile(yesterday, 0); 29 | FileArome file(filename, true); 30 | 31 | // Dimensions 32 | EXPECT_EQ(1, file.getNumEns()); 33 | EXPECT_EQ(67, file.getNumTime()); 34 | EXPECT_EQ(929, file.getNumLat()); 35 | EXPECT_EQ(719, file.getNumLon()); 36 | 37 | // Variables 38 | EXPECT_TRUE(file.hasVariable(Variable::PrecipAcc)); 39 | EXPECT_TRUE(file.hasVariable(Variable::U)); 40 | EXPECT_TRUE(file.hasVariable(Variable::V)); 41 | EXPECT_TRUE(file.hasVariable(Variable::T)); 42 | 43 | // Contents 44 | Field precip = *file.getField(Variable::PrecipAcc, 1); 45 | EXPECT_TRUE(Util::isValid(precip[0][0][0])); 46 | 47 | } 48 | } 49 | int main(int argc, char **argv) { 50 | ::testing::InitGoogleTest(&argc, argv); 51 | return RUN_ALL_TESTS(); 52 | } 53 | -------------------------------------------------------------------------------- /src/client/Uuid.h: -------------------------------------------------------------------------------- 1 | // #include 2 | // Type for unique id. boost::uuids::uuid isn't always installed 3 | // with boost, so an int can be used instead. 4 | typedef int Uuid; // boost::uuids::uuid 5 | -------------------------------------------------------------------------------- /src/client/Variable.cpp: -------------------------------------------------------------------------------- 1 | #include "Variable.h" 2 | #include 3 | #include "Util.h" 4 | 5 | Variable::Variable() { 6 | } 7 | Variable::Variable(std::string iName, float iMin, float iMax, std::string iUnits, std::string iStandardName, int iLevel): 8 | mName(iName), 9 | mMin(iMin), 10 | mMax(iMax), 11 | mUnits(iUnits), 12 | mStandardName(iStandardName), 13 | mLevel(iLevel) { 14 | } 15 | Variable::Variable(std::string iName, std::string iUnits, std::string iStandardName, int iLevel): 16 | mName(iName), 17 | mMin(Util::MV), 18 | mMax(Util::MV), 19 | mUnits(iUnits), 20 | mStandardName(iStandardName), 21 | mLevel(iLevel) { 22 | } 23 | Variable::Variable(const Options& iOptions): 24 | mName(""), 25 | mMin(Util::MV), 26 | mMax(Util::MV), 27 | mLevel(Util::MV), 28 | mStandardName(""), 29 | mUnits("") { 30 | iOptions.getRequiredValue("name", mName); 31 | iOptions.getValue("standardName", mStandardName); 32 | iOptions.getValue("units", mUnits); 33 | iOptions.getValue("min", mMin); 34 | iOptions.getValue("max", mMax); 35 | iOptions.getValue("level", mLevel); 36 | iOptions.check(); 37 | } 38 | 39 | float Variable::min() const { 40 | return mMin; 41 | } 42 | void Variable::min(float iValue) { 43 | mMin = iValue; 44 | } 45 | 46 | float Variable::max() const { 47 | return mMax; 48 | } 49 | void Variable::max(float iValue) { 50 | mMax = iValue; 51 | } 52 | 53 | float Variable::level() const { 54 | return mLevel; 55 | } 56 | void Variable::level(int iValue) { 57 | mLevel = iValue; 58 | } 59 | 60 | std::string Variable::name() const { 61 | return mName; 62 | } 63 | void Variable::name(std::string iValue) { 64 | mName = iValue; 65 | } 66 | 67 | std::string Variable::units() const { 68 | return mUnits; 69 | } 70 | void Variable::units(std::string iValue) { 71 | mUnits = iValue; 72 | } 73 | 74 | std::string Variable::standardName() const { 75 | return mStandardName; 76 | } 77 | void Variable::standardName(std::string iValue) { 78 | mStandardName = iValue; 79 | } 80 | 81 | void Variable::add(const Options& iOptions) { 82 | std::string value; 83 | if(iOptions.getValue("units", value)) { 84 | mUnits = value; 85 | } 86 | if(iOptions.getValue("standardName", value)) { 87 | mStandardName = value; 88 | } 89 | float min; 90 | if(iOptions.getValue("min", min)) { 91 | mMin = min; 92 | } 93 | float max; 94 | if(iOptions.getValue("min", max)) { 95 | mMax = max; 96 | } 97 | } 98 | 99 | bool Variable::operator<(const Variable &right) const { 100 | return mName < right.name(); 101 | } 102 | 103 | bool Variable::operator==(const Variable &right) const { 104 | return mName == right.name(); 105 | } 106 | -------------------------------------------------------------------------------- /src/client/Variable.h: -------------------------------------------------------------------------------- 1 | #ifndef VARIABLE_H 2 | #define VARIABLE_H 3 | #include 4 | #include 5 | #include "Options.h" 6 | 7 | //! Represents a meteorological variable and its metadata 8 | class Variable { 9 | public: 10 | 11 | Variable(); 12 | Variable(std::string iName, float iMin, float iMax, std::string iUnits, std::string iStandardName, int iLevel=Util::MV); 13 | Variable(std::string iName, std::string iUnits="", std::string iStandardName="", int iLevel=Util::MV); 14 | Variable(const Options& iOptions); 15 | 16 | // Name of variable 17 | std::string name() const; 18 | void name(std::string); 19 | 20 | //! Get the minimum possible attainable value for this variable 21 | float min() const; 22 | void min(float iValue); 23 | 24 | //! Get the maximum possible attainable value for this variable 25 | float max() const; 26 | void max(float iValue); 27 | 28 | //! Get the level in the heigh dimension 29 | float level() const; 30 | void level(int iValue); 31 | 32 | //! Returns the units of the variable 33 | std::string units() const; 34 | void units(std::string iValue); 35 | 36 | //! Returns the NetcdfCF standard name 37 | std::string standardName() const; 38 | void standardName(std::string iValue); 39 | 40 | void add(const Options& iOptions); 41 | 42 | bool operator<(const Variable &right) const; 43 | bool operator==(const Variable &right) const; 44 | 45 | private: 46 | float mMin; 47 | float mMax; 48 | std::string mUnits; 49 | std::string mStandardName; 50 | std::string mName; 51 | int mLevel; 52 | }; 53 | #endif 54 | -------------------------------------------------------------------------------- /src/client/Version.h: -------------------------------------------------------------------------------- 1 | #ifndef GRIDPP_VERSION 2 | #define GRIDPP_VERSION "0.3.4" 3 | #endif 4 | -------------------------------------------------------------------------------- /src/client/run_all_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x # Output every command 4 | for program in $(dirname $0)/src/client/*.exe; do 5 | "${program}" || exit $? 6 | done 7 | -------------------------------------------------------------------------------- /swig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set up swig 2 | find_package(SWIG REQUIRED) 3 | include(${SWIG_USE_FILE}) 4 | 5 | # Add subdirectories for each language if desired 6 | option(BUILD_PYTHON "Build Python SWIG module" ON) 7 | if(BUILD_PACKAGE) 8 | add_subdirectory(python-packaging) 9 | else() 10 | if(BUILD_PYTHON) 11 | add_subdirectory(python) 12 | endif() 13 | option(BUILD_R "Build R SWIG module" ON) 14 | if(BUILD_R) 15 | add_subdirectory(R) 16 | endif() 17 | endif() 18 | -------------------------------------------------------------------------------- /swig/R/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(R REQUIRED) 2 | include_directories(${R_INCLUDE_DIR}) 3 | 4 | set(CMAKE_SWIG_FLAGS "") 5 | set_source_files_properties(../${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) 6 | 7 | include_directories(../../include) 8 | 9 | # Add swig module 10 | swig_add_library(rgridpp TYPE SHARED LANGUAGE r SOURCES ../${PROJECT_NAME}.i ${SOURCES}) 11 | swig_link_libraries(rgridpp ${PROJECT_NAME} ${R_LIBRARIES}) 12 | #set_property(SOURCE ../gridpp.i PROPERTY SWIG_MODULE_NAME gridpp) 13 | #set_property(TARGET rgridpp PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON) 14 | set_property(TARGET rgridpp PROPERTY OUTPUT_NAME gridpp) 15 | 16 | # Files to install with Python 17 | #set(PYTHON_INSTALL_FILES 18 | # ${CMAKE_CURRENT_BINARY_DIR}/gridpp.py 19 | # ${CMAKE_CURRENT_BINARY_DIR}/_gridpp.so) 20 | 21 | 22 | # Declare install target for python 23 | #install(TARGETS swig_example 24 | # COMMAND "${PYTHON_EXECUTABLE} setup.py" 25 | # COMPONENT swig-python) 26 | 27 | # Install target to call setup.py 28 | #target_link_libraries(rgridpp 29 | # PRIVATE 30 | # gridpp 31 | # ${R_LIBRARIES} 32 | # ) 33 | add_custom_target(build-r 34 | DEPENDS rgridpp 35 | ) 36 | -------------------------------------------------------------------------------- /swig/python-packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PythonLibs REQUIRED) 2 | find_package(PythonInterp REQUIRED) 3 | include_directories(${PYTHON_INCLUDE_PATH}) 4 | 5 | # Files to install with Python 6 | file(GLOB CPP_INCLUDE_FILES ${CMAKE_SOURCE_DIR}/include/*.h) 7 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST.in DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 8 | file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 9 | 10 | # Configure setup.py and copy to output directory 11 | set(SETUP_PY_IN ${CMAKE_CURRENT_SOURCE_DIR}/setup.in.py) 12 | set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) 13 | configure_file(${SETUP_PY_IN} ${SETUP_PY_OUT}) 14 | 15 | set(CMAKE_SWIG_FLAGS "") 16 | if(SWIG_VERSION GREATER_EQUAL 4) 17 | set(CMAKE_SWIG_FLAGS "-doxygen") 18 | endif() 19 | 20 | # Install target to call setup.py 21 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src/api) 22 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) 23 | 24 | # set_source_files_properties(../SWIG/${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) 25 | # swig_add_library(gridpp2 TYPE SHARED LANGUAGE python SOURCES ../SWIG/${PROJECT_NAME}.i) 26 | # set_target_properties(_gridpp2 PROPERTIES LINKER_LANGUAGE CXX) 27 | add_custom_target(package-python 28 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/README.md ${CMAKE_CURRENT_BINARY_DIR} 29 | COMMAND ${CMAKE_COMMAND} -E copy ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/src/api 30 | COMMAND ${CMAKE_COMMAND} -E copy ${CPP_INCLUDE_FILES} ${CMAKE_CURRENT_BINARY_DIR}/include 31 | # COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/include/dependencies ${CMAKE_CURRENT_BINARY_DIR}/include 32 | COMMAND ${SWIG_EXECUTABLE} -python ${CMAKE_SWIG_FLAGS} -outdir ${CMAKE_CURRENT_BINARY_DIR} -c++ -I./include -o ${CMAKE_CURRENT_BINARY_DIR}/gridppPYTHON_wrap.cxx ${SWIG_INTERFACE} 33 | # COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY_OUT} sdist --dist-dir ${CMAKE_BINARY_DIR} 34 | COMMAND python3 ${SETUP_PY_OUT} sdist --dist-dir ${CMAKE_BINARY_DIR} 35 | ) 36 | # add_dependencies(package-python _gridpp2) 37 | -------------------------------------------------------------------------------- /swig/python-packaging/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include include/gridpp.h 2 | -------------------------------------------------------------------------------- /swig/python-packaging/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel", 5 | "Cython", 6 | "numpy==1.12.1; python_version<'3.5'", 7 | "oldest-supported-numpy; python_version>='3.5' and python_version<'3.9'", 8 | "numpy==2.0.2; python_version>='3.9'", 9 | ] 10 | 11 | build-backend = "setuptools.build_meta" 12 | -------------------------------------------------------------------------------- /swig/python-packaging/setup.sh: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | cmake .. -DBUILD_PACKAGE=ON 4 | VERBOSE=1 make package-python 5 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/__init__.py -------------------------------------------------------------------------------- /tests/check_installation.py: -------------------------------------------------------------------------------- 1 | import gridpp 2 | import numpy as np 3 | 4 | gridpp.neighbourhood(np.zeros([10, 10]), 3, gridpp.Mean) 5 | 6 | print("Check complete") 7 | -------------------------------------------------------------------------------- /tests/files/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | -------------------------------------------------------------------------------- /tests/files/10x10.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10.nc -------------------------------------------------------------------------------- /tests/files/10x10_copy.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_copy.nc -------------------------------------------------------------------------------- /tests/files/10x10_ec.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_ec.nc -------------------------------------------------------------------------------- /tests/files/10x10_noPrecip.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_noPrecip.nc -------------------------------------------------------------------------------- /tests/files/10x10_param.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_param.nc -------------------------------------------------------------------------------- /tests/files/10x10_param_xy.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_param_xy.nc -------------------------------------------------------------------------------- /tests/files/10x10_param_zero_altitude.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/10x10_param_zero_altitude.nc -------------------------------------------------------------------------------- /tests/files/1x1.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/1x1.nc -------------------------------------------------------------------------------- /tests/files/3x3.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/3x3.nc -------------------------------------------------------------------------------- /tests/files/invalidText1.txt: -------------------------------------------------------------------------------- 1 | 0 60 10 100 3.2 1.5 5.1 2 | 1 60 10 100 4 1 2 3 | 0 60 8 100 11 21 4 | -------------------------------------------------------------------------------- /tests/files/kalmanEmpty.txt: -------------------------------------------------------------------------------- 1 | 2014 9 29 0 2 | 0 23 3 | -------------------------------------------------------------------------------- /tests/files/mask0.txt: -------------------------------------------------------------------------------- 1 | time lat lon elev p1 2 | 0 3 5 0 223000 3 | 0 4 6 0 336000 4 | -------------------------------------------------------------------------------- /tests/files/parameters.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 p4 p5 p6 p7 p8 2 | 0 -1.123032 1.478224 0.07221776 -0.03363793 2.032966 -0.02185749 0.8214697 -2.716967 3 | -------------------------------------------------------------------------------- /tests/files/parametersCycling.txt: -------------------------------------------------------------------------------- 1 | time p1 2 | 0 3 3 | 1 4 4 | 2 5 5 | -------------------------------------------------------------------------------- /tests/files/parametersEmpty.txt: -------------------------------------------------------------------------------- 1 | time 2 | 0 3 | 1 4 | 2 5 | 3 6 | 4 7 | 5 8 | -------------------------------------------------------------------------------- /tests/files/parametersInvalidEntries.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 2 | 0 32 13 321 3 | 1 32 13 321 4 | 2 32 qwe 321 5 | 3 32 13 321 6 | -------------------------------------------------------------------------------- /tests/files/parametersInvalidTime.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 2 | 0 12 3 13 3 | q 32 13 21 4 | 213 13 12 1 5 | -------------------------------------------------------------------------------- /tests/files/parametersKriging.txt: -------------------------------------------------------------------------------- 1 | time lat lon elev p1 2 | 0 9 9 800 -1.1 3 | 0 9 8 850 -0.3 4 | 0 5 5 140 4.2 5 | 1 5 5 140 -5.4 6 | 1 0 0 150 4 7 | -------------------------------------------------------------------------------- /tests/files/parametersMultipleTime.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 p4 p5 p6 p7 p8 2 | 0 1.075699 -999 0.2039351 -999 0.5821089 -999 -999 -999 3 | 8 -1.080637 1.609452 0.08756094 -0.0294557 3.423424 0.3137212 0.3284096 -4.51702 4 | 9 -0.7713833 1.366888 0.1871979 -0.04779648 2.178242 0.1646034 0.9576774 -3.116768 5 | 10 -0.9282167 1.486332 0.1946525 -0.04703624 1.236911 0.08811797 1.511213 -2.208275 6 | 30 0.04198875 0.883741 0.2623097 -0.03174205 -1.511473 -0.04039751 3.455134 -0.1697881 7 | 31 -0.4365125 1.387379 0.2239042 -0.01922128 0.0199745 0.256357 2.618814 -1.530157 8 | 32 -0.140551 1.115612 0.1800583 -0.005325649 1.134685 0.4762077 2.038421 -2.66559 9 | 40 -0.1730453 1.204422 0.1772766 0.001285765 -2.491633 -0.213032 3.875426 0.705554 10 | -------------------------------------------------------------------------------- /tests/files/parametersNoTime.txt: -------------------------------------------------------------------------------- 1 | p1 p2 p3 p4 p5 p6 p7 p8 p9 2 | -1.2021 -0.2876 3.5069 -1.185046 1.513832 0.001459 0.0917731 -0.0193989 0.0007985 3 | -------------------------------------------------------------------------------- /tests/files/parametersPhase.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 2 | 0 273.7 274.7 3 | -------------------------------------------------------------------------------- /tests/files/parametersQq.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 p4 p5 p6 p7 p8 2 | 0 250 250 260 300 290 303 300 315 3 | -------------------------------------------------------------------------------- /tests/files/parametersSingleTime.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 p4 p5 p6 p7 p8 p9 2 | 0 -1.2021 -0.2876 3.5069 -1.185046 1.513832 0.001459 0.0917731 -0.0193989 0.0007985 3 | -------------------------------------------------------------------------------- /tests/files/parametersUnevenRows.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 2 | 1 32 13 234 3 | 2 13 1 4 | 3 155 12 123 5 | -------------------------------------------------------------------------------- /tests/files/parametersWindDirection.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 p4 p5 p6 p7 p8 p9 2 | 0 1.1326 0.0294 0.0678 0.0360 -0.0348 -0.0191 0.0245 -0.0153 -0.0292 3 | -------------------------------------------------------------------------------- /tests/files/regression0order.txt: -------------------------------------------------------------------------------- 1 | time p1 2 | 0 0.3 3 | -------------------------------------------------------------------------------- /tests/files/regression1order.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 2 | 0 0.3 1.2 3 | -------------------------------------------------------------------------------- /tests/files/regression2order.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 p3 2 | 0 -0.3 1.02 -0.8 3 | -------------------------------------------------------------------------------- /tests/files/regressionInvalid1.txt: -------------------------------------------------------------------------------- 1 | time 2 | 0 3 | -------------------------------------------------------------------------------- /tests/files/regressionMissing.txt: -------------------------------------------------------------------------------- 1 | time p1 p2 2 | 0 -999 4 3 | -------------------------------------------------------------------------------- /tests/files/sampleObsFcst.txt: -------------------------------------------------------------------------------- 1 | time lat lon elev p1 p2 2 | 0 5 5 100 2 0 3 | -------------------------------------------------------------------------------- /tests/files/training.txt: -------------------------------------------------------------------------------- 1 | 0 5 3 20 278 280 2 | 0 5 3 20 275 282 3 | 0 5 3 20 282 280 4 | 0 5 3 20 280 284 5 | 0 4 4 20 283 292 6 | 0 4 4 20 289 294 7 | 0 9 3 20 296 293 8 | 0 9 3 20 270 273 9 | 1 5 3 20 275 280 10 | 1 5 3 20 274 282 11 | 1 5 3 20 285 280 12 | 1 5 3 20 282 284 13 | 1 4 4 20 270 272 14 | 1 4 4 20 292 294 15 | 1 9 3 20 280 281 16 | 1 9 3 20 277 279 17 | -------------------------------------------------------------------------------- /tests/files/trainingDataFcst.txt: -------------------------------------------------------------------------------- 1 | # offset lat lon elev ens1 ens2 ens3 2 | 0 5 3.2 20 4 4 4 3 | 0 5 3.2 20 6 5 4 4 | 0 5 3.2 20 9 8 10 5 | -------------------------------------------------------------------------------- /tests/files/trainingDataObs.txt: -------------------------------------------------------------------------------- 1 | # offset lat lon elev obs 2 | 0 5 3.2 20 3.2 3 | 0 5 3.2 20 5 4 | 0 5 3.2 20 14 5 | -------------------------------------------------------------------------------- /tests/files/trainingDataTemperature.txt: -------------------------------------------------------------------------------- 1 | # offset lat lon elev obs ens1 ens2 ens3 2 | 0 5 3.2 20 3.2 4 4 4 3 | 0 5 3.2 20 5 6 5 4 4 | 0 5 3.2 20 14 9 8 10 5 | -------------------------------------------------------------------------------- /tests/files/validArome1.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validArome1.nc -------------------------------------------------------------------------------- /tests/files/validArome2.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validArome2.nc -------------------------------------------------------------------------------- /tests/files/validEc1.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc1.nc -------------------------------------------------------------------------------- /tests/files/validEc2.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc2.nc -------------------------------------------------------------------------------- /tests/files/validEc3.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc3.nc -------------------------------------------------------------------------------- /tests/files/validEc_gph.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc_gph.nc -------------------------------------------------------------------------------- /tests/files/validEc_gph_no_x.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc_gph_no_x.nc -------------------------------------------------------------------------------- /tests/files/validEc_multidim_altitude.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validEc_multidim_altitude.nc -------------------------------------------------------------------------------- /tests/files/validNetcdf1.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdf1.nc -------------------------------------------------------------------------------- /tests/files/validNetcdf2.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdf2.nc -------------------------------------------------------------------------------- /tests/files/validNetcdf3.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdf3.nc -------------------------------------------------------------------------------- /tests/files/validNetcdf4.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdf4.nc -------------------------------------------------------------------------------- /tests/files/validNetcdfAnalysis.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdfAnalysis.nc -------------------------------------------------------------------------------- /tests/files/validNetcdfAnalysis2.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdfAnalysis2.nc -------------------------------------------------------------------------------- /tests/files/validNetcdfDimNames.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdfDimNames.nc -------------------------------------------------------------------------------- /tests/files/validNetcdfGeopotential.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metno/gridpp/7f357ccbfec56f7d59482fecb5b51cb4dd46fa59/tests/files/validNetcdfGeopotential.nc -------------------------------------------------------------------------------- /tests/files/validPoint1.txt: -------------------------------------------------------------------------------- 1 | 0 290 2 | 1 288 3 | -------------------------------------------------------------------------------- /tests/files/validPoint2.txt: -------------------------------------------------------------------------------- 1 | 0 290 291 2 | 1 288 300 3 | -------------------------------------------------------------------------------- /tests/files/validText1.txt: -------------------------------------------------------------------------------- 1 | 0 60 10 100 3.2 2 | 1 60 10 100 4.1 3 | -------------------------------------------------------------------------------- /tests/files/validText2.txt: -------------------------------------------------------------------------------- 1 | 0 60 10 100 3.2 1.5 5.1 2 | 1 60 10 100 4 1 2 3 | 0 60 8 100 11 21 -1 4 | -------------------------------------------------------------------------------- /tests/test_calc_gradient.py: -------------------------------------------------------------------------------- 1 | import gridpp 2 | import unittest 3 | import numpy as np 4 | import random 5 | 6 | class Test(unittest.TestCase): 7 | def test_simple(self): 8 | base = np.expand_dims([0, 1, 2, 7, 15], 0) 9 | values = np.expand_dims([0, 1, 2, 1, 0], 0) 10 | halfwidth = 1 11 | min_num = 0 12 | min_range = 0 13 | default_gradient = -11 14 | gradient = gridpp.calc_gradient(base, values, gridpp.LinearRegression, halfwidth, min_num, min_range, default_gradient) 15 | np.testing.assert_array_almost_equal(gradient, [[1, 1, -0.064516, -0.151163, -1/8]]) 16 | 17 | def test_small(self): 18 | """ Check when halfwidth is larger than the array """ 19 | base = np.expand_dims([0, 1, 2], 0) 20 | values = np.expand_dims([0, 1, 2], 0) 21 | halfwidth = 5 22 | min_num = 0 23 | min_range = 0 24 | default_gradient = -11 25 | gradient = gridpp.calc_gradient(base, values, gridpp.LinearRegression, halfwidth, min_num, min_range, default_gradient) 26 | np.testing.assert_array_almost_equal(gradient, [[1, 1, 1]]) 27 | 28 | def test_num_min(self): 29 | """ Check when num_min is small """ 30 | base = np.expand_dims([0, 1, 2, 3, np.nan], 0) 31 | values = np.expand_dims([np.nan, 1, 2, 3, 4], 0) 32 | halfwidth = 1 33 | min_num = 2 34 | min_range = 0 35 | default_gradient = -11 36 | gradient = gridpp.calc_gradient(base, values, gridpp.LinearRegression, halfwidth, min_num, min_range, default_gradient) 37 | np.testing.assert_array_almost_equal(gradient, [[-11, 1, 1, 1, -11]]) 38 | 39 | def test_invalid_arguments(self): 40 | base = np.zeros([3, 2]) 41 | values = np.zeros([3, 2]) 42 | method = gridpp.LinearRegression 43 | halfwidth = 5 44 | min_num = 0 45 | min_range = 0 46 | dg = -11 47 | with self.assertRaises(ValueError) as e: 48 | gridpp.calc_gradient(np.zeros([3, 2]), np.zeros([2, 3]), method, halfwidth, min_num, min_range, dg) 49 | with self.assertRaises(ValueError) as e: 50 | gridpp.calc_gradient(base, values, method, -1, min_num, min_range, dg) 51 | with self.assertRaises(ValueError) as e: 52 | gridpp.calc_gradient(base, values, method, halfwidth, -1, min_range, dg) 53 | with self.assertRaises(ValueError) as e: 54 | gridpp.calc_gradient(base, values, method, halfwidth, min_num, -1, dg) 55 | 56 | def test_nan(self): 57 | base = np.random.rand(10, 10) # np.zeros([10, 10]) 58 | base[3:8, 3:8] = np.nan 59 | values = np.random.rand(10, 10) 60 | method = gridpp.LinearRegression 61 | min_num = 0 62 | min_range = 0 63 | halfwidth = 1 64 | default_gradient = 1 65 | output = gridpp.calc_gradient(base, values, method, halfwidth, min_num, min_range, default_gradient) 66 | 67 | 68 | if __name__ == '__main__': 69 | unittest.main() 70 | -------------------------------------------------------------------------------- /tests/test_distance.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_point_to_grid_geodetic(self): 9 | lons, lats = np.meshgrid([0, 1, 2], [0, 1]) 10 | grid = gridpp.Grid(lats, lons) 11 | points = gridpp.Points([0, 0], [0, 0.6]) 12 | # TODO 13 | # np.testing.assert_array_almost_equal(gridpp.distance(points, grid, 1), [[0, 44527.79], [111319.49, 119893.92], [222638.98,227046.33]]) 14 | 15 | def test_point_to_grid_cartesian(self): 16 | lons, lats = np.meshgrid([0, 1000, 2000], [0, 1000]) 17 | grid = gridpp.Grid(lats, lons, 0*lats, 0*lats, gridpp.Cartesian) 18 | points = gridpp.Points([0, 0], [0, 600], [0,0], [0,0], gridpp.Cartesian) 19 | np.testing.assert_array_almost_equal(gridpp.distance(points, grid, 1), [[0, 400, 1400], [1000, np.sqrt(1000**2 + 400**2), np.sqrt(1000**2 + 1400**2)]], 4) 20 | np.testing.assert_array_almost_equal(gridpp.distance(points, grid, 2), [[600, 1000, 2000], [np.sqrt(1000**2 + 600**2), np.sqrt(2) * 1000, np.sqrt(1000**2 + 2000**2)]], 4) 21 | np.testing.assert_array_almost_equal(gridpp.distance(points, grid, 10), [[600, 1000, 2000], [np.sqrt(1000**2 + 600**2), np.sqrt(2) * 1000, np.sqrt(1000**2 + 2000**2)]], 4) 22 | 23 | def test_grid_to_point_geodetic(self): 24 | lons, lats = np.meshgrid([0, 1, 2], [0, 1]) 25 | grid = gridpp.Grid(lats, lons) 26 | points = gridpp.Points([0, 0], [0, 0.6]) 27 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 1), [0, 44528], 0) 28 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 2), [111319.49, 66791.7], 0) 29 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 10), [248907.83, 191514.84], 0) 30 | 31 | def test_grid_to_point_cartesian(self): 32 | lons, lats = np.meshgrid([0, 1000, 2000], [0, 1000]) 33 | grid = gridpp.Grid(lats, lons, 0*lats, 0*lats, gridpp.Cartesian) 34 | points = gridpp.Points([0, 0], [0, 600], [0,0], [0,0], gridpp.Cartesian) 35 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 1), [0, 400], 4) 36 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 2), [1000, 600], 4) 37 | np.testing.assert_array_almost_equal(gridpp.distance(grid, points, 10), [np.sqrt(1000**2 + 2000**2), np.sqrt(1000**2 + 1400**2)], 4) 38 | 39 | 40 | if __name__ == '__main__': 41 | unittest.main() 42 | -------------------------------------------------------------------------------- /tests/test_distribution.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import time 6 | import collections 7 | 8 | 9 | class Test(unittest.TestCase): 10 | def test_1(self): 11 | levels = [0.5, 0.5, 0.5] 12 | shape = [1, 2, 7.5] 13 | scale = [2, 2, 1] 14 | output = gridpp.gamma_inv(levels, shape, scale) 15 | expected = [1.386,3.357, 7.169] 16 | np.testing.assert_array_almost_equal(output, expected, 3) 17 | 18 | def test_invalid_levels(self): 19 | ok_args = collections.OrderedDict({ 20 | 'levels': [0.1], 21 | 'shape': [1], 22 | 'scale': [1], 23 | }) 24 | invalid_args = { 25 | 'levels': [[-0.1], [1.1], [np.nan]], 26 | 'shape': [[-1], [np.nan]], 27 | 'scale': [[-1], [np.nan]], 28 | } 29 | 30 | for key in invalid_args.keys(): 31 | for arg in invalid_args[key]: 32 | args0 = ok_args.copy() 33 | args0[key] = arg 34 | q = [args0[f] for f in args0] 35 | with self.subTest(key=key, arg=arg): 36 | with self.assertRaises(ValueError) as e: 37 | output = gridpp.gamma_inv(*q) 38 | 39 | invalid = [-0.1, 1.1, np.nan] 40 | for i in range(len(invalid)): 41 | levels = [0.5, invalid[i], 0.5] 42 | shape = [1,1,1] 43 | scale = [1,1,1] 44 | with self.assertRaises(ValueError) as e: 45 | output = gridpp.gamma_inv(levels, shape, scale) 46 | 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/test_doping.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import collections 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def test_simple(self): 10 | N = 11 11 | x = np.linspace(0, 10000, N) 12 | y = np.linspace(0, 10000, N) 13 | xx, yy = np.meshgrid(x, y) 14 | grid = gridpp.Grid(xx, yy, 0*xx, 0*xx, gridpp.Cartesian) 15 | points = gridpp.Points([3000, 5000], [10000, 5000], [0, 0], [0, 0], gridpp.Cartesian) 16 | obs = [-10, 10] 17 | background = np.zeros([len(x), len(x)]) 18 | max_elev_diff = 200 19 | half_width = [1, 1] 20 | output = gridpp.doping_square(grid, background, points, obs, half_width, max_elev_diff) 21 | 22 | # Expect a field of 0s, with one square of 10s at 5,5 and one (half) square of -10 at 10,3 23 | expected = np.zeros([N, N]) 24 | expected[4:7, 4:7] = 10 25 | expected[9:11, 2:5] = -10 26 | np.testing.assert_array_almost_equal(output, expected) 27 | 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/test_downscale_probability.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def setUp(self) -> None: 9 | lons1, lats1 = np.meshgrid([10, 30], [50, 30]) 10 | lons2, lats2 = np.meshgrid([5, 15, 25], [45, 35, 25]) 11 | self.grid1 = gridpp.Grid(lats1, lons1) 12 | self.grid2 = gridpp.Grid(lats2, lons2) 13 | self.values = np.moveaxis(np.array([ [[-1., -1.],[-1., -1.]], 14 | [[0., 0.], [0., 0.]], 15 | [[1., 1.], [1., 1.]] 16 | ]), 0, -1) 17 | self.thresholds = np.array([ [-2., -0.5, 0.5], 18 | [0., 1., -1.], 19 | [2., 0.5, 0.]]) 20 | 21 | def test_downscale_probability_leq(self): 22 | output = gridpp.downscale_probability(self.grid1, self.grid2, self.values, self.thresholds, gridpp.Leq) 23 | np.testing.assert_array_equal(output, np.array([[0., 1./3., 2./3.], [2./3., 1., 1./3.], [1., 2./3., 2./3.]], dtype=np.float32)) 24 | 25 | def test_downscale_probability_gt(self): 26 | output = gridpp.downscale_probability(self.grid1, self.grid2, self.values, self.thresholds, gridpp.Gt) 27 | np.testing.assert_array_equal(output, np.array([[1., 2./3., 1./3.], [1./3., 0., 2./3.], [0., 1./3., 1./3.]], dtype=np.float32)) 28 | 29 | def test_downscale_probability_geq(self): 30 | self.values[1, 1, 0] = np.NaN 31 | output = gridpp.downscale_probability(self.grid1, self.grid2, self.values, self.thresholds, gridpp.Geq) 32 | np.testing.assert_array_equal(output, np.array([[1., 2./3., 1./3.], [2./3., 1./3., 1.], [0., 1./3., 1.]], dtype=np.float32)) 33 | 34 | def test_downscale_probability_lt(self): 35 | self.values[0, 0, :] = np.NaN 36 | output = gridpp.downscale_probability(self.grid1, self.grid2, self.values, self.thresholds, gridpp.Lt) 37 | np.testing.assert_array_equal(output, np.array([[np.NaN, np.NaN, 2./3.], [1./3., 2./3., 0.], [1., 2./3., 1./3.]], dtype=np.float32)) 38 | -------------------------------------------------------------------------------- /tests/test_fill.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | lons, lats = np.meshgrid([0, 1, 2], [0, 1, 2]) 8 | grid = gridpp.Grid(lats, lons) 9 | points = gridpp.Points([0, 1], [0, 1]) 10 | 11 | 12 | class Test(unittest.TestCase): 13 | def test_invalid_radii(self): 14 | values = np.zeros([3, 3]) 15 | radii = [-1, -1] 16 | value = 1 17 | for outside in [False, True]: 18 | with self.assertRaises(Exception) as e: 19 | gridpp.fill(grid, values, points, radii, value, outside) 20 | 21 | def test_invalid_number_of_radii(self): 22 | values = np.zeros([3, 3]) 23 | value = 1 24 | for outside in [False, True]: 25 | with self.assertRaises(Exception) as e: 26 | gridpp.fill(grid, values, points, [1], value, outside) 27 | 28 | def test_dimension_mismatch(self): 29 | values = np.zeros([3, 2]) 30 | radii = [1, 1] 31 | value = 1 32 | for outside in [False, True]: 33 | with self.assertRaises(Exception) as e: 34 | gridpp.fill(grid, values, points, radii, value, outside) 35 | 36 | def test_1(self): 37 | lons, lats = np.meshgrid([0, 1, 2, 3, 4], [0, 1, 2, 3, 4]) 38 | grid = gridpp.Grid(lats * 1000, lons * 1000, np.zeros([5, 5]), np.zeros([5, 5]), gridpp.Cartesian) 39 | points = gridpp.Points([0, 0, 3000], [0, 3000, 3000], [0, 0, 0], [0, 0, 0], gridpp.Cartesian) 40 | values = np.zeros([5, 5]) 41 | radii = [1010, 10, 2010] 42 | value = 1 43 | outside = False 44 | output = gridpp.fill(grid, values, points, radii, value, outside) 45 | np.testing.assert_array_almost_equal(output, [[1, 1, 0, 1, 0], [1, 0, 0, 1, 0],[0, 0, 1, 1, 1], [0, 1, 1, 1, 1], [0, 0, 1, 1, 1]]) 46 | 47 | outside = True 48 | output = gridpp.fill(grid, values, points, radii, value, outside) 49 | np.testing.assert_array_almost_equal(output, [[0, 0, 1, 0, 1], [0, 1, 1, 0, 1],[1, 1, 0, 0, 0], [1, 0, 0, 0, 0], [1, 1, 0, 0, 0]]) 50 | 51 | 52 | if __name__ == '__main__': 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /tests/test_fill_missing.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_linear(self): 9 | """Check that we are able to recover the missing values""" 10 | values0 = np.reshape(np.arange(25), [5, 5]).astype(float) 11 | 12 | # Add some missing values 13 | values = np.copy(values0) 14 | values[2, 1:4] = np.nan 15 | values[1, 1] = np.nan 16 | output = gridpp.fill_missing(values) 17 | np.testing.assert_array_equal(output, values0) 18 | 19 | def test_missing_on_edge(self): 20 | """Check that we can recover when one dimension has missing all the way to the edge""" 21 | values0 = np.reshape(np.arange(25), [5, 5]).astype(float) 22 | 23 | # Add some missing values 24 | values = np.copy(values0) 25 | values[1, 1] = np.nan 26 | values[1, 3:5] = np.nan 27 | values[1, 4] = np.nan 28 | values[1, 0:2] = np.nan 29 | output = gridpp.fill_missing(values) 30 | np.testing.assert_array_equal(output, values0) 31 | 32 | def test_missing_on_y_edge(self): 33 | """Regression test for bug when X is wider than Y, and a y-slice has missing all the way to the upper edge""" 34 | values0 = np.reshape(np.arange(24), [3, 8]).astype(float) 35 | values = np.copy(values0) 36 | values[1:, 1] = np.nan 37 | output = gridpp.fill_missing(values) 38 | np.testing.assert_array_equal(output, values0) 39 | 40 | def test_missing_on_both_edges(self): 41 | """Check that we are able to recover the missing values""" 42 | values0 = np.reshape(np.arange(25), [5, 5]).astype(float) 43 | 44 | # Add some missing values 45 | values = np.copy(values0) 46 | values[3:5, 3:5] = np.nan 47 | output = gridpp.fill_missing(values) 48 | np.testing.assert_array_equal(output[0:3, :], values0[0:3, :]) 49 | np.testing.assert_array_equal(output[:, 0:3], values0[:, 0:3]) 50 | np.testing.assert_array_equal(output[3:5, 3:5], np.nan * np.zeros([2, 2])) 51 | 52 | 53 | if __name__ == '__main__': 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /tests/test_get_neighbourhood_thresholds.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_invalid_arguments(self): 9 | """Check that exception is thrown for invalid arguments""" 10 | field = np.ones([5, 5]) 11 | nums = [-1, 0] 12 | for num in nums: 13 | with self.assertRaises(ValueError) as e: 14 | gridpp.get_neighbourhood_thresholds(field, num) 15 | 16 | def test_empty_argument(self): 17 | """Check that no thresholds are returned for an empty input""" 18 | for num in [1, 5]: 19 | np.testing.assert_array_equal(gridpp.get_neighbourhood_thresholds([], num), []) 20 | 21 | def test_high_num(self): 22 | """Check proper results when num > size of the input""" 23 | field = np.reshape(np.arange(4), [2, 2]) 24 | nums = [4, 5, 6] 25 | for num in nums: 26 | q = gridpp.get_neighbourhood_thresholds(field, num) 27 | np.testing.assert_array_equal(q, [0, 1, 2, 3]) 28 | 29 | def test_duplicates(self): 30 | """Check a real example with duplicates""" 31 | field = np.reshape([0, 0, 2, 3, 4, 5, 6, 11, 8, 9, 10, 11], [3, 4]) 32 | nums = range(1, 5) 33 | for num in nums: 34 | q = gridpp.get_neighbourhood_thresholds(field, num) 35 | self.assertTrue((q >= 0).all()) 36 | self.assertTrue((q <= 11).all()) 37 | 38 | def test_3d(self): 39 | np.random.seed(1000) 40 | values = np.random.rand(10, 10) 41 | values3 = np.zeros([10, 10, 5]) 42 | for i in range(5): 43 | values3[:, :, i] = values 44 | 45 | nums = [1, 5] 46 | for num in nums: 47 | output_2d = gridpp.get_neighbourhood_thresholds(values, num) 48 | output_3d = gridpp.get_neighbourhood_thresholds(values3, num) 49 | np.testing.assert_array_almost_equal(output_2d, output_3d) 50 | 51 | 52 | if __name__ == '__main__': 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /tests/test_init_vec.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_vec2(self): 9 | X = -1 10 | for func in [gridpp.init_vec2, gridpp.init_ivec2]: 11 | np.testing.assert_array_equal(func(2, 2, X), [[X, X], [X, X]]) 12 | np.testing.assert_array_equal(func(1, 2, X), [[X, X]]) 13 | np.testing.assert_array_equal(func(2, 1, X), [[X], [X]]) 14 | 15 | def test_vec3(self): 16 | X = -1 17 | for func in [gridpp.init_vec3, gridpp.init_ivec3]: 18 | np.testing.assert_array_equal(func(2, 2, 3, X), [[[X,X,X], [X,X,X]], [[X, X, X], [X,X,X]]]) 19 | np.testing.assert_array_equal(func(1, 2, 3, X), [[[X,X,X], [X,X,X]]]) 20 | np.testing.assert_array_equal(func(2, 1, 3, X), [[[X,X,X]], [[X, X, X]]]) 21 | np.testing.assert_array_equal(func(2, 2, 1, X), [[[X], [X]], [[X], [X]]]) 22 | 23 | if __name__ == '__main__': 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /tests/test_local_distribution_correction.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_parallel(self): 9 | """Check that the parallel version gives the same results""" 10 | lons, lats = np.meshgrid(np.linspace(0, 100000, 100), np.linspace(0, 100000, 100)) 11 | grid = gridpp.Grid(lats, lons, 0*lats, 0*lons, gridpp.Cartesian) 12 | 13 | lats = np.random.rand(100) * 100000 14 | lons = np.random.rand(100) * 100000 15 | points = gridpp.Points(lats, lons, 0*lats, 0*lons, gridpp.Cartesian) 16 | a = np.random.rand(100, 100) 17 | b = np.random.rand(1000) 18 | c = np.random.rand(1000) 19 | structure = gridpp.BarnesStructure(2500) 20 | values = dict() 21 | for ncores in [1, 8]: 22 | gridpp.set_omp_threads(ncores) 23 | values[ncores] = gridpp.local_distribution_correction(grid, a, 24 | points, b, c, structure, 0.1, 0.9, 5) 25 | 26 | np.testing.assert_array_equal(values[1], values[8]) 27 | 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/test_memory.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import os 6 | # import psutil 7 | 8 | 9 | def memory_usage(): 10 | process = psutil.Process(os.getpid()) 11 | return process.memory_info().rss 12 | 13 | 14 | class MemoryTest(unittest.TestCase): 15 | def test_memory_leak_objects(self): 16 | """Checks if there is a memory leak when creating gridpp objects""" 17 | return 18 | N = 1000 19 | last = None 20 | min = None 21 | max = None 22 | for i in range(5): 23 | lats = np.random.rand(N * N) 24 | lons = np.random.rand(N * N) 25 | lats2 = np.random.rand(N, N) 26 | lons2 = np.random.rand(N, N) 27 | points = gridpp.Points(lats, lons) 28 | grid = gridpp.Grid(lats2, lons2) 29 | curr = memory_usage() 30 | if min is None or max is None: 31 | min = curr 32 | max = curr 33 | if curr < min: 34 | min = curr 35 | if curr > max: 36 | max = curr 37 | last = curr 38 | self.assertTrue(max / min < 2) 39 | 40 | 41 | def test_memory_leak_input(self): 42 | """Checks if there is a memory leak when passing vectors""" 43 | return 44 | N = 1000 45 | last = None 46 | min = None 47 | max = None 48 | for i in range(5): 49 | vec = np.random.rand(N * N) 50 | vec2 = np.random.rand(N, N) 51 | output2 = gridpp.neighbourhood(vec2, 7, gridpp.Mean) 52 | output = gridpp.dewpoint(vec, vec) 53 | curr = memory_usage() 54 | if min is None or max is None: 55 | min = curr 56 | max = curr 57 | if curr < min: 58 | min = curr 59 | if curr > max: 60 | max = curr 61 | last = curr 62 | self.assertTrue(max / min < 2) 63 | 64 | 65 | if __name__ == '__main__': 66 | unittest.main() 67 | -------------------------------------------------------------------------------- /tests/test_metric_optimizer.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import time 6 | 7 | 8 | class Test(unittest.TestCase): 9 | """ Test that the curve reproduces the reference values """ 10 | def test_get_optimal_threshold(self): 11 | obs = [0, 1, 1.4, 1.6, 2] 12 | fcst = [1, 2, 2.4, 2.6, 3] 13 | threshold = 1.5 14 | metric = gridpp.Bias 15 | output = gridpp.get_optimal_threshold(obs, fcst, threshold, metric) 16 | self.assertTrue(output > 2.4 and output < 2.6) 17 | 18 | def test_metric_optimizer_curve(self): 19 | # Check that a quadratic relationship gets fixed 20 | obs = np.linspace(0, 10, 101) 21 | fcst = obs ** 2 22 | thresholds = [1, 2, 3, 4] 23 | for metric in [gridpp.Pc, gridpp.Bias]: 24 | with self.subTest(metric=metric): 25 | x, y = gridpp.metric_optimizer_curve(obs, fcst, thresholds, metric) 26 | expected = y**2 27 | np.testing.assert_array_almost_equal(expected, x, 0) 28 | 29 | def test_calc_score(self): 30 | # Use verif package to check results 31 | obs = [1, 2, 3] 32 | fcst = [2, 1, 3] 33 | thresholds = [-1, 1.5, 2.5, 3] 34 | expected = { 35 | gridpp.Bias: [1, 1, 1, 1], 36 | gridpp.Pc: [1, 0.333, 1, 1], 37 | gridpp.Ets: [np.nan, -0.2, 1, np.nan], 38 | gridpp.Kss: [np.nan, -0.5, 1, np.nan], 39 | gridpp.Hss: [np.nan, -0.5, 1, np.nan], 40 | gridpp.Ts: [1, 0.333, 1, np.nan] 41 | } 42 | for metric in expected.keys(): 43 | for t, threshold in enumerate(thresholds): 44 | with self.subTest(metric=metric, threshold=threshold): 45 | output = gridpp.calc_score(obs, fcst, threshold, metric) 46 | np.testing.assert_almost_equal(expected[metric][t], output, 3) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/test_neighbourhood_quantile.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | lats = [60, 60, 60, 60, 60, 70] 8 | lons = [10,10.1,10.2,10.3,10.4, 10] 9 | 10 | """Simple check 11 | 20 21 22 23 24 12 | 15 16 17 18 19 13 | 10 11 12 13 nan 14 | 5 6 7 nan 9 15 | 0 1 2 3 4 16 | """ 17 | values = np.reshape(range(25), [5, 5]).astype(float) 18 | values[1, 3] = np.nan 19 | values[2, 4] = np.nan 20 | values = np.array(values) 21 | 22 | class Test(unittest.TestCase): 23 | def test_invalid_arguments(self): 24 | field = np.ones([5, 5]) 25 | halfwidth = -1 26 | quantiles = [-0.1, 1.1, np.nan] 27 | 28 | for quantile in quantiles: 29 | with self.assertRaises(ValueError) as e: 30 | gridpp.neighbourhood_quantile(field, quantile, halfwidth) 31 | 32 | def test_empty_argument(self): 33 | halfwidth = 3 34 | for quantile in [0, 0.5, 1]: 35 | output = gridpp.neighbourhood_quantile([[]], quantile, halfwidth) 36 | self.assertEqual(len(output.shape), 2) 37 | self.assertEqual(output.shape[0], 0) 38 | self.assertEqual(output.shape[1], 0) 39 | 40 | def test_missing(self): 41 | """Checks that missing values are handled correctly""" 42 | empty = np.zeros([5, 5]) 43 | empty[0:3, 0:3] = np.nan 44 | output = gridpp.neighbourhood_quantile(empty, 0.5, 1) 45 | self.assertTrue(np.isnan(np.array(output)[0:2,0:2]).all()) 46 | 47 | def test_quantile(self): 48 | output = np.array(gridpp.neighbourhood_quantile(values, 0.5, 1)) 49 | self.assertEqual(output[2][2], 12.5) 50 | self.assertEqual(output[2][3], 13) 51 | self.assertEqual(output[0][4], 4) 52 | 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tests/test_neighbourhood_search.py: -------------------------------------------------------------------------------- 1 | import gridpp 2 | import unittest 3 | import numpy as np 4 | import random 5 | 6 | class Test(unittest.TestCase): 7 | def setUp(self): 8 | self.input_base = [[0, 0.5],[0.7,1.0]] 9 | self.input_values = [[10,15],[17,20]] 10 | 11 | self.input_base2 = [[0.9, 0.9],[1.0, 1.0]] 12 | self.input_values2 = [[12,14],[13,18]] 13 | 14 | self.input_base_nan = [[np.nan, 0.5],[0.7,1.0]] 15 | self.input_values_nan = [[10,15],[np.nan, 20]] 16 | 17 | def get_apply_array(self, base, min_value, max_value): 18 | return (np.array(base) >= min_value) & (np.array(base) <= max_value) 19 | 20 | def test_size_of_inputs(self): 21 | apply_array = self.get_apply_array(self.input_base, 0, 0.95) 22 | np.testing.assert_array_equal(len(self.input_base), 23 | len(gridpp.neighbourhood_search(self.input_values, self.input_base, 1, 0.7, 1.0, 0.1, apply_array))) 24 | 25 | def test_results(self): 26 | apply_array = self.get_apply_array(self.input_base, 0, 0.95) 27 | np.testing.assert_array_equal([[18.5, 18.5] , [18.5, 20]], 28 | gridpp.neighbourhood_search(self.input_values, self.input_base, 1,0.7,1.0,0.1, apply_array)) 29 | 30 | def test_no_apply_array(self): 31 | np.testing.assert_array_equal([[18.5, 18.5] , [18.5, 18.5]], 32 | gridpp.neighbourhood_search(self.input_values, self.input_base, 1,0.7,1.0,0.1)) 33 | 34 | def test_no_function_results(self): 35 | apply_array = self.get_apply_array(self.input_base2, 0, 0.85) 36 | np.testing.assert_array_equal(self.input_values2, 37 | gridpp.neighbourhood_search(self.input_values2, self.input_base2,1,0.7,1.0,0.1, apply_array)) 38 | 39 | def test_nan_input(self): 40 | apply_array = self.get_apply_array(self.input_base_nan, 0, 0.95) 41 | np.testing.assert_array_equal([[10, 20],[20, 20]], 42 | gridpp.neighbourhood_search(self.input_values_nan, self.input_base_nan, 1,0.7, 1.0, 0.1, apply_array)) 43 | 44 | def test_simple(self): 45 | output = gridpp.neighbourhood_search([[0, 1, 2]], [[0.5, 0.5, 1]], 1, 0.7, 1, 0.1) 46 | np.testing.assert_array_equal(output, [[0, 2, 2]]) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /tests/test_optimal_interpolation_ens.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import collections 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def test_no_obs(self): 10 | """ Check that we get the same field back if there are no observations """ 11 | grid = gridpp.Points([0], [0]) 12 | E = 3 13 | points = gridpp.Points([], []) 14 | psigmas = [] 15 | structure = gridpp.BarnesStructure(500000) 16 | pobs = [] 17 | background = np.zeros([grid.size(), E]) 18 | pbackground = np.zeros([0, E]) 19 | max_points = 10 20 | output0 = gridpp.optimal_interpolation_ensi(grid, background, points, pobs, psigmas, pbackground, structure, max_points) 21 | np.testing.assert_almost_equal(output0, background) 22 | 23 | def test_some_missing_obs(self): 24 | """ Check that if one observation is missing, that the whole output isn't nan """ 25 | grid = gridpp.Points([0], [0]) 26 | E = 3 27 | points = gridpp.Points([0, 0.1], [0, 0.1]) 28 | psigmas = [1, 1] 29 | structure = gridpp.BarnesStructure(500000) 30 | pobs = [np.nan, 0] 31 | background = np.zeros([grid.size(), E]) 32 | pbackground = np.zeros([points.size(), E]) 33 | max_points = 10 34 | output0 = gridpp.optimal_interpolation_ensi(grid, background, points, pobs, psigmas, pbackground, structure, max_points) 35 | np.testing.assert_almost_equal(output0, background) 36 | 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /tests/test_point.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_initialize(self): 9 | lat = 60 10 | lon = 0 11 | elev = 10 12 | laf = 0.3 13 | p = gridpp.Point(lat, lon, elev, laf, gridpp.Geodetic) 14 | self.assertAlmostEqual(p.lat, lat) 15 | self.assertAlmostEqual(p.lon, lon) 16 | self.assertAlmostEqual(p.elev, elev) 17 | self.assertAlmostEqual(p.laf, laf, delta=0.00001) 18 | 19 | s, x, y, z = gridpp.convert_coordinates(lat, lon, gridpp.Geodetic) 20 | self.assertAlmostEqual(p.x, x) 21 | self.assertAlmostEqual(p.y, y) 22 | self.assertAlmostEqual(p.z, z) 23 | 24 | p = gridpp.Point(lat, lon, elev, laf, gridpp.Geodetic, x, y, z) 25 | self.assertAlmostEqual(p.lat, lat) 26 | self.assertAlmostEqual(p.lon, lon) 27 | self.assertAlmostEqual(p.elev, elev) 28 | self.assertAlmostEqual(p.laf, laf, delta=0.00001) 29 | self.assertAlmostEqual(p.x, x) 30 | self.assertAlmostEqual(p.y, y) 31 | self.assertAlmostEqual(p.z, z) 32 | 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /tests/test_point_in_rectangle.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class Test(unittest.TestCase): 8 | def test_rotated(self): 9 | A = gridpp.Point(-8.59, -8.89) 10 | B = gridpp.Point(-3.41, -11.89) 11 | C = gridpp.Point(2.60, -1.5) 12 | D = gridpp.Point(-2.60, 1.5) 13 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(-3, -5.20))) 14 | 15 | def test_not_in_order(self): 16 | """Check case where 4 points are not in order""" 17 | A = gridpp.Point(0, 0) 18 | B = gridpp.Point(1, 1) 19 | C = gridpp.Point(0, 1) 20 | D = gridpp.Point(1, 0) 21 | self.assertFalse(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.6, 0.6))) 22 | 23 | def test_skew(self): 24 | A = gridpp.Point(0, 0) 25 | B = gridpp.Point(1, 0.25) 26 | C = gridpp.Point(1, 1.25) 27 | D = gridpp.Point(0, 1) 28 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.5, 0.5))) 29 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0, 0))) 30 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(1, 1.25))) 31 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.5, 0.25))) 32 | self.assertFalse(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.5, 0))) 33 | 34 | def test_cw(self): 35 | A = gridpp.Point(0, 0) 36 | B = gridpp.Point(1, 0) 37 | C = gridpp.Point(1, 1) 38 | D = gridpp.Point(0, 1) 39 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.5, 0.5))) 40 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0, 0))) 41 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(1, 1))) 42 | self.assertFalse(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(-0.1, -0.1))) 43 | 44 | def test_ccw(self): 45 | A = gridpp.Point(0, 0) 46 | B = gridpp.Point(0, 1) 47 | C = gridpp.Point(1, 1) 48 | D = gridpp.Point(1, 0) 49 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0.5, 0.5))) 50 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(0, 0))) 51 | self.assertTrue(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(1, 1))) 52 | self.assertFalse(gridpp.point_in_rectangle(A, B, C, D, gridpp.Point(-0.1, -0.1))) 53 | 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /tests/test_pressure.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import os 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def test_missing_values(self): 10 | self.assertTrue(np.isnan(gridpp.pressure(np.nan, 0, 101325))) 11 | self.assertTrue(np.isnan(gridpp.pressure(0, np.nan, 101325))) 12 | self.assertTrue(np.isnan(gridpp.pressure(0, 100, np.nan))) 13 | 14 | def test_no_elev_diff(self): 15 | for pressure in [101325, 500]: 16 | self.assertAlmostEqual(gridpp.pressure(0, 0, pressure), pressure) 17 | 18 | def test_standard(self): 19 | self.assertAlmostEqual(gridpp.pressure(0, 1000, 101325, 288.15), 89996.7, 0) 20 | self.assertAlmostEqual(gridpp.pressure(1000, 0, 89996.7, 288.15), 101325, 0) 21 | 22 | def test_temperature(self): 23 | self.assertAlmostEqual(gridpp.pressure(0, 1000, 101325, 258.15), 88765.2, 0) 24 | self.assertAlmostEqual(gridpp.pressure(1000, 0, 88765.2, 258.15), 101325, 0) 25 | 26 | def test_no_pressure(self): 27 | self.assertAlmostEqual(gridpp.pressure(0, 0, 0), 0) 28 | self.assertAlmostEqual(gridpp.pressure(0, 1000, 0), 0) 29 | 30 | def test_no_temperature(self): 31 | self.assertTrue(np.isnan(gridpp.pressure(0, 0, 0, 0))) 32 | self.assertTrue(np.isnan(gridpp.pressure(0, 0, 101325, 0))) 33 | 34 | def test_vector(self): 35 | """Check that vector function gives same answer as scalar""" 36 | ielev = [0, 100, 200, np.nan] 37 | oelev = [1000, 900, 800, np.nan] 38 | pressure = [1e5, 1.1e5, 1.2e5, np.nan] 39 | temperature = [280, 290, 300, np.nan] 40 | truth = [gridpp.pressure(ielev[i], oelev[i], pressure[i], temperature[i]) for i in range(len(ielev))] 41 | np.testing.assert_array_almost_equal(truth, gridpp.pressure(ielev, oelev, pressure, temperature)) 42 | 43 | def test_dimension_mismatch(self): 44 | """Check for exception when vector arguments have different sizes""" 45 | ielev = [0, 100, 200] 46 | oelev = [1000, 900, 800] 47 | pressure = [1e5, 1.1e5, 1.2e5] 48 | temperature = [280, 290, 300] 49 | with self.assertRaises(Exception) as e: 50 | gridpp.pressure(ielev + [100], oelev, pressure, temperature) 51 | with self.assertRaises(Exception) as e: 52 | gridpp.pressure(ielev, oelev + [100], pressure, temperature) 53 | with self.assertRaises(Exception) as e: 54 | gridpp.pressure(ielev, oelev, pressure + [100], temperature) 55 | with self.assertRaises(Exception) as e: 56 | gridpp.pressure(ielev, oelev, pressure, temperature + [100]) 57 | 58 | 59 | if __name__ == '__main__': 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /tests/test_qnh.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import os 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def test_invalid_input(self): 10 | """Check that dimension missmatch results in error""" 11 | with self.assertRaises(Exception) as e: 12 | gridpp.qnh([101325], [0, 20]) 13 | 14 | def test_invalid_values(self): 15 | self.assertTrue(np.isnan(gridpp.qnh([-1], [0]))) 16 | self.assertTrue(np.isnan(gridpp.qnh([101325], [np.nan]))) 17 | self.assertTrue(np.isnan(gridpp.qnh([np.nan], [0]))) 18 | 19 | def test_1(self): 20 | p = [101325, 90000, 90000, 110000] 21 | alt = [0, 1000, 0, -1000] 22 | expected = [101325, 101463.21875, 90000, 97752.90742927508] 23 | for i in range(len(p)): 24 | self.assertAlmostEqual(gridpp.qnh(p[i], alt[i]), expected[i], 1) 25 | np.testing.assert_almost_equal(gridpp.qnh(p, alt), expected, 1) 26 | 27 | def test_no_pressure(self): 28 | for altitude in [-1000, 0, 1000]: 29 | self.assertEqual(gridpp.qnh([0], [altitude]), [0]) 30 | self.assertEqual(gridpp.qnh(0, altitude), 0) 31 | 32 | def test_empty(self): 33 | np.testing.assert_almost_equal(gridpp.qnh([],[]), []) 34 | 35 | 36 | if __name__ == '__main__': 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /tests/test_quantile_mapping.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | 6 | 7 | class QuantileMappingTest(unittest.TestCase): 8 | """ Test that the curve reproduces the reference values """ 9 | def test_train(self): 10 | refs = [[1, 2, 3], [1, 1, 1]] 11 | fcst = [2, 3, 4] 12 | for ref in refs: 13 | new_fcst = gridpp.apply_curve(fcst, ref, fcst, gridpp.OneToOne, gridpp.OneToOne) 14 | np.testing.assert_array_equal(ref, new_fcst) 15 | 16 | """ Test that the curve doesn't allow negative correlations """ 17 | def test_negative(self): 18 | ref = [1, 0, -1] 19 | fcst = [2, 3, 4] 20 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst) 21 | np.testing.assert_array_equal(curve_fcst, fcst) 22 | np.testing.assert_array_equal(curve_ref, np.sort(ref)) 23 | new_fcst = gridpp.apply_curve(fcst, curve_ref, curve_fcst, gridpp.OneToOne, gridpp.OneToOne) 24 | np.testing.assert_array_equal(np.sort(ref), new_fcst) 25 | 26 | def test_quantiles(self): 27 | ref = np.arange(11) 28 | fcst = ref + 2 29 | quantiles = [0.1, 0.9] 30 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst, quantiles) 31 | np.testing.assert_array_equal([1, 9], curve_ref) 32 | np.testing.assert_array_equal([3, 11], curve_fcst) 33 | 34 | def test_single_point(self): 35 | ref = [1] 36 | fcst = [2] 37 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst) 38 | np.testing.assert_array_almost_equal(curve_ref, ref) 39 | np.testing.assert_array_almost_equal(curve_fcst, fcst) 40 | 41 | def test_dimension_mismatch(self): 42 | ref = [1, 2] 43 | fcst = [1, 2, 3] 44 | with self.assertRaises(Exception) as e: 45 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst) 46 | with self.assertRaises(Exception) as e: 47 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst, [0.1, 0.9]) 48 | 49 | def test_empty_curve(self): 50 | ref = [] 51 | fcst = [] 52 | np.testing.assert_array_almost_equal([[],[]], gridpp.quantile_mapping_curve(ref, fcst)) 53 | np.testing.assert_array_almost_equal([[],[]], gridpp.quantile_mapping_curve(ref, fcst, [0.1, 0.9])) 54 | 55 | def test_invalid_argument(self): 56 | ref = np.arange(11) 57 | fcst = ref + 2 58 | quantiles = [0.1, -1] 59 | with self.assertRaises(Exception) as e: 60 | curve_ref, curve_fcst = gridpp.quantile_mapping_curve(ref, fcst, quantiles) 61 | 62 | 63 | if __name__ == '__main__': 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /tests/test_sea_level_pressure.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import os 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def test_missing_values(self): 10 | self.assertTrue(np.isnan(gridpp.sea_level_pressure(np.nan, 20, 290))) 11 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, np.nan, 290) 12 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, 20, np.nan) 13 | 14 | def test_unphysical_values(self): 15 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, -1, 20, 290) 16 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, 20, -1) 17 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, 20, 290, -1) 18 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, 20, 290, 2) 19 | self.assertRaises(RuntimeError, gridpp.sea_level_pressure, 101325, 20, 290, 0.7, -1) 20 | 21 | def test_vector(self): 22 | ps = [101315., 101000., 102300., 99513.] 23 | alt = [38, 34, 51, 69] 24 | temperature = [290, 273, 293, 295] 25 | rh = [0.1, 0.5, 0.8, 0.9] 26 | dewpoint = [np.nan, np.nan, np.nan, np.nan] 27 | truth = [gridpp.sea_level_pressure(ps[i], alt[i], temperature[i], rh[i], dewpoint[i]) for i in range(len(ps))] 28 | np.testing.assert_array_almost_equal(truth, gridpp.sea_level_pressure(ps, alt, temperature, rh, dewpoint)) 29 | 30 | def test_dimension_mismatch(self): 31 | """Check for exception when vector arguments have different sizes""" 32 | ps = [101315., 101000., 102300., 99513.] 33 | alt = [38, 34, 51, 69] 34 | temperature = [290, 273, 293, 295] 35 | rh = [0.1, 0.5, 0.8, 0.9] 36 | dewpoint = [np.nan, np.nan, np.nan, np.nan] 37 | with self.assertRaises(Exception) as e: 38 | gridpp.sea_level_pressure(ps + [100], alt, temperature, rh, dewpoint) 39 | with self.assertRaises(Exception) as e: 40 | gridpp.sea_level_pressure(ps, alt + [100], temperature, rh, dewpoint) 41 | with self.assertRaises(Exception) as e: 42 | gridpp.sea_level_pressure(ps, alt, temperature + [100], rh, dewpoint) 43 | with self.assertRaises(Exception) as e: 44 | gridpp.sea_level_pressure(ps, alt, temperature, rh + [100], dewpoint) 45 | with self.assertRaises(Exception) as e: 46 | gridpp.sea_level_pressure(ps, alt, temperature, rh, dewpoint +[100] ) 47 | 48 | def test_pressure_results(self): 49 | self.assertAlmostEqual(gridpp.sea_level_pressure(101325.0, 20, 273.15), 101578.0) 50 | self.assertAlmostEqual(gridpp.sea_level_pressure(101325.0, 50, 273.15), 101960.25) 51 | 52 | 53 | if __name__ == '__main__': 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /tests/test_wind.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import unittest 3 | import gridpp 4 | import numpy as np 5 | import os 6 | 7 | 8 | class Test(unittest.TestCase): 9 | def setUp(self): 10 | self.xs = [0, -1, 1, 0, 1] 11 | self.ys = [0, -1, 1, 1, 0] 12 | self.speeds = [0, np.sqrt(2), np.sqrt(2), 1, 1] 13 | self.directions = [180, 45, 225, 180, 270] 14 | 15 | def test_speed(self): 16 | for i in range(len(self.xs)): 17 | self.assertAlmostEqual(self.speeds[i], gridpp.wind_speed(self.xs[i], self.ys[i])) 18 | np.testing.assert_array_almost_equal(self.speeds, gridpp.wind_speed(self.xs, self.ys)) 19 | 20 | def test_empty_input(self): 21 | np.testing.assert_array_almost_equal(gridpp.wind_speed([], []), []) 22 | np.testing.assert_array_almost_equal(gridpp.wind_direction([], []), []) 23 | 24 | def test_direction(self): 25 | for i in range(len(self.xs)): 26 | self.assertAlmostEqual(self.directions[i], gridpp.wind_direction(self.xs[i], self.ys[i])) 27 | np.testing.assert_array_almost_equal(self.directions, gridpp.wind_direction(self.xs, self.ys)) 28 | 29 | def test_missing(self): 30 | """Check that if one or more values are missing, the result is NaN""" 31 | for func in [gridpp.wind_speed, gridpp.wind_direction]: 32 | with self.subTest(func=func): 33 | self.assertTrue(np.isnan(func(0, np.nan))) 34 | self.assertTrue(np.isnan(func(np.nan, 0))) 35 | self.assertTrue(np.isnan(func(np.nan, np.nan))) 36 | np.testing.assert_array_almost_equal(func([0, np.nan, np.nan], [np.nan, 0, np.nan]), [np.nan, np.nan, np.nan]) 37 | 38 | def test_dimension_mismatch(self): 39 | for func in [gridpp.wind_speed, gridpp.wind_direction]: 40 | with self.subTest(func=func): 41 | with self.assertRaises(Exception) as e: 42 | func([0], [0, 1]) 43 | with self.assertRaises(Exception) as e: 44 | func([], [0, 1]) 45 | with self.assertRaises(Exception) as e: 46 | func([0], []) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | --------------------------------------------------------------------------------