Files
mfem/tests/unit/fem/test_cmplx_gridfunc_save.cpp
T
2025-10-29 15:13:30 -07:00

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