void saveFull( MultiBlock2D& multiBlock, FileName fName, IndexOrdering::OrderingT ordering ) { global::profiler().start("io"); SparseBlockStructure2D blockStructure(multiBlock.getBoundingBox()); Box2D bbox = multiBlock.getBoundingBox(); if (ordering==IndexOrdering::forward) { plint nBlocks = std::min(bbox.getNx(), (plint)global::mpi().getSize()); std::vector<std::pair<plint,plint> > ranges; util::linearRepartition(bbox.x0, bbox.x1, nBlocks, ranges); for (pluint iRange=0; iRange<ranges.size(); ++iRange) { blockStructure.addBlock ( Box2D( ranges[iRange].first, ranges[iRange].second, bbox.y0, bbox.y1 ), iRange ); } } else if (ordering==IndexOrdering::backward) { plint nBlocks = std::min(bbox.getNy(), (plint)global::mpi().getSize()); std::vector<std::pair<plint,plint> > ranges; util::linearRepartition(bbox.y0, bbox.y1, nBlocks, ranges); for (pluint iRange=0; iRange<ranges.size(); ++iRange) { blockStructure.addBlock ( Box2D( bbox.x0, bbox.x1, ranges[iRange].first, ranges[iRange].second ), iRange ); } } else { // Sparse ordering not defined. PLB_ASSERT( false ); } plint envelopeWidth=1; MultiBlockManagement2D adjacentMultiBlockManagement ( blockStructure, new OneToOneThreadAttribution, envelopeWidth ); MultiBlock2D* multiAdjacentBlock = multiBlock.clone(adjacentMultiBlockManagement); std::vector<plint> offset; std::vector<plint> myBlockIds; std::vector<std::vector<char> > data; bool dynamicContent = false; dumpData(*multiAdjacentBlock, dynamicContent, offset, myBlockIds, data); if (ordering==IndexOrdering::backward && myBlockIds.size()==1) { PLB_ASSERT( data.size()==1 ); Box2D domain; blockStructure.getBulk(myBlockIds[0], domain); plint sizeOfCell = multiAdjacentBlock->sizeOfCell(); PLB_ASSERT( domain.nCells()*sizeOfCell == (plint)data[0].size() ); transposeToBackward( sizeOfCell, domain, data[0] ); } plint totalSize = offset[offset.size()-1]; writeOneBlockXmlSpec(*multiAdjacentBlock, fName, totalSize, ordering); writeRawData(fName, myBlockIds, offset, data); delete multiAdjacentBlock; global::profiler().stop("io"); }
MultiGridManagement2D::MultiGridManagement2D(Box2D coarseBoundingBox, plint numLevels, plint overlapWidth_, plint referenceLevel_) : overlapWidth(overlapWidth_), referenceLevel(referenceLevel_), boundingBoxes(numLevels), coarseGridInterfaces(numLevels), fineGridInterfaces(numLevels), bulks(numLevels), coarseInterfaceOrientations(numLevels), fineInterfaceOrientations(numLevels) { PLB_ASSERT( numLevels > 0 ); PLB_ASSERT( referenceLevel < numLevels ); initialize(coarseBoundingBox); }
void addInternalProcessor( DataProcessorGenerator3D const& generator, MultiBlock3D& actor, std::vector<MultiBlock3D*> multiBlockArgs, plint level ) { MultiProcessing3D<DataProcessorGenerator3D const, DataProcessorGenerator3D > multiProcessing(generator, multiBlockArgs); std::vector<DataProcessorGenerator3D*> const& retainedGenerators = multiProcessing.getRetainedGenerators(); std::vector<std::vector<plint> > const& atomicBlockNumbers = multiProcessing.getAtomicBlockNumbers(); for (pluint iGenerator=0; iGenerator<retainedGenerators.size(); ++iGenerator) { std::vector<AtomicBlock3D*> extractedAtomicBlocks(multiBlockArgs.size()); for (pluint iBlock=0; iBlock<extractedAtomicBlocks.size(); ++iBlock) { extractedAtomicBlocks[iBlock] = &multiBlockArgs[iBlock]->getComponent(atomicBlockNumbers[iGenerator][iBlock]); } // It is assumed that the actor has the same distribution as block 0. PLB_ASSERT(!atomicBlockNumbers[iGenerator].empty()); AtomicBlock3D& atomicActor = actor.getComponent(atomicBlockNumbers[iGenerator][0]); // Delegate to the "AtomicBlock version" of addInternal. plb::addInternalProcessor(*retainedGenerators[iGenerator], atomicActor, extractedAtomicBlocks, level); } // Subscribe the processor in the multi-block. This guarantees that the multi-block is aware // of the maximal current processor level, and it instantiates the communication pattern // for an update of envelopes after processor execution. std::vector<MultiBlock3D*> updatedMultiBlocks; std::vector<modif::ModifT> typeOfModification; multiProcessing.multiBlocksWhichRequireUpdate(updatedMultiBlocks, typeOfModification); actor.subscribeProcessor ( level, updatedMultiBlocks, typeOfModification, BlockDomain::usesEnvelope(generator.appliesTo()) ); actor.storeProcessor(generator, multiBlockArgs, level); }
AtomicContainerBlock3D const& MultiContainerBlock3D:: getComponent(plint blockId) const { BlockMap::const_iterator it = blocks.find(blockId); PLB_ASSERT (it != blocks.end()); return *it->second; }
void BoxProcessingFunctional3D::getModificationPattern(std::vector<bool>& isWritten) const { std::vector<modif::ModifT> modified(isWritten.size()); getTypeOfModification(modified); PLB_ASSERT(modified.size()==isWritten.size()); for (pluint iBlock=0; iBlock<isWritten.size(); ++iBlock) { isWritten[iBlock] = modified[iBlock]==modif::nothing ? false:true; } }
MultiContainerBlock3D* MultiContainerBlock3D::clone ( MultiBlockManagement3D const& multiBlockManagement ) const { // By definition, a multi container block cannot be redistributed over // a different block arrangement. PLB_ASSERT( false ); return 0; }
ReductiveBoxProcessorGenerator2D::ReductiveBoxProcessorGenerator2D ( ReductiveBoxProcessingFunctional2D* functional_, Box2D domain ) : BoxedReductiveDataProcessorGenerator2D(domain), functional(functional_) { // Must be non-null, because it is then used without further checks. PLB_ASSERT(functional); }
std::vector<MultiBlock3D*> MultiBlock3D::ProcessorStorage3D::getMultiBlocks() const { std::vector<MultiBlock3D*> multiBlocks(multiBlockIds.size()); for(pluint iBlock=0; iBlock<multiBlockIds.size(); ++iBlock) { multiBlocks[iBlock] = multiBlockRegistration3D().find(multiBlockIds[iBlock]); PLB_ASSERT( multiBlocks[iBlock] ); } return multiBlocks; }
Box3D MultiBlockManagement3D::getUniqueBulk(plint blockId) const { Box3D uniqueBulk; #ifdef PLB_DEBUG bool ok = #endif sparseBlock.getUniqueBulk(blockId, uniqueBulk); PLB_ASSERT( ok ); return uniqueBulk; }
Box3D MultiBlockManagement3D::getEnvelope(plint blockId) const { Box3D bulk; #ifdef PLB_DEBUG bool ok = #endif sparseBlock.getBulk(blockId, bulk); PLB_ASSERT( ok ); return SmartBulk3D(sparseBlock, envelopeWidth, bulk).computeEnvelope(); }
void writeOneBlockXmlSpec( MultiBlock2D& multiBlock, FileName fName, plint dataSize, IndexOrdering::OrderingT ordering ) { fName.defaultExt("plb"); MultiBlockManagement2D const& management = multiBlock.getMultiBlockManagement(); std::vector<std::string> typeInfo = multiBlock.getTypeInfo(); std::string blockName = multiBlock.getBlockName(); PLB_ASSERT( !typeInfo.empty() ); XMLwriter xml; XMLwriter& xmlMultiBlock = xml["Block2D"]; xmlMultiBlock["General"]["Family"].setString(blockName); xmlMultiBlock["General"]["Datatype"].setString(typeInfo[0]); if (typeInfo.size()>1) { xmlMultiBlock["General"]["Descriptor"].setString(typeInfo[1]); } xmlMultiBlock["General"]["cellDim"].set(multiBlock.getCellDim()); bool dynamicContent = false; xmlMultiBlock["General"]["dynamicContent"].set(dynamicContent); Array<plint,4> boundingBox = multiBlock.getBoundingBox().to_plbArray(); xmlMultiBlock["Structure"]["BoundingBox"].set<plint,4>(boundingBox); xmlMultiBlock["Structure"]["EnvelopeWidth"].set(management.getEnvelopeWidth()); xmlMultiBlock["Structure"]["NumComponents"].set(1); xmlMultiBlock["Data"]["File"].setString(FileName(fName).setExt("dat")); if (ordering == IndexOrdering::forward) { xmlMultiBlock["Data"]["IndexOrdering"].setString("zIsFastest"); } else { xmlMultiBlock["Data"]["IndexOrdering"].setString("xIsFastest"); } XMLwriter& xmlBulks = xmlMultiBlock["Data"]["Component"]; xmlBulks.set<plint,4>(multiBlock.getBoundingBox().to_plbArray()); xmlMultiBlock["Data"]["Offsets"].set(dataSize); xml.print(FileName(fName).defaultPath(global::directories().getOutputDir())); }
void MultiProcessing3D<OriginalGenerator,MutableGenerator>::multiBlocksWhichRequireUpdate ( std::vector<MultiBlock3D*>& multiBlocksModifiedByProcessor, std::vector<modif::ModifT>& typesOfModification ) const { multiBlocksModifiedByProcessor.clear(); typesOfModification.clear(); // If the generator includes envelopes, the envelopes need no update in any case. if ( ! BlockDomain::usesEnvelope(generator.appliesTo()) ) { // Otherwise, all blocks that have been modified by the processor must // be updated. std::vector<modif::ModifT> allModifications(multiBlocks.size(), modif::undefined); // Default initialize to no-change. generator.getTypeOfModification(allModifications); for (pluint iBlock=0; iBlock<allModifications.size(); ++iBlock) { PLB_ASSERT( allModifications[iBlock] != modif::undefined ); if (allModifications[iBlock] != modif::nothing) { multiBlocksModifiedByProcessor.push_back(multiBlocks[iBlock]); typesOfModification.push_back(allModifications[iBlock]); } } } }
void writeXmlSpec( MultiBlock2D& multiBlock, FileName fName, std::vector<plint> const& offset, bool dynamicContent ) { fName.defaultExt("plb"); MultiBlockManagement2D const& management = multiBlock.getMultiBlockManagement(); std::map<plint,Box2D> const& bulks = management.getSparseBlockStructure().getBulks(); PLB_ASSERT( offset.empty() || bulks.size()==offset.size() ); std::vector<std::string> typeInfo = multiBlock.getTypeInfo(); std::string blockName = multiBlock.getBlockName(); PLB_ASSERT( !typeInfo.empty() ); XMLwriter xml; XMLwriter& xmlMultiBlock = xml["Block2D"]; xmlMultiBlock["General"]["Family"].setString(blockName); xmlMultiBlock["General"]["Datatype"].setString(typeInfo[0]); if (typeInfo.size()>1) { xmlMultiBlock["General"]["Descriptor"].setString(typeInfo[1]); } xmlMultiBlock["General"]["cellDim"].set(multiBlock.getCellDim()); xmlMultiBlock["General"]["dynamicContent"].set(dynamicContent); xmlMultiBlock["General"]["globalId"].set(multiBlock.getId()); Array<plint,4> boundingBox = multiBlock.getBoundingBox().to_plbArray(); xmlMultiBlock["Structure"]["BoundingBox"].set<plint,4>(boundingBox); xmlMultiBlock["Structure"]["EnvelopeWidth"].set(management.getEnvelopeWidth()); xmlMultiBlock["Structure"]["GridLevel"].set(management.getRefinementLevel()); xmlMultiBlock["Structure"]["NumComponents"].set(bulks.size()); xmlMultiBlock["Data"]["File"].setString(FileName(fName).setExt("dat")); XMLwriter& xmlBulks = xmlMultiBlock["Data"]["Component"]; std::map<plint,Box2D>::const_iterator it = bulks.begin(); plint iComp=0; for(; it != bulks.end(); ++it) { Box2D bulk = it->second; xmlBulks[iComp].set<plint,4>(bulk.to_plbArray()); ++iComp; } if (!offset.empty()) { xmlMultiBlock["Data"]["Offsets"].set(offset); } // The following prints a unique list of dynamics-id pairs for all dynamics // classes used in the multi-block. This is necessary, because dynamics // classes may be ordered differently from one compilation to the other, // or from one compiler to the other. // // Given that the dynamics classes are unique, they can be indexed by their // name (which is not the case of the data processors below). std::map<std::string,int> dynamicsDict; multiBlock.getDynamicsDict(multiBlock.getBoundingBox(), dynamicsDict); if (!dynamicsDict.empty()) { XMLwriter& xmlDynamicsDict = xmlMultiBlock["Data"]["DynamicsDict"]; for( std::map<std::string,int>::const_iterator it = dynamicsDict.begin(); it != dynamicsDict.end(); ++it ) { xmlDynamicsDict[it->first].set(it->second); } } // This is the only section in which actual content is stored outside the // binary blob: the serialization of the data processors. This // serialization was chosen to be in ASCII, because it takes little space // and can be somewhat complicated. // // It is important that the processors are indexed by a continuous index // "iProcessor". They cannot be indexed by the class name ("Name") or static // id ("id") because several instances of the same class may occur. XMLwriter& xmlProcessors = xmlMultiBlock["Data"]["Processor"]; std::vector<MultiBlock2D::ProcessorStorage2D> const& processors = multiBlock.getStoredProcessors(); for (plint iProcessor=0; iProcessor<(plint)processors.size(); ++iProcessor) { int id = processors[iProcessor].getGenerator().getStaticId(); if (id>=0) { Box2D domain; std::string data; processors[iProcessor].getGenerator().serialize(domain, data); xmlProcessors[iProcessor]["Name"].set(meta::processorRegistration2D().getName(id)); xmlProcessors[iProcessor]["Domain"].set<plint,4>(domain.to_plbArray()); xmlProcessors[iProcessor]["Data"].setString(data); xmlProcessors[iProcessor]["Level"].set(processors[iProcessor].getLevel()); xmlProcessors[iProcessor]["Blocks"].set(processors[iProcessor].getMultiBlockIds()); } } xml.print(FileName(fName).defaultPath(global::directories().getOutputDir())); }
void MultiProcessing3D<OriginalGenerator,MutableGenerator>::subdivideGenerator() { // To start with, determine which multi-blocks are read and which are written std::vector<bool> isWritten(multiBlocks.size()); generator.getModificationPattern(isWritten); PLB_ASSERT( isWritten.size() == multiBlocks.size() ); // The reference block (the one for which the envelope is included if // the domain generator.appliesTo() include the envelope) is either the // multi-block which is written, or the first multi-block if all are read-only. pluint referenceBlock = 0; for (pluint iBlock=0; iBlock<isWritten.size(); ++iBlock) { if (isWritten[iBlock]) { referenceBlock = iBlock; break; } } // In debug mode, make sure that a most one multi-block is written when envelope is included. #ifdef PLB_DEBUG if ( BlockDomain::usesEnvelope(generator.appliesTo()) ) { plint numWritten = 0; for (pluint iBlock=0; iBlock<isWritten.size(); ++iBlock) { if (isWritten[iBlock]) { ++numWritten; } } PLB_ASSERT( numWritten <= 1 ); } #endif // The first step is to access the domains of the the atomic blocks, as well // as their IDs in each of the coupled multi blocks. The domain corresponds // to the bulk and/or to the envelope, depending on the value of generator.appliesTo(). std::vector<std::vector<DomainAndId3D> > domainsWithId(multiBlocks.size()); for (pluint iMulti=0; iMulti<multiBlocks.size(); ++iMulti) { std::vector<plint> const& blocks = multiBlocks[iMulti]->getMultiBlockManagement().getLocalInfo().getBlocks(); for (pluint iBlock=0; iBlock<blocks.size(); ++iBlock) { plint blockId = blocks[iBlock]; SmartBulk3D bulk(multiBlocks[iMulti]->getMultiBlockManagement(), blockId); switch (generator.appliesTo()) { case BlockDomain::bulk: domainsWithId[iMulti].push_back(DomainAndId3D(bulk.getBulk(),blockId)); break; case BlockDomain::bulkAndEnvelope: // It's only the reference block that should have the envelope. However, we start // by assigning bulk and envelope to all of them, and eliminate overlapping // envelope components further down. domainsWithId[iMulti].push_back(DomainAndId3D(bulk.computeEnvelope(),blockId)); break; case BlockDomain::envelope: // For the reference block, we restrict ourselves to the envelope, because // that's the desired domain of application. if (iMulti==referenceBlock) { std::vector<Box3D> envelopeOnly; except(bulk.computeEnvelope(), bulk.getBulk(), envelopeOnly); for (pluint iEnvelope=0; iEnvelope<envelopeOnly.size(); ++iEnvelope) { domainsWithId[iMulti].push_back(DomainAndId3D(envelopeOnly[iEnvelope], blockId)); } } // For the other blocks, we need to take bulk and envelope, because all these domains // potentially intersect with the envelope of the reference block. else { domainsWithId[iMulti].push_back(DomainAndId3D(bulk.computeEnvelope(),blockId)); } break; } } } // If the multi-blocks are not at the same level of grid refinement, the level // of the first block is taken as reference, and the coordinates of the other // blocks are rescaled accordingly. plint firstLevel = multiBlocks[0]->getMultiBlockManagement().getRefinementLevel(); for (pluint iMulti=1; iMulti<multiBlocks.size(); ++iMulti) { plint relativeLevel = firstLevel - multiBlocks[iMulti]->getMultiBlockManagement().getRefinementLevel(); if (relativeLevel != 0) { for (pluint iBlock=0; iBlock<domainsWithId[iMulti].size(); ++iBlock) { domainsWithId[iMulti][iBlock].domain = global::getDefaultMultiScaleManager().scaleBox ( domainsWithId[iMulti][iBlock].domain, relativeLevel ); } } } // If the envelopes are included as well, it is assumed that at most one of // the multi blocks has write-access. All others (those that have read-only // access) need to be non-overlaping, to avoid multiple writes on the cells // of the write-access-multi-block. Thus, overlaps are now eliminitated in // the read-access-multi-blocks. if ( BlockDomain::usesEnvelope(generator.appliesTo()) ) { for (pluint iMulti=0; iMulti<multiBlocks.size(); ++iMulti) { if (!isWritten[iMulti]) { std::vector<DomainAndId3D> nonOverlapBlocks(getNonOverlapingBlocks(domainsWithId[iMulti])); domainsWithId[iMulti].swap(nonOverlapBlocks); } } } // This is the heart of the whole procedure: intersecting atomic blocks // between all coupled multi blocks are identified. std::vector<Box3D> finalDomains; std::vector<std::vector<plint> > finalIds; intersectDomainsAndIds(domainsWithId, finalDomains, finalIds); // And, to end with, re-create processor generators adapted to the // computed domains of intersection. if ( BlockDomain::usesEnvelope(generator.appliesTo()) ) { // In case the envelope is included, periodicity must be explicitly treated. // Indeed, the user indicates the domain of applicability with respect to // bulk nodes only. The generator is therefore shifted in all space directions // to englobe periodic boundary nodes as well. plint shiftX = firstMultiBlock->getNx(); plint shiftY = firstMultiBlock->getNy(); plint shiftZ = firstMultiBlock->getNz(); PeriodicitySwitch3D const& periodicity = firstMultiBlock->periodicity(); for (plint orientX=-1; orientX<=+1; ++orientX) { for (plint orientY=-1; orientY<=+1; ++orientY) { for (plint orientZ=-1; orientZ<=+1; ++orientZ) { if (periodicity.get(orientX,orientY,orientZ)) { extractGeneratorOnBlocks( finalDomains, finalIds, orientX*shiftX, orientY*shiftY, orientZ*shiftZ ); } } } } } else { extractGeneratorOnBlocks(finalDomains, finalIds); } }
DataUnSerializer* MultiGrid2D::getBlockUnSerializer ( Box2D const& domain, IndexOrdering::OrderingT ordering ) { PLB_ASSERT(false); return 0; }
void MultiContainerBlock3D::copyReceive ( MultiBlock3D const& fromBlock, Box3D const& fromDomain, Box3D const& toDomain, modif::ModifT whichData ) { PLB_ASSERT( false ); }