|
| 1 | + |
| 2 | +/***************************************************************************** |
| 3 | +* |
| 4 | +* Copyright (c) 2003-2026 by the esys.escript Group |
| 5 | +* https://github.com/LutzGross/esys-escript.github.io |
| 6 | +* |
| 7 | +* Primary Business: Queensland, Australia |
| 8 | +* Licensed under the Apache License, version 2.0 |
| 9 | +* http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +* |
| 11 | +* See CREDITS file for contributors and development history |
| 12 | +** |
| 13 | +*****************************************************************************/ |
| 14 | + |
| 15 | +#include "FinleyDomain.h" |
| 16 | + |
| 17 | +#include <escript/index.h> |
| 18 | + |
| 19 | +#include <sstream> |
| 20 | + |
| 21 | +namespace finley { |
| 22 | + |
| 23 | +namespace { |
| 24 | + |
| 25 | +/// the contact element type that goes with a given face element type |
| 26 | +ElementTypeId contactTypeFor(ElementTypeId faceType) |
| 27 | +{ |
| 28 | + switch (faceType) { |
| 29 | + case Point1: return Point1_Contact; |
| 30 | + case Line2: return Line2_Contact; |
| 31 | + case Line3: return Line3_Contact; |
| 32 | + case Tri3: return Tri3_Contact; |
| 33 | + case Tri6: return Tri6_Contact; |
| 34 | + case Rec4: return Rec4_Contact; |
| 35 | + case Rec8: return Rec8_Contact; |
| 36 | + case Rec9: return Rec9_Contact; |
| 37 | + default: break; |
| 38 | + } |
| 39 | + std::stringstream ss; |
| 40 | + ss << "createFromArrays: no contact element type is defined for face " |
| 41 | + "element type " << faceType; |
| 42 | + throw escript::ValueError(ss.str()); |
| 43 | +} |
| 44 | + |
| 45 | +/// fills an element table from flat arrays. `nodes` holds global node ids and |
| 46 | +/// its length fixes the element count. Ids are made globally unique when the |
| 47 | +/// caller does not supply them. |
| 48 | +void fillElementTable(ElementFile* ef, const std::vector<index_t>& nodes, |
| 49 | + const std::vector<index_t>& ids, |
| 50 | + const std::vector<int>& tags, |
| 51 | + const char* what, escript::JMPI mpiInfo) |
| 52 | +{ |
| 53 | + const int NN = ef->numNodes; |
| 54 | + if (NN < 1) { |
| 55 | + std::stringstream ss; |
| 56 | + ss << "createFromArrays: " << what << " reference element has no nodes"; |
| 57 | + throw escript::ValueError(ss.str()); |
| 58 | + } |
| 59 | + if (nodes.size() % NN != 0) { |
| 60 | + std::stringstream ss; |
| 61 | + ss << "createFromArrays: the " << what << " node table has " |
| 62 | + << nodes.size() << " entries, which is not a multiple of the " << NN |
| 63 | + << " nodes per element"; |
| 64 | + throw escript::ValueError(ss.str()); |
| 65 | + } |
| 66 | + const dim_t numElements = nodes.size() / NN; |
| 67 | + if (!ids.empty() && (dim_t)ids.size() != numElements) { |
| 68 | + std::stringstream ss; |
| 69 | + ss << "createFromArrays: " << what << " has " << numElements |
| 70 | + << " elements but " << ids.size() << " ids"; |
| 71 | + throw escript::ValueError(ss.str()); |
| 72 | + } |
| 73 | + if (!tags.empty() && (dim_t)tags.size() != numElements) { |
| 74 | + std::stringstream ss; |
| 75 | + ss << "createFromArrays: " << what << " has " << numElements |
| 76 | + << " elements but " << tags.size() << " tags"; |
| 77 | + throw escript::ValueError(ss.str()); |
| 78 | + } |
| 79 | + |
| 80 | + // When ids are not supplied, number the elements consecutively across ranks |
| 81 | + // so that they stay unique once the mesh is distributed. |
| 82 | + // |
| 83 | + // The scan runs UNCONDITIONALLY, and only its result is conditional. It is a |
| 84 | + // collective, and `ids.empty()` is a per-rank test: a rank that simply has |
| 85 | + // none of this kind of element - no boundary faces, say, because it owns |
| 86 | + // only interior cells - supplies an empty id list too, and cannot be told |
| 87 | + // apart from a caller that omitted them. Guarding the collective with that |
| 88 | + // test let such a rank enter the scan alone while the others went on, and |
| 89 | + // the run deadlocked here with the ranks in different collectives. |
| 90 | + index_t idOffset = 0; |
| 91 | +#ifdef ESYS_MPI |
| 92 | + if (mpiInfo->size > 1) { |
| 93 | + index_t local = numElements; |
| 94 | + index_t scan = 0; |
| 95 | + MPI_Exscan(&local, &scan, 1, MPI_DIM_T, MPI_SUM, mpiInfo->comm); |
| 96 | + if (ids.empty() && mpiInfo->rank != 0) |
| 97 | + idOffset = scan; |
| 98 | + } |
| 99 | +#endif |
| 100 | + |
| 101 | + ef->allocTable(numElements); |
| 102 | + ef->minColor = 0; |
| 103 | + ef->maxColor = numElements > 0 ? numElements - 1 : -1; |
| 104 | + |
| 105 | +#pragma omp parallel for |
| 106 | + for (index_t e = 0; e < numElements; e++) { |
| 107 | + ef->Id[e] = ids.empty() ? (idOffset + e) : ids[e]; |
| 108 | + ef->Tag[e] = tags.empty() ? 0 : tags[e]; |
| 109 | + ef->Owner[e] = mpiInfo->rank; |
| 110 | + ef->Color[e] = e; |
| 111 | + for (int k = 0; k < NN; k++) |
| 112 | + ef->Nodes[INDEX2(k, e, NN)] = nodes[e * NN + k]; |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +} // anonymous namespace |
| 117 | + |
| 118 | +escript::Domain_ptr FinleyDomain::createFromArrays(const MeshArrays& in, |
| 119 | + const std::string& name, |
| 120 | + int order, int reducedOrder, |
| 121 | + bool optimize, |
| 122 | + escript::JMPI mpiInfo) |
| 123 | +{ |
| 124 | + if (in.numDim != 2 && in.numDim != 3) { |
| 125 | + std::stringstream ss; |
| 126 | + ss << "createFromArrays: numDim is " << in.numDim << ", must be 2 or 3"; |
| 127 | + throw escript::ValueError(ss.str()); |
| 128 | + } |
| 129 | + if (in.elementType == NoRef || in.faceElementType == NoRef) |
| 130 | + throw escript::ValueError("createFromArrays: element type and face " |
| 131 | + "element type must both be set"); |
| 132 | + |
| 133 | + const dim_t numNodes = in.nodeId.size(); |
| 134 | + if (in.nodeCoords.size() != (size_t)numNodes * in.numDim) { |
| 135 | + std::stringstream ss; |
| 136 | + ss << "createFromArrays: " << numNodes << " nodes in " << in.numDim |
| 137 | + << " dimensions need " << (size_t)numNodes * in.numDim |
| 138 | + << " coordinates, got " << in.nodeCoords.size(); |
| 139 | + throw escript::ValueError(ss.str()); |
| 140 | + } |
| 141 | + if (!in.nodeTag.empty() && (dim_t)in.nodeTag.size() != numNodes) { |
| 142 | + std::stringstream ss; |
| 143 | + ss << "createFromArrays: " << numNodes << " nodes but " |
| 144 | + << in.nodeTag.size() << " node tags"; |
| 145 | + throw escript::ValueError(ss.str()); |
| 146 | + } |
| 147 | + |
| 148 | + FinleyDomain* out = new FinleyDomain(name, in.numDim, mpiInfo); |
| 149 | + |
| 150 | + const_ReferenceElementSet_ptr refElements( |
| 151 | + new ReferenceElementSet(in.elementType, order, reducedOrder)); |
| 152 | + const_ReferenceElementSet_ptr refFaceElements( |
| 153 | + new ReferenceElementSet(in.faceElementType, order, reducedOrder)); |
| 154 | + const_ReferenceElementSet_ptr refContactElements( |
| 155 | + new ReferenceElementSet(contactTypeFor(in.faceElementType), order, |
| 156 | + reducedOrder)); |
| 157 | + const_ReferenceElementSet_ptr refPoints( |
| 158 | + new ReferenceElementSet(Point1, order, reducedOrder)); |
| 159 | + |
| 160 | + ElementFile* elements = new ElementFile(refElements, mpiInfo); |
| 161 | + out->setElements(elements); |
| 162 | + ElementFile* faces = new ElementFile(refFaceElements, mpiInfo); |
| 163 | + out->setFaceElements(faces); |
| 164 | + out->setContactElements(new ElementFile(refContactElements, mpiInfo)); |
| 165 | + out->setPoints(new ElementFile(refPoints, mpiInfo)); |
| 166 | + |
| 167 | + // node table. The global id doubles as the degree of freedom: unlike the |
| 168 | + // structured generators there is no periodicity to fold away here. |
| 169 | + NodeFile* nodes = out->getNodes(); |
| 170 | + nodes->allocTable(numNodes); |
| 171 | +#pragma omp parallel for |
| 172 | + for (index_t i = 0; i < numNodes; i++) { |
| 173 | + nodes->Id[i] = in.nodeId[i]; |
| 174 | + nodes->Tag[i] = in.nodeTag.empty() ? 0 : in.nodeTag[i]; |
| 175 | + nodes->globalDegreesOfFreedom[i] = in.nodeId[i]; |
| 176 | + for (int d = 0; d < in.numDim; d++) |
| 177 | + nodes->Coordinates[INDEX2(d, i, in.numDim)] = |
| 178 | + in.nodeCoords[(size_t)i * in.numDim + d]; |
| 179 | + } |
| 180 | + |
| 181 | + fillElementTable(elements, in.elementNodes, in.elementId, in.elementTag, |
| 182 | + "element", mpiInfo); |
| 183 | + fillElementTable(faces, in.faceNodes, in.faceId, in.faceTag, |
| 184 | + "face element", mpiInfo); |
| 185 | + out->getContactElements()->allocTable(0); |
| 186 | + out->getPoints()->allocTable(0); |
| 187 | + |
| 188 | + for (TagMap::const_iterator it = in.tagMap.begin(); |
| 189 | + it != in.tagMap.end(); ++it) { |
| 190 | + out->setTagMap(it->first, it->second); |
| 191 | + } |
| 192 | + |
| 193 | + // resolve the global node references, then distribute and build the |
| 194 | + // overlap, the mappings and the element colouring |
| 195 | + out->resolveNodeIds(); |
| 196 | + out->prepare(optimize); |
| 197 | + return out->getPtr(); |
| 198 | +} |
| 199 | + |
| 200 | +} // namespace finley |
0 commit comments