• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CSMMLab / KiT-RT / #95

28 May 2025 02:06AM UTC coverage: 46.655% (-11.1%) from 57.728%
#95

push

travis-ci

web-flow
Release 2025 (#44)

* New neural models (#30)

* extend kitrt script

* added new neural models

* extend to m3 models

* added M3_2D models

* added M2 and M4 models

* configure ml optimizer for high order models

* added configuration for high order models

* add option for rotation postprcessing in neural networks. remove unneccessary python scripts

* started rotational symmetric postprocessing

* added rotational invariance postprocessing for m2 and m1 models

* fix post merge bugs

* created hohlraum mesh

* add hohlraum tes case

* add hohlraum test case

* add hohlraum cfg filees

* fixed hohlraum testcase

* add hohlraum cfg files

* changed hohlraum cfg

* changed hohlraum testcase to isotropic inflow source boundary condition

* added ghost cell bonudary for hohlraum testcase

* update readme and linesource mesh creator

* added proper scaling for linesource reference solution

* regularized newton debugging

* Data generator with reduced objective functional (#33)

* added reduced optimizer for sampling

* remove old debugging comments

* mesh acceleration (#38)

* added new ansatz (#36)

* added new ansatz

* debug new ansatz

* branch should be abandoned here

* debug new ansatz

* fix scaling error new ansatz

* fix config errors

* temporarily fixed dynamic ansatz rotation bug

* fix inheritance error for new ansatz

* mesh acceleration

* add structured hohlraum

* Mesh acc (#41)

* mesh acceleration

* added floor value for starmap moment methods

* enable accelleration

* delete minimum value starmap

* Update README.md

* Spherical harmonics nn (#40)

* added new ansatz

* debug new ansatz

* branch should be abandoned here

* debug new ansatz

* fix scaling error new ansatz

* fix config errors

* temporarily fixed dynamic ansatz rotation bug

* fix inheritance error for new ansatz

* mesh acceleration

* add structured hohlraum

* added sph... (continued)

767 of 3019 new or added lines in 51 files covered. (25.41%)

131 existing lines in 8 files now uncovered.

4422 of 9478 relevant lines covered (46.66%)

57163.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.81
/src/common/mesh.cpp
1
#include "common/mesh.hpp"
2
#include "common/config.hpp"
3
#include "common/io.hpp"
4
#include <algorithm>
5
#include <chrono>
6
#include <filesystem>
7
#include <iostream>
8
#include <map>
9
#ifdef IMPORT_MPI
10
#include <mpi.h>
11
#endif
12
#include <omp.h>
13
#include <set>
14

15
Mesh::Mesh( const Config* settings,
34✔
16
            std::vector<Vector> nodes,
17
            std::vector<std::vector<unsigned>> cells,
18
            std::vector<std::pair<BOUNDARY_TYPE, std::vector<unsigned>>> boundaries )
34✔
19
    : _dim( nodes[0].size() ), _numCells( cells.size() ), _numNodes( nodes.size() ), _numNodesPerCell( cells[0].size() ),
68✔
20
      _numBoundaries( boundaries.size() ), _ghostCellID( _numCells ), _nodes( nodes ), _cells( cells ), _boundaries( boundaries ) {
68✔
21

22
    _settings = settings;
34✔
23
    if( _dim == 2 ) {
34✔
24
        _numNodesPerBoundary = 2u;
34✔
25
    }
26
    else {
27
        ErrorMessages::Error( "Unsupported mesh dimension!", CURRENT_FUNCTION );
×
28
    }
29
    int nprocs = 1;
34✔
30
    int rank   = 0;
34✔
31
#ifdef IMPORT_MPI
32
    MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
33
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
34
#endif
35
    if( rank == 0 ) {
34✔
36

37
        auto log = spdlog::get( "event" );
102✔
38
        log->info( "| Compute cell areas..." );
34✔
39
    }
40
    ComputeCellAreas();
34✔
41
    if( rank == 0 ) {
34✔
42
        auto log = spdlog::get( "event" );
102✔
43
        log->info( "| Compute cell midpoints..." );
34✔
44
    }
45
    ComputeCellMidpoints();
34✔
46

47
    // Connectivity
48
    std::string connectivityFile = _settings->GetMeshFile();
68✔
49
    size_t lastDotIndex          = connectivityFile.find_last_of( '.' );
34✔
50
    connectivityFile             = connectivityFile.substr( 0, lastDotIndex );
34✔
51
    connectivityFile += ".con";
34✔
52
    if( !std::filesystem::exists( connectivityFile ) || _settings->GetForcedConnectivity() ) {
34✔
53
        if( rank == 0 ) {
19✔
54
            auto log = spdlog::get( "event" );
57✔
55

56
            log->info( "| Compute mesh connectivity..." );
19✔
57
        }
58
        ComputeConnectivity();    // Computes  _cellNeighbors, _cellInterfaceMidPoints, _cellNormals, _cellBoundaryTypes
19✔
59
        if( !_settings->GetForcedConnectivity() ) {
19✔
60
            if( rank == 0 ) {
3✔
61
                auto log = spdlog::get( "event" );
6✔
62

63
                log->info( "| Save mesh connectivity to file " + connectivityFile );
3✔
64
                WriteConnecitivityToFile(
3✔
65
                    connectivityFile, _cellNeighbors, _cellInterfaceMidPoints, _cellNormals, _cellBoundaryTypes, _numCells, _dim );
3✔
66
            }
67
        }
68
    }
69
    else {
70
        // Resize the outer vector to have nCells elements
71
        _cellNeighbors.resize( _numCells );
15✔
72
        _cellInterfaceMidPoints.resize( _numCells );
15✔
73
        _cellNormals.resize( _numCells );
15✔
74
        _cellBoundaryTypes.resize( _numCells );
15✔
75
        if( rank == 0 ) {
15✔
76
            auto log = spdlog::get( "event" );
30✔
77
            log->info( "| Load mesh connectivity from file " + connectivityFile );
15✔
78
        }
79
        LoadConnectivityFromFile(
15✔
80
            connectivityFile, _cellNeighbors, _cellInterfaceMidPoints, _cellNormals, _cellBoundaryTypes, _numCells, _numNodesPerCell, _dim );
15✔
81
    }
82
    if( rank == 0 ) {
34✔
83
        auto log = spdlog::get( "event" );
102✔
84
        log->info( "| Compute boundary..." );
34✔
85
    }
86
    ComputeBounds();
34✔
87
    if( rank == 0 ) {
34✔
88
        auto log = spdlog::get( "event" );
102✔
89
        log->info( "| Mesh created." );
34✔
90
    }
91
}
34✔
92

93
Mesh::~Mesh() {}
16✔
94

95
void Mesh::ComputeConnectivity() {
19✔
96
    int nprocs = 1;
19✔
97
    int rank   = 0;
19✔
98
#ifdef IMPORT_MPI
99
    MPI_Comm_size( MPI_COMM_WORLD, &nprocs );
100
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
101
#endif
102

103
    unsigned comm_size = 1;    // No MPI implementation right now
19✔
104
    // determine number/chunk size and indices of cells treated by each mpi thread (deactivated for now)
105
    unsigned chunkSize    = _numCells;    // std::ceil( static_cast<float>( _numCells ) / static_cast<float>( comm_size ) );
19✔
106
    unsigned mpiCellStart = 0;            // comm_rank * chunkSize;
19✔
107
    unsigned mpiCellEnd   = _numCells;    // std::min( ( comm_rank + 1 ) * chunkSize, _numCells );
19✔
108

109
    // 'flat' vectors are a flattened representation of the neighbors numCells<numNodesPerCell> nested vectors; easier for MPI
110
    // 'part' vectors store information for each single MPI thread
111
    std::vector<int> neighborsFlatPart( _numNodesPerCell * chunkSize, -1 );
38✔
112
    std::vector<Vector> normalsFlatPart( _numNodesPerCell * chunkSize, Vector( _dim, -1.0 ) );
57✔
113
    std::vector<Vector> interfaceMidFlatPart( _numNodesPerCell * chunkSize, Vector( _dim, -1.0 ) );
57✔
114

115
    // ++++++
116
    std::vector<std::vector<int>> neighbors( _numCells );
38✔
117
    std::vector<std::vector<Vector>> normals( _numCells );
38✔
118
    std::vector<std::vector<Vector>> interfaceMid( _numCells );
38✔
119

120
    std::map<std::pair<unsigned, unsigned>, std::vector<unsigned>> edge_to_cells;
19✔
121
    //  Step 1: Build a mapping from edges to cells
122
    if( rank == 0 ) {
19✔
123
        auto log = spdlog::get( "event" );
57✔
124
        log->info( "| ...map edges to cells..." );
19✔
125
    }
126
    for( unsigned i = 0; i < _numCells; ++i ) {
5,117✔
127
        const auto& cell = _cells[i];
5,098✔
128
        for( unsigned j = 0; j < _numNodesPerCell; ++j ) {
23,992✔
129
            unsigned v1                        = cell[j];
18,894✔
130
            unsigned v2                        = cell[( j + 1 ) % _numNodesPerCell];
18,894✔
131
            std::pair<unsigned, unsigned> edge = std::minmax( v1, v2 );
18,894✔
132
            edge_to_cells[edge].push_back( i );
18,894✔
133
        }
134
    }
135

136
    // Step 2: Determine neighbors
137
    if( rank == 0 ) {
19✔
138
        auto log = spdlog::get( "event" );
57✔
139
        log->info( "| ...determine neighbors of cells..." );
19✔
140
    }
141

142
    for( const auto& item : edge_to_cells ) {
9,822✔
143
        const auto& cell_list     = item.second;
9,803✔
144
        const auto& nodes_of_edge = item.first;
9,803✔
145
        // std::cout << "nodes_of_edge: " << nodes_of_edge.first << " " << nodes_of_edge.second << std::endl;
146
        if( cell_list.size() == 2 ) {
9,803✔
147
            if( cell_list[0] == cell_list[1] ) {
9,091✔
NEW
148
                ErrorMessages::Error( "Error", CURRENT_FUNCTION );
×
149
            }
150
            unsigned cell1 = cell_list[0];
9,091✔
151
            unsigned cell2 = cell_list[1];
9,091✔
152

153
            neighbors[cell1].push_back( cell2 );
9,091✔
154
            neighbors[cell2].push_back( cell1 );
9,091✔
155

156
            normals[cell1].push_back(
18,182✔
157
                ComputeOutwardFacingNormal( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second], _cellMidPoints[cell1] ) );
18,182✔
158
            normals[cell2].push_back(
18,182✔
159
                ComputeOutwardFacingNormal( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second], _cellMidPoints[cell2] ) );
18,182✔
160
            interfaceMid[cell1].push_back( ComputeCellInterfaceMidpoints( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second] ) );
9,091✔
161
            interfaceMid[cell2].push_back( ComputeCellInterfaceMidpoints( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second] ) );
9,091✔
162
        }
163
        else if( /* condition */ cell_list.size() == 1 ) {
712✔
164

165
            unsigned cell1 = cell_list[0];
712✔
166
            neighbors[cell1].push_back( _ghostCellID );    // neighbor must be a ghost cell
712✔
167
            normals[cell1].push_back(
1,424✔
168
                ComputeOutwardFacingNormal( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second], _cellMidPoints[cell1] ) );
1,424✔
169
            interfaceMid[cell1].push_back( ComputeCellInterfaceMidpoints( _nodes[nodes_of_edge.first], _nodes[nodes_of_edge.second] ) );
712✔
170
        }
171
        else {
NEW
172
            ErrorMessages::Error( "More than 2 cells share an edge: " + std::to_string( cell_list.size() ), CURRENT_FUNCTION );
×
173
        }
174
    }
175
    // check for any unassigned faces
176
    _cellNeighbors.resize( _numCells );
19✔
177
    for( unsigned i = 0; i < _numCells; ++i ) {
5,117✔
178
        _cellNeighbors[i].resize( _numNodesPerCell );
5,098✔
179
        if( neighbors[i].size() != _numNodesPerCell ) {
5,098✔
NEW
180
            ErrorMessages::Error( "Not " + std::to_string( _numNodesPerCell ) + " neighbors detected: " + std::to_string( i ), CURRENT_FUNCTION );
×
181
        }
182
        if( normals[i].size() != _numNodesPerCell ) {
5,098✔
NEW
183
            ErrorMessages::Error( "Not" + std::to_string( _numNodesPerCell ) + " normals detected: " + std::to_string( i ), CURRENT_FUNCTION );
×
184
        }
185
        if( interfaceMid[i].size() != _numNodesPerCell ) {
5,098✔
NEW
186
            ErrorMessages::Error( "Not" + std::to_string( _numNodesPerCell ) + " interfaceMid detected: " + std::to_string( i ), CURRENT_FUNCTION );
×
187
        }
188
        for( unsigned j = 0; j < _numNodesPerCell; ++j ) {
23,992✔
189
            if( neighbors[i][j] == -1 ) {
18,894✔
NEW
190
                ErrorMessages::Error( "Face not assigned: " + std::to_string( i ) + " " + std::to_string( j ), CURRENT_FUNCTION );
×
191
            }
192
            _cellNeighbors[i][j] = neighbors[i][j];
18,894✔
193
        }
194
    }
195
    _cellNormals            = normals;
19✔
196
    _cellInterfaceMidPoints = interfaceMid;
19✔
197

198
    /*
199
    // pre sort cells and boundaries; sorting is needed for std::set_intersection
200
    log->info( "| ...sort cells..." );
201

202
    auto sortedCells( _cells );
203
#pragma omp parallel for
204
    for( unsigned i = 0; i < _numCells; ++i ) {
205
        std::sort( sortedCells[i].begin(), sortedCells[i].end() );
206
    }
207
    std::vector<std::vector<unsigned>> sortedBoundaries;
208
    for( unsigned i = 0; i < _numBoundaries; ++i ) {
209
        sortedBoundaries.push_back( _boundaries[i].second );
210
        std::sort( sortedBoundaries[i].begin(), sortedBoundaries[i].end() );
211
    }
212

213
    // save which cell has which nodes
214
    log->info( "| ...connect cells to nodes..." );
215
    blaze::CompressedMatrix<bool> connMat( _numCells, _numNodes );
216

217
    // #pragma omp parallel for
218
    for( unsigned i = mpiCellStart; i < mpiCellEnd; ++i ) {
219
        for( auto j : _cells[i] ) connMat.set( i, j, true );
220
    }
221

222
    // determine neighbor cells and normals with MPI and OpenMP
223
    log->info( "| ...determine neighbors of cells..." );
224

225
#pragma omp parallel for schedule( guided )
226
    for( unsigned i = mpiCellStart; i < mpiCellEnd; ++i ) {
227
        std::vector<unsigned>* cellsI = &sortedCells[i];
228
        unsigned ctr                  = 0;
229
        for( unsigned j = 0; j < _numCells; ++j ) {
230
            if( i == j )
231
                continue;
232
            else if( ctr == _numNodesPerCell )
233
                break;
234
            else if( static_cast<unsigned>( blaze::dot( blaze::row( connMat, i ), blaze::row( connMat, j ) ) ) ==
235
                     _numNodesPerBoundary ) {    // in 2D cells are neighbors if they share two nodes std::vector<unsigned>* cellsJ =
236
                                                 // &sortedCells[j];
237
                // which are the two common nodes and which edge belongs to them?
238
                std::vector<unsigned>* cellsJ = &sortedCells[j];
239
                std::vector<unsigned> commonElements;    // vector of nodes that are shared by cells i and j
240
                commonElements.reserve( _numNodesPerBoundary );
241
                std::set_intersection( cellsI->begin(),
242
                                       cellsI->end(),
243
                                       cellsJ->begin(),
244
                                       cellsJ->end(),
245
                                       std::back_inserter( commonElements ) );    // find common nodes of two cells
246
                // determine unused index
247
                unsigned pos0 = _numNodesPerCell * ( i - mpiCellStart );
248
                unsigned pos  = pos0;
249
                while( neighborsFlatPart[pos] != -1 && pos < pos0 + _numNodesPerCell - 1 && pos < chunkSize * _numNodesPerCell - 1 ) {
250
                    pos++;    // neighbors should be at same edge position for cells i AND j
251
                }
252
                neighborsFlatPart[pos] = j;
253
                // compute normal vector
254
                normalsFlatPart[pos]      = ComputeOutwardFacingNormal( _nodes[commonElements[0]], _nodes[commonElements[1]], _cellMidPoints[i] );
255
                interfaceMidFlatPart[pos] = ComputeCellInterfaceMidpoints( _nodes[commonElements[0]], _nodes[commonElements[1]] );
256
                ctr++;
257
            }
258
        }
259

260
        // boundaries are treated similarly to normal cells, but need a special treatment due to the absence of a neighboring cell
261
        for( unsigned k = 0; k < _boundaries.size(); ++k ) {
262
            std::vector<unsigned>* bNodes = &sortedBoundaries[k];
263
            for( unsigned j = 0; j < _boundaries[k].second.size(); ++j ) {
264
                std::vector<unsigned> commonElements;    // vector of nodes that are shared by cells i and j
265
                commonElements.reserve( _numNodesPerBoundary );
266
                std::set_intersection( cellsI->begin(),
267
                                       cellsI->end(),
268
                                       bNodes->begin(),
269
                                       bNodes->end(),
270
                                       std::back_inserter( commonElements ) );    // find common nodes of two cells
271
                // _boundaries[k].second has all boundary nodes of boundary k. Therefore if all cell nodes lie on the boundary, the number of
272
                // common nodes can be 3 for triangles, 4 for quadrangles etc
273
                if( commonElements.size() >= _numNodesPerBoundary && commonElements.size() <= _numNodesPerCell ) {
274
                    unsigned pos0 = _numNodesPerCell * ( i - mpiCellStart );
275
                    unsigned pos  = pos0;
276
                    while( neighborsFlatPart[pos] != -1 && pos < pos0 + _numNodesPerCell - 1 && pos < chunkSize * _numNodesPerCell - 1 ) {
277
                        pos++;
278
                    }
279
                    neighborsFlatPart[pos]    = _ghostCellID;
280
                    normalsFlatPart[pos]      = ComputeOutwardFacingNormal( _nodes[commonElements[0]], _nodes[commonElements[1]],
281
_cellMidPoints[i] ); interfaceMidFlatPart[pos] = ComputeCellInterfaceMidpoints( _nodes[commonElements[0]], _nodes[commonElements[1]] );
282
                }
283
            }
284
        }
285
    }
286

287
    // gather distributed data on all MPI threads
288
    std::vector<int> neighborsFlat( _numNodesPerCell * chunkSize * comm_size, -1 );
289
    std::vector<Vector> normalsFlat( _numNodesPerCell * chunkSize * comm_size, Vector( _dim, 0.0 ) );
290
    std::vector<Vector> interfaceMidFlat( _numNodesPerCell * chunkSize * comm_size, Vector( _dim, 0.0 ) );
291
    // if( comm_size == 1 ) {    // can be done directly if there is only one MPI thread
292
    neighborsFlat.assign( neighborsFlatPart.begin(), neighborsFlatPart.end() );
293
    normalsFlat.assign( normalsFlatPart.begin(), normalsFlatPart.end() );
294
    interfaceMidFlat.assign( interfaceMidFlatPart.begin(), interfaceMidFlatPart.end() );
295
    //}
296
    // else {
297
    //    MPI_Allgather( neighborsFlatPart.data(),
298
    //                   _numNodesPerCell * chunkSize,
299
    //                   MPI_INT,
300
    //                   neighborsFlat.data(),
301
    //                   _numNodesPerCell * chunkSize,
302
    //                   MPI_INT,
303
    //                   MPI_COMM_WORLD );
304
    //}
305

306
    // check for any unassigned faces
307
    if( std::any_of( neighborsFlat.begin(), neighborsFlat.end(), []( int i ) { return i == -1; } ) ) {    // if any entry in neighborsFlat is -1
308
        for( unsigned idx = 0; idx < neighborsFlat.size(); ++idx ) {
309
            ErrorMessages::Error( "Detected unassigned faces at index " + std::to_string( idx ) + " !", CURRENT_FUNCTION );
310
        }
311
    }
312

313
    // reorder neighbors and normals into nested structure
314
    _cellNeighbors.resize( _numCells );
315
    _cellNormals.resize( _numCells );
316
    _cellInterfaceMidPoints.resize( _numCells );
317

318
    for( unsigned i = 0; i < neighborsFlat.size(); ++i ) {
319
        unsigned IDi = static_cast<unsigned>( i / static_cast<double>( _numNodesPerCell ) );
320
        unsigned IDj = neighborsFlat[i];
321
        if( IDi == IDj ) continue;     // avoid self assignment
322
        if( IDj == _ghostCellID ) {    // cell is boundary cell
323
            // if( std::find( _cellNeighbors[IDi].begin(), _cellNeighbors[IDi].end(), _ghostCellID ) == _cellNeighbors[IDi].end() ) {
324
            _cellNeighbors[IDi].push_back( _ghostCellID );
325
            _cellNormals[IDi].push_back( normalsFlat[i] );
326
            _cellInterfaceMidPoints[IDi].push_back( interfaceMidFlat[i] );
327
            // temp++;
328
            // }
329
            // std::cout << temp << "\n";
330
        }
331
        else {    // normal cell neighbor
332
            // if( std::find( _cellNeighbors[IDi].begin(), _cellNeighbors[IDi].end(), IDj ) == _cellNeighbors[IDi].end() ) {
333
            _cellNeighbors[IDi].push_back( neighborsFlat[i] );
334
            _cellNormals[IDi].push_back( normalsFlat[i] );
335
            _cellInterfaceMidPoints[IDi].push_back( interfaceMidFlat[i] );
336
            //}
337
        }
338
    }
339
*/
340

341
    // assign boundary types to all cells
342
    _cellBoundaryTypes.resize( _numCells, BOUNDARY_TYPE::NONE );
19✔
343
#pragma omp parallel for
19✔
344
    for( unsigned i = 0; i < _numCells; ++i ) {
345
        if( std::any_of( _cellNeighbors[i].begin(), _cellNeighbors[i].end(), [this]( unsigned i ) {
17,038✔
346
                return i == _ghostCellID;
17,038✔
347
            } ) ) {                           // cell is boundary cell
348
            for( auto bc : _boundaries ) {    // loop over all boundaries and boundary nodes and search for the respective nodeID
349
                for( auto cNodes : _cells[i] ) {
350
                    if( std::find( bc.second.begin(), bc.second.end(), cNodes ) != bc.second.end() ) {
351
                        _cellBoundaryTypes[i] = bc.first;
352
                    }
353
                }
354
            }
355
        }
356
    }
357
}
19✔
358

359
void Mesh::ComputeCellAreas() {
34✔
360
    _cellAreas.resize( _numCells );
34✔
361
#pragma omp parallel for
34✔
362
    for( unsigned i = 0; i < _numCells; ++i ) {
363
        switch( _numNodesPerCell ) {
364
            case 3: {    // triangular cells
365
                _cellAreas[i] = std::abs( ( _nodes[_cells[i][0]][0] * ( _nodes[_cells[i][1]][1] - _nodes[_cells[i][2]][1] ) +
366
                                            _nodes[_cells[i][1]][0] * ( _nodes[_cells[i][2]][1] - _nodes[_cells[i][0]][1] ) +
367
                                            _nodes[_cells[i][2]][0] * ( _nodes[_cells[i][0]][1] - _nodes[_cells[i][1]][1] ) ) /
368
                                          2 );
369
                break;
370
            }
371
            case 4: {    // quadrilateral cells
372
                std::vector d1{ _nodes[_cells[i][0]][0] - _nodes[_cells[i][1]][0], _nodes[_cells[i][0]][1] - _nodes[_cells[i][1]][1] };
373
                std::vector d2{ _nodes[_cells[i][1]][0] - _nodes[_cells[i][2]][0], _nodes[_cells[i][1]][1] - _nodes[_cells[i][2]][1] };
374
                std::vector d3{ _nodes[_cells[i][2]][0] - _nodes[_cells[i][3]][0], _nodes[_cells[i][2]][1] - _nodes[_cells[i][3]][1] };
375
                std::vector d4{ _nodes[_cells[i][3]][0] - _nodes[_cells[i][0]][0], _nodes[_cells[i][3]][1] - _nodes[_cells[i][0]][1] };
376

377
                double a = std::sqrt( d1[0] * d1[0] + d1[1] * d1[1] );
378
                double b = std::sqrt( d2[0] * d2[0] + d2[1] * d2[1] );
379
                double c = std::sqrt( d3[0] * d3[0] + d3[1] * d3[1] );
380
                double d = std::sqrt( d4[0] * d4[0] + d4[1] * d4[1] );
381
                double T = 0.5 * ( a + b + c + d );
382

383
                double alpha = std::acos( ( d4[0] * d1[0] + d4[1] * d1[1] ) / ( a * d ) );
384
                double beta  = std::acos( ( d2[0] * d3[0] + d2[1] * d3[1] ) / ( b * c ) );
385

386
                _cellAreas[i] = std::sqrt( ( T - a ) * ( T - b ) * ( T - c ) * ( T - d ) -
387
                                           a * b * c * d * std::cos( 0.5 * ( alpha + beta ) ) * std::cos( 0.5 * ( alpha + beta ) ) );
388
                break;
389
            }
390
            default: {
391
                ErrorMessages::Error( "Area computation for cells with " + std::to_string( _numNodesPerCell ) + " nodes is not implemented yet!",
392
                                      CURRENT_FUNCTION );
393
            }
394
        }
395
    }
396
}
34✔
397

398
void Mesh::ComputeCellMidpoints() {
34✔
399
    _cellMidPoints = std::vector( _numCells, Vector( _dim, 0.0 ) );
34✔
400
#pragma omp parallel for
34✔
401
    for( unsigned j = 0; j < _numCells; ++j ) {               // loop over cells
402
        for( unsigned l = 0; l < _cells[j].size(); ++l ) {    // loop over nodes of the cell
403
            _cellMidPoints[j] = _cellMidPoints[j] + _nodes[_cells[j][l]];
404
        }
405
        _cellMidPoints[j] = _cellMidPoints[j] / static_cast<double>( _cells[j].size() );    // arithmetic mean of node coord
406
    }
407
}
34✔
408

409
Vector Mesh::ComputeCellInterfaceMidpoints( const Vector& nodeA, const Vector& nodeB ) {
18,894✔
410
    Vector interfaceMidPt( _dim, 0.0 );
18,894✔
411
    interfaceMidPt = 0.5 * ( nodeA + nodeB );
18,894✔
412
    return interfaceMidPt;
18,894✔
413
}
414

415
Vector Mesh::ComputeOutwardFacingNormal( const Vector& nodeA, const Vector& nodeB, const Vector& cellCenter ) {
18,894✔
416
    double dx = nodeA[0] - nodeB[0];
18,894✔
417
    double dy = nodeA[1] - nodeB[1];
18,894✔
418
    Vector n{ -dy, dx };                    // normal vector
18,894✔
419
    Vector p{ nodeA[0], nodeA[1] };         // take arbitrary node
37,788✔
420
    if( dot( n, cellCenter - p ) > 0 ) {    // dot product is negative for outward facing normals
18,894✔
421
        n *= -1.0;                          // if dot product is positive -> flip normal
9,167✔
422
    }
423
    return n;
37,788✔
424
}
425

426
void Mesh::ComputeSlopes( unsigned nq, VectorVector& psiDerX, VectorVector& psiDerY, const VectorVector& psi ) const {
44✔
427
#pragma omp parallel for
44✔
428
    for( unsigned idx_cell = 0; idx_cell < _numCells; ++idx_cell ) {
429
        for( unsigned idx_sys = 0; idx_sys < nq; ++idx_sys ) {
430
            psiDerX[idx_cell][idx_sys] = 0.0;
431
            psiDerY[idx_cell][idx_sys] = 0.0;
432
            if( _cellBoundaryTypes[idx_cell] != 2 ) continue;    // skip ghost cells
433
            //  compute derivative by summing over cell boundary using Green Gauss Theorem
434
            for( unsigned idx_nbr = 0; idx_nbr < _cellNeighbors[idx_cell].size(); ++idx_nbr ) {
435
                psiDerX[idx_cell][idx_sys] +=
436
                    0.5 * ( psi[idx_cell][idx_sys] + psi[_cellNeighbors[idx_cell][idx_nbr]][idx_sys] ) * _cellNormals[idx_cell][idx_nbr][0];
437
                psiDerY[idx_cell][idx_sys] +=
438
                    0.5 * ( psi[idx_cell][idx_sys] + psi[_cellNeighbors[idx_cell][idx_nbr]][idx_sys] ) * _cellNormals[idx_cell][idx_nbr][1];
439
            }
440
            psiDerX[idx_cell][idx_sys] /= _cellAreas[idx_cell];
441
            psiDerY[idx_cell][idx_sys] /= _cellAreas[idx_cell];
442
        }
443
    }
444
}
44✔
445

446
void Mesh::ComputeSlopes1D( unsigned nq, VectorVector& psiDerX, const VectorVector& psi ) const {
×
447
    // assume equidistant ordered mesh
448
    double dx = _cellMidPoints[2][0] - _cellMidPoints[1][0];
×
449

450
#pragma omp parallel for
×
451
    for( unsigned idx_cell = 0; idx_cell < _numCells; ++idx_cell ) {
452
        for( unsigned idx_sys = 0; idx_sys < nq; ++idx_sys ) {
453
            psiDerX[idx_cell][idx_sys] = 0.0;
454
            if( _cellNeighbors[idx_cell].size() <= 2 ) {    // right neighbor + ghostcell ==> its a boundary cell
455
                continue;                                   // skip computation
456
            }
457
            // compute derivative by second order difference
458
            psiDerX[idx_cell][idx_sys] = ( psi[idx_cell + 1][idx_sys] - 2 * psi[idx_cell][idx_sys] + psi[idx_cell - 1][idx_sys] ) / ( dx * dx );
459
        }
460
    }
461
}
462

463
void Mesh::ComputeLimiter(
42✔
464
    unsigned nSys, const VectorVector& solDx, const VectorVector& solDy, const VectorVector& sol, VectorVector& limiter ) const {
465
    double const eps = 1e-10;
42✔
466
#pragma omp parallel for
42✔
467
    for( unsigned idx_cell = 0; idx_cell < _numCells; idx_cell++ ) {
468
        if( _cellBoundaryTypes[idx_cell] != 2 ) {
469
            for( unsigned idx_sys = 0; idx_sys < nSys; idx_sys++ ) {
470
                limiter[idx_cell][idx_sys] = 0.0;    // turn to first order on boundaries
471
            }
472
            continue;    // skip computation
473
        }
474

475
        for( unsigned idx_sys = 0; idx_sys < nSys; idx_sys++ ) {
476
            double r      = 0.0;
477
            double minSol = sol[idx_cell][idx_sys];
478
            double maxSol = sol[idx_cell][idx_sys];
479

480
            Vector localLimiter( _numNodesPerCell, 0.0 );
481
            for( unsigned idx_nbr = 0; idx_nbr < _cellNeighbors[idx_cell].size(); idx_nbr++ ) {
482
                // Compute ptswise max and minimum solultion values of current and neighbor cells
483
                unsigned glob_nbr = _cellNeighbors[idx_cell][idx_nbr];
484
                if( sol[glob_nbr][idx_sys] > maxSol ) {
485
                    maxSol = sol[glob_nbr][idx_sys];
486
                }
487
                if( sol[glob_nbr][idx_sys] < minSol ) {
488
                    minSol = sol[glob_nbr][idx_sys];
489
                }
490
            }
491

492
            for( unsigned idx_nbr = 0; idx_nbr < _cellNeighbors[idx_cell].size(); idx_nbr++ ) {
493
                // Compute value at interface midpoint, called gaussPt
494
                double gaussPt = 0.0;
495
                // gauss point is at cell vertex
496
                gaussPt = ( solDx[idx_cell][idx_sys] * ( _nodes[_cells[idx_cell][idx_nbr]][0] - _cellMidPoints[idx_cell][0] ) +
497
                            solDy[idx_cell][idx_sys] * ( _nodes[_cells[idx_cell][idx_nbr]][1] - _cellMidPoints[idx_cell][1] ) );
498

499
                // Compute limiter input
500
                if( std::abs( gaussPt ) > eps ) {
501
                    if( gaussPt > 0.0 ) {
502
                        r = ( maxSol - sol[idx_cell][idx_sys] ) / gaussPt;
503
                    }
504
                    else {
505
                        r = ( minSol - sol[idx_cell][idx_sys] ) / gaussPt;
506
                    }
507
                }
508
                else {
509
                    r = 1.0;
510
                }
511
                if( r < 0.0 ) {
512
                    std::cout << "r <0.0 \n";    // if this happens there is a bug or a deformend mesh
513
                }
514
                localLimiter[idx_nbr] = std::min( r, 1.0 );    // LimiterBarthJespersen( r );
515
            }
516
            // get smallest limiter
517
            limiter[idx_cell][idx_sys] = localLimiter[0];
518
            for( unsigned idx_nbr = 0; idx_nbr < _cellNeighbors[idx_cell].size(); idx_nbr++ ) {
519
                if( localLimiter[idx_nbr] < limiter[idx_cell][idx_sys] ) limiter[idx_cell][idx_sys] = localLimiter[idx_nbr];
520
            }
521
        }
522
    }
523
}
42✔
524

525
void Mesh::ComputeLimiter1D( unsigned nSys, const VectorVector& sol, VectorVector& limiter ) const {
×
UNCOV
526
    double const eps = 1e-10;
×
NEW
527
#pragma omp parallel for
×
528
    for( unsigned idx_cell = 0; idx_cell < _numCells; idx_cell++ ) {
529
        for( unsigned idx_sys = 0; idx_sys < nSys; idx_sys++ ) {
530
            double r = 0.0;
531
            if( _cellNeighbors[idx_cell].size() <= 2 ) {    // right neighbor + ghostcell ==> its a boundary cell
532
                limiter[idx_cell][idx_sys] = 0.0;           // turn to first order on boundaries
533
                continue;                                   // skip computation
534
            }
535
            double up   = sol[idx_cell][idx_sys] - sol[_cellNeighbors[idx_cell][0]][idx_sys];
536
            double down = sol[_cellNeighbors[idx_cell][1]][idx_sys] - sol[idx_cell][idx_sys];
537

538
            up > 0 ? up += eps : up -= eps;          // to prevent divbyzero
539
            down > 0 ? down += eps : down -= eps;    // to prevent divbyzero
540

541
            r                          = up / down;
542
            limiter[idx_cell][idx_sys] = std::max( std::min( r, 1.0 ), 0.0 );    // minmod limiter
543
        }
544
    }
545
}
546

547
// double LimiterBarthJespersen( double r ) { return std::min( r, 1.0 ); }
548

549
void Mesh::ComputeBounds() {
34✔
550
    _bounds = std::vector( _dim, std::make_pair( 0.0, 1.0 ) );    // Currently not used
34✔
551
    //_bounds = std::vector( _dim, std::make_pair( std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity() ) );
552
    //
553
    //     for( unsigned i = 0; i < _numNodes; ++i ) {
554
    //         for( unsigned j = 0; j < _dim; ++j ) {
555
    //             if( _nodes[i][j] < _bounds[j].first ) _bounds[j].first = _nodes[i][j];
556
    //             if( _nodes[i][j] > _bounds[j].second ) _bounds[j].second = _nodes[i][j];
557
    //         }
558
    //     }
559
}
34✔
560

561
const std::vector<Vector>& Mesh::GetNodes() const { return _nodes; }
42✔
562
const std::vector<Vector>& Mesh::GetCellMidPoints() const { return _cellMidPoints; }
128✔
563
const std::vector<std::vector<unsigned>>& Mesh::GetCells() const { return _cells; }
42✔
564
const std::vector<double>& Mesh::GetCellAreas() const { return _cellAreas; }
134✔
565
const std::vector<std::vector<unsigned>>& Mesh::GetNeighbours() const { return _cellNeighbors; }
72✔
566
const std::vector<std::vector<Vector>>& Mesh::GetNormals() const { return _cellNormals; }
70✔
567
const std::vector<BOUNDARY_TYPE>& Mesh::GetBoundaryTypes() const { return _cellBoundaryTypes; }
48✔
UNCOV
568
const std::vector<std::pair<double, double>> Mesh::GetBounds() const { return _bounds; }
×
569
const std::vector<std::vector<Vector>> Mesh::GetInterfaceMidPoints() const { return _cellInterfaceMidPoints; }
24✔
570

571
void Mesh::SetBoundaryType( int idx_cell, BOUNDARY_TYPE boundary_type ) { _cellBoundaryTypes[idx_cell] = boundary_type; }
×
572

573
double Mesh::GetDistanceToOrigin( unsigned idx_cell ) const {
×
574
    double distance = 0.0;
×
575
    for( unsigned idx_dim = 0; idx_dim < _dim; idx_dim++ ) {
×
576
        distance += _cellMidPoints[idx_cell][idx_dim] * _cellMidPoints[idx_cell][idx_dim];
×
577
    }
578
    return sqrt( distance );
×
579
}
580

NEW
581
unsigned Mesh::GetCellOfKoordinate( const double x, const double y ) const {
×
582
    // Experimental parallel implementation
NEW
583
    unsigned koordinate_cell_id = std::numeric_limits<unsigned>::max();
×
NEW
584
    bool found                  = false;
×
585

586
    // #pragma omp parallel for shared( found )
NEW
587
    for( unsigned idx_cell = 0; idx_cell < _numCells; idx_cell++ ) {
×
NEW
588
        if( IsPointInsideCell( idx_cell, x, y ) ) {
×
589
            // #pragma omp critical
590
            {
NEW
591
                if( !found ) {
×
NEW
592
                    koordinate_cell_id = idx_cell;
×
NEW
593
                    found              = true;
×
594
                }
595
            }
596
        }
597
        // Check if cancellation has been requested
598
    }
NEW
599
    if( !found ) {
×
NEW
600
        ErrorMessages::Error( "Probing point (" + std::to_string( x ) + "," + std::to_string( y ) + ") is not contained in mesh.", CURRENT_FUNCTION );
×
601
    }
NEW
602
    return koordinate_cell_id;
×
603
}
604

NEW
605
std::vector<unsigned> Mesh::GetCellsofBall( const double x, const double y, const double r ) const {
×
NEW
606
    std::vector<unsigned> cells_in_ball;
×
607

608
// Experimental parallel implementation
NEW
609
#pragma omp parallel for
×
610
    for( unsigned idx_cell = 0; idx_cell < _numCells; idx_cell++ ) {
611
        // Assume GetCellCenter returns the center coordinates of the cell
612
        double cell_x = _cellMidPoints[idx_cell][0];
613
        double cell_y = _cellMidPoints[idx_cell][1];
614

615
        // Calculate the distance from the cell center to the point (x, y)
616
        double distance = std::sqrt( ( cell_x - x ) * ( cell_x - x ) + ( cell_y - y ) * ( cell_y - y ) );
617

618
        if( distance <= r ) {
619
#pragma omp critical
620
            { cells_in_ball.push_back( idx_cell ); }
621
        }
622
    }
623

NEW
624
    if( cells_in_ball.empty() ) {    // take the only cell that contains the point
×
NEW
625
        std::cout << "No cells found within the ball centered at (" << x << "," << y << ") with radius " << r << "." << std::endl;
×
NEW
626
        std::cout << "Taking the only cell that contains the point." << std::endl;
×
NEW
627
        cells_in_ball.push_back( GetCellOfKoordinate( x, y ) );
×
628
        // ErrorMessages::Error( "No cells found within the ball centered at (" + std::to_string( x ) + "," + std::to_string( y ) + ") with radius " +
629
        // std::to_string( r ) + ".",
630
        // CURRENT_FUNCTION );
631
    }
632

NEW
633
    return cells_in_ball;
×
634
}
635

NEW
636
std::vector<unsigned> Mesh::GetCellsofRectangle( const std::vector<std::vector<double>>& cornercoordinates ) const {
×
637

NEW
638
    std::vector<unsigned> cells_in_rectangle;
×
639

640
    // Compute x_min, x_max, y_min, y_max from the corner coordinates
NEW
641
    double x_min = cornercoordinates[0][0];
×
NEW
642
    double x_max = cornercoordinates[0][0];
×
NEW
643
    double y_min = cornercoordinates[0][1];
×
NEW
644
    double y_max = cornercoordinates[0][1];
×
645

646
    // Loop over the cornercoordinates to find the minimum and maximum x and y values
NEW
647
    for( const auto& corner : cornercoordinates ) {
×
NEW
648
        x_min = std::min( x_min, corner[0] );
×
NEW
649
        x_max = std::max( x_max, corner[0] );
×
NEW
650
        y_min = std::min( y_min, corner[1] );
×
NEW
651
        y_max = std::max( y_max, corner[1] );
×
652
    }
653
    //  Loop over all cells and check if their midpoints fall within the rectangle
NEW
654
#pragma omp parallel for
×
655
    for( unsigned idx_cell = 0; idx_cell < _numCells; ++idx_cell ) {
656
        double cell_x = _cellMidPoints[idx_cell][0];
657
        double cell_y = _cellMidPoints[idx_cell][1];
658

659
        // Check if the cell's midpoint is inside the rectangle
660
        if( cell_x >= x_min && cell_x <= x_max && cell_y >= y_min && cell_y <= y_max ) {
661
#pragma omp critical
662
            { cells_in_rectangle.push_back( idx_cell ); }
663
        }
664
    }
NEW
665
    if( cells_in_rectangle.empty() ) {    // take the only cell that contains the point
×
NEW
666
        std::cout << "No cells found within rectangle centered at defined by corner coordinates. " << std::endl;
×
NEW
667
        std::cout << "Taking the only cell that contains the point." << std::endl;
×
NEW
668
        cells_in_rectangle.push_back( GetCellOfKoordinate( x_min + x_max / 2.0, y_min + y_max / 2.0 ) );
×
669
    }
670

NEW
671
    return cells_in_rectangle;
×
672
}
673

NEW
674
bool Mesh::IsPointInsideCell( unsigned idx_cell, double x, double y ) const {
×
NEW
675
    bool inside = false;
×
NEW
676
    if( _numNodesPerCell == 3 ) {
×
NEW
677
        inside = isPointInTriangle( x,
×
678
                                    y,
NEW
679
                                    _nodes[_cells[idx_cell][0]][0],
×
NEW
680
                                    _nodes[_cells[idx_cell][0]][1],
×
NEW
681
                                    _nodes[_cells[idx_cell][1]][0],
×
NEW
682
                                    _nodes[_cells[idx_cell][1]][1],
×
NEW
683
                                    _nodes[_cells[idx_cell][2]][0],
×
NEW
684
                                    _nodes[_cells[idx_cell][2]][1] );
×
685
    }
NEW
686
    else if( _numNodesPerCell == 4 ) {    // quadrilateral cell  +> divide into 2 triangles
×
NEW
687
        inside = isPointInTriangle( x,
×
688
                                    y,
NEW
689
                                    _nodes[_cells[idx_cell][0]][0],
×
NEW
690
                                    _nodes[_cells[idx_cell][0]][1],
×
NEW
691
                                    _nodes[_cells[idx_cell][1]][0],
×
NEW
692
                                    _nodes[_cells[idx_cell][1]][1],
×
NEW
693
                                    _nodes[_cells[idx_cell][2]][0],
×
NEW
694
                                    _nodes[_cells[idx_cell][2]][1] ) ||
×
NEW
695
                 isPointInTriangle( x,
×
696
                                    y,
NEW
697
                                    _nodes[_cells[idx_cell][0]][0],
×
NEW
698
                                    _nodes[_cells[idx_cell][0]][1],
×
NEW
699
                                    _nodes[_cells[idx_cell][2]][0],
×
NEW
700
                                    _nodes[_cells[idx_cell][2]][1],
×
NEW
701
                                    _nodes[_cells[idx_cell][3]][0],
×
NEW
702
                                    _nodes[_cells[idx_cell][3]][1] );
×
703
    }
704
    else {
NEW
705
        ErrorMessages::Error( "Unsupported number of nodes per cell: " + std::to_string( _numNodesPerCell ), CURRENT_FUNCTION );
×
706
    }
707
    // if( inside ) {
708
    //     std::cout << "Cells: " << idx_cell << "pt: " << _cellMidPoints[idx_cell][0] << " " << _cellMidPoints[idx_cell][1] << " pt: " << x << " " <<
709
    //     y
710
    //               << "\n";
711
    // }
712

NEW
713
    return inside;
×
714
}
715

NEW
716
bool Mesh::isPointInTriangle( double x, double y, double x1, double y1, double x2, double y2, double x3, double y3 ) const {
×
717
    // Calculate the area of the triangle
718
    double d1, d2, d3;
719
    bool hasNeg, hasPos;
720

NEW
721
    d1 = ( x - x2 ) * ( y1 - y2 ) - ( x1 - x2 ) * ( y - y2 );
×
NEW
722
    d2 = ( x - x3 ) * ( y2 - y3 ) - ( x2 - x3 ) * ( y - y3 );
×
NEW
723
    d3 = ( x - x1 ) * ( y3 - y1 ) - ( x3 - x1 ) * ( y - y1 );
×
724

NEW
725
    hasNeg = ( d1 < 0 ) || ( d2 < 0 ) || ( d3 < 0 );
×
NEW
726
    hasPos = ( d1 > 0 ) || ( d2 > 0 ) || ( d3 > 0 );
×
727

NEW
728
    return !( hasNeg && hasPos );
×
729
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc