From d32f16ed31bf38b5bbdf488ed9901c6e48ab248b Mon Sep 17 00:00:00 2001 From: kstppd Date: Sun, 28 Nov 2021 14:55:36 +0200 Subject: [PATCH 1/8] Get tests up to date --- tests/benchmark.cpp | 5 +++-- tests/test.cpp | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/benchmark.cpp b/tests/benchmark.cpp index 25808ea..14e25fd 100644 --- a/tests/benchmark.cpp +++ b/tests/benchmark.cpp @@ -22,8 +22,9 @@ */ template void timeit(std::array globalSize, std::array isPeriodic, int iterations){ - double t1,t2; - FsGrid testGrid(globalSize, MPI_COMM_WORLD, isPeriodic); + double t1,t2; + FsGridCouplingInformation gridCoupling; + FsGrid testGrid(globalSize, MPI_COMM_WORLD, isPeriodic,gridCoupling); int rank,size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); diff --git a/tests/test.cpp b/tests/test.cpp index 86ed4f1..df75760 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -33,8 +33,9 @@ int main(int argc, char** argv) { // Create a 8×8 Testgrid std::array globalSize{20,20,1}; std::array isPeriodic{false,false,true}; + FsGridCouplingInformation gridCoupling; { - FsGrid testGrid(globalSize, MPI_COMM_WORLD, isPeriodic); + FsGrid testGrid(globalSize, MPI_COMM_WORLD, isPeriodic,gridCoupling); /* if(rank == 0) { std::cerr << " --- Test task mapping functions ---" << std::endl; @@ -190,9 +191,7 @@ int main(int argc, char** argv) { } } } - - MPI_Finalize(); return 0; From 1345abb8b1e5c165ca5c7c197ace391e7f5d8efe Mon Sep 17 00:00:00 2001 From: kstppd Date: Mon, 29 Nov 2021 14:43:29 +0200 Subject: [PATCH 2/8] Added some new FsGrid operators + lerp method --- fsgrid.hpp | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 152 insertions(+), 8 deletions(-) diff --git a/fsgrid.hpp b/fsgrid.hpp index 7746966..8920353 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -141,8 +141,21 @@ template class FsGrid : public FsGridTools{ * \param MPI_Comm The MPI communicator this grid should use. * \param isPeriodic An array specifying, for each dimension, whether it is to be treated as periodic. */ - FsGrid(std::array globalSize, MPI_Comm parent_comm, std::array isPeriodic, FsGridCouplingInformation& coupling) - : globalSize(globalSize),coupling(coupling) { + FsGrid(std::array globalSize, MPI_Comm parent_comm, std::array isPeriodic, FsGridCouplingInformation &coupling) + : globalSize(globalSize), coupling(coupling){ + this->parentCom = parent_comm; + this->init(parent_comm,isPeriodic); + } + + //Copy constructor + FsGrid(const FsGrid &other) + : periodic(other.periodic), globalSize(other.globalSize), coupling(other.coupling){ + this->parentCom=other.parentCom; + init(parentCom,periodic); + this->data = other.data; + } + + void init(MPI_Comm &parent_comm,std::array isPeriodic){ int status; int size; @@ -976,16 +989,54 @@ template class FsGrid : public FsGridTools{ return *this; } - + + FsGrid& operator+=(const FsGrid& rhs) { + assert(this->data.size()==rhs.data.size()); + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]+rhs.data[i]; + } + return *this; + } + + FsGrid& operator-=(const FsGrid& rhs) { + assert(this->data.size()==rhs.data.size()); + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]-rhs.data[i]; + } + return *this; + } + + FsGrid& operator*=(const FsGrid& rhs) { + assert(this->data.size()==rhs.data.size()); + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]*rhs.data[i]; + } + return *this; + } - + // FsGrid& operator*=(const FsGrid& rhs) { + // assert(this->data.size()==rhs.data.size()); + // for (size_t i=0; i< this->data.size(); i++){ + // this->data[i] = this->data[i]*rhs.data[i]; + // } + // return *this; + // } + + FsGrid& operator/=(const FsGrid& rhs) { + assert(this->data.size()==rhs.data.size()); + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]/rhs.data[i]; + } + return *this; + } + private: //! MPI Cartesian communicator used in this grid - MPI_Comm comm3d; - int rank; //!< This task's rank in the communicator - std::vector requests; - uint numRequests; + MPI_Comm comm3d, parentCom; + int rank; //!< This task's rank in the communicator + std::vector requests; + uint numRequests; std::array neighbour; //!< Tasks of the 26 neighbours (plus ourselves) std::vector neighbour_index; //!< Lookup table from rank to index in the neighbour array @@ -1040,3 +1091,96 @@ template class FsGrid : public FsGridTools{ array[2] = a; } }; + + +// std Array Operators Overloads +template +static inline std::array operator+(const std::array& ob1, const std::array& ob2){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i] = ob1[i] + ob2[i]; + } + return res; +} +template +static inline std::array operator-(const std::array& ob1, const std::array& ob2){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i] = ob1[i] - ob2[i]; + } + return res; +} + +template +static inline std::array operator*(const std::array &ob1, const std::array &ob2){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i] = ob1[i] * ob2[i]; + } + return res; +} + +template +static inline std::array operator/(const std::array& ob1, const std::array& ob2){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i] = ob1[i] / ob2[i]; + } + return res; +} + + + + +// FsGrid Operators Overloads +template +static inline FsGrid operator+(const FsGrid &lhs, const FsGrid &rhs){ + FsGrid a(lhs); + return a+=rhs; +} + +template +static inline FsGrid operator-(const FsGrid &lhs, const FsGrid &rhs){ + FsGrid a(lhs); + return a-=rhs; +} + +template +static inline FsGrid operator*(const FsGrid &lhs, const FsGrid &rhs){ + FsGrid a(lhs); + return a *= rhs; +} + +template +static inline FsGrid operator/(const FsGrid &lhs, const FsGrid &rhs){ + FsGrid a(lhs); + return a /= rhs; +} + +template +static inline FsGrid lerp(const FsGrid &lhs, const FsGrid &rhs,float t0, float t1,float t){ + + bool doAble = (t0<=t) && (t<=t1); + if (!doAble){ + //Do we crash here? + std::cerr<<"Interpolation not doable in the range of "< targetGrid(lhs); + + //Let's now find the intepolation weights; + float range=t1-t0; + float d0= abs(t-t0); + float d1= abs(t-t1); + float w0= 1.- (d0/range); + float w1= 1.- (d1/range); + + //Do interpolate + + + + // return ; +} + + From d71cd59438b098da2bfdcdfbd8c7d96123ea1eee Mon Sep 17 00:00:00 2001 From: kstppd Date: Mon, 29 Nov 2021 16:00:28 +0200 Subject: [PATCH 3/8] Attempt: Adding new FsGrid operators and temporal lerp method --- fsgrid.hpp | 89 ++++++++++++++++++++++++--------------------- tests/Makefile | 8 ++-- tests/operators.cpp | 81 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 44 deletions(-) create mode 100644 tests/operators.cpp diff --git a/fsgrid.hpp b/fsgrid.hpp index 8920353..492074c 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -978,15 +978,11 @@ template class FsGrid : public FsGridTools{ //! Copy the entire data from another FsGrid of the same signature over. FsGrid& operator=(const FsGrid& other) { - // Don't copy if sizes mismatch. - // (Should this instead crash the program?) - if(other.localSize[0] != localSize[0] || - other.localSize[1] != localSize[1] || - other.localSize[2] != localSize[2]) { - return *this; - } + // Fail if sizes mismatch. + assert(other.localSize[0] == this->localSize[0]); + assert(other.localSize[1] == this->localSize[1]); + assert(other.localSize[2] == this->localSize[2]); data = other.data; - return *this; } @@ -1013,15 +1009,7 @@ template class FsGrid : public FsGridTools{ } return *this; } - - // FsGrid& operator*=(const FsGrid& rhs) { - // assert(this->data.size()==rhs.data.size()); - // for (size_t i=0; i< this->data.size(); i++){ - // this->data[i] = this->data[i]*rhs.data[i]; - // } - // return *this; - // } - + FsGrid& operator/=(const FsGrid& rhs) { assert(this->data.size()==rhs.data.size()); for (size_t i=0; i< this->data.size(); i++){ @@ -1030,6 +1018,13 @@ template class FsGrid : public FsGridTools{ return *this; } + template ::value>::type * = nullptr> + FsGrid &operator*(S mult){ + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]*mult; + } + return *this; + } private: //! MPI Cartesian communicator used in this grid @@ -1093,46 +1088,53 @@ template class FsGrid : public FsGridTools{ }; -// std Array Operators Overloads +//Array Operator Overloads template -static inline std::array operator+(const std::array& ob1, const std::array& ob2){ +static inline std::array operator+(const std::array& arr1, const std::array& arr2){ std::array res; for (size_t i = 0; i < N; ++i){ - res[i] = ob1[i] + ob2[i]; + res[i] = arr1[i] + arr2[i]; } return res; } + template -static inline std::array operator-(const std::array& ob1, const std::array& ob2){ +static inline std::array operator-(const std::array& arr1, const std::array& arr2){ std::array res; for (size_t i = 0; i < N; ++i){ - res[i] = ob1[i] - ob2[i]; + res[i] = arr1[i] - arr2[i]; } return res; } template -static inline std::array operator*(const std::array &ob1, const std::array &ob2){ +static inline std::array operator*(const std::array &arr1, const std::array &arr2){ std::array res; for (size_t i = 0; i < N; ++i){ - res[i] = ob1[i] * ob2[i]; + res[i] = arr1[i] * arr2[i]; } return res; } template -static inline std::array operator/(const std::array& ob1, const std::array& ob2){ +static inline std::array operator/(const std::array& arr1, const std::array& arr2){ std::array res; for (size_t i = 0; i < N; ++i){ - res[i] = ob1[i] / ob2[i]; + res[i] = arr1[i] / arr2[i]; } return res; } +template ::value>::type* = nullptr> +std::array operator*(std::array& arr, S mult){ + for (size_t i = 0; i < N; ++i) + arr[i]*=mult; + return arr; +} -// FsGrid Operators Overloads +// FsGrid Operator Overloads template static inline FsGrid operator+(const FsGrid &lhs, const FsGrid &rhs){ FsGrid a(lhs); @@ -1157,30 +1159,35 @@ static inline FsGrid operator/(const FsGrid &lhs, const return a /= rhs; } -template -static inline FsGrid lerp(const FsGrid &lhs, const FsGrid &rhs,float t0, float t1,float t){ +template ::value>::type* = nullptr> +static inline FsGrid lerp_t(const FsGrid &lhs, const FsGrid &rhs,S t0, S t1,S t){ + //Sanity check for timestamps. If sizes mismatch then this will make it crash in the '=' operator bool doAble = (t0<=t) && (t<=t1); if (!doAble){ + #pragma message(": warning: We should probably crash here instead of returning nonesense") //Do we crash here? std::cerr<<"Interpolation not doable in the range of "< targetGrid(lhs); - - //Let's now find the intepolation weights; - float range=t1-t0; - float d0= abs(t-t0); - float d1= abs(t-t1); - float w0= 1.- (d0/range); - float w1= 1.- (d1/range); - //Do interpolate + //First let's create the targer grid by copying one of the inputs + FsGrid targetGrid(lhs); + //Let's now calculate the intepolation weights; + S range=t1-t0; + S d0= abs(t-t0); + S d1= abs(t-t1); + S w0= 1.- (d0/range); + S w1= 1.- (d1/range); + //Do interpolate-targetGrid=w0*lhs+w1*rhs + FsGrid fsgrid_tmp(lhs); + targetGrid=fsgrid_tmp*w0; + fsgrid_tmp=rhs; + targetGrid += fsgrid_tmp * w1; - // return ; + return targetGrid; } diff --git a/tests/Makefile b/tests/Makefile index 1c2427e..0618e02 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,14 +1,16 @@ -CXX=CC +CXX=mpic++ CXXFLAGS= -O3 -std=c++11 -march=native -g -Wall -all: benchmark test ddtest +all: benchmark test ddtest operators benchmark: benchmark.cpp ../fsgrid.hpp $(CXX) $(CXXFLAGS) -o $@ $< +operators: operators.cpp ../fsgrid.hpp + $(CXX) $(CXXFLAGS) -o $@ $< test: test.cpp ../fsgrid.hpp $(CXX) $(CXXFLAGS) -o $@ $< ddtest: ddtest.cpp $(CXX) $(CXXFLAGS) -o $@ $< clean: - -rm test + -rm test benchmark ddtest operators diff --git a/tests/operators.cpp b/tests/operators.cpp new file mode 100644 index 0000000..4b71804 --- /dev/null +++ b/tests/operators.cpp @@ -0,0 +1,81 @@ +#include +#include "../fsgrid.hpp" +/* + Copyright (C) 2016 Finnish Meteorological Institute + Copyright (C) 2016 CSC -IT Center for Science + + This file is part of fsgrid + + fsgrid is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + fsgrid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; + without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with fsgrid. If not, see . +*/ + + + +int main(int argc, char** argv) { + typedef float Real; + MPI_Init(&argc,&argv); + + int rank,size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + // Create a 8×8 Testgrid + std::array globalSize{2000,1000,1}; + std::array globalSize2{3000,1000,1}; + std::array isPeriodic{false,false,true}; + + + + FsGridCouplingInformation gridCoupling; + FsGrid, 2> A(globalSize, MPI_COMM_WORLD, isPeriodic, gridCoupling); + FsGrid, 2> B(globalSize, MPI_COMM_WORLD, isPeriodic, gridCoupling); + FsGrid, 2> C(globalSize, MPI_COMM_WORLD, isPeriodic, gridCoupling); + FsGrid, 2> D(globalSize2, MPI_COMM_WORLD, isPeriodic, gridCoupling); + + // FsGrid, 2> newGuy(A + B); + // + + A=B+C; + // += + A+=B; + // - + A=A-C; + // -= + A-=B; + D=D*3.0; + C=lerp_t(A,B,5.,10.,7.); + //supposed to fail; + // A=B+D; + + + + + + + + + + + + + + + + + + + + MPI_Finalize(); + return 0; +} From 4c08d239184f3aea40d9b021041b7bc6567d2cdb Mon Sep 17 00:00:00 2001 From: kstppd Date: Mon, 29 Nov 2021 18:36:49 +0200 Subject: [PATCH 4/8] Handle some of Urs' comments --- fsgrid.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fsgrid.hpp b/fsgrid.hpp index 492074c..aaf1e30 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -142,8 +142,7 @@ template class FsGrid : public FsGridTools{ * \param isPeriodic An array specifying, for each dimension, whether it is to be treated as periodic. */ FsGrid(std::array globalSize, MPI_Comm parent_comm, std::array isPeriodic, FsGridCouplingInformation &coupling) - : globalSize(globalSize), coupling(coupling){ - this->parentCom = parent_comm; + : parentCom(parent_comm),globalSize(globalSize), coupling(coupling){ this->init(parent_comm,isPeriodic); } @@ -1176,6 +1175,12 @@ static inline FsGrid lerp_t(const FsGrid &lhs, const F //Let's now calculate the intepolation weights; S range=t1-t0; + + //in case range is zero return lhs + if (range==0.0){ + return lhs; + } + S d0= abs(t-t0); S d1= abs(t-t1); S w0= 1.- (d0/range); From d9aef3b0a1faa0d166db19046db96af7c86204b8 Mon Sep 17 00:00:00 2001 From: kstppd Date: Tue, 30 Nov 2021 02:47:21 +0200 Subject: [PATCH 5/8] Some fixes. A few more operators and new lerp method logic. --- fsgrid.hpp | 100 +++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/fsgrid.hpp b/fsgrid.hpp index aaf1e30..e4d8c33 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -1018,9 +1018,17 @@ template class FsGrid : public FsGridTools{ } template ::value>::type * = nullptr> - FsGrid &operator*(S mult){ + FsGrid &operator*=(S factor){ for (size_t i=0; i< this->data.size(); i++){ - this->data[i] = this->data[i]*mult; + this->data[i] = this->data[i]*factor; + } + return *this; + } + + template ::value>::type * = nullptr> + FsGrid &operator/=(S factor){ + for (size_t i=0; i< this->data.size(); i++){ + this->data[i] = this->data[i]/factor; } return *this; } @@ -1091,19 +1099,19 @@ template class FsGrid : public FsGridTools{ template static inline std::array operator+(const std::array& arr1, const std::array& arr2){ std::array res; - for (size_t i = 0; i < N; ++i){ - res[i] = arr1[i] + arr2[i]; - } - return res; + for (size_t i = 0; i < N; ++i){ + res[i] = arr1[i] + arr2[i]; + } + return res; } template static inline std::array operator-(const std::array& arr1, const std::array& arr2){ std::array res; - for (size_t i = 0; i < N; ++i){ - res[i] = arr1[i] - arr2[i]; - } - return res; + for (size_t i = 0; i < N; ++i){ + res[i] = arr1[i] - arr2[i]; + } + return res; } template @@ -1118,17 +1126,19 @@ static inline std::array operator*(const std::array &arr1, const std template static inline std::array operator/(const std::array& arr1, const std::array& arr2){ std::array res; - for (size_t i = 0; i < N; ++i){ - res[i] = arr1[i] / arr2[i]; - } - return res; + for (size_t i = 0; i < N; ++i){ + res[i] = arr1[i] / arr2[i]; + } + return res; } template ::value>::type* = nullptr> -std::array operator*(std::array& arr, S mult){ - for (size_t i = 0; i < N; ++i) - arr[i]*=mult; - return arr; +std::array operator*(std::array& arr, S factor){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i]=arr[i]*factor; + } + return res; } @@ -1152,47 +1162,33 @@ static inline FsGrid operator*(const FsGrid &lhs, const return a *= rhs; } +template ::value>::type * = nullptr> +static inline FsGrid operator*(const FsGrid& lhs, S factor){ + FsGrid a(lhs); + return a*=factor; +} + +template ::value>::type * = nullptr> +static inline FsGrid operator/(const FsGrid& lhs, S factor){ + FsGrid a(lhs); + return a/=factor; +} + template static inline FsGrid operator/(const FsGrid &lhs, const FsGrid &rhs){ FsGrid a(lhs); return a /= rhs; } -template ::value>::type* = nullptr> -static inline FsGrid lerp_t(const FsGrid &lhs, const FsGrid &rhs,S t0, S t1,S t){ - - //Sanity check for timestamps. If sizes mismatch then this will make it crash in the '=' operator - bool doAble = (t0<=t) && (t<=t1); - if (!doAble){ - #pragma message(": warning: We should probably crash here instead of returning nonesense") - //Do we crash here? - std::cerr<<"Interpolation not doable in the range of "< targetGrid(lhs); - - //Let's now calculate the intepolation weights; - S range=t1-t0; - - //in case range is zero return lhs - if (range==0.0){ - return lhs; - } - - S d0= abs(t-t0); - S d1= abs(t-t1); - S w0= 1.- (d0/range); - S w1= 1.- (d1/range); - - //Do interpolate-targetGrid=w0*lhs+w1*rhs - FsGrid fsgrid_tmp(lhs); - targetGrid=fsgrid_tmp*w0; - fsgrid_tmp=rhs; - targetGrid += fsgrid_tmp * w1; - - return targetGrid; +/* Here tNorm is the interpolation time but normalized. + * So t0=0 , t1=1 and then for any t>1 we have a + * linear extrapolation */ +template ::value>::type* = nullptr> +static inline FsGrid lerp_t(const FsGrid &lhs, const FsGrid &rhs,S tNorm){ + //If sizes mismatch then this will make it crash in the '=' operator later on + //Do interpolate-targetGrid=lhs(1-t)+rhs*t1 + return lhs*(1. - tNorm) + rhs*tNorm; } From 4c30a7d42033ec04f0c4c4ea44f57686d407b757 Mon Sep 17 00:00:00 2001 From: kstppd Date: Tue, 30 Nov 2021 03:02:50 +0200 Subject: [PATCH 6/8] Some fixes. A few more operators and new lerp method logic. --- fsgrid.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fsgrid.hpp b/fsgrid.hpp index e4d8c33..ffd10be 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -148,8 +148,7 @@ template class FsGrid : public FsGridTools{ //Copy constructor FsGrid(const FsGrid &other) - : periodic(other.periodic), globalSize(other.globalSize), coupling(other.coupling){ - this->parentCom=other.parentCom; + : parentCom(other.parentCom),periodic(other.periodic), globalSize(other.globalSize), coupling(other.coupling){ init(parentCom,periodic); this->data = other.data; } From f84165ebfa1e3785921b8f234576a4304d1ff438 Mon Sep 17 00:00:00 2001 From: Yann Pfau-Kempf Date: Wed, 2 Feb 2022 15:33:22 +0200 Subject: [PATCH 7/8] Fix globalToLocal off-by-one check. --- fsgrid.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsgrid.hpp b/fsgrid.hpp index ffd10be..99f0bd1 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -485,7 +485,7 @@ template class FsGrid : public FsGridTools{ retval[1] = y - localStart[1]; retval[2] = z - localStart[2]; - if(retval[0] > localSize[0] || retval[1] > localSize[1] || retval[2] > localSize[2] + if(retval[0] >= localSize[0] || retval[1] >= localSize[1] || retval[2] >= localSize[2] || retval[0] < 0 || retval[1] < 0 || retval[2] < 0) { return {-1,-1,-1}; } From 32f19c4af5ac8e9b89d25f000568968ca90bff4f Mon Sep 17 00:00:00 2001 From: kstppd Date: Wed, 9 Mar 2022 10:49:30 +0200 Subject: [PATCH 8/8] Add / operator for std array --- fsgrid.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fsgrid.hpp b/fsgrid.hpp index ffd10be..15ce487 100644 --- a/fsgrid.hpp +++ b/fsgrid.hpp @@ -1140,6 +1140,14 @@ std::array operator*(std::array& arr, S factor){ return res; } +template ::value>::type* = nullptr> +std::array operator/(std::array& arr, S factor){ + std::array res; + for (size_t i = 0; i < N; ++i){ + res[i]=arr[i]/factor; + } + return res; +} // FsGrid Operator Overloads