203 lines
7.7 KiB
C++
203 lines
7.7 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include "mfem.hpp"
|
|
#include "unit_tests.hpp"
|
|
|
|
using namespace mfem;
|
|
|
|
TEST_CASE("ComplexGridFunction Save", "[ComplexGridFunction]")
|
|
{
|
|
const int order = 3;
|
|
|
|
Mesh mesh = Mesh::MakeCartesian3D(1, 2, 3, Element::PYRAMID,
|
|
1.0, 2.0, 3.0);
|
|
|
|
H1_FECollection fec_h1(order, mesh.Dimension());
|
|
ND_FECollection fec_nd(order, mesh.Dimension());
|
|
RT_FECollection fec_rt(order-1, mesh.Dimension());
|
|
L2_FECollection fec_l2(order-1, mesh.Dimension());
|
|
|
|
FiniteElementSpace fes_h1(&mesh, &fec_h1);
|
|
FiniteElementSpace fes_nd(&mesh, &fec_nd);
|
|
FiniteElementSpace fes_rt(&mesh, &fec_rt);
|
|
FiniteElementSpace fes_l2(&mesh, &fec_l2);
|
|
|
|
ComplexGridFunction gf_h1(&fes_h1);
|
|
ComplexGridFunction gf_nd(&fes_nd);
|
|
ComplexGridFunction gf_rt(&fes_rt);
|
|
ComplexGridFunction gf_l2(&fes_l2);
|
|
|
|
gf_h1.Randomize(1);
|
|
gf_nd.Randomize(1);
|
|
gf_rt.Randomize(1);
|
|
gf_l2.Randomize(1);
|
|
|
|
Vector zeroVec(3); zeroVec = 0.0;
|
|
ConstantCoefficient zeroCoef(0.0);
|
|
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
|
|
|
const double norm_h1 = gf_h1.ComputeL2Error(zeroCoef, zeroCoef);
|
|
const double norm_nd = gf_nd.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double norm_rt = gf_rt.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double norm_l2 = gf_l2.ComputeL2Error(zeroCoef, zeroCoef);
|
|
|
|
std::ofstream ofs_h1("cmplx_h1.gf"); ofs_h1.precision(8);
|
|
std::ofstream ofs_nd("cmplx_nd.gf"); ofs_nd.precision(8);
|
|
std::ofstream ofs_rt("cmplx_rt.gf"); ofs_rt.precision(8);
|
|
std::ofstream ofs_l2("cmplx_l2.gf"); ofs_l2.precision(8);
|
|
|
|
gf_h1.Save(ofs_h1); ofs_h1.close();
|
|
gf_nd.Save(ofs_nd); ofs_nd.close();
|
|
gf_rt.Save(ofs_rt); ofs_rt.close();
|
|
gf_l2.Save(ofs_l2); ofs_l2.close();
|
|
|
|
std::ifstream ifs_h1("cmplx_h1.gf");
|
|
std::ifstream ifs_nd("cmplx_nd.gf");
|
|
std::ifstream ifs_rt("cmplx_rt.gf");
|
|
std::ifstream ifs_l2("cmplx_l2.gf");
|
|
|
|
ComplexGridFunction gf_h1_read(&mesh, ifs_h1); ifs_h1.close();
|
|
ComplexGridFunction gf_nd_read(&mesh, ifs_nd); ifs_nd.close();
|
|
ComplexGridFunction gf_rt_read(&mesh, ifs_rt); ifs_rt.close();
|
|
ComplexGridFunction gf_l2_read(&mesh, ifs_l2); ifs_l2.close();
|
|
|
|
gf_h1_read -= gf_h1;
|
|
gf_nd_read -= gf_nd;
|
|
gf_rt_read -= gf_rt;
|
|
gf_l2_read -= gf_l2;
|
|
|
|
const double diff_h1 = gf_h1_read.ComputeL2Error(zeroCoef, zeroCoef);
|
|
const double diff_nd = gf_nd_read.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double diff_rt = gf_rt_read.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double diff_l2 = gf_l2_read.ComputeL2Error(zeroCoef, zeroCoef);
|
|
|
|
REQUIRE(diff_h1 < 1e-8 * norm_h1);
|
|
REQUIRE(diff_nd < 1e-8 * norm_nd);
|
|
REQUIRE(diff_rt < 1e-8 * norm_rt);
|
|
REQUIRE(diff_l2 < 1e-8 * norm_l2);
|
|
|
|
// Clean up
|
|
REQUIRE(std::remove("cmplx_h1.gf") == 0);
|
|
REQUIRE(std::remove("cmplx_nd.gf") == 0);
|
|
REQUIRE(std::remove("cmplx_rt.gf") == 0);
|
|
REQUIRE(std::remove("cmplx_l2.gf") == 0);
|
|
}
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
|
|
TEST_CASE("ParComplexGridFunction Save", "[ParComplexGridFunction][Parallel]")
|
|
{
|
|
const int num_procs = Mpi::WorldSize();
|
|
const int my_rank = Mpi::WorldRank();
|
|
const int order = 3;
|
|
|
|
const int nx = (int)rint(cbrt(real_t(num_procs)));
|
|
const int ny = (int)rint(2.0 * cbrt(real_t(num_procs)));
|
|
const int nz = (int)rint(3.0 * cbrt(real_t(num_procs)));
|
|
Mesh mesh = Mesh::MakeCartesian3D(nx, ny, nz, Element::PYRAMID,
|
|
1.0, 2.0, 3.0);
|
|
|
|
// Define a parallel mesh by a partitioning of the serial mesh.
|
|
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
|
|
|
H1_FECollection fec_h1(order, mesh.Dimension());
|
|
ND_FECollection fec_nd(order, mesh.Dimension());
|
|
RT_FECollection fec_rt(order-1, mesh.Dimension());
|
|
L2_FECollection fec_l2(order-1, mesh.Dimension());
|
|
|
|
ParFiniteElementSpace pfes_h1(&pmesh, &fec_h1);
|
|
ParFiniteElementSpace pfes_nd(&pmesh, &fec_nd);
|
|
ParFiniteElementSpace pfes_rt(&pmesh, &fec_rt);
|
|
ParFiniteElementSpace pfes_l2(&pmesh, &fec_l2);
|
|
|
|
ParComplexGridFunction pgf_h1(&pfes_h1);
|
|
ParComplexGridFunction pgf_nd(&pfes_nd);
|
|
ParComplexGridFunction pgf_rt(&pfes_rt);
|
|
ParComplexGridFunction pgf_l2(&pfes_l2);
|
|
|
|
pgf_h1.Randomize(1);
|
|
pgf_nd.Randomize(1);
|
|
pgf_rt.Randomize(1);
|
|
pgf_l2.Randomize(1);
|
|
|
|
// Ensure that the L-DOFs are set consistently on all ranks
|
|
pgf_h1.real().SetTrueVector(); pgf_h1.real().SetFromTrueVector();
|
|
pgf_h1.imag().SetTrueVector(); pgf_h1.imag().SetFromTrueVector();
|
|
pgf_nd.real().SetTrueVector(); pgf_nd.real().SetFromTrueVector();
|
|
pgf_nd.imag().SetTrueVector(); pgf_nd.imag().SetFromTrueVector();
|
|
pgf_rt.real().SetTrueVector(); pgf_rt.real().SetFromTrueVector();
|
|
pgf_rt.imag().SetTrueVector(); pgf_rt.imag().SetFromTrueVector();
|
|
pgf_l2.real().SetTrueVector(); pgf_l2.real().SetFromTrueVector();
|
|
pgf_l2.imag().SetTrueVector(); pgf_l2.imag().SetFromTrueVector();
|
|
|
|
Vector zeroVec(3); zeroVec = 0.0;
|
|
ConstantCoefficient zeroCoef(0.0);
|
|
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
|
|
|
const double norm_h1 = pgf_h1.ComputeL2Error(zeroCoef, zeroCoef);
|
|
const double norm_nd = pgf_nd.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double norm_rt = pgf_rt.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double norm_l2 = pgf_l2.ComputeL2Error(zeroCoef, zeroCoef);
|
|
|
|
std::ostringstream name_h1, name_nd, name_rt, name_l2;
|
|
name_h1 << "cmplx_gf_h1." << std::setfill('0') << std::setw(6) << my_rank;
|
|
name_nd << "cmplx_gf_nd." << std::setfill('0') << std::setw(6) << my_rank;
|
|
name_rt << "cmplx_gf_rt." << std::setfill('0') << std::setw(6) << my_rank;
|
|
name_l2 << "cmplx_gf_l2." << std::setfill('0') << std::setw(6) << my_rank;
|
|
|
|
std::ofstream ofs_h1(name_h1.str().c_str()); ofs_h1.precision(8);
|
|
std::ofstream ofs_nd(name_nd.str().c_str()); ofs_nd.precision(8);
|
|
std::ofstream ofs_rt(name_rt.str().c_str()); ofs_rt.precision(8);
|
|
std::ofstream ofs_l2(name_l2.str().c_str()); ofs_l2.precision(8);
|
|
|
|
pgf_h1.Save(ofs_h1); ofs_h1.close();
|
|
pgf_nd.Save(ofs_nd); ofs_nd.close();
|
|
pgf_rt.Save(ofs_rt); ofs_rt.close();
|
|
pgf_l2.Save(ofs_l2); ofs_l2.close();
|
|
|
|
std::ifstream ifs_h1(name_h1.str().c_str());
|
|
std::ifstream ifs_nd(name_nd.str().c_str());
|
|
std::ifstream ifs_rt(name_rt.str().c_str());
|
|
std::ifstream ifs_l2(name_l2.str().c_str());
|
|
|
|
ParComplexGridFunction pgf_h1_read(&pmesh, ifs_h1); ifs_h1.close();
|
|
ParComplexGridFunction pgf_nd_read(&pmesh, ifs_nd); ifs_nd.close();
|
|
ParComplexGridFunction pgf_rt_read(&pmesh, ifs_rt); ifs_rt.close();
|
|
ParComplexGridFunction pgf_l2_read(&pmesh, ifs_l2); ifs_l2.close();
|
|
|
|
pgf_h1_read -= pgf_h1;
|
|
pgf_nd_read -= pgf_nd;
|
|
pgf_rt_read -= pgf_rt;
|
|
pgf_l2_read -= pgf_l2;
|
|
|
|
const double diff_h1 = pgf_h1_read.ComputeL2Error(zeroCoef, zeroCoef);
|
|
const double diff_nd = pgf_nd_read.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double diff_rt = pgf_rt_read.ComputeL2Error(zeroVecCoef, zeroVecCoef);
|
|
const double diff_l2 = pgf_l2_read.ComputeL2Error(zeroCoef, zeroCoef);
|
|
|
|
if (my_rank == 0)
|
|
{
|
|
REQUIRE(diff_h1 < 1e-8 * norm_h1);
|
|
REQUIRE(diff_nd < 1e-8 * norm_nd);
|
|
REQUIRE(diff_rt < 1e-8 * norm_rt);
|
|
REQUIRE(diff_l2 < 1e-8 * norm_l2);
|
|
}
|
|
|
|
// Clean up
|
|
REQUIRE(std::remove(name_h1.str().c_str()) == 0);
|
|
REQUIRE(std::remove(name_nd.str().c_str()) == 0);
|
|
REQUIRE(std::remove(name_rt.str().c_str()) == 0);
|
|
REQUIRE(std::remove(name_l2.str().c_str()) == 0);
|
|
}
|
|
|
|
#endif // MFEM_USE_MPI
|