DPsim
DAESolver.cpp
1 /* Copyright 2017-2021 Institute for Automation of Complex Power Systems,
2  * EONERC, RWTH Aachen University
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  *********************************************************************************/
8 
9 #include <dpsim-models/SimPowerComp.h>
10 #include <dpsim-models/Solver/MNAInterface.h>
11 #include <dpsim/DAESolver.h>
12 
13 using namespace DPsim;
14 using namespace CPS;
15 
16 //#define NVECTOR_DATA(vec) NV_DATA_S (vec) // Returns pointer to the first element of array vec
17 
18 DAESolver::DAESolver(String name, const CPS::SystemTopology &system, Real dt,
19  Real t0)
20  : Solver(name, CPS::Logger::Level::info), mSystem(system), mTimestep(dt) {
21 
22  // Defines offset vector of the residual which is composed as follows:
23  // mOffset[0] = # nodal voltage equations
24  // mOffset[1] = # of components and their respective equations (1 per component for now as inductance is not yet considered)
25 
26  mOffsets.push_back(0);
27  mOffsets.push_back(0);
28 
29  // Set initial values of all required variables and create IDA solver environment
30  for (IdentifiedObject::Ptr comp : mSystem.mComponents) {
31  auto daeComp = std::dynamic_pointer_cast<DAEInterface>(comp);
32  std::cout << "Added Comp" << std::endl;
33  if (!daeComp)
34  throw CPS::
35  Exception(); // Component does not support the DAE solver interface
36 
37  mComponents.push_back(comp);
38  }
39 
40  for (auto baseNode : mSystem.mNodes) {
41  // Add nodes to the list and ignore ground nodes.
42  if (!baseNode->isGround()) {
43  auto node = std::dynamic_pointer_cast<CPS::SimNode<Complex>>(baseNode);
44  mNodes.push_back(node);
45  std::cout << "Added Node" << std::endl;
46  }
47  }
48 
49  mNEQ = mComponents.size() + (2 * mNodes.size());
50  std::cout << std::endl;
51  std::cout << "Number of Eqn. " << mNEQ << std::endl;
52 
53  std::cout << "Processing Nodes" << std::endl;
54  UInt matrixNodeIndexIdx = 0;
55 
56  for (UInt idx = 0; idx < mNodes.size(); ++idx) {
57 
58  mNodes[idx]->setMatrixNodeIndex(0, matrixNodeIndexIdx);
59  ++matrixNodeIndexIdx;
60  if (mNodes[idx]->phaseType() == PhaseType::ABC) {
61  mNodes[idx]->setMatrixNodeIndex(1, matrixNodeIndexIdx);
62  ++matrixNodeIndexIdx;
63  mNodes[idx]->setMatrixNodeIndex(2, matrixNodeIndexIdx);
64  ++matrixNodeIndexIdx;
65  }
66  }
67 
68  std::cout << "Nodes Setup Done" << std::endl;
69  mT0 = t0;
70  initialize();
71 }
72 
74  int ret;
75  int counter = 0;
76  realtype *sval = NULL, *s_dtval = NULL;
77  std::cout << "Init states" << std::endl;
78 
79  // Allocate state vectors
80  state = N_VNew_Serial(mNEQ);
81  dstate_dt = N_VNew_Serial(mNEQ);
82 
83  std::cout << "State Init done" << std::endl;
84  std::cout << "Pointer Init" << std::endl;
85  sval = N_VGetArrayPointer_Serial(state);
86  s_dtval = N_VGetArrayPointer_Serial(dstate_dt);
87  std::cout << "Pointer Init done" << std::endl << std::endl;
88 
89  for (auto node : mNodes) {
90  // Initialize nodal voltages of state vector
91  Real tempVolt;
92  PhaseType phase = node->phaseType();
93  tempVolt = std::real(node->initialSingleVoltage(phase));
94 
95  std::cout << "Node Volt " << counter << ": " << tempVolt << std::endl;
96  sval[counter++] = tempVolt;
97  }
98 
99  for (IdentifiedObject::Ptr comp : mComponents) {
100  auto emtComp = std::dynamic_pointer_cast<SimPowerComp<Complex>>(comp);
101  if (emtComp) {
102  emtComp->initializeFromNodesAndTerminals(
103  mSystem.mSystemFrequency); // Set initial values of all components
104  }
105 
106  auto daeComp = std::dynamic_pointer_cast<DAEInterface>(comp);
107  if (!daeComp)
108  throw CPS::Exception();
109 
110  // Initialize component values of state vector
111  sval[counter++] = std::real(daeComp->daeInitialize());
112  std::cout << "Comp Volt " << counter - 1 << ": " << sval[counter - 1]
113  << std::endl;
114  // sval[counter++] = component inductance;
115 
116  // Register residual functions of components
117 
118  mResidualFunctions.push_back(
119  [daeComp](double ttime, const double state[], const double dstate_dt[],
120  double resid[], std::vector<int> &off) {
121  daeComp->daeResidual(ttime, state, dstate_dt, resid, off);
122  });
123  }
124 
125  for (int j = 0; j < (int)mNodes.size(); j++) {
126  // Initialize nodal current equations
127  sval[counter++] = 0; //TODO: check for correctness
128  std::cout << "Nodal Equation value " << sval[counter - 1] << std::endl;
129  }
130 
131  std::cout << std::endl;
132 
133  // Set initial values for state derivative for now all equal to 0
134  for (int i = 0; i < (mNEQ); i++) {
135  s_dtval[i] = 0; // TODO: add derivative calculation
136  std::cout << "derivative " << i << ": " << s_dtval[i] << std::endl;
137  }
138 
139  std::cout << std::endl;
140 
141  std::cout << "Init Tolerances" << std::endl;
142  rtol = RCONST(1.0e-10); // Set relative tolerance
143  abstol = RCONST(1.0e-4); // Set absolute error
144  std::cout << "Init Tolerances done" << std::endl;
145 
146  mem = IDACreate();
147  if (mem != NULL)
148  std::cout << "Memory Ok" << std::endl;
149  std::cout << "Define Userdata" << std::endl;
150  // This passes the solver instance as the user_data argument to the residual functions
151  ret = IDASetUserData(mem, this);
152 
153  std::cout << "Call IDAInit" << std::endl;
154 
155  ret =
157 
158  std::cout << "Call IDATolerances" << std::endl;
159  ret = IDASStolerances(mem, rtol, abstol);
160 
161  std::cout << "Call IDA Solver Stuff" << std::endl;
162  // Allocate and connect Matrix A and solver LS to IDA
163  A = SUNDenseMatrix(mNEQ, mNEQ);
164  LS = SUNDenseLinearSolver(state, A);
165  ret = IDADlsSetLinearSolver(mem, LS, A);
166 
167  //TODO: Optional IDA input functions
168  //ret = IDASetMaxNumSteps(mem, -1); //Max. number of timesteps until tout (-1 = unlimited)
169  //ret = IDASetMaxConvFails(mem, 100); //Max. number of convergence failures at one step
170  (void)ret;
171 }
172 
173 int DAESolver::residualFunctionWrapper(realtype ttime, N_Vector state,
174  N_Vector dstate_dt, N_Vector resid,
175  void *user_data) {
176  DAESolver *self = reinterpret_cast<DAESolver *>(user_data);
177 
178  return self->residualFunction(ttime, state, dstate_dt, resid);
179 }
180 
181 int DAESolver::residualFunction(realtype ttime, N_Vector state,
182  N_Vector dstate_dt, N_Vector resid) {
183  mOffsets[0] = 0; // Reset Offset
184  mOffsets[1] = 0; // Reset Offset
185  double *residual = NV_DATA_S(resid);
186  double *tempstate = NV_DATA_S(state);
187  // Solve for all node Voltages
188  for (auto node : mNodes) {
189 
190  Real tempVolt = 0;
191 
192  tempVolt += std::real(node->singleVoltage());
193 
194  residual[mOffsets[0]] = tempVolt - tempstate[mOffsets[0]];
195  mOffsets[0] += 1;
196  }
197 
198  // Call all registered component residual functions
199  for (auto resFn : mResidualFunctions) {
200  resFn(ttime, NV_DATA_S(state), NV_DATA_S(dstate_dt), NV_DATA_S(resid),
201  mOffsets);
202  }
203 
204  // If successful; positive value if recoverable error, negative if fatal error
205  // TODO: Error handling
206  return 0;
207 }
208 
209 Real DAESolver::step(Real time) {
210 
211  Real NextTime = time + mTimestep;
212  std::cout << "Current Time " << NextTime << std::endl;
213  int ret = IDASolve(mem, NextTime, &tret, state, dstate_dt,
214  IDA_NORMAL); // TODO: find alternative to IDA_NORMAL
215 
216  if (ret == IDA_SUCCESS) {
217  return NextTime;
218  } else {
219  std::cout << "Ida Error " << ret << std::endl;
220  //throw CPS::Exception();
221  void(IDAGetNumSteps(mem, &interalSteps));
222  void(IDAGetNumResEvals(mem, &resEval));
223  std::cout << "Interal steps: " << interalSteps << std::endl;
224  std::cout << "Res Eval :" << resEval << std::endl;
225  return NextTime;
226  }
227 }
228 
229 Task::List DAESolver::getTasks() {
230  // TODO
231  return Task::List();
232 }
233 
235  // Releasing all memory allocated by IDA
236  IDAFree(&mem);
237  N_VDestroy(state);
238  N_VDestroy(dstate_dt);
239  SUNLinSolFree(LS);
240  SUNMatDestroy(A);
241 }
Real mSystemFrequency
System frequency.
IdentifiedObject::List mComponents
List of network components.
TopologicalNode::List mNodes
List of network nodes.
Solver class which uses Differential Algebraic Equation(DAE) systems.
Definition: DAESolver.h:30
void * mem
Memory block allocated by IDA.
Definition: DAESolver.h:50
N_Vector state
Vector of problem variables.
Definition: DAESolver.h:52
N_Vector dstate_dt
Derivates of the state vector with respect to time.
Definition: DAESolver.h:54
SUNLinearSolver LS
Linear solver object.
Definition: DAESolver.h:64
Real step(Real time)
Solve system for the current time.
Definition: DAESolver.cpp:209
Int mNEQ
Number of equations in problem.
Definition: DAESolver.h:39
CPS::Task::List getTasks() override
Get tasks for scheduler.
Definition: DAESolver.cpp:229
std::vector< Int > mOffsets
Offsets vector for adding new equations to the residual vector.
Definition: DAESolver.h:35
Real mTimestep
Constant time step.
Definition: DAESolver.h:37
~DAESolver()
Deallocate all memory.
Definition: DAESolver.cpp:234
CPS::SimNode< Complex >::List mNodes
Nodes of the Problem.
Definition: DAESolver.h:43
DAESolver(String name, const CPS::SystemTopology &system, Real dt, Real mT0)
Create solve object with given parameters.
Definition: DAESolver.cpp:18
realtype rtol
Relative tolerance.
Definition: DAESolver.h:60
realtype abstol
Scalar absolute tolerance.
Definition: DAESolver.h:58
void initialize() final
Initialize Components & Nodes with initial values.
Definition: DAESolver.cpp:73
CPS::IdentifiedObject::List mComponents
Components of the Problem.
Definition: DAESolver.h:41
static int residualFunctionWrapper(realtype ttime, N_Vector state, N_Vector dstate_dt, N_Vector resid, void *user_data)
Residual Function of entire System.
Definition: DAESolver.cpp:173
realtype tret
Time IDA reached while solving.
Definition: DAESolver.h:56
SUNMatrix A
Template Jacobian Matrix.
Definition: DAESolver.h:62
Base class for more specific solvers such as MNA, ODE or IDA.
Definition: Solver.h:30