Generation of RBF object that represents a signed distance function.
Generation of RBF object that represents a signed distance functionbitpit for levelset RBF generation This file explains how to use the script linking the Levelset and RBF capabilities of bitpit to train an RBF collection that represents the signed distance field of STL-defined objects.
The executable generates two files.
#if BITPIT_ENABLE_MPI==1
#include <mpi.h>
#endif
#include "bitpit_surfunstructured.hpp"
#include "bitpit_voloctree.hpp"
#include "bitpit_levelset.hpp"
#include "bitpit_LA.hpp"
#include "bitpit_operators.hpp"
#include "bitpit_IO.hpp"
#include "bitpit_RBF.hpp"
#include "bitpit_common.hpp"
#include <array>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <numeric>
#include <algorithm>
#include <iomanip>
using namespace std;
using namespace bitpit;
void
parse_parameters(std::map<std::string, std::vector<double>> &map,
std::string file,
const std::string delimiter)
{
std::ifstream myfile(file);
if (myfile.is_open())
{
std::string line;
std::vector<std::string> column_names;
std::vector<double> line_of_data;
unsigned int line_count = 0;
while (std::getline(myfile, line))
{
std::vector<std::string> list_of_words_base;
std::string s = line;
size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
list_of_words_base.push_back(token);
s.erase(0, pos + delimiter.length());
}
std::vector<std::string> list_of_words_clean;
for (std::size_t i = 0; i < list_of_words_base.size(); ++i)
{
if (list_of_words_base[i] != "")
{
list_of_words_clean.push_back(list_of_words_base[i]);
}
}
if (line_count != 0)
{
line_of_data.resize(list_of_words_clean.size());
for (std::size_t i = 0; i < line_of_data.size(); i++)
{
line_of_data[i] = std::stod(list_of_words_clean[i]);
}
for (std::size_t i = 0; i < line_of_data.size(); ++i)
{
map[column_names[i]].push_back(line_of_data[i]);
}
}
else
{
column_names = list_of_words_clean;
for (std::size_t i = 0; i < list_of_words_clean.size(); ++i)
{
std::vector<double> base_vector;
map[column_names[i]] = base_vector;
}
}
++line_count;
}
myfile.close();
}
else
std::cout << "Unable to open file";
std::vector<std::string> names = {"nb_subdivision",
"nb_adaptions",
"radius_ratio",
"base_function",
"mesh_range",
"tolerance",
"scaling"};
std::vector<double> values = {16,
0,
1,
2,
0.2,
1e-8,
1.0};
for (std::size_t i = 0; i < names.size(); i++)
{
if (map.find("f") == map.end())
map[names[i]].push_back(values[i]);
}
}
void run(std::string filename,
std::string data_path,
std::string parameter_file) {
constexpr int dimensions(3);
std::map<std::string, std::vector<double>> parameters;
parse_parameters(parameters, parameter_file," ");
int nb_subdivision = static_cast<int>(parameters["nb_subdivision"][0]);
int nb_adaptions = static_cast<int>(parameters["nb_adaptions"][0]);
double radius_ratio = parameters["radius_ratio"][0];
int base_function = static_cast<int>(parameters["base_function"][0]);
double mesh_range = parameters["mesh_range"][0];
double TOL = parameters["tolerance"][0];
double scaling = parameters["scaling"][0];
std::vector<std::string> timers_name;
std::vector<double> timers_values;
timers_name.push_back("load_geometry");
double time_start = MPI_Wtime();
#if BITPIT_ENABLE_MPI
#else
#endif
try {
STL0->importSTL(data_path + filename + ".stl", true);
}
catch (const std::bad_alloc &) {
STL0->importSTL(data_path + filename + ".stl", false);
}
STL0->deleteCoincidentVertices();
STL0->initializeAdjacencies();
STL0->getVTK().setName("levelset");
std::array<double, dimensions> center{};
STL0->scale(scaling,scaling,scaling,center);
std::array<double, dimensions> stlMin, stlMax, meshMin, meshMax, delta;
double h(0.), dh;
STL0->getBoundingBox(stlMin, stlMax);
delta = stlMax - stlMin;
meshMin = stlMin - mesh_range * delta;
meshMax = stlMax + mesh_range * delta;
for (int i = 0; i < dimensions; ++i) {
h = std::max(h, meshMax[i] - meshMin[i]);
}
dh = h / nb_subdivision;
#if BITPIT_ENABLE_MPI
#else
#endif
mesh.initializeAdjacencies();
mesh.initializeInterfaces();
mesh.update();
mesh.getVTK().setName("RBF_" + filename);
#if BITPIT_ENABLE_MPI
mesh.setVTKWriteTarget(PatchKernel::WriteTarget::WRITE_TARGET_CELLS_INTERNAL);
#endif
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("compute_levelset");
time_start = MPI_Wtime();
int id0 = levelset.
addObject(std::move(STL0), 0);
mesh.write();
std::vector<bitpit::adaption::Info> adaptionData_levelset;
for (int r = 0; r < nb_adaptions; ++r) {
for (auto &cell: mesh.getCells()) {
long cellId = cell.getId();
if (object0.
evalCellValue(cellId,
false) < mesh.evalCellSize(cellId))
mesh.markCellForRefinement(cellId);
}
adaptionData_levelset = mesh.update(true);
levelset.
update(adaptionData_levelset);
mesh.write();
}
unsigned long nP_total = mesh.getCellCount();
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("add_nodes_to_RBF");
time_start = MPI_Wtime();
switch (base_function) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
case 13:
break;
case 14:
break;
default:
break;
}
std::vector<double> values;
std::vector<double> weights;
std::vector<double> radii;
std::vector<std::array<double, dimensions>> nodes;
values.resize(nP_total);
weights.resize(nP_total);
nodes.resize(nP_total);
radii.resize(nP_total);
for (size_t it_RBF = 0; it_RBF < nP_total; it_RBF++) {
nodes[it_RBF] = mesh.evalCellCentroid(it_RBF);
radii[it_RBF] = mesh.evalCellSize(it_RBF) * radius_ratio;
}
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("fill_matrix");
time_start = MPI_Wtime();
int nNZ = nP_total*(1+(2*(int)ceil(radius_ratio)^dimensions));
#if BITPIT_ENABLE_MPI==1
SparseMatrix A(MPI_COMM_WORLD,
true, nP_total, nP_total, nNZ);
#else
#endif
std::vector<long> rowPattern;
std::vector<double> rowValues;
for (unsigned long i = 0; i < nP_total; i++) {
for (unsigned long j = 0; j < nP_total; j++) {
rowPattern.push_back(j);
rowValues.push_back(v_ij);
}
}
A.addRow(rowPattern, rowValues);
rowPattern.clear();
rowValues.clear();
}
A.assembly();
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("fill_RHS");
time_start = MPI_Wtime();
for (unsigned long i = 0; i < nP_total; ++i) {
b[i] = values[i];
}
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("solve_system");
time_start = MPI_Wtime();
for (unsigned long i = 0; i < nP_total; ++i) {
initialSolution[i] = values[i];
}
timers_values.push_back(MPI_Wtime() - time_start);
timers_name.push_back("add_weights_to_RBFObject");
time_start = MPI_Wtime();
for (unsigned long i = 0; i < nP_total; i++) {
weights[i] = x[i];
}
std::size_t nMaxCellVertices = mesh.getCell(0).getVertexIds().size();
timers_values.push_back(MPI_Wtime()-time_start);
timers_name.push_back("output_RBF_and_analytical");
time_start = MPI_Wtime();
std::vector<double> display_values;
display_values.resize(nP_total);
ReferenceElementInfo::MAX_ELEM_VERTICES)
const PatchKernel::CellConstRange &cellWriteRange = mesh.getVTKCellWriteRange();
for (
const Cell &cell:cellWriteRange){
const unsigned long global_id = cell.getId();
mesh.getCellVertexCoordinates(global_id, vertexCoordinates);
std::array<double, dimensions> point = cell.evalCentroid(vertexCoordinates);
std::vector<double> temp_disp = RBFObject.
evalRBF(point);
display_values[global_id] = temp_disp[0];
}
mesh.getVTK().addData<double>("RBF", VTKFieldType::SCALAR, VTKLocation::CELL, display_values);
mesh.write();
timers_values.push_back(MPI_Wtime()-time_start);
timers_name.push_back("output_RBF_to_file");
time_start = MPI_Wtime();
ofstream fw("RBF_" + filename + ".output", std::ofstream::ate);
if (fw.is_open()) {
fw << "support_radius basis_function node_x node_y ";
if (dimensions == 3)
{
fw << "node_z ";
}
fw << "weight \n";
for (unsigned long line = 0; line < nP_total; line++) {
int PRECISION = 20;
std::ostringstream streamObj_support_radius;
std::ostringstream streamObj_x;
std::ostringstream streamObj_y;
std::ostringstream streamObj_z;
std::ostringstream streamObj_weight;
streamObj_support_radius << std::fixed;
streamObj_x << std::fixed;
streamObj_y << std::fixed;
streamObj_z << std::fixed;
streamObj_weight << std::fixed;
streamObj_support_radius << std::setprecision(PRECISION);
streamObj_x << std::setprecision(PRECISION);
streamObj_y << std::setprecision(PRECISION);
streamObj_z << std::setprecision(PRECISION);
streamObj_weight << std::setprecision(PRECISION);
streamObj_support_radius << radii[line];
streamObj_x << nodes[line][0];
streamObj_y << nodes[line][1];
streamObj_z << nodes[line][2];
streamObj_weight << weights[line];
std::string strObj_support_radius = streamObj_support_radius.str();
std::string strObj_x = streamObj_x.str();
std::string strObj_y = streamObj_y.str();
std::string strObj_z = streamObj_z.str();
std::string strObj_weight = streamObj_weight.str();
fw << strObj_support_radius << " " << base_function << " "<<
strObj_x << " " << strObj_y << " ";
if (dimensions == 3)
{
fw << strObj_z << " ";
}
fw << strObj_weight << " ";
if (line < nP_total -1)
fw << "\n";
}
fw.close();
timers_values.push_back(MPI_Wtime()-time_start);
int nb_timers = timers_name.size();
for (int t = 0; t < nb_timers; t++)
{
}
}
int main(int argc, char *argv[])
{
int nProcs = 1;
int rank = 0;
#if BITPIT_ENABLE_MPI==1
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &nProcs);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (nProcs>1)
{
exit(1);
}
#endif
std::vector<std::string> argList;
for(int i=0;i<argc;i++)
argList.emplace_back(argv[i]);
std::string data_path = "../test/integration_tests/levelset/data/";
std::string filename = "cube";
std::string parameter_file = "RBF_example_00001.param";
if (argc > 3)
{
data_path = argList[1];
filename = argList[2];
parameter_file = argList[3];
}
log::manager().initialize(log::MODE_COMBINE, true, nProcs, rank);
log::cout() << log::fileVerbosity(log::LEVEL_INFO);
log::cout() << log::disableConsole();
try {
run(filename,
data_path,
parameter_file);
}
catch (const std::exception &exception) {
log::cout() << exception.what();
exit(1);
}
#if BITPIT_ENABLE_MPI==1
MPI_Finalize();
#endif
return 0;
}
The Cell class defines the cells.
Interface class for all objects with respect to whom the levelset function may be computed.
void setCellBulkEvaluationMode(LevelSetBulkEvaluationMode evaluationMode)
virtual double evalCellValue(long id, bool signedLevelSet) const
void enableVTKOutput(const LevelSetFieldset &fieldset, bool enable=true)
LevelSetObject & getObject(int) const
void setNarrowBandSize(double size=0)
void setMesh(VolumeKernel *mesh)
void update(const std::vector< adaption::Info > &adaptionData)
int addObject(LevelSetBooleanOperation, int, int, int id=levelSetDefaults::OBJECT)
void setMode(RBFMode mode)
void setSupportRadius(double)
void setFunction(RBFBasisFunction)
std::vector< double > evalRBF(const std::array< double, 3 > &)
double evalBasisPair(int i, int j)
Class to handle Radial Basis Function with a large set of 3D points as nodes.
int addNode(const std::array< double, 3 > &)
The SurfUnstructured class defines an unstructured surface triangulation.
The SystemSolver class provides methods for building and solving large linear systems.
double * getSolutionRawPtr()
void restoreSolutionRawPtr(double *raw_solution)
const double * getSolutionRawReadPtr() const
void restoreRHSRawPtr(double *raw_rhs)
void assembly(const SparseMatrix &matrix)
The VolOctree defines a Octree patch.
std::array< T, d > abs(const std::array< T, d > &x)
RBFBasisFunction
Enum class defining types of RBF kernel functions that could be used in bitpit::RBF class.
#define BITPIT_CREATE_WORKSPACE(workspace, item_type, size, stack_size)
@ SIGN_PROPAGATION
Sign is propagated from the narrow band, no other data will be evaluated.
Logger & cout(log::Level defaultSeverity, log::Visibility defaultVisibility)