mnsolver.cpp 12.9 KB
Newer Older
1
#include "solvers/mnsolver.h"
2
3
#include "common/config.h"
#include "common/io.h"
4
#include "common/mesh.h"
5
6
7
#include "entropies/entropybase.h"
#include "fluxes/numericalflux.h"
#include "optimizers/optimizerbase.h"
8
#include "problems/problembase.h"
9
10
#include "quadratures/quadraturebase.h"
#include "solvers/sphericalharmonics.h"
11
#include "toolboxes/errormessages.h"
12
#include "toolboxes/textprocessingtoolbox.h"
13

14
// externals
15
#include "spdlog/spdlog.h"
16
#include <mpi.h>
steffen.schotthoefer's avatar
steffen.schotthoefer committed
17

18
#include <fstream>
19
20
//#include <chrono>

21
MNSolver::MNSolver( Config* settings ) : Solver( settings ) {
steffen.schotthoefer's avatar
steffen.schotthoefer committed
22

23
    // Is this good (fast) code using a constructor list?
24
25
    _LMaxDegree    = settings->GetMaxMomentDegree();
    _nTotalEntries = GlobalIndex( _LMaxDegree, int( _LMaxDegree ) ) + 1;
26
27
28
29
30
31
32

    // build quadrature object and store quadrature points and weights
    _quadPoints       = _quadrature->GetPoints();
    _weights          = _quadrature->GetWeights();
    _nq               = _quadrature->GetNq();
    _quadPointsSphere = _quadrature->GetPointsSphere();
    _settings->SetNQuadPoints( _nq );
33

34
35
36
    // Initialize Scatter Matrix --
    _scatterMatDiag = Vector( _nTotalEntries, 0.0 );
    ComputeScatterMatrix();
steffen.schotthoefer's avatar
steffen.schotthoefer committed
37

38
39
40
41
42
    // Initialize Entropy
    _entropy = EntropyBase::Create( _settings );

    // Initialize Optimizer
    _optimizer = OptimizerBase::Create( _settings );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
43

44
45
    // Initialize lagrange Multiplier
    _alpha = VectorVector( _nCells, Vector( _nTotalEntries, 0.0 ) );
46
47

    // Initialize and Pre-Compute Moments at quadrature points
48
    _basis = new SphericalHarmonics( _LMaxDegree );
49

50
    _moments = VectorVector( _nq, Vector( _nTotalEntries, 0.0 ) );
51
    ComputeMoments();
52
53

    // Solver output
54
    PrepareOutputFields();
steffen.schotthoefer's avatar
steffen.schotthoefer committed
55
56
}

57
58
59
MNSolver::~MNSolver() {
    delete _entropy;
    delete _optimizer;
60
    delete _basis;
61
}
62

63
64
65
66
67
68
69
70
71
void MNSolver::ComputeScatterMatrix() {

    // --- Isotropic ---
    _scatterMatDiag[0] = -1.0;
    for( unsigned idx_diag = 1; idx_diag < _nTotalEntries; idx_diag++ ) {
        _scatterMatDiag[idx_diag] = 0.0;
    }
}

steffen.schotthoefer's avatar
steffen.schotthoefer committed
72
73
74
75
76
77
int MNSolver::GlobalIndex( int l, int k ) const {
    int numIndicesPrevLevel  = l * l;    // number of previous indices untill level l-1
    int prevIndicesThisLevel = k + l;    // number of previous indices in current level
    return numIndicesPrevLevel + prevIndicesThisLevel;
}

78
void MNSolver::ComputeMoments() {
steffen.schotthoefer's avatar
steffen.schotthoefer committed
79
    double my, phi;
80
81

    for( unsigned idx_quad = 0; idx_quad < _nq; idx_quad++ ) {
82
83
        my  = _quadPointsSphere[idx_quad][0];
        phi = _quadPointsSphere[idx_quad][1];
84

85
        _moments[idx_quad] = _basis->ComputeSphericalBasis( my, phi );
86
    }
87
88
89
90
91
}

Vector MNSolver::ConstructFlux( unsigned idx_cell ) {

    // ---- Integration of Moment of flux ----
92
    double entropyL, entropyR, entropyFlux;
93

94
    Vector flux( _nTotalEntries, 0.0 );
95

96
    for( unsigned idx_quad = 0; idx_quad < _nq; idx_quad++ ) {
97
98
99

        entropyFlux = 0.0;    // Reset temorary flux

100
101
        entropyL = _entropy->EntropyPrimeDual( blaze::dot( _alpha[idx_cell], _moments[idx_quad] ) );

102
103
104
105
106
        for( unsigned idx_neigh = 0; idx_neigh < _neighbors[idx_cell].size(); idx_neigh++ ) {
            // Store fluxes in psiNew, to save memory
            if( _boundaryCells[idx_cell] == BOUNDARY_TYPE::NEUMANN && _neighbors[idx_cell][idx_neigh] == _nCells )
                entropyR = entropyL;
            else {
107
                entropyR = _entropy->EntropyPrimeDual( blaze::dot( _alpha[_neighbors[idx_cell][idx_neigh]], _moments[idx_quad] ) );
108
            }
109
            entropyFlux += _g->Flux( _quadPoints[idx_quad], entropyL, entropyR, _normals[idx_cell][idx_neigh] );
110
        }
111
        flux += _moments[idx_quad] * ( _weights[idx_quad] * entropyFlux );
112
113

        // ------- Relizablity Reconstruction Step ----
114
    }
115
    return flux;
116
117
}

118
119
void MNSolver::ComputeRealizableSolution( unsigned idx_cell ) {
    double entropyReconstruction = 0.0;
120
    _sol[idx_cell]               = 0;
121
    for( unsigned idx_quad = 0; idx_quad < _nq; idx_quad++ ) {
122
        // Make entropyReconstruction a member vector, s.t. it does not have to be re-evaluated in ConstructFlux
123
        entropyReconstruction = _entropy->EntropyPrimeDual( blaze::dot( _alpha[idx_cell], _moments[idx_quad] ) );
124
        _sol[idx_cell] += _moments[idx_quad] * ( _weights[idx_quad] * entropyReconstruction );
125
126
127
    }
}

steffen.schotthoefer's avatar
steffen.schotthoefer committed
128
129
130
131
132
133
134
135
void MNSolver::Solve() {

    int rank;
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );

    auto log = spdlog::get( "event" );

    // angular flux at next time step (maybe store angular flux at all time steps, since time becomes energy?)
136
    VectorVector psiNew = _sol;
steffen.schotthoefer's avatar
steffen.schotthoefer committed
137

138
    double mass = 0;
139
140
141

    Save( -1 );

142
143
144
    if( rank == 0 ) log->info( "{:10}  {:10}", "t", "mass" );

    // if( rank == 0 ) log->info( "{:03.8f}   {:01.5e} {:01.5e}", -1.0, dFlux, mass1 ); Should be deleted
steffen.schotthoefer's avatar
steffen.schotthoefer committed
145
146

    // Loop over energies (pseudo-time of continuous slowing down approach)
147
    for( unsigned idx_energy = 0; idx_energy < _nEnergies; idx_energy++ ) {
steffen.schotthoefer's avatar
steffen.schotthoefer committed
148

149
        // ------- Reconstruction Step ----------------
150

151
        _optimizer->SolveMultiCell( _alpha, _sol, _moments );
152

153
154
        // Loop over the grid cells
        for( unsigned idx_cell = 0; idx_cell < _nCells; idx_cell++ ) {
155

156
157
            // ------- Relizablity Reconstruction Step ----

158
            ComputeRealizableSolution( idx_cell );
159
160

            // ------- Flux Computation Step --------------
161

162
163
            // Dirichlet Boundaries are finished now
            if( _boundaryCells[idx_cell] == BOUNDARY_TYPE::DIRICHLET ) continue;
steffen.schotthoefer's avatar
steffen.schotthoefer committed
164

165
            psiNew[idx_cell] = ConstructFlux( idx_cell );
166

167
            // ------ Finite Volume Update Step ------
168

169
170
171
            // NEED TO VECTORIZE
            for( unsigned idx_system = 0; idx_system < _nTotalEntries; idx_system++ ) {

172
                psiNew[idx_cell][idx_system] = _sol[idx_cell][idx_system] -
173
                                               ( _dE / _areas[idx_cell] ) * psiNew[idx_cell][idx_system] /* cell averaged flux */
174
                                               - _dE * _sol[idx_cell][idx_system] *
175
                                                     ( _sigmaT[idx_energy][idx_cell]                                    /* absorbtion influence */
176
                                                       + _sigmaS[idx_energy][idx_cell] * _scatterMatDiag[idx_system] ); /* scattering influence */
177
            }
steffen.schotthoefer's avatar
steffen.schotthoefer committed
178
        }
179

180
181
        // Update Solution
        _sol = psiNew;
182

183
        // --- VTK and CSV Output ---
184
        mass = WriteOutputFields( idx_energy );
185
        Save( idx_energy );
186
        // WriteNNTrainingData( idx_energy );
187

188
        // --- Screen Output ---
189
        if( rank == 0 ) log->info( "{:03.8f}   {:01.5e}", _energies[idx_energy], mass );
steffen.schotthoefer's avatar
steffen.schotthoefer committed
190
191
    }
}
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
void MNSolver::PrepareOutputFields() {
    unsigned nGroups = (unsigned)_settings->GetNVolumeOutput();

    _outputFieldNames.resize( nGroups );
    _outputFields.resize( nGroups );

    // Prepare all OutputGroups ==> Specified in option VOLUME_OUTPUT
    for( unsigned idx_group = 0; idx_group < nGroups; idx_group++ ) {
        // Prepare all Output Fields per group

        // Different procedure, depending on the Group...
        switch( _settings->GetVolumeOutput()[idx_group] ) {
            case MINIMAL:
                // Currently only one entry ==> rad flux
                _outputFields[idx_group].resize( 1 );
                _outputFieldNames[idx_group].resize( 1 );

210
211
                _outputFields[idx_group][0].resize( _nCells );
                _outputFieldNames[idx_group][0] = "radiation flux density";
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
                break;

            case MOMENTS:
                // As many entries as there are moments in the system
                _outputFields[idx_group].resize( _nTotalEntries );
                _outputFieldNames[idx_group].resize( _nTotalEntries );

                for( int idx_l = 0; idx_l <= (int)_LMaxDegree; idx_l++ ) {
                    for( int idx_k = -idx_l; idx_k <= idx_l; idx_k++ ) {
                        _outputFields[idx_group][GlobalIndex( idx_l, idx_k )].resize( _nCells );

                        _outputFieldNames[idx_group][GlobalIndex( idx_l, idx_k )] =
                            std::string( "u_" + std::to_string( idx_l ) + "^" + std::to_string( idx_k ) );
                    }
                }
                break;
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

            case DUAL_MOMENTS:
                // As many entries as there are moments in the system
                _outputFields[idx_group].resize( _nTotalEntries );
                _outputFieldNames[idx_group].resize( _nTotalEntries );

                for( int idx_l = 0; idx_l <= (int)_LMaxDegree; idx_l++ ) {
                    for( int idx_k = -idx_l; idx_k <= idx_l; idx_k++ ) {
                        _outputFields[idx_group][GlobalIndex( idx_l, idx_k )].resize( _nCells );

                        _outputFieldNames[idx_group][GlobalIndex( idx_l, idx_k )] =
                            std::string( "alpha_" + std::to_string( idx_l ) + "^" + std::to_string( idx_k ) );
                    }
                }
                break;

            case ANALYTIC:
                // one entry per cell
                _outputFields[idx_group].resize( 1 );
                _outputFieldNames[idx_group].resize( 1 );
                _outputFields[idx_group][0].resize( _nCells );
                _outputFieldNames[idx_group][0] = std::string( "analytic radiation flux density" );
                break;

252
253
254
255
256
            default: ErrorMessages::Error( "Volume Output Group not defined for MN Solver!", CURRENT_FUNCTION ); break;
        }
    }
}

257
double MNSolver::WriteOutputFields( unsigned idx_pseudoTime ) {
258
259
260
    double mass                   = 0.0;
    unsigned nGroups              = (unsigned)_settings->GetNVolumeOutput();
    double firstMomentScaleFactor = sqrt( 4 * M_PI );
261

262
263
264
    // Compute total "mass" of the system ==> to check conservation properties
    for( unsigned idx_cell = 0; idx_cell < _nCells; ++idx_cell ) {
        mass += _sol[idx_cell][0] * _areas[idx_cell];    // Should probably go to postprocessing
265
    }
266
267
    mass *= firstMomentScaleFactor;

268
269
    if( _settings->GetOutputFrequency() != 0 && idx_pseudoTime % (unsigned)_settings->GetOutputFrequency() == 0 ||
        idx_pseudoTime == _nEnergies - 1 /* need sol at last iteration */ ) {
270

271
272
273
        for( unsigned idx_group = 0; idx_group < nGroups; idx_group++ ) {
            switch( _settings->GetVolumeOutput()[idx_group] ) {
                case MINIMAL:
274
                    for( unsigned idx_cell = 0; idx_cell < _nCells; ++idx_cell ) {
275
                        _outputFields[idx_group][0][idx_cell] = firstMomentScaleFactor * _sol[idx_cell][0];
276
                    }
277
278
279
280
281
282
                    break;
                case MOMENTS:
                    for( unsigned idx_sys = 0; idx_sys < _nTotalEntries; idx_sys++ ) {
                        for( unsigned idx_cell = 0; idx_cell < _nCells; ++idx_cell ) {
                            _outputFields[idx_group][idx_sys][idx_cell] = _sol[idx_cell][idx_sys];
                        }
283
                    }
284
285
286
287
288
289
290
291
292
293
294
                    break;
                case DUAL_MOMENTS:
                    for( unsigned idx_sys = 0; idx_sys < _nTotalEntries; idx_sys++ ) {
                        for( unsigned idx_cell = 0; idx_cell < _nCells; ++idx_cell ) {
                            _outputFields[idx_group][idx_sys][idx_cell] = _alpha[idx_cell][idx_sys];
                        }
                    }
                    break;
                case ANALYTIC:
                    // Compute total "mass" of the system ==> to check conservation properties
                    for( unsigned idx_cell = 0; idx_cell < _nCells; ++idx_cell ) {
295

296
                        double time = idx_pseudoTime * _dE;
297

298
299
300
301
                        _outputFields[idx_group][0][idx_cell] = _problem->GetAnalyticalSolution(
                            _mesh->GetCellMidPoints()[idx_cell][0], _mesh->GetCellMidPoints()[idx_cell][1], time, _sigmaS[idx_pseudoTime][idx_cell] );
                    }
                    break;
302

303
304
                default: ErrorMessages::Error( "Volume Output Group not defined for MN Solver!", CURRENT_FUNCTION ); break;
            }
305
306
        }
    }
307
308
    return mass;
}
309

310
void MNSolver::Save() const { ExportVTK( _settings->GetOutputFile(), _outputFields, _outputFieldNames, _mesh ); }
311

312
void MNSolver::Save( int currEnergy ) const {
313
314
315
    if( _settings->GetOutputFrequency() != 0 && currEnergy % (unsigned)_settings->GetOutputFrequency() == 0 ) {
        ExportVTK( _settings->GetOutputFile() + "_" + std::to_string( currEnergy ), _outputFields, _outputFieldNames, _mesh );
    }
316
}
317
318
319
320
321
322
323

void MNSolver::WriteNNTrainingData( unsigned idx_pseudoTime ) {
    std::string filename = "trainNN.csv";
    std::ofstream myfile;
    myfile.open( filename, std::ofstream::app );

    for( unsigned idx_cell = 0; idx_cell < _nCells; idx_cell++ ) {
324
        myfile << 0 << ", " << _nTotalEntries << "," << idx_pseudoTime;
325
        for( unsigned idx_sys = 0; idx_sys < _nTotalEntries; idx_sys++ ) {
326
            myfile << "," << _sol[idx_cell][idx_sys];
327
        }
328
        myfile << " \n" << 1 << ", " << _nTotalEntries << "," << idx_pseudoTime;
329
        for( unsigned idx_sys = 0; idx_sys < _nTotalEntries; idx_sys++ ) {
330
            myfile << "," << _alpha[idx_cell][idx_sys];
331
        }
332
        myfile << "\n";
333
334
335
    }
    myfile.close();
}