diff --git a/tests/test_basics.cpp b/tests/test_basics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5eee4db9208ef620be4e340bb815f6d02b74bc19 --- /dev/null +++ b/tests/test_basics.cpp @@ -0,0 +1,258 @@ +#include <iostream> +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("test"); + + std::vector<std::pair<int,int>> objects; + objects.push_back( + std::make_pair(3, gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0) ) + ); + objects.push_back( + std::make_pair(3, gmo::addSphere(0.5, 0.5, 0.5, SPHERE_RADIUS) ) + ); + + std::vector<std::pair<int, int>> tools; + gmsh::vectorpair odt; + std::vector<gmsh::vectorpair> odtm; + gmo::fragment(objects, tools, odt, odtm); + + gmo::synchronize(); + + int pgtag = gm::addPhysicalGroup(2, {7}); + gm::setPhysicalName(2, pgtag, "SphereSurface"); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + +int +test_basics(int geometric_order, int approximation_order) +{ + make_geometry(0, 0.1); + + model mod(geometric_order, approximation_order); + + auto idx = geometric_order - 1; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + double vol_e0 = (4.0/3.0)*M_PI*SPHERE_RADIUS*SPHERE_RADIUS*SPHERE_RADIUS; + double vol_e1 = 1.0 - vol_e0; + + /*****************************************/ + std::cout << yellowfg << "Volumes by reference quadrature points and determinants"; + std::cout << nofg << std::endl; + std::vector<double> t1_vals { 0.07, 0.00145, 0.00024, 1.83e-5 }; + COMPARE_VALUES_RELATIVE("Entity 0 vol r+d", mod.entity_at(0).measure(), vol_e0, t1_vals[idx]); + std::vector<double> t2_vals { 0.0024, 5.02e-5, 8.21e-06, 6.35e-7 }; + COMPARE_VALUES_RELATIVE("Entity 1 vol r+d", mod.entity_at(1).measure(), vol_e1, t2_vals[idx]); + + /*****************************************/ + std::cout << yellowfg << "Volumes by physical quadrature points" << nofg << std::endl; + double vol_e0_pqp = 0.0; + auto& e0 = mod.entity_at(0); + for (const auto& e : e0) + { + const auto& pqps = e.integration_points(); + for (const auto& qp : pqps) + vol_e0_pqp += qp.weight(); + } + COMPARE_VALUES_RELATIVE("Entity 0 vol pqp", vol_e0_pqp, vol_e0, t1_vals[idx]); + + double vol_e1_pqp = 0.0; + auto& e1 = mod.entity_at(1); + for (const auto& e : e1) + { + const auto& pqps = e.integration_points(); + for (const auto& qp : pqps) + vol_e1_pqp += qp.weight(); + } + COMPARE_VALUES_RELATIVE("Entity 1 vol pqp", vol_e1_pqp, vol_e1, t2_vals[idx]); + + /*****************************************/ + std::cout << yellowfg << "Volumes by jacobians" << nofg << std::endl; + double vol_e0_j = 0.0; + for (const auto& e : e0) + { + const auto& js = e.jacobians(); + auto& re = e0.cell_refelem(e); + auto rqps = re.integration_points(); + auto dets = e.determinants(); + for (size_t i = 0; i < js.size(); i++) + { + double dj = js[i].determinant(); + double dg = dets[i]; + auto relerr = dj/dg - 1; + assert( std::abs(relerr) < 3e-14 ); + vol_e0_j += dj*rqps[i].weight(); + } + } + COMPARE_VALUES_RELATIVE("Entity 0 vol j", vol_e0_j, vol_e0, t1_vals[idx]); + + double vol_e1_j = 0.0; + for (const auto& e : e1) + { + const auto& js = e.jacobians(); + auto& re = e1.cell_refelem(e); + auto rqps = re.integration_points(); + auto dets = e.determinants(); + for (size_t i = 0; i < js.size(); i++) + { + double dj = js[i].determinant(); + double dg = dets[i]; + auto relerr = dj/dg - 1; + assert( std::abs(relerr) < 3e-14 ); + vol_e1_j += dj*rqps[i].weight(); + } + } + COMPARE_VALUES_RELATIVE("Entity 1 vol j", vol_e1_j, vol_e1, t2_vals[idx]); + + /*****************************************/ + std::cout << yellowfg << "Volumes by integrating f(x) = 1 over the domain"; + std::cout << nofg << std::endl; + double vol_e0_mass = 0.0; + auto f = [](const point_3d& pt) -> double { return 1; }; + vecxd f_e0 = e0.project(f); + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + auto num_bf = re.num_basis_functions(); + matxd mass = e0.mass_matrix(iT); + vecxd f_pe = f_e0.segment(num_bf*iT, num_bf); + vol_e0_mass += f_pe.transpose() * mass * f_pe; + } + COMPARE_VALUES_RELATIVE("Entity 0 vol mass", vol_e0_mass, vol_e0, t1_vals[idx]); + + double vol_e1_mass = 0.0; + vecxd f_e1 = e1.project(f); + for (size_t iT = 0; iT < e1.num_cells(); iT++) + { + auto& pe = e1.cell(iT); + auto& re = e1.cell_refelem(pe); + auto num_bf = re.num_basis_functions(); + matxd mass = e1.mass_matrix(iT); + vecxd f_pe = f_e1.segment(num_bf*iT, num_bf); + vol_e1_mass += f_pe.transpose() * mass * f_pe; + } + COMPARE_VALUES_RELATIVE("Entity 1 vol mass", vol_e1_mass, vol_e1, t2_vals[idx]); + + return 0; +} +#include <fstream> +int +test_normals(int geometric_order, int approximation_order) +{ + make_geometry(0, 0.1); + model mod(geometric_order, approximation_order); + + gmsh::vectorpair pgs; + gm::getPhysicalGroups(pgs); + assert(pgs.size() == 1); + + std::cout << cyanfg << "Testing normals (order " << geometric_order; + std::cout << ")" << reset << std::endl; + + for (auto [dim, pgtag] : pgs) + { + std::vector<int> tags; + gm::getEntitiesForPhysicalGroup(dim, pgtag, tags); + assert(tags.size() == 1); + + std::vector<int> elemTypes; + gmm::getElementTypes(elemTypes, dim, tags[0]); + + assert(elemTypes.size() == 1); + if (elemTypes.size() != 1) + throw std::invalid_argument("Only one element type per entity is allowed"); + + for (auto& elemType : elemTypes) + { + std::vector<size_t> faceTags, faceNodesTags; + gmm::getElementsByType(elemType, faceTags, faceNodesTags, tags[0]); + + std::sort(faceTags.begin(), faceTags.end()); + + auto& e0 = mod.entity_at(0); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + auto ft = e0.face_tags(); + + auto bm = mod.get_bnd(7); + + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + for (size_t iF = 0; iF < 4; iF++) + { + auto pf = e0.face(4*iT+iF); + auto rf = e0.face_refelem(pf); + auto qps = pf.integration_points(); + + auto ek = element_key(pf); + if ( !std::binary_search(bm.begin(), bm.end(), ek) ) + continue; + + size_t num_qp = (geometric_order == 1) ? 1 : qps.size(); + + for (size_t iQp = 0; iQp < num_qp; iQp++) + { + Eigen::Vector3d n = ed.normals.row((4*iT+iF)*num_qp + iQp); + point_3d p = qps[iQp].point(); + point_3d v = p - point_3d(0.5, 0.5, 0.5); + Eigen::Vector3d nref; + nref(0) = v.x(); nref(1) = v.y(); nref(2) = v.z(); + nref /= nref.norm(); + + if ( n.cross(nref).norm() > 1e-2 ) + { + std::cout << "Wrong normal. Expected: " << nref.transpose() << ", "; + std::cout << "got: " << n.transpose() << std::endl; + return 1; + } + } + } + } + } + } + + return 0; +} + +int main(void) +{ + gmsh::initialize(); + gmsh::option::setNumber("General.Terminal", 0); + gmsh::option::setNumber("Mesh.Algorithm", 1); + gmsh::option::setNumber("Mesh.Algorithm3D", 1); + + + int failed_tests = 0; + + std::cout << Bmagentafg << " *** TESTING: BASIC OPERATIONS ***" << reset << std::endl; + for (size_t go = 1; go < 5; go++) + { + for (size_t ao = go; ao < 5; ao++) + { + failed_tests += test_basics(go, ao); + } + } + + std::cout << Bmagentafg << " *** TESTING: NORMAL COMPUTATION ***" << reset << std::endl; + for (size_t i = 1; i < 5; i++) + test_normals(i,i); + + return failed_tests; +} \ No newline at end of file diff --git a/tests/test_differentiation.cpp b/tests/test_differentiation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ffc813b127b0feef2576bdcec5d26925f5e3010 --- /dev/null +++ b/tests/test_differentiation.cpp @@ -0,0 +1,152 @@ +#include <iostream> +#include <iomanip> + +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" +#include "entity_data.h" +#include "kernels_cpu.h" +#include "timecounter.h" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("difftest"); + gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + gmo::synchronize(); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + +int test_differentiation_convergence(int geometric_order, int approximation_order) +{ + std::vector<double> sizes({ 0.32, 0.16, 0.08, 0.04 }); + std::vector<double> errors; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + auto f = [](const point_3d& pt) -> double { + return std::sin(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dx = [](const point_3d& pt) -> double { + return M_PI*std::cos(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dy = [](const point_3d& pt) -> double { + return M_PI*std::sin(M_PI*pt.x())*std::cos(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dz = [](const point_3d& pt) -> double { + return M_PI*std::sin(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::cos(M_PI*pt.z()); + }; + + std::vector<double> errs_x; + std::vector<double> errs_y; + std::vector<double> errs_z; + + for (size_t i = 0; i < sizes.size(); i++) + { + double h = sizes[i]; + make_geometry(0,h); + model mod(geometric_order, approximation_order); + + auto& e0 = mod.entity_at(0); + e0.sort_by_orientation(); + vecxd Pf_e0 = e0.project(f); + vecxd df_dx_e0 = vecxd::Zero( Pf_e0.rows() ); + vecxd df_dy_e0 = vecxd::Zero( Pf_e0.rows() ); + vecxd df_dz_e0 = vecxd::Zero( Pf_e0.rows() ); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + + timecounter tc; + tc.tic(); + compute_field_derivatives(ed, Pf_e0, df_dx_e0, df_dy_e0, df_dz_e0); + double time = tc.toc(); + + if (geometric_order == 1) + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = 21*ed.num_bf*ed.num_bf*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + else + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = ((21*ed.num_bf+6)*ed.num_bf*ed.num_qp + 3*(2*ed.num_bf-1)*ed.num_bf)*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + + vecxd Pdf_dx_e0 = e0.project(df_dx); + vecxd Pdf_dy_e0 = e0.project(df_dy); + vecxd Pdf_dz_e0 = e0.project(df_dz); + + double err_x = 0.0; + double err_y = 0.0; + double err_z = 0.0; + + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + matxd mass = e0.mass_matrix(iT); + auto num_bf = re.num_basis_functions(); + vecxd diff_x = Pdf_dx_e0.segment(iT*num_bf, num_bf) - df_dx_e0.segment(iT*num_bf, num_bf); + vecxd diff_y = Pdf_dy_e0.segment(iT*num_bf, num_bf) - df_dy_e0.segment(iT*num_bf, num_bf); + vecxd diff_z = Pdf_dz_e0.segment(iT*num_bf, num_bf) - df_dz_e0.segment(iT*num_bf, num_bf); + + err_x += diff_x.dot(mass*diff_x); + err_y += diff_y.dot(mass*diff_y); + err_z += diff_z.dot(mass*diff_z); + } + std::cout << "Errors: " << std::sqrt(err_x) << " " << std::sqrt(err_y); + std::cout << " " << std::sqrt(err_z) << std::endl; + + errs_x.push_back( std::sqrt(err_x) ); + errs_y.push_back( std::sqrt(err_y) ); + errs_z.push_back( std::sqrt(err_z) ); + } + + double rate_x = 0.0; + double rate_y = 0.0; + double rate_z = 0.0; + + std::cout << Byellowfg << "rate df/dx rate df/dy rate df/dz" << reset << std::endl; + for (size_t i = 1; i < sizes.size(); i++) + { + std::cout << (rate_x = std::log2(errs_x[i-1]/errs_x[i]) ) << " "; + std::cout << (rate_y = std::log2(errs_y[i-1]/errs_y[i]) ) << " "; + std::cout << (rate_z = std::log2(errs_z[i-1]/errs_z[i]) ) << std::endl; + } + + COMPARE_VALUES_ABSOLUTE("df/dx", rate_x, double(approximation_order), 0.2); + COMPARE_VALUES_ABSOLUTE("df/dy", rate_y, double(approximation_order), 0.2); + COMPARE_VALUES_ABSOLUTE("df/dz", rate_z, double(approximation_order), 0.2); + + return 0; +} + +int main(void) +{ + gmsh::initialize(); + gmsh::option::setNumber("General.Terminal", 0); + gmsh::option::setNumber("Mesh.Algorithm", 1); + gmsh::option::setNumber("Mesh.Algorithm3D", 1); + + + int failed_tests = 0; + + std::cout << Bmagentafg << " *** TESTING: DIFFERENTIATION ***" << reset << std::endl; + for (size_t go = 1; go < 5; go++) + for (size_t ao = go; ao < 5; ao++) + failed_tests += test_differentiation_convergence(go, ao); + + return failed_tests; +} \ No newline at end of file diff --git a/tests/test_differentiation_gpu.cpp b/tests/test_differentiation_gpu.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4e65650307234c019ec9412d4822b50fa57aa03 --- /dev/null +++ b/tests/test_differentiation_gpu.cpp @@ -0,0 +1,158 @@ +#include <iostream> +#include <iomanip> + +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" +#include "entity_data.h" +#include "kernels_cpu.h" +#include "timecounter.h" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("difftest"); + gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + gmo::synchronize(); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + +int test_differentiation_convergence(int geometric_order, int approximation_order) +{ + std::vector<double> sizes({ 0.32, 0.16, 0.08, 0.04 }); + std::vector<double> errors; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + auto f = [](const point_3d& pt) -> double { + return std::sin(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dx = [](const point_3d& pt) -> double { + return M_PI*std::cos(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dy = [](const point_3d& pt) -> double { + return M_PI*std::sin(M_PI*pt.x())*std::cos(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + auto df_dz = [](const point_3d& pt) -> double { + return M_PI*std::sin(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::cos(M_PI*pt.z()); + }; + + std::vector<double> errs_x; + std::vector<double> errs_y; + std::vector<double> errs_z; + + for (size_t i = 0; i < sizes.size(); i++) + { + double h = sizes[i]; + make_geometry(0,h); + model mod(geometric_order, approximation_order); + + auto& e0 = mod.entity_at(0); + e0.sort_by_orientation(); + vecxd Pf_e0 = e0.project(f); + vecxd df_dx_e0 = vecxd::Zero( Pf_e0.rows() ); + vecxd df_dy_e0 = vecxd::Zero( Pf_e0.rows() ); + vecxd df_dz_e0 = vecxd::Zero( Pf_e0.rows() ); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + + entity_data_gpu edg(ed); + +#if 0 + timecounter tc; + tc.tic(); + compute_field_derivatives(ed, Pf_e0, df_dx_e0, df_dy_e0, df_dz_e0); + double time = tc.toc(); + + if (geometric_order == 1) + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = 21*ed.num_bf*ed.num_bf*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + else + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = ((21*ed.num_bf+6)*ed.num_bf*ed.num_qp + 3*(2*ed.num_bf-1)*ed.num_bf)*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + + vecxd Pdf_dx_e0 = e0.project(df_dx); + vecxd Pdf_dy_e0 = e0.project(df_dy); + vecxd Pdf_dz_e0 = e0.project(df_dz); + + double err_x = 0.0; + double err_y = 0.0; + double err_z = 0.0; + + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + matxd mass = e0.mass_matrix(iT); + auto num_bf = re.num_basis_functions(); + vecxd diff_x = Pdf_dx_e0.segment(iT*num_bf, num_bf) - df_dx_e0.segment(iT*num_bf, num_bf); + vecxd diff_y = Pdf_dy_e0.segment(iT*num_bf, num_bf) - df_dy_e0.segment(iT*num_bf, num_bf); + vecxd diff_z = Pdf_dz_e0.segment(iT*num_bf, num_bf) - df_dz_e0.segment(iT*num_bf, num_bf); + + err_x += diff_x.dot(mass*diff_x); + err_y += diff_y.dot(mass*diff_y); + err_z += diff_z.dot(mass*diff_z); + } + std::cout << "Errors: " << std::sqrt(err_x) << " " << std::sqrt(err_y); + std::cout << " " << std::sqrt(err_z) << std::endl; + + errs_x.push_back( std::sqrt(err_x) ); + errs_y.push_back( std::sqrt(err_y) ); + errs_z.push_back( std::sqrt(err_z) ); +#endif + } + + double rate_x = 0.0; + double rate_y = 0.0; + double rate_z = 0.0; + +#if 0 + std::cout << Byellowfg << "rate df/dx rate df/dy rate df/dz" << reset << std::endl; + for (size_t i = 1; i < sizes.size(); i++) + { + std::cout << (rate_x = std::log2(errs_x[i-1]/errs_x[i]) ) << " "; + std::cout << (rate_y = std::log2(errs_y[i-1]/errs_y[i]) ) << " "; + std::cout << (rate_z = std::log2(errs_z[i-1]/errs_z[i]) ) << std::endl; + } + + COMPARE_VALUES_ABSOLUTE("df/dx", rate_x, double(approximation_order), 0.2); + COMPARE_VALUES_ABSOLUTE("df/dy", rate_y, double(approximation_order), 0.2); + COMPARE_VALUES_ABSOLUTE("df/dz", rate_z, double(approximation_order), 0.2); +#endif + + return 0; +} + +int main(void) +{ + gmsh::initialize(); + gmsh::option::setNumber("General.Terminal", 0); + gmsh::option::setNumber("Mesh.Algorithm", 1); + gmsh::option::setNumber("Mesh.Algorithm3D", 1); + + + int failed_tests = 0; + + std::cout << Bmagentafg << " *** TESTING: DIFFERENTIATION ***" << reset << std::endl; + for (size_t go = 1; go < 5; go++) + for (size_t ao = go; ao < 5; ao++) + failed_tests += test_differentiation_convergence(go, ao); + + return failed_tests; +} \ No newline at end of file diff --git a/tests/test_lifting.cpp b/tests/test_lifting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..154cccee435232ee017419bcf649732786d424f6 --- /dev/null +++ b/tests/test_lifting.cpp @@ -0,0 +1,260 @@ +#include <iostream> +#include <iomanip> +#include <sstream> +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" +#include "entity_data.h" +#include "kernels_cpu.h" +#include "timecounter.h" +#include "silo_output.hpp" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("difftest"); + gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + gmo::synchronize(); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + + +int test_lifting_xx(int geometric_order, int approximation_order) +{ + make_geometry(0, 0.25); + model mod(geometric_order, approximation_order); + + auto& e0 = mod.entity_at(0); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + + for (size_t iO = 0; iO < e0.num_cell_orientations(); iO++) + { + auto& re = e0.cell_refelem(iO); + auto num_bf = re.num_basis_functions(); + + auto lift_cols = ed.lifting_matrices.cols(); + + matxd mass = re.mass_matrix(); + matxd curlift = mass * ed.lifting_matrices.block(iO*num_bf, 0, num_bf, lift_cols); + + vecxd ones_cell = vecxd::Ones(num_bf); + vecxd ones_face = vecxd::Ones(lift_cols/4); + + for (size_t iF = 0; iF < 4; iF++) + { + matxd liftblock = curlift.block(0,iF*lift_cols/4,num_bf,lift_cols/4); + std::cout << ones_cell.dot(liftblock*ones_face) << std::endl; + } + } + + return 0; +} + +int test_lifting(int geometric_order, int approximation_order) +{ + std::vector<double> sizes({ 0.32, 0.16, 0.08, 0.04 }); + std::vector<double> errors; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + /* Test field */ + auto Fx = [](const point_3d& pt) -> double { + return 1; //std::sin(M_PI*pt.x()); + }; + + auto Fy = [](const point_3d& pt) -> double { + return 1; //std::sin(M_PI*pt.y()); + }; + + auto Fz = [](const point_3d& pt) -> double { + return 1; //std::sin(M_PI*pt.z()); + }; + + auto F = [&](const point_3d& pt) -> vecxd { + vec3d Fret; + Fret(0) = Fx(pt); + Fret(1) = Fy(pt); + Fret(2) = Fz(pt); + return Fret; + }; + + /* Expected divergence */ + auto div_F = [](const point_3d& pt) -> double { + return 0; + auto dFx_dx = M_PI*std::cos(M_PI*pt.x()); + auto dFy_dy = M_PI*std::cos(M_PI*pt.y()); + auto dFz_dz = M_PI*std::cos(M_PI*pt.z()); + return dFx_dx + dFy_dy + dFz_dz; + }; + + for (size_t sz = 0; sz < sizes.size(); sz++) + { + double h = sizes[sz]; + make_geometry(0,h); + model mod(geometric_order, approximation_order); + + std::stringstream ss; + ss << "lifting_test_" << sz << ".silo"; + + silo s; + s.create_db( ss.str() ); + s.import_mesh_from_gmsh(); + s.write_mesh(); + + auto& e0 = mod.entity_at(0); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + + vecxd exp_field = e0.project(div_F); + vecxd Fx_proj = e0.project(Fx); + vecxd Fy_proj = e0.project(Fy); + vecxd Fz_proj = e0.project(Fz); + + auto flx_size = e0.num_cells() * 4*ed.num_fluxes; + vecxd Fx_flux = vecxd::Zero( flx_size ); + vecxd Fy_flux = vecxd::Zero( flx_size ); + vecxd Fz_flux = vecxd::Zero( flx_size ); + + for (size_t i = 0; i < flx_size; i++) + { + Fx_flux(i) = Fx_proj( ed.fluxdofs_mine[i] ); + Fy_flux(i) = Fy_proj( ed.fluxdofs_mine[i] ); + Fz_flux(i) = Fz_proj( ed.fluxdofs_mine[i] ); + } + + vecxd flux = vecxd::Zero( flx_size ); + + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + for (size_t iF = 0; iF < 4; iF++) + { + vec3d n = ed.normals.row(4*iT+iF); + for (size_t k = 0; k < ed.num_fluxes; k++) + { + auto dof_ofs = (4*iT+iF)*ed.num_fluxes + k; + flux(dof_ofs) = Fx_flux(dof_ofs)*n(0) + + Fy_flux(dof_ofs)*n(1) + + Fz_flux(dof_ofs)*n(2); + } + } + } + + vecxd lift_field = vecxd::Zero( e0.num_cells() * ed.num_bf ); +#if 0 + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + for (size_t iF = 0; iF < 4; iF++) + { + auto& pf = e0.face(4*iT+iF); + auto& rf = e0.face_refelem(pf); + auto num_fluxes = rf.num_basis_functions(); + const auto pqps = pf.integration_points(); + + matxd mass = matxd::Zero(num_fluxes, num_fluxes); + vecxd rhs = vecxd::Zero(num_fluxes); + for (size_t iQp = 0; iQp < pqps.size(); iQp++) + { + vec3d n; + if (ed.g_order == 1) + n = ed.normals.row(4*iT+iF); + else + n = ed.normals.row( (4*iT+iF)*pqps.size() + iQp ); + + const auto& pqp = pqps[iQp]; + const vecxd phi = rf.basis_functions(iQp); + mass += pqp.weight() * phi * phi.transpose(); + rhs += pqp.weight() * F(pqp.point()).dot(n) * phi; + } + auto fluxofs = (4*iT+iF)*num_fluxes; + flux.segment(fluxofs, num_fluxes) = mass.ldlt().solve(rhs); + } + } +#endif + timecounter tc; + tc.tic(); + compute_flux_lifting(ed, flux, lift_field); + double time = tc.toc(); + + std::vector<double> var_div_F( e0.num_cells() ); + std::vector<double> var_lift( e0.num_cells() ); + std::vector<double> var_volp( e0.num_cells() ); + std::vector<double> var_elift( e0.num_cells() ); + std::vector<double> var_err( e0.num_cells() ); + std::vector<double> var_orient( e0.num_cells() ); + + double err = 0.0; + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + auto num_bf = re.num_basis_functions(); + matxd mass = matxd::Zero(num_bf, num_bf); + vecxd vol = vecxd::Zero(num_bf); + + const auto pqps = pe.integration_points(); + for(size_t iQp = 0; iQp < pqps.size(); iQp++) + { + const auto& pqp = pqps[iQp]; + const vecxd phi = re.basis_functions(iQp); + const matxd dphi = re.basis_gradients(iQp); + mass += pqp.weight() * phi * phi.transpose(); + vol += pqp.weight() * dphi * F(pqp.point()); + } + + vecxd expected = exp_field.segment(iT*num_bf, num_bf); + vecxd lf = lift_field.segment(iT*num_bf, num_bf); + vecxd volp = mass.ldlt().solve(vol); + + vecxd comp_field = lf - volp; + vecxd diff = comp_field - expected; + double loc_err = diff.dot(mass*diff); + err += loc_err; + + auto bar = pe.barycenter(); + vecxd phi_bar = re.basis_functions({1./3., 1./3., 1./3.}); + + var_div_F.at( pe.original_position() ) = expected.dot(phi_bar); + var_lift.at( pe.original_position() ) = lf.dot(phi_bar); + var_volp.at( pe.original_position() ) = volp.dot(phi_bar); + var_elift.at( pe.original_position() ) = (volp + expected).dot(phi_bar); + var_err.at( pe.original_position() ) = loc_err; + var_orient.at( pe.original_position() ) = pe.orientation(); + } + + s.write_zonal_variable("div_F", var_div_F); + s.write_zonal_variable("lift", var_lift); + s.write_zonal_variable("vol", var_volp); + s.write_zonal_variable("exp_lift", var_elift); + s.write_zonal_variable("err", var_err); + s.write_zonal_variable("orient", var_orient); + + std::cout << std::sqrt(err) << std::endl; + + if (geometric_order == 1) + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = 3*(ed.num_bf)*4*ed.num_fluxes*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + else + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = ((21*ed.num_bf+6)*ed.num_bf*ed.num_qp + 3*(2*ed.num_bf-1)*ed.num_bf)*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + } + + return 0; +} \ No newline at end of file diff --git a/tests/test_mass.cpp b/tests/test_mass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5690cefb3844db6b61bfa757d00f465210b5f60d --- /dev/null +++ b/tests/test_mass.cpp @@ -0,0 +1,115 @@ +#include <iostream> +#include <iomanip> + +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("difftest"); + gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + gmo::synchronize(); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + +#define OVERINTEGRATE +int test_mass_convergence(int geometric_order, int approximation_order) +{ + std::vector<double> sizes({ 0.32, 0.16, 0.08, 0.04 }); + std::vector<double> errors; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + auto test_f = [](const point_3d& pt) -> double { + return std::sin(M_PI*pt.x())*std::sin(M_PI*pt.y())*std::sin(M_PI*pt.z()); + }; + + for (size_t i = 0; i < sizes.size(); i++) + { + double h = sizes[i]; + make_geometry(0,h); + model mod(geometric_order, approximation_order); + + auto& e0 = mod.entity_at(0); + e0.sort_by_orientation(); + vecxd test_f_e0 = e0.project(test_f); + double proj_error_e0 = 0.0; +#ifdef OVERINTEGRATE + auto [rps, pqps] = integrate(e0, 2*approximation_order+1); + auto num_qp = rps.size(); +#endif + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + auto num_bf = re.num_basis_functions(); + + vecxd pvals = test_f_e0.segment(iT*num_bf, num_bf); +#ifdef OVERINTEGRATE + for (size_t iQp = 0; iQp < num_qp; iQp++) + { + vecxd phi = re.basis_functions( rps[iQp] ); + auto pqpofs = num_qp*iT + iQp; + double diff = (pvals.dot(phi) - test_f(pqps[pqpofs].point())); + proj_error_e0 += std::abs(pqps[pqpofs].weight())*diff*diff; + } +#else + auto pqps = pe.integration_points(); + for (size_t iQp = 0; iQp < pqps.size(); iQp++) + { + vecxd phi = re.basis_functions( iQp ); + double diff = (pvals.dot(phi) - test_f(pqps[iQp].point())); + proj_error_e0 += std::abs(pqps[iQp].weight())*diff*diff; + } +#endif + } + + errors.push_back( std::sqrt(proj_error_e0) ); + } + + std::cout << Byellowfg << "Errors: " << reset; + for (auto& error : errors) + std::cout << error << " "; + std::cout << std::endl; + + std::cout << Byellowfg << "Rates: " << reset; + double rate = 0.0; + for (size_t i = 1; i < errors.size(); i++) + std::cout << (rate = std::log2(errors[i-1]/errors[i])) << " "; + std::cout << std::endl; + + COMPARE_VALUES_ABSOLUTE("Projection", rate, double(approximation_order+1), 0.2); + + return 0; +} + +int main(void) +{ + gmsh::initialize(); + gmsh::option::setNumber("General.Terminal", 0); + gmsh::option::setNumber("Mesh.Algorithm", 1); + gmsh::option::setNumber("Mesh.Algorithm3D", 1); + + + int failed_tests = 0; + + std::cout << Bmagentafg << " *** TESTING: PROJECTIONS ***" << reset << std::endl; + for (size_t go = 1; go < 5; go++) + { + for (size_t ao = go; ao < 5; ao++) + failed_tests += test_mass_convergence(go, ao); + } + + return failed_tests; +} \ No newline at end of file