395 lines
16 KiB
C++
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;
|
|
}
|
|
|
|
|