Files
mfem/tests/unit/fem/test_white_noise.cpp
T

229 lines
5.6 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;
// These unit tests use the Monte Carlo method to verify that
// E[b] = 0 and E[bbᵀ] = M,
// where M is the mass matrix.
namespace white_noise
{
static int N = 20000;
TEST_CASE("WhiteGaussianNoiseDomainLFIntegrator on 2D NCMesh")
{
// Setup
const auto order = GENERATE(1, 2, 3);
Mesh mesh = Mesh::MakeCartesian2D(3, 3, Element::QUADRILATERAL);
// Make the mesh NC
mesh.EnsureNCMesh();
{
Array<int> elements_to_refine(1);
elements_to_refine[0] = 1;
mesh.GeneralRefinement(elements_to_refine, 1, 0);
}
H1_FECollection fec(order, mesh.Dimension());
FiniteElementSpace fespace(&mesh, &fec);
const SparseMatrix * P = fespace.GetConformingProlongation();
int ndofs = fespace.GetTrueVSize();
LinearForm b(&fespace);
int seed = 4000;
WhiteGaussianNoiseDomainLFIntegrator *WhiteNoise = new
WhiteGaussianNoiseDomainLFIntegrator(seed);
WhiteNoise->SaveFactors(mesh.GetNE());
b.AddDomainIntegrator(WhiteNoise);
Vector B;
SECTION("Mean")
{
// Compute population mean
Vector Bmean(ndofs);
Bmean = 0.0;
for (int i = 0; i < N; i++)
{
b.Assemble();
if (P)
{
B.SetSize(ndofs);
P->MultTranspose(b,B);
}
else
{
B.SetDataAndSize(b.GetData(),ndofs);
}
Bmean += B;
}
Bmean *= 1.0/(double)N;
// Compare population mean to the zero vector
REQUIRE(Bmean.Normlinf() < 5.0e-3);
}
SECTION("Covariance")
{
// Compute population covariance
DenseMatrix C(ndofs);
C = 0.0;
for (int i = 0; i < N; i++)
{
b.Assemble();
if (P)
{
B.SetSize(ndofs);
P->MultTranspose(b,B);
}
else
{
B.SetDataAndSize(b.GetData(),ndofs);
}
B.HostRead();
AddMultVVt(B, C);
}
C *= 1.0/(double)N;
// Compute mass matrix
BilinearForm a(&fespace);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
SparseMatrix M;
Array<int> empty;
a.FormSystemMatrix(empty,M);
DenseMatrix Mdense;
M.ToDenseMatrix(Mdense);
// Compare population covariance to mass matrix
Mdense -= C;
REQUIRE(Mdense.MaxMaxNorm() < 2.0e-3);
}
}
#ifdef MFEM_USE_MPI
TEST_CASE("Parallel WhiteGaussianNoiseDomainLFIntegrator on 2D NCMesh",
"[Parallel]")
{
// Setup
const auto order = GENERATE(1, 2, 3);
Mesh mesh = Mesh::MakeCartesian2D(3, 3, Element::QUADRILATERAL);
// Make the mesh NC
mesh.EnsureNCMesh();
{
Array<int> elements_to_refine(1);
elements_to_refine[0] = 1;
mesh.GeneralRefinement(elements_to_refine, 1, 0);
}
ParMesh pmesh(MPI_COMM_WORLD,mesh);
mesh.Clear();
H1_FECollection fec(order, pmesh.Dimension());
ParFiniteElementSpace fespace(&pmesh, &fec);
HypreParMatrix * P = fespace.Dof_TrueDof_Matrix();
int ntdofs = fespace.GetTrueVSize();
int nvdofs = fespace.GetVSize();
ParLinearForm b(&fespace);
int seed = 4000;
WhiteGaussianNoiseDomainLFIntegrator *WhiteNoise = new
WhiteGaussianNoiseDomainLFIntegrator(MPI_COMM_WORLD,seed);
WhiteNoise->SaveFactors(pmesh.GetNE());
b.AddDomainIntegrator(WhiteNoise);
Vector B;
SECTION("Mean")
{
// Compute population mean
Vector bmean(nvdofs);
bmean = 0.0;
for (int i = 0; i < N; i++)
{
b.Assemble();
bmean += b;
}
bmean *= 1.0/(double)N;
Vector Bmean(ntdofs);
P->MultTranspose(bmean,Bmean);
// Compare population mean to the zero vector
REQUIRE(Bmean.Normlinf() < 5.0e-3);
}
SECTION("Covariance")
{
// Compute population covariance
DenseMatrix C(nvdofs);
C = 0.;
for (int i = 0; i < N; i++)
{
b.Assemble();
AddMultVVt(b, C);
}
C *= 1.0/(double)N;
// Create a "sparse" matrix from C;
SparseMatrix S(nvdofs);
for (int i = 0; i<nvdofs; i++)
{
for (int j = 0; j<nvdofs; j++)
{
S.Set(i,j,C(i,j));
}
}
S.Finalize();
HypreParMatrix * BlockC = new HypreParMatrix(MPI_COMM_WORLD,
fespace.GlobalVSize(),
fespace.GetDofOffsets(), &S);
HypreParMatrix * ParC = RAP(BlockC, P);
delete BlockC;
// Compute mass matrix
ParBilinearForm a(&fespace);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
HypreParMatrix M;
Array<int> empty;
a.FormSystemMatrix(empty,M);
// Compare population covariance to mass matrix
M *= -1.;
HypreParMatrix * diff = ParAdd(ParC, &M);
SparseMatrix diag, offd;
diff->GetDiag(diag);
HYPRE_BigInt * cmap;
diff->GetOffd(offd,cmap);
REQUIRE(diag.MaxNorm() < 2.0e-3);
REQUIRE(offd.MaxNorm() < 2.0e-3);
delete diff;
delete ParC;
}
}
#endif
} // namespace white_noise