-
Notifications
You must be signed in to change notification settings - Fork 2
/
FreeWheel.cpp
214 lines (173 loc) · 7.94 KB
/
FreeWheel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "SundialsWrapper.hpp"
/*
*
* When doing a freewheeling plasma the time-dependent variables are
* V ( or equivalently the Mach number ), T_i, T_e
*
*/
int ARKStep_FreeWheel( realtype t, N_Vector u, N_Vector uDot, void* voidPlasma )
{
MirrorPlasma* plasmaPtr = reinterpret_cast<MirrorPlasma*>( voidPlasma );
double TiOld = plasmaPtr->IonTemperature;
double TeOld = plasmaPtr->ElectronTemperature;
double VOld = plasmaPtr->ImposedVoltage;
if ( ION_TEMPERATURE( u ) < 0.0 ) {
#if defined( DEBUG )
std::cerr << "Error in SUNDIALS solve, due to negative ion temperature" << std::endl;
#endif
return 1;
}
if ( ELECTRON_TEMPERATURE( u ) < 0.0 ) {
#if defined( DEBUG )
std::cerr << "Error in SUNDIALS solve, due to negative electron temperature" << std::endl;
#endif
return 2;
}
// We're now evolving the voltage with time
plasmaPtr->IonTemperature = ION_TEMPERATURE( u );
plasmaPtr->ElectronTemperature = ELECTRON_TEMPERATURE( u );
plasmaPtr->ImposedVoltage = VOLTAGE( u );
try {
plasmaPtr->SetTime(t);
} catch ( std::domain_error &e ) {
// Timestep too long?
#ifdef DEBUG
std::cerr << "Evaluating RHS at t = " << std::setprecision( 20 ) << t << " ?!" << std::endl;
#endif
return 3;
}
plasmaPtr->SetMachFromVoltage();
plasmaPtr->UpdatePhi();
plasmaPtr->ComputeSteadyStateNeutrals();
#if defined( DEBUG ) && defined( SUNDIALS_DEBUG ) && defined( INTERNAL_RK_DEBUG )
std::cerr << "t = " << t << " ; T_i = " << plasmaPtr->IonTemperature << " ; T_e = " << plasmaPtr->ElectronTemperature << " MachNumber " << plasmaPtr->MachNumber << std::endl;
#endif
// I d omega / dt = ( Change in Angular Momentum )
// I d omega / dt = ( MomentumToVoltage )^(-1) dV/dt
// so dV/dt = MtoV * ( Change in Angular Momentum )
double MomentumToVoltage = plasmaPtr->PlasmaColumnWidth * plasmaPtr->PlasmaCentralRadius() * plasmaPtr->CentralCellFieldStrength / plasmaPtr->MomentOfInertia();
try {
double IonHeating = plasmaPtr->IonHeating();
double IonHeatLoss = plasmaPtr->IonHeatLosses();
double ElectronHeating = plasmaPtr->ElectronHeating();
double ElectronHeatLoss = plasmaPtr->ElectronHeatLosses();
#if defined( DEBUG ) && defined( SUNDIALS_DEBUG ) && defined( INTERNAL_RK_DEBUG )
std::cerr << " Ion Heating = " << IonHeating << " ; Ion Heat Loss = " << IonHeatLoss << std::endl;
std::cerr << " Electron Heating = " << ElectronHeating << " ; Electron Heat Loss = " << ElectronHeatLoss << std::endl;
#endif
ION_HEAT_BALANCE( uDot ) = ( IonHeating - IonHeatLoss );
ELECTRON_HEAT_BALANCE( uDot ) = ( ElectronHeating - ElectronHeatLoss );
// Should be negative to decelerate the plasma
double RadialCurrent = -VOLTAGE( u ) / plasmaPtr->ExternalResistance;
double AngularMomentumInjection = plasmaPtr->InjectedTorque( RadialCurrent );
double AngularMomentumLoss = plasmaPtr->TotalAngularMomentumLosses();
MOMENTUM_BALANCE( uDot ) = ( MomentumToVoltage ) * ( AngularMomentumInjection - AngularMomentumLoss );
} catch ( std::exception& e ) {
return -1;
}
plasmaPtr->IonTemperature = TiOld;
plasmaPtr->ElectronTemperature = TeOld;
plasmaPtr->ImposedVoltage = VOld;
plasmaPtr->SetMachFromVoltage();
plasmaPtr->UpdatePhi();
plasmaPtr->ComputeSteadyStateNeutrals();
return ARK_SUCCESS;
}
// Let the plasma decay through a resistor
void MCTransConfig::doFreeWheel( MirrorPlasma& plasma ) const
{
sundials::Context sunctx;
sunindextype NDims = 3; // Two temps & a voltage
N_Vector initialCondition = N_VNew_Serial( NDims, sunctx );
ION_TEMPERATURE( initialCondition ) = plasma.IonTemperature;
ELECTRON_TEMPERATURE( initialCondition ) = plasma.ElectronTemperature;
VOLTAGE( initialCondition ) = plasma.ImposedVoltage;
realtype t0 = plasma.time;
void *arkMem = ARKStepCreate( nullptr, ARKStep_FreeWheel, t0, initialCondition, sunctx );
if ( arkMem == nullptr ) {
throw std::runtime_error( "Cannot allocate ARKStep Working Memory" );
}
// Dummy Jacobian, will be filled by ARKStep with finite-difference approximations
SUNMatrix Jacobian = SUNDenseMatrix( NDims, NDims, sunctx );
// Small system, direct solve is fastest
SUNLinearSolver LS = SUNLinSol_Dense( initialCondition, Jacobian, sunctx );
ArkodeErrorWrapper( ARKStepSetLinearSolver( arkMem, LS, Jacobian ), "ARKStepSetLinearSolver" );
double abstol = plasma.SundialsAbsTol;
double reltol = plasma.SundialsRelTol;
#ifdef DEBUG
std::cerr << "Using SundialsAbsTol = " << abstol << " and SundialsRelTol = " << reltol << std::endl;
#endif
ArkodeErrorWrapper( ARKStepSStolerances( arkMem, reltol, abstol ), "ARKStepSStolerances" );
ArkodeErrorWrapper( ARKStepSetTableNum( arkMem, IRK_SCHEME, ARKSTEP_NULL_STEPPER ), "ARKStepSetTableNum" );
ArkodeErrorWrapper( ARKStepSetUserData( arkMem, reinterpret_cast<void*>( &plasma ) ), "ARKStepSetUserData" );
N_Vector positivityEnforcement = N_VNew_Serial( NDims, sunctx );
N_VConst( 0.0, positivityEnforcement ); // Default to no constraints
ION_TEMPERATURE( positivityEnforcement ) = 2.0; // T_i > 0
ELECTRON_TEMPERATURE( positivityEnforcement ) = 2.0; // T_e > 0
ArkodeErrorWrapper( ARKStepSetConstraints( arkMem, positivityEnforcement ), "ARKStepSetConstraints" );
// Because the scheme is 4th order, we request cubic hermite interpolation between
// internal timesteps, and don't allow the timestep to exceed 5*dt where dt is the
// time between outputs.
ArkodeErrorWrapper( ARKStepSetInterpolantDegree( arkMem, 3 ), "ARKStepSetInterpolantDegree" );
ArkodeErrorWrapper( ARKStepSetMaxStep( arkMem, OutputDeltaT*5 ), "ARKStepSetMaxStep" );
const unsigned long MaxSteps = 1e4;
ArkodeErrorWrapper( ARKStepSetMaxNumSteps( arkMem, MaxSteps ), "ARKStepSetMaxNumSteps" );
realtype t,tRet = 0;
int errorFlag;
#ifdef DEBUG
std::cerr << "Solving from t = " << plasma.time << " to t = " << EndTime << std::endl;
std::cerr << "Writing output every " << OutputDeltaT << std::endl;
#endif
ArkodeErrorWrapper( ARKStepSetStopTime( arkMem, EndTime ), "ARKStepSetStopTime" );
for ( t = t0 + OutputDeltaT; t < EndTime; t += OutputDeltaT )
{
#if defined( DEBUG )
double curTime;
ArkodeErrorWrapper( ARKStepGetCurrentTime( arkMem, &curTime ), "ARKStepGetCurrentTime" );
#endif
if ( t > EndTime )
t = EndTime;
errorFlag = ARKStepEvolve( arkMem, t, initialCondition, &tRet, ARK_NORMAL );
switch ( errorFlag ) {
case ARK_SUCCESS:
#if defined( DEBUG )
std::cerr << "Internal time is " << curTime << " Evolved to " << tRet << " with intent of reaching " << t << std::endl;
#endif
break;
default:
throw std::runtime_error( "ARKStep failed with error " + std::to_string( errorFlag ) );
break;
}
// ARKStep has evolved us to t = tRet, update the plasma object and write it out.
plasma.SetTime( tRet );
plasma.ElectronTemperature = ELECTRON_TEMPERATURE( initialCondition );
plasma.IonTemperature = ION_TEMPERATURE( initialCondition );
plasma.ImposedVoltage = VOLTAGE( initialCondition );
plasma.SetMachFromVoltage();
plasma.ComputeSteadyStateNeutrals();
plasma.WriteTimeslice( tRet );
#if defined( DEBUG )
std::cerr << "Writing timeslice at t = " << tRet << std::endl;
#endif
#if defined( DEBUG ) && defined( SUNDIALS_DEBUG )
std::cerr << "After evolving to " << tRet << " T_i = " << ION_TEMPERATURE( initialCondition ) << " ; T_e = " << ELECTRON_TEMPERATURE( initialCondition ) << std::endl;
#endif
if ( plasma.IonTemperature < 1e-2 && plasma.ElectronTemperature < 1e-2 ) {
std::cerr << "Stopping decaying plasma simulation, plasma is now cold (below 10eV)" << std::endl;
break;
}
}
#ifdef DEBUG
long nSteps = 0,nfeEvals = 0,nfiEvals = 0;
ArkodeErrorWrapper( ARKStepGetNumSteps( arkMem, &nSteps ), "ARKGetNumSteps" );
ArkodeErrorWrapper( ARKStepGetNumRhsEvals( arkMem, &nfeEvals, &nfiEvals ), "ARKGetNumRhsEvals" );
std::cerr << "SUNDIALS Timestepping took " << nSteps << " internal timesteps resulting in " << nfiEvals << " implicit function evaluations" << std::endl;
#endif
// Teardown
{
SUNLinSolFree( LS );
SUNMatDestroy( Jacobian );
N_VDestroy( initialCondition );
ARKStepFree( &arkMem );
}
}