Files
LBPM/tests/TestWriter.cpp
2021-01-06 11:58:43 -05:00

395 lines
16 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <exception>
#include <stdexcept>
#include <fstream>
#include <memory>
#include "common/UnitTest.h"
#include "common/Utilities.h"
#include "common/MPI.h"
#include "IO/MeshDatabase.h"
#include "IO/Reader.h"
#include "IO/Writer.h"
#include "ProfilerApp.h"
inline bool approx_equal( const Point& A, const Point& B )
{
double tol = 1e-7*sqrt(A.x*A.x+A.y*A.y+A.z*A.z);
return fabs(A.x-B.x)<=tol && fabs(A.y-B.y)<=tol && fabs(A.z-B.z)<=tol;
}
inline bool approx_equal( const double& A, const double& B )
{
return fabs(A-B) <= std::max<double>(1e-7*fabs(A+B),1e-20);
}
inline double distance( const Point& p )
{
return sqrt(p.x*p.x+p.y*p.y+p.z*p.z);
}
// Test writing and reading the given format
void testWriter( const std::string& format, std::vector<IO::MeshDataStruct>& meshData, UnitTest& ut )
{
Utilities::MPI comm( MPI_COMM_WORLD );
int nprocs = comm.getSize();
comm.barrier();
// Get the format
std::string format2 = format;
auto precision = IO::DataType::Double;
if ( format == "silo-double" ) {
format2 = "silo";
precision = IO::DataType::Double;
} else if ( format == "silo-float" ) {
format2 = "silo";
precision = IO::DataType::Float;
}
// Set the precision for the variables
for ( auto& data : meshData ) {
data.precision = precision;
for ( auto& var : data.vars )
var->precision = precision;
}
// Write the data
PROFILE_START(format+"-write");
IO::initialize( "test_"+format, format2, false );
IO::writeData( 0, meshData, comm );
IO::writeData( 3, meshData, comm );
comm.barrier();
PROFILE_STOP(format+"-write");
// Get the summary name for reading
std::string path = "test_" + format;
std::string summary_name;
if ( format=="old" || format=="new" )
summary_name = "summary.LBM";
else if ( format=="silo-float" || format=="silo-double" )
summary_name = "LBM.visit";
else
ERROR("Unknown format");
// Get direct access to the meshes used to test the reader
const auto pointmesh = dynamic_cast<IO::PointList*>( meshData[0].mesh.get() );
const auto trimesh = dynamic_cast<IO::TriMesh*>( meshData[1].mesh.get() );
const auto trilist = dynamic_cast<IO::TriList*>( meshData[2].mesh.get() );
const auto domain = dynamic_cast<IO::DomainMesh*>( meshData[3].mesh.get() );
const size_t N_tri = trimesh->A.size();
// Get a list of the timesteps
PROFILE_START(format+"-read-timesteps");
auto timesteps = IO::readTimesteps( path + "/" + summary_name );
PROFILE_STOP(format+"-read-timesteps");
if ( timesteps.size()==2 )
ut.passes(format+": Corrent number of timesteps");
else
ut.failure(format+": Incorrent number of timesteps");
// Check the mesh lists
for ( const auto& timestep : timesteps ) {
// Load the list of meshes and check its size
PROFILE_START(format+"-read-getMeshList");
auto databaseList = IO::getMeshList(path,timestep);
PROFILE_STOP(format+"-read-getMeshList");
if ( databaseList.size()==meshData.size() )
ut.passes(format+": Corrent number of meshes found");
else
ut.failure(format+": Incorrent number of meshes found");
// Check the number of domains for each mesh
bool pass = true;
for ( const auto& database : databaseList )
pass = pass && (int)database.domains.size()==nprocs;
if ( pass ) {
ut.passes(format+": Corrent number of domains for mesh");
} else {
ut.failure(format+": Incorrent number of domains for mesh");
continue;
}
// For each domain, load the mesh and check its data
for ( const auto& database : databaseList ) {
pass = true;
for (size_t k=0; k<database.domains.size(); k++) {
PROFILE_START(format+"-read-getMesh");
auto mesh = IO::getMesh(path,timestep,database,k);
PROFILE_STOP(format+"-read-getMesh");
if ( mesh.get()==NULL ) {
printf("Failed to load %s\n",database.name.c_str());
pass = false;
break;
}
if ( database.name=="pointmesh" ) {
// Check the pointmesh
auto pmesh = IO::getPointList(mesh);
if ( pmesh.get()==NULL ) {
pass = false;
break;
}
if ( pmesh->points.size() != pointmesh->points.size() ) {
pass = false;
break;
}
}
if ( database.name=="trimesh" || database.name=="trilist" ) {
// Check the trimesh/trilist
auto mesh1 = IO::getTriMesh(mesh);
auto mesh2 = IO::getTriList(mesh);
if ( mesh1.get()==NULL || mesh2.get()==NULL ) {
pass = false;
break;
}
if ( mesh1->A.size()!=N_tri || mesh1->B.size()!=N_tri || mesh1->C.size()!=N_tri ||
mesh2->A.size()!=N_tri || mesh2->B.size()!=N_tri || mesh2->C.size()!=N_tri )
{
pass = false;
break;
}
const std::vector<Point>& P1 = mesh1->vertices->points;
const std::vector<int>& A1 = mesh1->A;
const std::vector<int>& B1 = mesh1->B;
const std::vector<int>& C1 = mesh1->C;
const std::vector<Point>& A2 = mesh2->A;
const std::vector<Point>& B2 = mesh2->B;
const std::vector<Point>& C2 = mesh2->C;
const std::vector<Point>& A = trilist->A;
const std::vector<Point>& B = trilist->B;
const std::vector<Point>& C = trilist->C;
for (size_t i=0; i<N_tri; i++) {
if ( !approx_equal(P1[A1[i]],A[i]) || !approx_equal(P1[B1[i]],B[i]) || !approx_equal(P1[C1[i]],C[i]) )
pass = false;
if ( !approx_equal(A2[i],A[i]) || !approx_equal(B2[i],B[i]) || !approx_equal(C2[i],C[i]) )
pass = false;
}
}
if ( database.name=="domain" && format!="old" ) {
// Check the domain mesh
const IO::DomainMesh& mesh1 = *std::dynamic_pointer_cast<IO::DomainMesh>(mesh);
if ( mesh1.nprocx!=domain->nprocx || mesh1.nprocy!=domain->nprocy || mesh1.nprocz!=domain->nprocz )
pass = false;
if ( mesh1.nx!=domain->nx || mesh1.ny!=domain->ny || mesh1.nz!=domain->nz )
pass = false;
if ( mesh1.Lx!=domain->Lx || mesh1.Ly!=domain->Ly || mesh1.Lz!=domain->Lz )
pass = false;
}
}
if ( pass ) {
ut.passes(format+": Mesh \"" + database.name + "\" loaded correctly");
} else {
ut.failure(format+": Mesh \"" + database.name + "\" did not load correctly");
continue;
}
// Load the variables and check their data
if ( format=="old" )
continue; // Old format does not support variables
const IO::MeshDataStruct* mesh0 = NULL;
for (size_t k=0; k<meshData.size(); k++) {
if ( meshData[k].meshName == database.name ) {
mesh0 = &meshData[k];
break;
}
}
for (size_t k=0; k<database.domains.size(); k++) {
auto mesh = IO::getMesh(path,timestep,database,k);
for (size_t v=0; v<mesh0->vars.size(); v++) {
PROFILE_START(format+"-read-getVariable");
auto variable = IO::getVariable(path,timestep,database,k,mesh0->vars[v]->name);
if ( format=="new" )
IO::reformatVariable( *mesh, *variable );
PROFILE_STOP(format+"-read-getVariable");
const IO::Variable& var1 = *mesh0->vars[v];
const IO::Variable& var2 = *variable;
pass = var1.name == var2.name;
pass = pass && var1.dim == var2.dim;
pass = pass && var1.type == var2.type;
pass = pass && var1.data.length() == var2.data.length();
if ( pass ) {
for (size_t m=0; m<var1.data.length(); m++)
pass = pass && approx_equal(var1.data(m),var2.data(m));
}
if ( pass ) {
ut.passes(format+": Variable \"" + variable->name + "\" matched");
} else {
ut.failure(format+": Variable \"" + variable->name + "\" did not match");
break;
}
}
}
}
}
}
// Main
int main(int argc, char **argv)
{
Utilities::startup( argc, argv );
Utilities::MPI comm( MPI_COMM_WORLD );
int rank = comm.getRank();
int nprocs = comm.getSize();
Utilities::setAbortBehavior(true,2);
Utilities::setErrorHandlers();
UnitTest ut;
// Create some points
const int N_points = 8;
const int N_tri = 12;
double x[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
double y[8] = { 0, 0, 1, 1, 0, 0, 1, 1 };
double z[8] = { 0, 0, 0, 0, 1, 1, 1, 1 };
int tri[N_tri][3] = {
{0,1,3}, {0,3,2}, {4,5,7}, {4,7,6}, // z faces
{0,1,4}, {1,4,5}, {2,3,6}, {3,6,7}, // y faces
{0,2,4}, {2,4,6}, {1,3,5}, {3,5,7} // x faces
};
// Create the meshes
auto set1 = std::make_shared<IO::PointList>(N_points);
for (int i=0; i<N_points; i++) {
set1->points[i].x = x[i];
set1->points[i].y = y[i];
set1->points[i].z = z[i];
}
auto trimesh = std::make_shared<IO::TriMesh>(N_tri,set1);
for (int i=0; i<N_tri; i++) {
trimesh->A[i] = tri[i][0];
trimesh->B[i] = tri[i][1];
trimesh->C[i] = tri[i][2];
}
auto trilist = std::make_shared<IO::TriList>(*trimesh);
for (int i=0; i<N_tri; i++) {
Point A(x[tri[i][0]],y[tri[i][0]],z[tri[i][0]]);
Point B(x[tri[i][1]],y[tri[i][1]],z[tri[i][1]]);
Point C(x[tri[i][2]],y[tri[i][2]],z[tri[i][2]]);
if ( !approx_equal(trilist->A[i],A) || !approx_equal(trilist->B[i],B) || !approx_equal(trilist->C[i],C) )
{
printf("Failed to create trilist\n");
return -1;
}
}
RankInfoStruct rank_data( rank, nprocs, 1, 1 );
auto domain = std::make_shared<IO::DomainMesh>(rank_data,6,7,8,1.0,1.0,1.0);
// Create the variables
const auto NodeVar = IO::VariableType::NodeVariable;
const auto VolVar = IO::VariableType::VolumeVariable;
auto set_node_mag = std::make_shared<IO::Variable>(1,NodeVar,"Node_set_mag");
auto set_node_vec = std::make_shared<IO::Variable>(3,NodeVar,"Node_set_vec");
auto list_node_mag = std::make_shared<IO::Variable>(1,NodeVar,"Node_list_mag");
auto list_node_vec = std::make_shared<IO::Variable>(3,NodeVar,"Node_list_vec");
auto point_node_mag = std::make_shared<IO::Variable>(1,NodeVar,"Node_point_mag");
auto point_node_vec = std::make_shared<IO::Variable>(3,NodeVar,"Node_point_vec");
auto domain_node_mag = std::make_shared<IO::Variable>(1,NodeVar,"Node_domain_mag");
auto domain_node_vec = std::make_shared<IO::Variable>(3,NodeVar,"Node_domain_vec");
auto set_cell_mag = std::make_shared<IO::Variable>(1,VolVar,"Cell_set_mag");
auto set_cell_vec = std::make_shared<IO::Variable>(3,VolVar,"Cell_set_vec");
auto list_cell_mag = std::make_shared<IO::Variable>(1,VolVar,"Cell_list_mag");
auto list_cell_vec = std::make_shared<IO::Variable>(3,VolVar,"Cell_list_vec");
auto domain_cell_mag = std::make_shared<IO::Variable>(1,VolVar,"Cell_domain_mag");
auto domain_cell_vec = std::make_shared<IO::Variable>(3,VolVar,"Cell_domain_vec");
point_node_mag->data.resize( N_points );
point_node_vec->data.resize( N_points, 3 );
for (int i=0; i<N_points; i++) {
point_node_mag->data(i) = distance(set1->points[i]);
point_node_vec->data(i,0) = set1->points[i].x;
point_node_vec->data(i,1) = set1->points[i].y;
point_node_vec->data(i,2) = set1->points[i].z;
}
set_node_mag->data = point_node_mag->data;
set_node_vec->data = point_node_vec->data;
list_node_mag->data.resize( 3*N_tri );
list_node_vec->data.resize( 3*N_tri, 3 );
for (int i=0; i<N_points; i++) {
list_node_mag->data(3*i+0) = distance(trilist->A[i]);
list_node_mag->data(3*i+1) = distance(trilist->B[i]);
list_node_mag->data(3*i+2) = distance(trilist->C[i]);
list_node_vec->data(3*i+0,0) = trilist->A[i].x;
list_node_vec->data(3*i+0,1) = trilist->A[i].y;
list_node_vec->data(3*i+0,2) = trilist->A[i].z;
list_node_vec->data(3*i+1,0) = trilist->B[i].x;
list_node_vec->data(3*i+1,1) = trilist->B[i].y;
list_node_vec->data(3*i+1,2) = trilist->B[i].z;
list_node_vec->data(3*i+2,0) = trilist->C[i].x;
list_node_vec->data(3*i+2,1) = trilist->C[i].y;
list_node_vec->data(3*i+2,2) = trilist->C[i].z;
}
domain_node_mag->data.resize(domain->nx+1,domain->ny+1,domain->nz+1);
domain_node_vec->data.resize({(size_t)domain->nx+1,(size_t)domain->ny+1,(size_t)domain->nz+1,3});
for (int i=0; i<domain->nx+1; i++) {
for (int j=0; j<domain->ny+1; j++) {
for (int k=0; k<domain->nz+1; k++) {
domain_node_mag->data(i,j,k) = distance(Point(i,j,k));
domain_node_vec->data(i,j,k,0) = Point(i,j,k).x;
domain_node_vec->data(i,j,k,1) = Point(i,j,k).y;
domain_node_vec->data(i,j,k,2) = Point(i,j,k).z;
}
}
}
set_cell_mag->data.resize( N_tri );
set_cell_vec->data.resize( N_tri, 3 );
for (int i=0; i<N_tri; i++) {
set_cell_mag->data(i) = i;
set_cell_vec->data(i,0) = 3*i+0;
set_cell_vec->data(i,1) = 3*i+1;
set_cell_vec->data(i,2) = 3*i+2;
}
list_cell_mag->data = set_cell_mag->data;
list_cell_vec->data = set_cell_vec->data;
domain_cell_mag->data.resize(domain->nx,domain->ny,domain->nz);
domain_cell_vec->data.resize({(size_t)domain->nx,(size_t)domain->ny,(size_t)domain->nz,3});
for (int i=0; i<domain->nx; i++) {
for (int j=0; j<domain->ny; j++) {
for (int k=0; k<domain->nz; k++) {
domain_cell_mag->data(i,j,k) = distance(Point(i,j,k));
domain_cell_vec->data(i,j,k,0) = Point(i,j,k).x;
domain_cell_vec->data(i,j,k,1) = Point(i,j,k).y;
domain_cell_vec->data(i,j,k,2) = Point(i,j,k).z;
}
}
}
// Create the MeshDataStruct
std::vector<IO::MeshDataStruct> meshData(4);
meshData[0].meshName = "pointmesh";
meshData[0].mesh = set1;
meshData[0].vars.push_back(point_node_mag);
meshData[0].vars.push_back(point_node_vec);
meshData[1].meshName = "trimesh";
meshData[1].mesh = trimesh;
meshData[1].vars.push_back(set_node_mag);
meshData[1].vars.push_back(set_node_vec);
meshData[1].vars.push_back(set_cell_mag);
meshData[1].vars.push_back(set_cell_vec);
meshData[2].meshName = "trilist";
meshData[2].mesh = trilist;
meshData[2].vars.push_back(list_node_mag);
meshData[2].vars.push_back(list_node_vec);
meshData[2].vars.push_back(list_cell_mag);
meshData[2].vars.push_back(list_cell_vec);
meshData[3].meshName = "domain";
meshData[3].mesh = domain;
meshData[3].vars.push_back(domain_node_mag);
meshData[3].vars.push_back(domain_node_vec);
meshData[3].vars.push_back(domain_cell_mag);
meshData[3].vars.push_back(domain_cell_vec);
// Run the tests
testWriter( "old", meshData, ut );
testWriter( "new", meshData, ut );
testWriter( "silo-double", meshData, ut );
testWriter( "silo-float", meshData, ut );
// Finished
ut.report();
PROFILE_SAVE("TestWriter",true);
int N_errors = ut.NumFailGlobal();
comm.barrier();
Utilities::shutdown();
return N_errors;
}