#include "TransientBCSetter.h" #include "GridGenerator/grid/Grid.h" #include "GridGenerator/grid/BoundaryConditions/BoundaryCondition.h" #include <logger/Logger.h> #include <math.h> #include <sstream> #include <fstream> #include <iostream> #include <algorithm> SPtr<FileCollection> createFileCollection(std::string prefix, FileType type) { switch(type) { case FileType::VTK: return std::make_shared<VTKFileCollection>(prefix); break; default: return nullptr; } } SPtr<TransientBCInputFileReader> createReaderForCollection(SPtr<FileCollection> fileCollection, uint readLevel) { switch(fileCollection->getFileType()) { case FileType::VTK: return std::make_shared<VTKReader>(std::static_pointer_cast<VTKFileCollection>(fileCollection), readLevel); break; default: return nullptr; } } template<typename T> std::vector<T> readStringToVector(std::string s) { std::vector<T> out; std::stringstream input(s); float num; while(input >> num) { out.push_back(num); } return out; } std::string readElement(std::string line) { size_t elemStart = line.find("<")+1; // size_t elemEnd = line.find("/>", elemStart); size_t nameLen = line.find(" ", elemStart)-elemStart; return line.substr(elemStart, nameLen); } std::string readAttribute(std::string line, std::string attributeName) { size_t attributeStart = line.find(attributeName)+attributeName.size() + 2; // add 2 for '="' size_t attributeLen = line.find("\"", attributeStart)-attributeStart; return line.substr(attributeStart, attributeLen); } void VTKFile::readHeader() { //TODO make this more flexible std::ifstream file(this->fileName); std::string line; getline(file, line); // VTKFile if(line[1]=='?') getline(file, line); // ignore first line if xml version getline(file, line); // ImageData std::vector<int> wholeExtent = readStringToVector<int>(readAttribute(line, "WholeExtent")); std::vector<float> origin = readStringToVector<float>(readAttribute(line, "Origin")); std::vector<float> spacing = readStringToVector<float>(readAttribute(line, "Spacing")); getline(file, line); // Piece std::vector<int> pieceExtent = readStringToVector<int>(readAttribute(line, "Extent")); getline(file, line); // PointData getline(file, line); while(strcmp(readElement(line).c_str(), "DataArray")==0) { Quantity quant = Quantity(); quant.name = readAttribute(line, "Name"); quant.offset = std::stoi(readAttribute(line, "offset")); this->quantities.push_back( quant ); getline(file, line); } getline(file, line); // </Piece getline(file, line); // </ImageData getline(file, line); // AppendedData int offset = int(file.tellg())+sizeof(char)+4; // skip underscore and bytesPerVal for(auto& quantity: this->quantities) { quantity.offset += offset; } file.close(); this->deltaX = spacing[0]; this->deltaY = spacing[1]; this->deltaZ = spacing[2]; this->nx = pieceExtent[1]-pieceExtent[0]+1; this->ny = pieceExtent[3]-pieceExtent[2]+1; this->nz = pieceExtent[5]-pieceExtent[4]+1; this->minX = origin[0]+this->deltaX*pieceExtent[0]; this->maxX = (this->nx-1)*this->deltaX+this->minX; this->minY = origin[1]+this->deltaY*pieceExtent[2]; this->maxY = (this->ny-1)*this->deltaY+this->minY; this->minZ = origin[2]+this->deltaZ*pieceExtent[4]; this->maxZ = (this->nz-1)*this->deltaZ+this->minZ; // printFileInfo(); } bool VTKFile::markNANs(std::vector<uint> readIndices) { std::ifstream buf(fileName.c_str(), std::ios::in | std::ios::binary); std::vector<double> tmp; tmp.reserve(readIndices.size()); buf.seekg(this->quantities[0].offset); buf.read((char*) tmp.data(), sizeof(double)*readIndices.size()); auto firstNAN = std::find_if(tmp.begin(), tmp.end(), [](auto it){ return isnan(it); }); return firstNAN != tmp.end(); } void VTKFile::loadFile() { std::ifstream buf(this->fileName.c_str(), std::ios::in | std::ios::binary); for(auto& quantity: this->quantities) { quantity.values.resize(getNumberOfPoints()); buf.seekg(quantity.offset); buf.read(reinterpret_cast<char*>(quantity.values.data()), this->getNumberOfPoints()*sizeof(double)); } buf.close(); this->loaded = true; } void VTKFile::unloadFile() { for(auto& quantity : this->quantities) { std::vector<double> replacement; quantity.values.swap(replacement); } this->loaded = false; } void VTKFile::getData(real *data, uint numberOfNodes, const std::vector<uint> &readIndices, const std::vector<uint> &writeIndices, uint offsetRead, uint offsetWrite) { if(!this->loaded) loadFile(); size_t nPoints = writeIndices.size(); for(size_t j=0; j<this->quantities.size(); j++) { real* quant = &data[j*numberOfNodes]; for(size_t i=0; i<nPoints; i++) { quant[offsetWrite+writeIndices[i]] = this->quantities[j].values[readIndices[i]+offsetRead]; } } } void VTKFile::printFileInfo() { printf("file %s with \n nx %i ny %i nz %i \n origin %f %f %f \n spacing %f %f %f \n", fileName.c_str(), nx, ny, nz, minX, minY, minZ, deltaX, deltaY, deltaZ); for(auto quantity: this->quantities) { printf("\t quantity %s offset %i \n", quantity.name.c_str(), quantity.offset); } } void VTKFileCollection::findFiles() { bool foundLastLevel = false; while(!foundLastLevel) { bool foundLastID = false; std::vector<std::vector<VTKFile>> filesOnThisLevel; while(!foundLastID) { bool foundLastPart = false; std::vector<VTKFile> filesWithThisId; while (!foundLastPart) { std::string fname = makeFileName((int)files.size(), (int)filesOnThisLevel.size(), (int)filesWithThisId.size()); std::ifstream f(fname); if(f.good()) filesWithThisId.emplace_back(fname); else foundLastPart = true; } if(!filesWithThisId.empty()) { VF_LOG_INFO("VTKFileCollection found {} files with ID {} level {}", filesWithThisId.size(), filesOnThisLevel.size(), files.size() ); filesOnThisLevel.push_back(filesWithThisId); } else foundLastID = true; } if(!filesOnThisLevel.empty()) files.push_back(filesOnThisLevel); else foundLastLevel = true; } if(files.empty()) VF_LOG_CRITICAL("VTKFileCollection found no files!"); } void TransientBCInputFileReader::getNeighbors(uint* neighbor0PP, uint* neighbor0PM, uint* neighbor0MP, uint* neighbor0MM) { std::copy(planeNeighbor0PP.begin(), planeNeighbor0PP.end(), &neighbor0PP[writingOffset]); std::copy(planeNeighbor0PM.begin(), planeNeighbor0PM.end(), &neighbor0PM[writingOffset]); std::copy(planeNeighbor0MP.begin(), planeNeighbor0MP.end(), &neighbor0MP[writingOffset]); std::copy(planeNeighbor0MM.begin(), planeNeighbor0MM.end(), &neighbor0MM[writingOffset]); } void TransientBCInputFileReader::getWeights(real* _weights0PP, real* _weights0PM, real* _weights0MP, real* _weights0MM) { std::copy(weights0PP.begin(), weights0PP.end(), &_weights0PP[writingOffset]); std::copy(weights0PM.begin(), weights0PM.end(), &_weights0PM[writingOffset]); std::copy(weights0MP.begin(), weights0MP.end(), &_weights0MP[writingOffset]); std::copy(weights0MM.begin(), weights0MM.end(), &_weights0MM[writingOffset]); } void VTKReader::initializeIndexVectors() { this->readIndices.resize(this->fileCollection->files.size()); this->writeIndices.resize(this->fileCollection->files.size()); this->nFile.resize(this->fileCollection->files.size()); for(size_t lev=0; lev<this->fileCollection->files.size(); lev++) { this->readIndices[lev].resize(this->fileCollection->files[lev].size()); this->writeIndices[lev].resize(this->fileCollection->files[lev].size()); this->nFile[lev].resize(this->fileCollection->files[lev].size()); } } void VTKReader::fillArrays(std::vector<real>& coordsY, std::vector<real>& coordsZ) { this->nPoints = (uint)coordsY.size(); this->initializeIndexVectors(); real max_diff = 1e-4; // maximum distance between point on grid and precursor plane to count as exact match real eps = 1e-7; // small number to avoid division by zero bool perfect_match = true; this->weights0PP.reserve(this->nPoints); this->weights0PM.reserve(this->nPoints); this->weights0MP.reserve(this->nPoints); this->weights0MM.reserve(this->nPoints); this->planeNeighbor0PP.reserve(this->nPoints); this->planeNeighbor0PM.reserve(this->nPoints); this->planeNeighbor0MP.reserve(this->nPoints); this->planeNeighbor0MM.reserve(this->nPoints); for(uint i=0; i<nPoints; i++) { real posY = coordsY[i]; real posZ = coordsZ[i]; bool found0PP = false, found0PM = false, found0MP = false, found0MM = false, foundAll = false; uint level = this->readLevel; for(int fileId=0; fileId<(int)this->fileCollection->files[level].size(); fileId++) { VTKFile &file = this->fileCollection->files[level][fileId][0]; if(!file.inBoundingBox(posY, posZ, 0.0f)) continue; // y in simulation is x in precursor/file, z in simulation is y in precursor/file // simulation -> file: N -> E, S -> W, T -> N, B -> S int idx = file.findNeighborMMM(posY, posZ, 0.f); //!> index of nearest WSB neighbor on precursor file if(idx!=-1) { // Filter for exact matches if(abs(posY-file.getX(idx)) < max_diff && abs(posZ-file.getY(idx)) < max_diff) { this->weights0PP.emplace_back(1e6f); this->weights0PM.emplace_back(0.f); this->weights0MP.emplace_back(0.f); this->weights0MM.emplace_back(0.f); uint writeIdx = this->getWriteIndex(level, fileId, idx); //!> writeIdx: index on host/device array where precursor value will be written to after loading from file this->planeNeighbor0PP.push_back(writeIdx); //!> neighbor lists mapping where BC kernel should read from on host/device array this->planeNeighbor0PM.push_back(writeIdx); this->planeNeighbor0MP.push_back(writeIdx); this->planeNeighbor0MM.push_back(writeIdx); found0PP = true; found0PM = true; found0MM = true; found0MP = true; } else { perfect_match = false; } if(!found0MM) { found0MM = true; real dy = file.getX(idx)-posY; real dz = file.getY(idx)-posZ; this->weights0MM.emplace_back(1.f/(dy*dy+dz*dz+eps)); this->planeNeighbor0MM.emplace_back(getWriteIndex(level, fileId, idx)); } } if(!found0PP) //NT in simulation is EN in precursor { int index = file.findNeighborPPM(posY, posZ, 0.f); if(index!=-1) { found0PP = true; real dy = file.getX(index)-posY; real dz = file.getY(index)-posZ; this->weights0PP.emplace_back(1.f/(dy*dy+dz*dz+eps)); this->planeNeighbor0PP.emplace_back(getWriteIndex(level, fileId, index)); } } if(!found0PM) //NB in simulation is ES in precursor { int index = file.findNeighborPMM(posY, posZ, 0.f); if(index!=-1) { found0PM = true; real dy = file.getX(index)-posY; real dz = file.getY(index)-posZ; this->weights0PM.emplace_back(1.f/(dy*dy+dz*dz+eps)); this->planeNeighbor0PP.emplace_back(getWriteIndex(level, fileId, index)); } } if(!found0MP) //ST in simulation is WN in precursor { int index = file.findNeighborMPM(posY, posZ, 0.f); if(index!=-1) { found0MP = true; real dy = file.getX(index)-posY; real dz = file.getY(index)-posZ; this->weights0MP.emplace_back(1.f/(dy*dy+dz*dz+eps)); this->planeNeighbor0MP.emplace_back(getWriteIndex(level, fileId, index)); } } foundAll = found0PP && found0PM && found0MP && found0MM; if(foundAll) break; } if(!foundAll) { VF_LOG_CRITICAL("Found no matching precursor neighbors for grid point at y={}, z={} \n", posY, posZ); throw std::runtime_error("VTKReader::fillArrays(): Did not find neighbors in the FileCollection for all points"); } } if(perfect_match) printf("Precursor was a perfect match \n"); for(size_t level=0; level<this->fileCollection->files.size(); level++){ for(size_t id=0; id<this->fileCollection->files[level].size(); id++){ if(this->fileCollection->files[level][id][0].markNANs(this->readIndices[level][id])) throw std::runtime_error("Found a NAN in the precursor where a velocity is needed"); }} } uint VTKReader::getWriteIndex(int level, int id, int linearIndex) { auto it = std::find(this->writeIndices[level][id].begin(), this->writeIndices[level][id].end(), linearIndex); uint idx = it-this->writeIndices[level][id].begin(); if(it==this->writeIndices[level][id].end()) { this->writeIndices[level][id].push_back(this->nPointsRead); //!> index on host/device array where value from file will be written to this->readIndices[level][id].push_back(linearIndex); //!> index in file that will be read from this->nPointsRead++; } return idx; } void VTKReader::getNextData(real* data, uint numberOfNodes, real time) { // for(size_t level=0; level<this->fileCollection->files.size(); level++) // { uint level = this->readLevel; for(size_t id=0; id<this->fileCollection->files[level].size(); id++) { size_t numberOfFiles = this->nFile[level][id]; if(!this->fileCollection->files[level][id][numberOfFiles].inZBounds(time)) { numberOfFiles++; printf("switching to precursor file no. %zu\n", numberOfFiles); if(numberOfFiles == this->fileCollection->files[level][id].size()) throw std::runtime_error("Not enough Precursor Files to read"); this->fileCollection->files[level][id][numberOfFiles-1].unloadFile(); if(numberOfFiles+1<this->fileCollection->files[level][id].size()) { VTKFile* nextFile = &this->fileCollection->files[level][id][numberOfFiles+1]; if(! nextFile->isLoaded()) { read.wait(); read = std::async(std::launch::async, [](VTKFile* file){ file->loadFile(); }, &this->fileCollection->files[level][id][numberOfFiles+1]); } } } VTKFile* file = &this->fileCollection->files[level][id][numberOfFiles]; int off = file->getClosestIdxZ(time)*file->getNumberOfPointsInXYPlane(); file->getData(data, numberOfNodes, this->readIndices[level][id], this->writeIndices[level][id], off, this->writingOffset); this->nFile[level][id] = numberOfFiles; } // } }