229 lines
5.6 KiB
C++
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
|