1 /* $Header: /var/cvs/mbdyn/mbdyn/mbdyn-1.0/modules/module-asynchronous_machine/module-asynchronous_machine.cc,v 1.11 2017/01/12 14:47:25 masarati Exp $ */
2 /*
3  * MBDyn (C) is a multibody analysis code.
4  * http://www.mbdyn.org
5  *
6  * Copyright (C) 1996-2017
7  *
8  * Pierangelo Masarati	<masarati@aero.polimi.it>
9  * Paolo Mantegazza	<mantegazza@aero.polimi.it>
10  *
11  * Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
12  * via La Masa, 34 - 20156 Milano, Italy
13  * http://www.aero.polimi.it
14  *
15  * Changing this copyright notice is forbidden.
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation (version 2 of the License).
20  *
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  */
31 /*
32  AUTHOR: Reinhard Resch <r.resch@secop.com>
33         Copyright (C) 2011(-2017) all rights reserved.
34 
35         The copyright of this code is transferred
36         to Pierangelo Masarati and Paolo Mantegazza
37         for use in the software MBDyn as described
38         in the GNU Public License version 2.1
39 */
40 
41 /**
42  * \mainpage
43  * This code is a simple example on how to implement a user defined element for the free multibody software <A HREF="http://www.aero.polimi.it/mbdyn/">MBDyn</A>.
44  * Since no documentation exists for user defined elements in MBDyn at the time of writing I have written this document in the hope that it will be helpful for others who want to write their own user defined elements for MBDyn.<BR>
45  *
46  * The purpose of this code is the simulation of an asynchronous electrical machine as a part of a multibody model.
47  * With this software the coupling between the mechanical and the electrical differential equations can be considered.
48  * The theoretical background is based on the book <A HREF="http://books.google.at/books?id=z6B3lcxDgkkC&printsec=frontcover&dq=Maschinendynamik++%26+dresig&hl=de&ei=SwvETYi8NJSw4Ab_luX3BA&sa=X&oi=book_result&ct=result&resnum=1&ved=0CDIQ6AEwAA#v=onepage&q&f=false">
49  * Maschinendynamik Springer 2009, Dresig, Holzweißig.</A><BR>
50  * In the formulas in this book the stator resistance is neglected and the free run slip is assumed to be zero.
51  * Also small perturbations around the mean angular velocity are assumed.<BR><BR>
52  * The input parameters for the simulation are:<BR>
53  *  Breakdown torque \f$M_K\f$.<BR>
54  *  Slip at the breakdown torque \f$s_K\f$.<BR>
55  *  Synchronous angular velocity \f$\Omega_S=2\,\pi\,\frac{f}{p}\f$ (might be a function of the time in case of an frequency inverter).<BR>
56  *  A flag that determines whether the power supply is turned on or off \f$\gamma(t)\f$.<BR>
57  *  Optional the initial value of the motor torque \f$M\f$.<BR>
58  *  Optional the initial value of the first derivative of the motor torque \f$\dot{M}\f$.<BR>
59  *
60  *  The sense of rotation is determined by the sign of \f$\Omega_S\f$.
61  *  If the sign is positive, the sense of rotation is also positive in the reference frame of the stator node.
62  *
63  *  This code is implemented as an user defined element for <A HREF="http://www.aero.polimi.it/mbdyn/">MBDyn</A>.
64  *  The element is connected to two structural nodes. The rotor node and the stator node.
65  *  The axis of rotation is assumed to be axis three in the reference frame of the stator node.
66  *  It is assumed that the axis of the rotor node is parallel to the axis of the stator node.
67  *  The torque is imposed to the structural nodes in direction of axis three in the reference frame of the stator node.
68  *  The synchronous angular velocity can be specified by means of a drive caller. This allows to simulate a frequency inverter.
69  */
70 
71 #include "mbconfig.h"           // This goes first in every *.c,*.cc file
72 
73 #include <cassert>
74 #include <cstdio>
75 #include <cmath>
76 #include <cfloat>
77 #include <iostream>
78 #include <iomanip>
79 #include <limits>
80 
81 #include "dataman.h"
82 #include "userelem.h"
83 #include "module-asynchronous_machine.h"
84 
85 class asynchronous_machine
86 : virtual public Elem, public UserDefinedElem
87 {
88 public:
89 	asynchronous_machine(unsigned uLabel, const DofOwner *pDO,
90 		DataManager* pDM, MBDynParser& HP);
91 	virtual ~asynchronous_machine(void);
92 	virtual void Output(OutputHandler& OH) const;
93 	virtual unsigned int iGetNumDof(void) const;
94 	virtual DofOrder::Order GetDofType(unsigned int i) const;
95 	virtual std::ostream& DescribeDof(std::ostream& out, const char *prefix, bool bInitial) const;
96 	virtual std::ostream& DescribeEq(std::ostream& out, const char *prefix, bool bInitial) const;
97 	virtual unsigned int iGetNumPrivData(void) const;
98 	virtual unsigned int iGetPrivDataIdx(const char *s) const;
99 	virtual doublereal dGetPrivData(unsigned int i) const;
100 	virtual void SetInitialValue(VectorHandler& X);
101 	virtual void Update(const VectorHandler& XCurr,const VectorHandler& XPrimeCurr);
102 	virtual void AfterPredict(VectorHandler& X, VectorHandler& XP);
103 	virtual void WorkSpaceDim(integer* piNumRows, integer* piNumCols) const;
104 	VariableSubMatrixHandler&
105 	AssJac(VariableSubMatrixHandler& WorkMat,
106 		doublereal dCoef,
107 		const VectorHandler& XCurr,
108 		const VectorHandler& XPrimeCurr);
109 	SubVectorHandler&
110 	AssRes(SubVectorHandler& WorkVec,
111 		doublereal dCoef,
112 		const VectorHandler& XCurr,
113 		const VectorHandler& XPrimeCurr);
114 	virtual int iGetNumConnectedNodes(void) const;
115 	virtual void GetConnectedNodes(std::vector<const Node *>& connectedNodes) const;
116 	virtual void SetValue(DataManager *pDM, VectorHandler& X, VectorHandler& XP,
117 			SimulationEntity::Hints *ph);
118 	virtual std::ostream& Restart(std::ostream& out) const;
119 	virtual unsigned int iGetInitialNumDof(void) const;
120 	virtual void
121 	InitialWorkSpaceDim(integer* piNumRows, integer* piNumCols) const;
122    	virtual VariableSubMatrixHandler&
123 	InitialAssJac(VariableSubMatrixHandler& WorkMat,
124 		      const VectorHandler& XCurr);
125    	virtual SubVectorHandler&
126 	InitialAssRes(SubVectorHandler& WorkVec, const VectorHandler& XCurr);
127 
128 private:
129 	bool IsMotorOn(void) const;
130 
131 private:
132 	const StructNode* m_pRotorNode;	/**< node of the rotor where the torque \f$\boldsymbol{f}_3\f$ is imposed and the angular velocity \f$\dot{\boldsymbol{\varphi}}_1\f$ is determined */
133 	const StructNode* m_pStatorNode;	/**< node of the stator where the torque \f$\boldsymbol{f}_4\f$ is applied and the angular velocity \f$\dot{\boldsymbol{\varphi}}_2\f$ is determined (axis 3 of the stator node is assumed to be the axis of rotation) */
134 	doublereal m_MK;				/**< breakdown torque \f$M_K\f$	*/
135 	doublereal m_sK;				/**< slip at the breakdown torque \f$s_K\f$	*/
136 	DriveOwner m_OmegaS;		/**< drive that returns the synchronous angular velocity \f$\Omega_S=2\,\pi\,\frac{f}{p}\f$ (might be a function of the time in case of an frequency inverter)	*/
137 	DriveOwner m_MotorOn;		/**< drive that returns whether the power supply is turned on or off	\f$\gamma(t)\f$ */
138 	doublereal m_M;					/**< holds the motor torque \f$M\f$ after convergence for convenience	*/
139 	doublereal m_dM_dt;				/**< holds the first derivative of the motor torque \f$\dot{M}\f$ after convergence	*/
140 	doublereal m_dM_dt2;			/**< holds the second derivative of the motor torque \f$\ddot{M}\f$ after convergence	*/
141 	doublereal m_omega;				/**< holds the angular velocity \f$\dot{\varphi}\f$ of the rotor relative to the stator around axis 3 of the stator node after convergence	*/
142 	doublereal m_domega_dt;			/**< holds the angular acceleration \f$\ddot{\varphi}\f$ of the rotor node relative to the stator node after convergence	*/
143     static const doublereal sm_SingTol;
144 };
145 
146 const doublereal asynchronous_machine::sm_SingTol = std::pow(std::numeric_limits<doublereal>::epsilon(), 0.9);
147 
148 /**
149  * \param uLabel the label assigned to this element in the input file
150  * \param pDO
151  * \param pDM pointer to the data manager (needed to read structural nodes for example)
152  * \param HP reference to the math parser (needed to read from the input file)
153  * \brief Constructs the element from the information in the input file.<BR>
154  *
155  * A description of the exact input file syntax can be retrieved by adding the following statement to the MBDyn input file:<BR>
156  * user defined: 1, asynchronous_machine, help;
157  */
asynchronous_machine(unsigned uLabel,const DofOwner * pDO,DataManager * pDM,MBDynParser & HP)158 asynchronous_machine::asynchronous_machine(
159 	unsigned uLabel, const DofOwner *pDO,
160 	DataManager* pDM, MBDynParser& HP)
161 : 	Elem(uLabel, flag(0)),
162 	UserDefinedElem(uLabel, pDO),
163 	m_pRotorNode(0),
164 	m_pStatorNode(0),
165 	m_MK(0.),
166 	m_sK(0.),
167 	m_OmegaS(0),
168 	m_MotorOn(0),
169 	m_M(0.),
170 	m_dM_dt(0.),
171 	m_dM_dt2(0.),
172 	m_omega(0.),
173 	m_domega_dt(0.)
174 {
175 	if (HP.IsKeyWord("help")) {
176 		silent_cout(
177 			"\n"
178 			"Module: 	asynchronous_machine\n"
179 			"\n"
180 			"	This module implements an asynchronous electric machine\n"
181 			"   according to\n"
182 			"\n"
183 			"	Maschinendynamik\n"
184 			"	Hans Dresig, Franz Holzweißig\n"
185 			"	Springer, 2009\n"
186 			"	page 58 equation 1.122 and 1.123\n"
187 			"\n"
188 			"Syntax:\n"
189 			"	asynchronous_machine,\n"
190 			"		rotor, (label) <rotor_node>,\n"
191 			"		stator, (label) <stator_node>,\n"
192 			"		MK, (real) <MK>,\n"
193 			"		sK, (real) <sK>,\n"
194 			"		OmegaS, (DriveCaller) <omega_s>\n"
195 			"		[ , motor on , (DriveCaller) <motor_on> ]\n"
196 			"		[ , M0 , (real) <M0> ]\n"
197 			"		[ , MP0 , (real) <MP0> ]\n"
198 			"\n"
199 			"       MK ... breakdown torque ( MK > 0 )\n"
200 			"       sK ... breakdown slip ( sK > 0 )\n"
201 			"       OmegaS = 2 * pi * f / p * sense_of_rotation ... synchronous angular velocity\n"
202 			"       motor_on ... power supply is turned off when 0, on otherwise\n"
203 			"       M0 ... initial torque ( default 0 )\n"
204 			"       MP0 ... initial torque derivative ( default 0 )\n"
205 			"\n"
206 			"       The axis of rotation is assumed to be axis 3 of the reference frame of the stator node.\n"
207 			<< std::endl);
208 
209 		if (!HP.IsArg()) {
210 			// Exit quietly if nothing else is provided
211 			throw NoErr(MBDYN_EXCEPT_ARGS);
212 		}
213 	}
214 
215 	if (!HP.IsKeyWord("rotor")) {
216 		silent_cerr("asynchronous_machine(" << GetLabel() << "): keyword \"rotor\" expected at line " << HP.GetLineData() << std::endl);
217 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
218 	}
219 
220 	m_pRotorNode = pDM->ReadNode<const StructNode, Node::STRUCTURAL>(HP);
221 
222 	if (!m_pRotorNode) {
223 		silent_cerr("asynchronous_machine(" << GetLabel() << "): structural node expected at line " << HP.GetLineData() << std::endl);
224 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
225 	}
226 
227 	if (!HP.IsKeyWord("stator")) {
228 		silent_cerr("asynchronous_machine(" << GetLabel() << "): keyword \"stator\" expected at line " << HP.GetLineData() << std::endl);
229 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
230 	}
231 
232 	m_pStatorNode = pDM->ReadNode<const StructNode, Node::STRUCTURAL>(HP);
233 
234 	if (!m_pStatorNode) {
235 		silent_cerr("asynchronous_machine(" << GetLabel() << "): structural node expected at line " << HP.GetLineData() << std::endl);
236 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
237 	}
238 
239 	if (!HP.IsKeyWord("MK")) {
240 		silent_cerr("asynchronous_machine(" << GetLabel() << "): keyword \"MK\" expected at line " << HP.GetLineData() << std::endl);
241 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
242 	}
243 	m_MK = HP.GetReal();
244 
245 	if (!HP.IsKeyWord("sK")) {
246 		silent_cerr("asynchronous_machine(" << GetLabel() << "): keyword \"sK\" expected at line " << HP.GetLineData() << std::endl);
247 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
248 	}
249 	m_sK = HP.GetReal();
250 
251 	if (!HP.IsKeyWord("OmegaS")) {
252 		silent_cerr("asynchronous_machine(" << GetLabel() << "): keyword \"OmegaS\" expected" << std::endl);
253 		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
254 	}
255 	m_OmegaS.Set(HP.GetDriveCaller());
256 
257 	if (HP.IsKeyWord("motor" "on") || HP.IsKeyWord("motor_on")) {
258 		m_MotorOn.Set(HP.GetDriveCaller());
259 	} else {
260 		m_MotorOn.Set(new OneDriveCaller());
261 	}
262 
263 	if (HP.IsKeyWord("M0")) {
264 		m_M = HP.GetReal();
265 	}
266 
267 	if (HP.IsKeyWord("MP0")) {
268 		m_dM_dt = HP.GetReal();
269 	}
270 
271 	SetOutputFlag(pDM->fReadOutput(HP, Elem::LOADABLE));
272 
273 	Vec3 e3(m_pStatorNode->GetRCurr().GetCol(3));
274 	const Vec3& omega1 = m_pRotorNode->GetWCurr();
275 	const Vec3& omega2 = m_pStatorNode->GetWCurr();
276 
277 	m_omega = e3.Dot(omega1 - omega2);
278 
279 	const doublereal OmegaS = m_OmegaS.dGet();
280 
281 	pDM->GetLogFile() << "asynchronous machine: "
282 		<< uLabel << " "
283 		<< m_pRotorNode->GetLabel() << " "
284 		<< m_pStatorNode->GetLabel() << " "
285 		<< m_MK << " "
286 		<< m_sK << " "
287 		<< OmegaS << " "
288 		<< IsMotorOn() << " "
289 		<< m_M << " "
290 		<< m_dM_dt << " "
291 		<< m_omega
292 		<< std::endl;
293 
294 	// Note: The ODE for the asynchronous machine is defined for positive sign of OmegaS only!
295 	// At user level the signs of M, dM/dt and d^2M/dt^2 are defined to be positive
296 	// in positive coordinate system direction in the reference frame of the stator.
297 	m_M *= copysign(1., OmegaS);
298 	m_dM_dt *= copysign(1., OmegaS);
299 	m_dM_dt2 *= copysign(1., OmegaS);
300 }
301 
302 /**
303  * @param X vector of global degrees of freedom \f$\boldsymbol{y}\f$ after convergence at the current time step.
304  * @param XP derivative of the global degrees of freedom \f$\dot{\boldsymbol{y}}\f$ after convergence at the current time step.<BR>
305  * \brief This member function is called after each iteration in the nonlinear solution phase.
306  *
307  * In our element this member function saves the current state of the private degrees of freedom.
308  * This is needed for the implementation of the Output() and the dGetPrivData() member functions.
309  * If the private degrees of freedom are needed just for Output(), the code that saves it's state should be moved to AfterPredict() since this member function is called only once per time step.
310  */
311 void
Update(const VectorHandler & X,const VectorHandler & XP)312 asynchronous_machine::Update(const VectorHandler& X,const VectorHandler& XP)
313 {
314 	const integer iFirstIndex = iGetFirstIndex();
315 
316 	const integer intTorqueDerivativeRowIndex 	 = iFirstIndex + 1;
317 	const integer intTorqueRowIndex 			 = iFirstIndex + 2;
318 	const integer intOmegaRowIndex 				 = iFirstIndex + 3;
319 
320 	// save the current state needed by dGetPrivData() and Output()
321 	m_M 		= X(intTorqueRowIndex);
322 	m_dM_dt		= X(intTorqueDerivativeRowIndex);
323 	m_dM_dt2	= XP(intTorqueDerivativeRowIndex);
324 	m_omega 	= X(intOmegaRowIndex);
325 	m_domega_dt = XP(intOmegaRowIndex);
326 }
327 
AfterPredict(VectorHandler & X,VectorHandler & XP)328 void asynchronous_machine::AfterPredict(VectorHandler& X, VectorHandler& XP)
329 {
330 	Update(X, XP);
331 }
332 
333 /**
334  * @return Returns true if the motor is powered on.<BR>
335  * \brief If a drive caller has been specified in the input file the value returned by the drive caller is used to determine if the motor is powered on.
336  * If no drive caller has been specified in the input file it is assumed that the motor is always powered on.
337  */
338 bool
IsMotorOn(void) const339 asynchronous_machine::IsMotorOn(void) const
340 {
341 	return (m_MotorOn.dGet() != 0.);
342 }
343 
~asynchronous_machine(void)344 asynchronous_machine::~asynchronous_machine(void)
345 {
346 	// destroy private data
347 #ifdef DEBUG
348 	std::cerr << __FILE__ << ":" << __LINE__ << ":" << __PRETTY_FUNCTION__ << std::endl;
349 #endif
350 }
351 
352 /**
353  * @param OH OH.Loadable() returns a reference to the stream intended for the output of loadable elements.<BR>
354  * \brief Writes private data to a file at each time step.
355  *
356  * The output format is as follows:<BR>
357  * <TABLE>
358  * <TR><TD>Column</TD>          <TD>Value</TD>                 <TD>Description</TD></TR>
359  * <TR><TD>1</TD>               <TD>GetLabel()</TD>            <TD>The label of the element.</TD></TR>
360  * <TR><TD>2</TD>               <TD>\f$M\f$</TD>               <TD>motor torque</TD></TR>
361  * <TR><TD>3</TD>               <TD>\f$\dot{M}\f$</TD>         <TD>derivative of the motor torque versus time</TD></TR>
362  * <TR><TD>4</TD>               <TD>\f$\ddot{M}\f$</TD>        <TD>second derivative of the motor torque versus time</TD></TR>
363  * <TR><TD>5</TD>               <TD>\f$\dot{\varphi}\f$</TD>         <TD>the angular velocity of the rotor node with respect to the stator node</TD></TR>
364  * <TR><TD>6</TD>               <TD>\f$\ddot{\varphi}\f$</TD>   <TD>the angular acceleration of the rotor node with respect to the stator node</TD></TR>
365  * <TR><TD>7</TD>               <TD>\f$\Omega_S\f$</TD>        <TD>the synchronous angular velocity specified by the a drive</TD></TR>
366  * <TR><TD>8</TD>               <TD>\f$\gamma\f$</TD>          <TD>a flag which specifies if the motor is powered on</TD></TR>
367  * </TABLE>
368  */
369 
370 void
Output(OutputHandler & OH) const371 asynchronous_machine::Output(OutputHandler& OH) const
372 {
373 	if (bToBeOutput()) {
374 		// Note: The ODE for the asynchronous machine is defined for positive sign of OmegaS only!
375 		// At user level the signs of M, dM/dt and d^2M/dt^2 are defined to be positive
376 		// in positive coordinate system direction in the reference frame of the stator.
377 		doublereal OmegaS = m_OmegaS.dGet();
378 		doublereal M = m_M*copysign(1., OmegaS);
379 		doublereal dM_dt = m_dM_dt*copysign(1., OmegaS);
380 		doublereal dM_dt2 = m_dM_dt2*copysign(1., OmegaS);
381 
382 		if (OH.UseText(OutputHandler::LOADABLE)) {
383 			// output the current state: the column layout is as follows
384 
385 			// 1 2     3      4     5         6      7
386 			// M dM_dt dM_dt2 omega domega_dt OmegaS gamma
387 			//
388 			// 0	label of the element
389 			// 1	motor torque
390 			// 2	derivative of the motor torque versus time
391 			// 3	second derivative of the motor torque versus time
392 			// 4	angular velocity of the rotor node relative to the stator node around axis 3
393 			// 5	angular acceleration of the rotor node relative to the stator node around axis 3
394 			// 6	synchronous angular velocity (might be a function of the time in case of an frequency inverter)
395 			// 7	power supply is turned on or off
396 
397 			OH.Loadable() << GetLabel()
398 				<< " " << M
399 				<< " " << dM_dt
400 				<< " " << dM_dt2
401 				<< " " << m_omega
402 				<< " " << m_domega_dt
403 				<< " " << OmegaS
404 				<< " " << IsMotorOn()
405 				<< std::endl;
406 		}
407 	}
408 }
409 
410 /**
411  * @param piNumRows pointer to a variable which receives the number of rows of the contribution to the residual vector and the jacobian matrix
412  * @param piNumCols pointer to a variable which receives the number of columns of the contribution to the jacobian matrix
413  * \brief The size of the contribution to the residual and the jacobian matrix is determined by this member function.
414  */
415 void
WorkSpaceDim(integer * piNumRows,integer * piNumCols) const416 asynchronous_machine::WorkSpaceDim(integer* piNumRows, integer* piNumCols) const
417 {
418 	*piNumRows = 9;
419 	*piNumCols = 9;
420 }
421 
422 /**
423  * @return Returns the number of private degrees of freedom. In our case \f$M\f$, \f$\dot{M}\f$ and \f$\dot{\varphi}\f$ are private degrees of freedom only accessible to this element.
424  * \brief The number of private degrees of freedom is determined by this member function.
425  */
426 unsigned int
iGetNumDof(void) const427 asynchronous_machine::iGetNumDof(void) const
428 {
429 	return 3;
430 }
431 
432 /**
433  * @param i zero based index of the private degree of freedom (zero for the first private degree of freedom, one for second ...)
434  * @return Returns if the private degrees of freedom specified by i refer to differential or algebraic variables. In our case all private degrees of freedom are differential variables.
435  * \brief The type of the equation of the private degrees of freedom is determined by this member function.
436  */
437 DofOrder::Order
GetDofType(unsigned int i) const438 asynchronous_machine::GetDofType(unsigned int i) const
439 {
440 
441 	return DofOrder::DIFFERENTIAL;
442 }
443 
444 /**
445  * @param out output stream that receives the formated output
446  * @param prefix should be output in the first column
447  *
448  * \brief This member function is called if the statement "print: dof description;" is specified in the input file.
449  *
450  * It prints a short description of the private degrees of freedom \f$M\f$, \f$\dot{M}\f$ and \f$\dot{\varphi}\f$.
451  */
452 std::ostream&
DescribeDof(std::ostream & out,const char * prefix,bool bInitial) const453 asynchronous_machine::DescribeDof(std::ostream& out, const char *prefix, bool bInitial) const
454 {
455 	integer iIndex = iGetFirstIndex();
456 
457 	out
458 		<< prefix << iIndex + 1 << ": motor torque derivative [MP]" << std::endl
459 		<< prefix << iIndex + 2 << ": motor torque [M]" << std::endl
460 		<< prefix << iIndex + 3 << ": relative angular velocity [omega]" << std::endl;
461 
462 	return out;
463 }
464 
465 /**
466  * @param out output stream that receives the formated output
467  * @param prefix should be output in the first column
468  *
469  * \brief This member function is called if the statement "print: equation description;" is specified in the input file.
470  *
471  * It prints a short description of the residual of the private degrees of freedom \f$f_1\f$, \f$f_2\f$ and \f$f_9\f$.
472  */
473 std::ostream&
DescribeEq(std::ostream & out,const char * prefix,bool bInitial) const474 asynchronous_machine::DescribeEq(std::ostream& out, const char *prefix, bool bInitial) const
475 {
476 	integer iIndex = iGetFirstIndex();
477 
478 	out
479 		<< prefix << iIndex + 1 << ": motor DAE [f1]" << std::endl
480 		<< prefix << iIndex + 2 << ": motor torque derivative [f2]" << std::endl
481 		<< prefix << iIndex + 3 << ": angular velocity derivative [f9]" << std::endl;
482 
483 	return out;
484 }
485 
486 /**
487  * @return Returns the number of private data available for this element.
488  * \brief This member function is called when a bind statement or a element drive is used to access private data of this element.
489  *
490  * In our case \f$M\f$, \f$\dot{M}\f$, \f$\ddot{M}\f$, \f$\dot{\varphi}\f$ and \f$\ddot{\varphi}\f$ are available.
491  */
492 unsigned int
iGetNumPrivData(void) const493 asynchronous_machine::iGetNumPrivData(void) const
494 {
495 	return 5;
496 }
497 
498 /**
499  * @param s specifies the name of the private data to be accessed by a bind statement or an element drive caller.
500  * @return returns the one based index of the private data.
501  * \brief The following string values can be specified in a bind statement or in an element drive caller.
502  *
503  * <TABLE>
504  * <TR><TD>index</TD>   <TD>string</TD>  <TD>variable</TD></TR>
505  * <TR><TD>1</TD>       <TD>M</TD>       <TD>\f$M\f$</TD></TR>
506  * <TR><TD>2</TD>       <TD>MP</TD>      <TD>\f$\dot{M}\f$</TD></TR>
507  * <TR><TD>3</TD>       <TD>MPP</TD>     <TD>\f$\ddot{M}\f$</TD></TR>
508  * <TR><TD>4</TD>       <TD>omega</TD>   <TD>\f$\dot{\varphi}\f$</TD></TR>
509  * <TR><TD>5</TD>       <TD>omegaP</TD>  <TD>\f$\ddot{\varphi}\f$</TD></TR>
510  * </TABLE>
511  */
512 unsigned int
iGetPrivDataIdx(const char * s) const513 asynchronous_machine::iGetPrivDataIdx(const char *s) const
514 {
515 	static const struct {
516 		int index;
517 		char name[7];
518 	}
519 
520 	data[] = {
521 			{ 1, "M" },		// motor torque
522 			{ 2, "MP"},		// derivative of the motor torque versus time diff(M,t) (named MP instead of dM_dt for compatibility with other elements)
523 			{ 3, "MPP"},	// second derivative of the motor torque  versus time diff(M,t,2) (named MPP instead of dM_dt2 for compatibility with other elements)
524 			{ 4, "omega"},	// angular velocity of the rotor node relative to the stator node
525 			{ 5, "omegaP"}	// angular acceleration of the rotor node relative to the stator node (named omegaP instead of domega_dt for compatibility with other elements)
526 	};
527 
528 	for (unsigned i = 0; i < sizeof(data) / sizeof(data[0]); ++i ) {
529 		if (0 == strcmp(data[i].name,s)) {
530 			return data[i].index;
531 		}
532 	}
533 
534 	silent_cerr("asynchronous_machine(" << GetLabel() << "): no private data \"" << s << "\"" << std::endl);
535 
536 	return 0;
537 }
538 
539 /**
540  * @param i the one based index of the private data
541  * @return returns the value of the private data addressed by i.
542  * \brief This member function is called whenever a bind statement or a element drive is used to access private data.
543  */
544 doublereal
dGetPrivData(unsigned int i) const545 asynchronous_machine::dGetPrivData(unsigned int i) const
546 {
547 	// Note: The ODE for the asynchronous machine is defined for positive sign of OmegaS only!
548 	// At user level the signs of M, dM/dt and d^2M/dt^2 are defined to be positive
549 	// in positive coordinate system direction in the reference frame of the stator.
550 
551 	const doublereal OmegaS = m_OmegaS.dGet();
552 
553 	switch (i) {
554 	case 1:
555 		return m_M*copysign(1., OmegaS);
556 	case 2:
557 		return m_dM_dt*copysign(1., OmegaS);
558 	case 3:
559 		return m_dM_dt2*copysign(1., OmegaS);
560 	case 4:
561 		return m_omega;
562 	case 5:
563 		return m_domega_dt;
564 	}
565 
566 	throw DataManager::ErrGeneric(MBDYN_EXCEPT_ARGS);
567 }
568 
569 /**
570  * See iGetInitialNumDof().
571  */
572 void
SetInitialValue(VectorHandler & X)573 asynchronous_machine::SetInitialValue(VectorHandler& X)
574 {
575 	return;
576 }
577 
578 /**
579  * @param WorkMat sparse or full submatrix which receives the contribution to the jacobian matrix \f$Jac\f$
580  * @param dCoef the derivative coefficient is defined as \f$\Delta\boldsymbol{y} = dCoef \, \Delta\dot{\boldsymbol{y}}\f$
581  * @param XCurr the vector of the global degrees of freedom \f$\boldsymbol{y}\f$ at the current iteration step
582  * @param XPrimeCurr the vector of the derivative of the global degrees of freedom \f$\dot{\boldsymbol{y}}\f$<BR>
583  * \brief Computes the contribution to the jacobian matrix \f$\boldsymbol{Jac}\f$.<BR>
584  *
585  * \f[\boldsymbol{Jac}=-\frac{\partial\boldsymbol{f}}{\partial\dot{\boldsymbol{y}}}-dCoef\,\frac{\partial\boldsymbol{f}}{\partial\boldsymbol{y}}\f]<BR>
586  * For the definition of \f$\boldsymbol{f}\f$ see AssRes().<BR>
587  * \f[\frac{\partial\boldsymbol{f}}{\partial\boldsymbol{y}}=\left(\begin{array}{ccccc} \frac{\partial f_{1}}{\partial y_{1}} & \frac{\partial f_{1}}{\partial y_{2}} & \frac{\partial f_{1}}{\partial\boldsymbol{y}_{3}} & \frac{\partial f_{1}}{\partial\boldsymbol{y}_{4}} & \frac{\partial f_{1}}{\partial y_{5}}\\ \frac{\partial f_{2}}{\partial y_{1}} & \frac{\partial f_{2}}{\partial y_{2}} & \frac{\partial f_{2}}{\partial\boldsymbol{y}_{3}} & \frac{\partial f_{2}}{\partial\boldsymbol{y}_{4}} & \frac{\partial f_{2}}{\partial y_{5}}\\ \frac{\partial\boldsymbol{f}_{3}}{\partial y_{1}} & \frac{\partial\boldsymbol{f}_{3}}{\partial y_{2}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{y}_{3}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{y}_{4}} & \frac{\partial\boldsymbol{f}_{3}}{\partial y_{5}}\\ \frac{\partial\boldsymbol{f}_{4}}{\partial y_{1}} & \frac{\partial\boldsymbol{f}_{4}}{\partial y_{2}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{y}_{3}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{y}_{4}} & \frac{\partial\boldsymbol{f}_{4}}{\partial y_{5}}\\ \frac{\partial f_{5}}{\partial y_{1}} & \frac{\partial f_{5}}{\partial y_{2}} & \frac{\partial f_{5}}{\partial\boldsymbol{y}_{3}} & \frac{\partial f_{5}}{\partial\boldsymbol{y}_{4}} & \frac{\partial f_{5}}{\partial y_{5}}\end{array}\right)\f]<BR>
588  * \f[\frac{\partial\boldsymbol{f}}{\partial\boldsymbol{\dot{y}}}=\left(\begin{array}{ccccc} \frac{\partial f_{1}}{\partial\dot{y}_{1}} & \frac{\partial f_{1}}{\partial\dot{y}_{2}} & \frac{\partial f_{1}}{\partial\boldsymbol{\dot{y}}_{3}} & \frac{\partial f_{1}}{\partial\boldsymbol{\dot{y}}_{4}} & \frac{\partial f_{1}}{\partial\dot{y}_{5}}\\ \frac{\partial f_{2}}{\partial\dot{y}_{1}} & \frac{\partial f_{2}}{\partial\dot{y}_{2}} & \frac{\partial f_{2}}{\partial\boldsymbol{\dot{y}}_{3}} & \frac{\partial f_{2}}{\partial\boldsymbol{\dot{y}}_{4}} & \frac{\partial f_{2}}{\partial\dot{y}_{5}}\\ \frac{\partial\boldsymbol{f}_{3}}{\partial\dot{y}_{1}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\dot{y}_{2}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{\dot{y}}_{3}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{\dot{y}}_{4}} & \frac{\partial\boldsymbol{f}_{3}}{\partial\dot{y}_{5}}\\ \frac{\partial\boldsymbol{f}_{4}}{\partial\dot{y}_{1}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\dot{y}_{2}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{\dot{y}}_{3}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{\dot{y}}_{4}} & \frac{\partial\boldsymbol{f}_{4}}{\partial\dot{y}_{5}}\\ \frac{\partial f_{5}}{\partial\dot{y}_{1}} & \frac{\partial f_{5}}{\partial\dot{y}_{2}} & \frac{\partial f_{5}}{\partial\dot{\boldsymbol{y}}_{3}} & \frac{\partial f_{5}}{\partial\boldsymbol{\dot{y}}_{4}} & \frac{\partial f_{5}}{\partial\dot{y}_{5}}\end{array}\right)\f]<BR>
589  * \f[\frac{\partial f_{1}}{\partial y_{1}}=\left(2\, s_{K}\,+\frac{sign\left(\Omega_{S}\right)\,\dot{y}_{5}}{s\,\Omega_{S}^{2}}\right)\,\left|\Omega_{S}\right|\f]
590  * \f[\frac{\partial f_{1}}{\partial\dot{y}_{1}}=1\f]
591  * \f[\frac{\partial f_{1}}{\partial y_{2}}=\left(s_{K}^{2}+s^{2}\right)\,\Omega_{S}^{2}+\frac{sign\left(\Omega_{S}\right)\,\dot{y}_{5}\, sK}{s}\f]
592  * \f[\frac{\partial f_{1}}{\partial y_{5}}=\frac{\partial f_{1}}{\partial s}\,\frac{\partial s}{\partial y_{5}}\f]
593  * \f[\frac{\partial f_{1}}{\partial s}=-\frac{y_{1}\,\dot{y}_{5}}{s^{2}\,\Omega_{S}}+\left(2\, s\,\Omega_{S}^{2}-\frac{sign\left(\Omega_{S}\right)\:\dot{y}_{5}\, s_{K}}{s^{2}}\right)\, y_{2}-2\, M_{K}\, s_{K}\,\Omega_{S}^{2}\f]
594  * \f[\frac{\partial s}{\partial y_{5}}=-\frac{1}{\Omega_{S}}\f]
595  * \f[\frac{\partial f_{1}}{\partial\dot{y}_{5}}=\frac{y_{1}}{s\,\Omega_{S}}+sign\left(\Omega_{S}\right)\,\frac{s_{K}}{s}\, y_{2}\f]
596  * \f[\frac{\partial f_{2}}{\partial y_{1}}=-1\f]
597  * \f[\frac{\partial f_{2}}{\partial\dot{y}_{2}}=1\f]
598  * \f[\frac{\partial f_{3}}{\partial y_{2}}=\boldsymbol{R}_{2}\,\boldsymbol{e}_{3}\, sign\left(\Omega_{S}\right)\f]
599  * \f[\frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{y}_{4}}=-\left\langle \boldsymbol{R}_{2}^{\left(0\right)}\,\boldsymbol{e}_{3}\, y_{2}\, sign\left(\Omega_{S}\right)\right\rangle \f]
600  * \f[\frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{y}_{2}}=-\frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{y}_{2}}\f]
601  * \f[\frac{\partial\boldsymbol{f}_{4}}{\partial\boldsymbol{y}_{4}}=-\frac{\partial\boldsymbol{f}_{3}}{\partial\boldsymbol{y}_{4}}\f]
602  * \f[\frac{\partial f_{5}}{\partial\boldsymbol{y}_{3}}=\boldsymbol{e}_{3}^{T}\,\boldsymbol{R}_{2}^{T}\,\left\langle \boldsymbol{\omega}_{ref_{1}}\right\rangle =-\left(\left\langle \boldsymbol{\omega}_{ref_{1}}\right\rangle \,\boldsymbol{R}_{2}\,\boldsymbol{e}_{3}\right)^{T}\f]
603  * \f[\frac{\partial f_{5}}{\partial\boldsymbol{y}_{4}}=-\boldsymbol{e}_{3}^{T}\,\boldsymbol{R}_{2}^{\left(0\right)^{T}}\,\left\langle \boldsymbol{\omega}_{1}-\boldsymbol{\omega}_{2}\right\rangle -\boldsymbol{e}_{3}^{T}\,\boldsymbol{R}_{2}^{T}\,\left\langle \boldsymbol{\omega}_{ref_{2}}\right\rangle =\left[\left\langle \boldsymbol{\omega}_{1}-\boldsymbol{\omega}_{2}\right\rangle \,\boldsymbol{R}_{2}^{\left(0\right)}\,\boldsymbol{e}_{3}+\left\langle \boldsymbol{\omega}_{ref_{2}}\right\rangle \,\boldsymbol{R}_{2}\,\boldsymbol{e}_{3}\right]^{T}\f]
604  * In order to compute the derivatives versus the global degrees of freedom of structural nodes the following rules can be applied:
605  * \f[\frac{\partial \left(\boldsymbol{R}_1\,\boldsymbol{v}_1\right)}{\partial \boldsymbol{g}_1}\approx-\left\langle \boldsymbol{R}^{\left(0\right)}\,\boldsymbol{v} \right\rangle \f]<BR>
606  * \f[\frac{\partial \left(\boldsymbol{R}_1^{T}\,\boldsymbol{v}\right)}{\partial \boldsymbol{g}_1}\approx\boldsymbol{R}_1^{\left(0\right)T}\,\left\langle\boldsymbol{v}\right\rangle\f]
607  * \f[\frac{\partial\boldsymbol{\omega}_1}{\partial\dot{\boldsymbol{g}}_1}\approx\boldsymbol{I}\f]
608  * \f[\frac{\partial\boldsymbol{\omega}_1}{\partial\boldsymbol{g}_1}\approx-\left\langle\boldsymbol{\omega}_{1_{ref}}\right\rangle\f]
609  * \f$\boldsymbol{R}_1\f$ is the corrected rotation matrix of node 1 at the current iteration which can be obtained by GetRCurr().<BR>
610  * \f$\boldsymbol{R}^{(0)}_1\f$ is the predicted rotation matrix of node 1 at the current time step which can be obtained by GetRRef().<BR>
611  * \f$\boldsymbol{g}_1\f$ is the vector of Gibbs Rodriguez rotation parameters of node 1.<BR>
612  * The rotation matrix \f$\boldsymbol{R}_1\f$ is internally computed as follows:<BR>
613  * \f[\boldsymbol{R}_1=\boldsymbol{R}_{1_{\Delta}}\,\boldsymbol{R}^{(0)}_1\f]
614  * \f$\boldsymbol{R}_{1_{\Delta}}\f$ is the incremental rotation matrix which can be computed by means of the Gibbs Rodriguez rotation parameters \f$\boldsymbol{g}_1\f$.<BR>
615  * \f[\boldsymbol{R}_{1_{\Delta}}=\boldsymbol{I}+\frac{4}{4+\boldsymbol{g}_1^T\,\boldsymbol{g}_1}\,\left(\left\langle\boldsymbol{g}\right\rangle+\frac{1}{2}\,\left\langle\boldsymbol{g}_1\right\rangle\,\left\langle\boldsymbol{g}_1\right\rangle\right)\f]
616  * \f$\boldsymbol{\omega}_1\f$ is the corrected angular velocity of node 1 at the current iteration which can be obtained by GetWCurr().<BR>
617  * \f[\left\langle\boldsymbol{\omega}_1\right\rangle=\dot{\boldsymbol{R}}_1\,\boldsymbol{R}_1^T\f]
618  * \f[\boldsymbol{\omega}_1\approx\dot{\boldsymbol{g}}_1+\boldsymbol{R}_{1_{\Delta}}\,\boldsymbol{\omega}_{1_{ref}}\f]
619  * \f$\boldsymbol{\omega}_{1_{ref}}\f$ is the predicted angular velocity of node 1 at the current time step which can be obtained by GetWRef().<BR>
620  * \f[\left\langle\boldsymbol{\omega}_{1_{ref}}\right\rangle=\dot{\boldsymbol{R}}_1^{(0)}\,\boldsymbol{R}_1^{(0)T}\f]
621  * \f$\boldsymbol{v}\f$ is a const vector in the reference frame \f$\boldsymbol{R}_1\f$.<BR>
622  * \f[\boldsymbol{v}=\left(\begin{array}{c} v_{1}\\ v_{2}\\ v_{3}\end{array}\right)\f]
623  * \f$\left\langle \boldsymbol{v}\right\rangle\f$ is a matrix that rearranges the components of \f$\boldsymbol{v}\f$ according to the cross product rule:<BR>
624  * \f$\left\langle\boldsymbol{v}\right\rangle\,\boldsymbol{a}=-\left\langle\boldsymbol{a}\right\rangle\,\boldsymbol{v}=\boldsymbol{v}\times\boldsymbol{a}=-\boldsymbol{a}\times\boldsymbol{v}\f$<BR>
625  * \f[\left\langle \boldsymbol{v} \right\rangle=\left(\begin{array}{ccc} 0 & -v_{3} & v_{2}\\ v_{3} & 0 & -v_{1}\\ -v_{2} & v_{1} & 0\end{array}\right)\f]
626  */
627 VariableSubMatrixHandler&
AssJac(VariableSubMatrixHandler & WorkMat_,doublereal dCoef,const VectorHandler & XCurr,const VectorHandler & XPrimeCurr)628 asynchronous_machine::AssJac(VariableSubMatrixHandler& WorkMat_,
629 	doublereal dCoef,
630 	const VectorHandler& XCurr,
631 	const VectorHandler& XPrimeCurr)
632 {
633 #ifdef DEBUG
634 	std::cerr << __FILE__ << ':' << __LINE__ << ':' << __PRETTY_FUNCTION__ << std::endl;
635 #endif
636 
637 	integer iNumRows = 0;
638 	integer iNumCols = 0;
639 
640 	WorkSpaceDim(&iNumRows, &iNumCols);
641 
642 	FullSubMatrixHandler& WorkMat = WorkMat_.SetFull();
643 
644 	WorkMat.ResizeReset(iNumRows, iNumCols);
645 
646 	const integer iFirstIndex = iGetFirstIndex();
647 
648 	const integer intTorqueDerivativeColumnIndex = iFirstIndex + 1;
649 	const integer intTorqueColumnIndex 			 = iFirstIndex + 2;
650 	const integer intOmegaColumnIndex 			 = iFirstIndex + 3;
651 
652 	const integer intTorqueDerivativeRowIndex 	 = iFirstIndex + 1;
653 	const integer intTorqueRowIndex 			 = iFirstIndex + 2;
654 	const integer intOmegaRowIndex 				 = iFirstIndex + 3;
655 
656 	const integer intRotorPositionIndex  = m_pRotorNode->iGetFirstPositionIndex();
657 	const integer intStatorPositionIndex = m_pStatorNode->iGetFirstPositionIndex();
658 
659 	const integer intRotorMomentumIndex  = m_pRotorNode->iGetFirstMomentumIndex();
660 	const integer intStatorMomentumIndex = m_pStatorNode->iGetFirstMomentumIndex();
661 
662 	/*
663 	 * M 		 ... motor torque
664 	 * diff(M,t) ... derivative of the motor torque versus time
665 	 * g1		 ... rotation parameter of the rotor
666 	 * g2		 ... rotation parameter of the stator
667 	 * R2 		 ... rotation matrix of the stator node
668 	 * omega1    ... rotor angular velocity
669 	 * omega2 	 ... stator angular velocity
670 	 *
671 	 * e3 = ( 0, 0, 1 )^T ... axis of rotation in the reference frame of the stator node
672 	 *
673 	 * omega = e3^T * R2^T * ( omega1 - omega2 )
674 	 *
675 	 * s = 1 - omega / OmegaS
676 	 *
677 	 * 		| y1 |	 | diff(M,t) |
678 	 * 		| y2 |	 | M 		 |
679 	 * 	y =	| y3 | = | g1		 |
680 	 * 		| y4 |	 | g2 		 |
681 	 * 		| y5 |   | omega     |
682 	 *
683 	 * 		| f1 |   |  diff(y1,t) + ( 2 * sK + sign(OmegaS) * diff(y5,t) / ( s * OmegaS^2 ) ) * y1 * abs(OmegaS) + ( ( sK^2 + s^2 ) * OmegaS^2 + sign(OmegaS) * diff(y5,t) * sK / s ) * y2 - 2 * MK * sK * s * OmegaS^2 |
684 	 * 		| f2 |   |  diff(y2,t) - y1																																				    		   	   					 |																																				    		   |
685 	 * 	f =	| f3 | = |  R2 * e3 * y2																																				    		   	   					 |
686 	 * 		| f4 |   | -R2 * e3 * y2																																				    		       					 |
687 	 * 		| f5 |   |  y5 - e3^T * R2^T * ( omega1 - omega2 )																																	       					 |
688 	 *
689 	 * 					      1,       2,       3,       6,    	  9
690 	 * 		          | df1/dy1, df1/dy2, df1/dy3, df1/dy4, df1/dy5 |  1
691 	 * 				  | df2/dy1, df2/dy2, df2/dy3, df2/dy4, df2/dy5 |  2
692 	 * 	diff(f,y) =   | df3/dy1, df3/dy2, df3/dy3, df3/dy4, df3/dy5 |  3
693 	 * 		          | df4/dy1, df4/dy2, df4/dy3, df4/dy4, df4/dy5 |  6
694 	 * 		          | df5/dy1, df5/dy2, df5/dy3, df5/dy4, df5/dy5 |  9
695 	 *
696 	 * WorkMat = -diff(f,diff(y,t)) - dCoef * diff(f,y)
697 	 */
698 	WorkMat.PutRowIndex(1, intTorqueDerivativeRowIndex);
699 	WorkMat.PutColIndex(1, intTorqueDerivativeColumnIndex);
700 
701 	WorkMat.PutRowIndex(2, intTorqueRowIndex);
702 	WorkMat.PutColIndex(2, intTorqueColumnIndex);
703 
704 	for (int iCnt = 1; iCnt <= 3; ++iCnt) {
705 		WorkMat.PutRowIndex(iCnt + 2, intRotorMomentumIndex + iCnt + 3);
706 		WorkMat.PutColIndex(iCnt + 2, intRotorPositionIndex + iCnt + 3);
707 
708 		WorkMat.PutRowIndex(iCnt + 5, intStatorMomentumIndex + iCnt + 3);
709 		WorkMat.PutColIndex(iCnt + 5, intStatorPositionIndex + iCnt + 3);
710 	}
711 
712 	WorkMat.PutRowIndex(9, intOmegaRowIndex);
713 	WorkMat.PutColIndex(9, intOmegaColumnIndex);
714 
715 	Vec3 e3 = m_pStatorNode->GetRCurr().GetCol(3);	// corrected axis of rotation of the stator node
716 	Vec3 e3_0 = m_pStatorNode->GetRRef().GetCol(3);	// predicted axis of rotation of the stator node
717 	const Vec3& omega1 = m_pRotorNode->GetWCurr();	// corrected angular velocity of the rotor node
718 	const Vec3& omega2 = m_pStatorNode->GetWCurr(); // corrected angular velocity of the rotor node
719 	const Vec3& omega1_ref = m_pRotorNode->GetWRef(); // predicted angular velocity of the rotor node
720 	const Vec3& omega2_ref = m_pStatorNode->GetWRef(); // predicted angular velocity of the stator node
721 
722 	const doublereal OmegaS = m_OmegaS.dGet(); // synchronous angular velocity
723 
724 	const doublereal y1 		= XCurr(intTorqueDerivativeRowIndex);
725 	const doublereal y2 		= XCurr(intTorqueRowIndex);
726 	const doublereal y5 		= XCurr(intOmegaRowIndex);
727 #if 0 // unused
728 	const doublereal y1_dot 	= XPrimeCurr(intTorqueDerivativeRowIndex);
729 	const doublereal y2_dot 	= XPrimeCurr(intTorqueRowIndex);
730 #endif
731 	const doublereal y5_dot 	= XPrimeCurr(intOmegaRowIndex);
732 
733 	doublereal s = 1 - y5 / OmegaS; // slip of the rotor
734 
735     if ( std::abs(s) < sm_SingTol )
736     {
737         silent_cerr("\nasynchronous_machine(" << GetLabel() << "): warning slip rate s = " << s << " is close to machine precision!\n");
738         //FIXME: avoid division by zero
739         //FIXME: results might be inaccurate
740         s = copysign(sm_SingTol, s);
741     }
742 
743 	doublereal df1_dy1, df1_dy2, df1_dy5;
744 	doublereal df1_dy1_dot, df1_dy2_dot, df1_dy5_dot;
745 	doublereal df2_dy1, df2_dy2_dot;
746 
747 	if (IsMotorOn()) {
748 		// power supply is turned on
749 
750 		// df1_dy1 = diff(f1,y1)
751 		df1_dy1 = ( 2 * m_sK + copysign(1., OmegaS) * y5_dot / ( s * std::pow(OmegaS,2) ) ) * abs(OmegaS);
752 		// df1_dy2 = diff(f1,y2)
753 		df1_dy2 = ( std::pow(m_sK,2) + std::pow(s,2) ) * std::pow(OmegaS,2) + copysign(1., OmegaS) * y5_dot * m_sK / s;
754 		// df1_dy1_dot = diff(f1,diff(y1,t))
755 		df1_dy1_dot = 1.;
756 		// df2_dy1 = diff(f2,dy1)
757 		df2_dy1 = -1.;
758 		// df2_dy2_dot = diff(f2,diff(y2,t))
759 		df2_dy2_dot = 1.;
760 		// df1_ds = diff(f1,s)
761 		const doublereal df1_ds = -y1 * y5_dot / ( std::pow(s,2) * OmegaS ) + ( 2 * s * std::pow(OmegaS,2) - copysign(1., OmegaS) * y5_dot * m_sK / std::pow(s,2) ) * y2 - 2 * m_MK * m_sK * std::pow(OmegaS,2);
762 		// df1_dy5 = diff(f1,y5) = diff(f1,s) * diff(s,y5)
763 		df1_dy5 = df1_ds * ( -1. / OmegaS );
764 		// df1_dy5_dot = diff(f1,diff(y5,t))
765 		df1_dy5_dot = y1 / ( s * OmegaS ) + copysign(1., OmegaS) * y2 * m_sK / s;
766 
767 	} else {
768 		// power supply is turned off
769 		df1_dy1 = 0.;
770 		df1_dy2 = 1.;
771 		df1_dy5 = 0.;
772 		df1_dy1_dot = 0.;
773 		df1_dy2_dot = 0.;
774 		df1_dy5_dot = 0.;
775 
776 		df2_dy1 = 1.;
777 		df2_dy2_dot = 0;
778 	}
779 
780 	// df3_dy2 = diff(f3,y2)
781 	const Vec3 df3_dy2 = e3 * copysign(1., OmegaS); // df3_dy2 = R2 * e3 * sign(OmegaS)
782 	// df4_dy2 = diff(f4,y2)
783 	const Vec3 df4_dy2 = -df3_dy2; // df4_dy2 = -R2 * e3
784 	// df3_dy4 = diff(f3,y4)
785 	const Mat3x3 df3_dy4 = -Mat3x3(MatCross, e3_0 * (y2 * copysign(1., OmegaS)) ); // df3_dy4 = -skew( R2^(0) * e3 * y2 * sign(OmegaS) )
786 	// df4_dy4 = diff(f4,y4)
787 	const Mat3x3 df4_dy4 = -df3_dy4;
788 	// df5_dy5 = diff(f5,y5)
789 	const doublereal df5_dy5 = 1.;
790 	// df5_dy3_dot_T = transpose(diff(f5,diff(y3,t)))
791 	const Vec3 df5_dy3_dot_T = -e3; // diff(y5,diff(y3,t)) = -e3^T * R2^T
792 	// df5_dy4_dot_T = transpose(diff(f5,diff(y4,t)))
793 	const Vec3 df5_dy4_dot_T = -df5_dy3_dot_T;  // diff(y5,diff(y4,t)) = e3^T * R2^T
794 	// df5_dy3_T = transpose(diff(y5,y3))
795 	const Vec3 df5_dy3_T = -omega1_ref.Cross(e3);
796 	// df5_dy4_T = transpose(diff(y5,y4))
797 	const Vec3 df5_dy4_T = ( omega1 - omega2 ).Cross( e3_0 ) + omega2_ref.Cross( e3 );
798 
799 	WorkMat.PutCoef( 1, 1,  -df1_dy1_dot   - dCoef * df1_dy1 );
800 	WorkMat.PutCoef( 1, 2, 				   - dCoef * df1_dy2 );
801 	WorkMat.PutCoef( 1, 9,  -df1_dy5_dot   - dCoef * df1_dy5 );
802 	WorkMat.PutCoef( 2, 1, 				   - dCoef * df2_dy1 );
803 	WorkMat.PutCoef( 2, 2,  -df2_dy2_dot 						    );
804 	WorkMat.Put(     3, 2,				   	 df3_dy2   * ( -dCoef ) );
805 	WorkMat.Put(     3, 6,				   	 df3_dy4   * ( -dCoef ) );
806 	WorkMat.Put(     6, 2,                 	 df4_dy2   * ( -dCoef ) );
807 	WorkMat.Put(     6, 6,                 	 df4_dy4   * ( -dCoef ) );
808 	WorkMat.PutT(    9, 3,	-df5_dy3_dot_T + df5_dy3_T * ( -dCoef ) );
809 	WorkMat.PutT(    9, 6,  -df5_dy4_dot_T + df5_dy4_T * ( -dCoef ) );
810 	WorkMat.PutCoef( 9, 9,				     df5_dy5   * ( -dCoef ) );
811 
812 #ifdef DEBUG
813 	std::cerr << __FILE__ ":" << __LINE__ << ":" << __PRETTY_FUNCTION__ << std::endl;
814 	std::cerr << "s = " << s << std::endl;
815 	std::cerr << "WorkMat=" << std::endl
816 		 << WorkMat << std::endl << std::endl;
817 #endif
818 
819 	return WorkMat_;
820 }
821 
822 /**
823  * @param WorkVec subvector which receives the contribution to the residual \f$\boldsymbol{f}\f$
824  * @param dCoef the derivative coefficient is defined as \f$\Delta\boldsymbol{y} = dCoef \, \Delta\dot{\boldsymbol{y}}\f$
825  * @param XCurr the vector of the global degrees of freedom \f$\boldsymbol{y}\f$ at the current iteration step
826  * @param XPrimeCurr the vector of the derivative of the global degrees of freedom \f$\dot{\boldsymbol{y}}\f$<BR>
827  * \brief This member function implements the equation of an asynchronous machine according to
828  * <A HREF="http://books.google.at/books?id=z6B3lcxDgkkC&printsec=frontcover&dq=Maschinendynamik++%26+dresig&hl=de&ei=SwvETYi8NJSw4Ab_luX3BA&sa=X&oi=book_result&ct=result&resnum=1&ved=0CDIQ6AEwAA#v=onepage&q&f=false">
829  * Maschinendynamik Springer 2009, Dresig, Holzweißig.
830  * </A><BR>
831  *
832  * \f[\ddot{M}+\left(2\, s_{K}+\frac{sign\left(\Omega_{S}\right)\,\ddot{\varphi}}{s\,\Omega_{S}^{2}}\right)\,\dot{M\,}\left|\Omega_{s}\right|+\left[\left(s_{K}^{2}+s^{2}\right)\,\Omega_{S}^{2}+\frac{sign\left(\Omega_{S}\right)\,\ddot{\varphi}\, s_{K}}{s}\right]\, M=2\, M_{K}\, s_{K}\, s\,\Omega_{S}^{2}\f]<BR>
833  * The term \f$sign\left(\Omega_{S}\right)\f$ and the absolute operator in \f$\left|\Omega_{s}\right|\f$ are modifications of the original formula since the formula in the literature does not handle negative synchronous angular velocities \f$\Omega_{S}\f$.
834  * If the synchronous angular velocity \f$\Omega_{S}\f$ has a negative value, the sense of rotation is assumed to be negative.<BR>
835  * \f$M\f$ is motor torque<BR>
836  * \f$M_{K}\f$ is the breakdown torque<BR>
837  * \f$s_{K}\f$ is the breakdown slip<BR>
838  * \f$\Omega_{S}\f$ is the synchronous angular velocity of the machine<BR>
839  * \f$\Omega_{S}=2\,\pi\frac{f}{p}\f$<BR>
840  * \f$f\f$ is the power frequency<BR>
841  * \f$p\f$ is the number of terminal pairs<BR>
842  * \f$s\f$ is the actual slip<BR>
843  * \f$s=1-\frac{\dot{\varphi}}{\Omega_{S}}\f$<BR>
844  * \f$\dot{\varphi}\f$ is the relative angular velocity of the rotor with respect to the stator<BR>
845  * The axis of rotation is assumed to be axis 3 in the reference frame of the stator node.<BR>
846  * \f$\dot{\varphi}=\boldsymbol{e}_{3}^{T}\cdot\boldsymbol{R}_{2}^{T}\cdot\left(\boldsymbol{\omega}_{1}-\boldsymbol{\omega}_{2}\right)\f$<BR>
847  * \f$\boldsymbol{\omega}_{1}\f$ is the angular velocity of the rotor node in the global reference frame which can be obtained by GetWCurr()<BR>
848  * \f$\boldsymbol{\omega}_{2}\f$ is the angular velocity of the stator node in the global reference frame<BR>
849  * \f$\boldsymbol{R}_{2}\f$ is the rotation matrix of the stator node which can be obtained by GetRCurr()<BR>
850  * The subvector of the global degrees of freedom XCurr used by this element is<BR>
851  * \f[\boldsymbol{y}=\left(\begin{array}{c} y_{1}\\ y_{2}\\ \boldsymbol{y}_{3}\\ \boldsymbol{y}_{4}\\ y_{5}\end{array}\right)=\left(\begin{array}{c}\dot{M}\\M\\ \boldsymbol{g}_{1}\\ \boldsymbol{g}_{2}\\ \dot{\varphi}\end{array}\right)\f]<BR>
852  * \f$\boldsymbol{g}_{1}\f$ is the vector of rotation parameters of the rotor node.<BR>
853  * \f$\boldsymbol{g}_{2}\f$ is the vector of the rotation parameters of the stator node.<BR>
854  * The index of \f$\boldsymbol{g}_1\f$ and \f$\boldsymbol{g}_2\f$ can be obtained by iGetFirstMomentumIndex() + 4 ... 6.<BR>
855  * The index of \f$y_1\f$, \f$y_2\f$ and \f$y_5\f$ in the vector of the global degrees of freedom XCurr can be obtained by iGetFirstIndex() + 1 ... 3.<BR>
856  * The subvector of the derivatives of the global degrees of freedom used by this element is<BR>
857  * \f[\dot{\boldsymbol{y}}=\left(\begin{array}{c} \dot{y_{1}}\\ \dot{y_{2}}\\ \dot{\boldsymbol{y}_{3}}\\ \dot{\boldsymbol{y}_{4}}\\ \dot{y_{5}}\end{array}\right)=\left(\begin{array}{c} \ddot{M}\\ \dot{M}\\ \dot{\boldsymbol{g}_{1}}\\ \dot{\boldsymbol{g}_{2}}\\ \ddot{\varphi}\end{array}\right)\f]<BR>
858  * The additional degree of freedom \f$y_{5}=\dot{\varphi}\f$ is needed since MBDyn does not provide the angular acceleration of a node during the nonlinear solution phase.
859  * The value returned by GetWPCurr() is updated only after convergence and can not be used for this reason.
860  * The contribution to the residual of this element is<BR>
861  * \f[\boldsymbol{f}=\left(\begin{array}{c} f_{1}\\ f_{2}\\ \boldsymbol{f}_{3}\\ \boldsymbol{f}_{4}\\ f_{5}\end{array}\right)\f]<BR>
862  * \f[f_{1}=\dot{y}_{1}+\left(2\, s_{K}+\frac{sign\left(\Omega_{S}\right)\,\dot{y}_{5}}{s\,\Omega_{S}^{2}}\right)\, y_{1}\,\left|\Omega_{S}\right|+\left[\left(s_{K}^{2}+s^{2}\right)\,\Omega_{S}^{2}+\frac{sign\left(\Omega_{S}\right)\,\dot{y}_{5}\, s_{K}}{s}\right]\, y_{2}-2\, M_{K}\, s_{K}\, s\,\Omega_{S}^{2}\f]<BR>
863  * \f[f_{2}=\dot{y}_{2}-y_{1}\f]<BR>
864  * \f$\boldsymbol{f}_3\f$ is the torque imposed to the rotor node.<BR>
865  * \f[\boldsymbol{f}_{3}=\boldsymbol{R}_{2}\,\boldsymbol{e}_{3}\, y_{2}\, sign\left(\Omega_{S}\right)\f]<BR>
866  * \f$\boldsymbol{f}_{4}\f$ is the torque imposed to the stator node.<BR>
867  * \f[\boldsymbol{f}_{4}=-\boldsymbol{f}_{3}\f]<BR>
868  * \f[f_{5}=y_{5}-\boldsymbol{e}_{3}^{T}\,\boldsymbol{R}_{2}^{T}\,\left(\boldsymbol{\omega}_{1}-\boldsymbol{\omega}_{2}\right)\f]<BR>
869  */
870 
871 SubVectorHandler&
AssRes(SubVectorHandler & WorkVec,doublereal dCoef,const VectorHandler & XCurr,const VectorHandler & XPrimeCurr)872 asynchronous_machine::AssRes(SubVectorHandler& WorkVec,
873 	doublereal dCoef,
874 	const VectorHandler& XCurr,
875 	const VectorHandler& XPrimeCurr)
876 {
877 #ifdef DEBUG
878 	std::cerr << __FILE__ << ':' << __LINE__ << ':' << __PRETTY_FUNCTION__ << std::endl;
879 #endif
880 	integer iNumRows = 0;
881 	integer iNumCols = 0;
882 
883 	WorkSpaceDim(&iNumRows, &iNumCols);
884 
885 	WorkVec.ResizeReset(iNumRows);
886 
887 	const integer iFirstIndex = iGetFirstIndex();
888 
889 	const integer intTorqueDerivativeRowIndex 	 = iFirstIndex + 1;
890 	const integer intTorqueRowIndex 			 = iFirstIndex + 2;
891 	const integer intOmegaRowIndex 				 = iFirstIndex + 3;
892 
893 	const integer intRotorMomentumIndex  = m_pRotorNode->iGetFirstMomentumIndex();
894 	const integer intStatorMomentumIndex = m_pStatorNode->iGetFirstMomentumIndex();
895 
896 	WorkVec.PutRowIndex(1, intTorqueDerivativeRowIndex);
897 	WorkVec.PutRowIndex(2, intTorqueRowIndex);
898 
899 	for (int iCnt = 1; iCnt <= 3; ++iCnt) {
900 		WorkVec.PutRowIndex(iCnt + 2, intRotorMomentumIndex + iCnt + 3);
901 		WorkVec.PutRowIndex(iCnt + 5, intStatorMomentumIndex + iCnt + 3);
902 	}
903 
904 	WorkVec.PutRowIndex(9, intOmegaRowIndex);
905 
906 	Vec3 e3 = m_pStatorNode->GetRCurr().GetCol(3);
907 	const Vec3& omega1 = m_pRotorNode->GetWCurr();
908 	const Vec3& omega2 = m_pStatorNode->GetWCurr();
909 
910 	const doublereal OmegaS = m_OmegaS.dGet();
911 
912 	const doublereal y1 		= XCurr(intTorqueDerivativeRowIndex); 				// y1 = diff(M,t)
913 	const doublereal y2 		= XCurr(intTorqueRowIndex);			  				// y2 = M
914 	const doublereal y5 		= XCurr(intOmegaRowIndex);							// y5 = omega
915 	const doublereal y1_dot 	= XPrimeCurr(intTorqueDerivativeRowIndex); 			// diff(y1,t) = diff(M,t,2)
916 	const doublereal y2_dot 	= XPrimeCurr(intTorqueRowIndex);					// diff(y2,t) = diff(M,t)
917 	const doublereal y5_dot 	= XPrimeCurr(intOmegaRowIndex); 					// diff(y5,t) = diff(omega,t)
918 
919 	doublereal s = 1 - y5 / OmegaS;
920 
921     if ( std::abs(s) < sm_SingTol )
922     {
923         silent_cerr("\nasynchronous_machine(" << GetLabel() << "): warning slip rate s = " << s << " is close to machine precision!\n");
924         //FIXME: avoid division by zero
925         //FIXME: results might be inaccurate
926         s = copysign(sm_SingTol, s);
927     }
928 
929 	doublereal f1, f2;
930 
931 	if (IsMotorOn()) {
932 		// power supply is switched on
933 		f1 = y1_dot + ( 2 * m_sK + copysign(1., OmegaS) * y5_dot / ( s * std::pow(OmegaS,2) ) ) * y1 * abs(OmegaS)
934 			+ ( ( std::pow(m_sK,2) + std::pow(s,2) ) * std::pow(OmegaS,2) + copysign(1., OmegaS) * y5_dot * m_sK / s ) * y2 - 2 * m_MK * m_sK * s * std::pow(OmegaS,2);
935 		f2 = y2_dot - y1;
936 
937 	} else {
938 		// power supply is switched off: torque must be zero
939 		f1 = y2;
940 		f2 = y1;
941 	}
942 
943 	const Vec3 f3 = e3 * (y2 * copysign(1., OmegaS));
944 	// const Vec3 f4 = -f3;
945 
946 	const doublereal f5 = y5 - e3.Dot( omega1 - omega2 );
947 
948 	WorkVec.PutCoef( 1, f1 );
949 	WorkVec.PutCoef( 2, f2 );
950 	WorkVec.Put(     3,	f3 );
951 	WorkVec.Sub(     6, f3 );
952 	WorkVec.PutCoef( 9, f5 );
953 
954 #ifdef DEBUG
955 	std::cerr
956 		<< __FILE__ ":" << __LINE__ << ":" << __PRETTY_FUNCTION__ << std::endl
957 		<< "s = " << s << std::endl
958 		<< "y1 = " << y1 << std::endl
959 		<< "y1_dot = " << y1_dot << std::endl
960 		<< "y2 = " << y2 << std::endl
961 		<< "y2_dot = " << y2_dot << std::endl
962 		<< "f1 = " << f1 << std::endl
963 		<< "f2 = " << f2 << std::endl
964 		<< "f3 = " << f3 << std::endl
965 		<< "f4 = " << -f3 << std::endl
966 		<< "f5 = " << f5 << std::endl
967 		<< "WorkVec=" << std::endl
968 		<< WorkVec << std::endl << std::endl;
969 #endif
970 
971 	return WorkVec;
972 }
973 /**
974  * @return returns the number of connected nodes.
975  * \brief This member function is called if the statement "print: node connection;" is specified in the input file.
976  * */
977 int
iGetNumConnectedNodes(void) const978 asynchronous_machine::iGetNumConnectedNodes(void) const
979 {
980 	return 2;
981 }
982 
983 /**
984  * @param connectedNodes vector which receives pointers to the connected nodes
985  * \brief This member function is called if the statement "print: node connection;" is specified in the input file.
986  */
987 void
GetConnectedNodes(std::vector<const Node * > & connectedNodes) const988 asynchronous_machine::GetConnectedNodes(std::vector<const Node *>& connectedNodes) const
989 {
990 	connectedNodes.resize(iGetNumConnectedNodes());
991 	connectedNodes[0] = m_pRotorNode;
992 	connectedNodes[1] = m_pStatorNode;
993 }
994 
995 /**
996  * @param X vector of global degrees of freedom \f$\boldsymbol{y}\f$ that receives the initial values provided by this element.
997  * @param XP vector of the derivative of the global degrees of freedom \f$\dot{\boldsymbol{y}}\f$
998  * \brief This member function is called before the integration starts in order to set the initial values for the private degrees of freedom.
999  *
1000  * In our case the initial values for \f$M\f$, \f$\dot{M}\f$ and \f$\dot{\varphi}\f$ are set in this routine to the values specified in the input file.
1001  */
1002 void
SetValue(DataManager * pDM,VectorHandler & X,VectorHandler & XP,SimulationEntity::Hints * ph)1003 asynchronous_machine::SetValue(DataManager *pDM,
1004 	VectorHandler& X, VectorHandler& XP,
1005 	SimulationEntity::Hints *ph)
1006 {
1007 	const integer intFirstIndex = iGetFirstIndex();
1008 
1009 	//                             1        2        3
1010 	X.Put( intFirstIndex + 1, Vec3(m_dM_dt, m_M,     m_omega) );
1011 	XP.Put(intFirstIndex + 1, Vec3(     0., m_dM_dt,      0.) );
1012 }
1013 
1014 /**
1015  * @param out stream of the restart file
1016  * \brief This member function is called if "make restart file;" is specified in the input file.
1017  *
1018  * It should reproduce the syntax in the input file used to create this element.
1019  */
1020 std::ostream&
Restart(std::ostream & out) const1021 asynchronous_machine::Restart(std::ostream& out) const
1022 {
1023 	// FIXME: mbdyn crashes when "make restart file;" is specified in the input file before this member function is invoked!
1024 	out << "asynchronous_machine, "
1025 		"rotor, " << m_pRotorNode->GetLabel() << ", "
1026 		"stator, " << m_pStatorNode->GetLabel() << ", "
1027 		"MK, " << m_MK << ", "
1028 		"sK, " << m_sK << ", "
1029 		"OmegaS, ";
1030 		m_OmegaS.pGetDriveCaller()->Restart(out) << ", "
1031 		"motor on, ";
1032 		m_MotorOn.pGetDriveCaller()->Restart(out) << ", "
1033 		"M0, " << m_M * copysign(1., m_OmegaS.dGet()) << ", "
1034 		"MP0, " << m_dM_dt * copysign(1., m_OmegaS.dGet())  << ";" << std::endl;
1035 
1036 	return out;
1037 }
1038 
1039 /**
1040  * \brief This member function must be implemented only if the initial assembly feature is requested.
1041  *
1042  * The initial assembly phase is needed only if initial values are provided which are not consistent with the algebraic constraints.
1043  * Since this element does not use algebraic constraints we do not have to implement
1044  * iGetInitialNumDof(), InitialWorkSpaceDim(), InitialAssJac(), InitialAssRes() and SetInitialValue().
1045  */
1046 unsigned int
iGetInitialNumDof(void) const1047 asynchronous_machine::iGetInitialNumDof(void) const
1048 {
1049 	return 0;
1050 }
1051 
1052 /**
1053  * See iGetInitialNumDof().
1054  */
1055 void
InitialWorkSpaceDim(integer * piNumRows,integer * piNumCols) const1056 asynchronous_machine::InitialWorkSpaceDim(
1057 	integer* piNumRows,
1058 	integer* piNumCols) const
1059 {
1060 	*piNumRows = 0;
1061 	*piNumCols = 0;
1062 }
1063 
1064 /**
1065  * See iGetInitialNumDof().
1066  */
1067 VariableSubMatrixHandler&
InitialAssJac(VariableSubMatrixHandler & WorkMat,const VectorHandler & XCurr)1068 asynchronous_machine::InitialAssJac(
1069 	VariableSubMatrixHandler& WorkMat,
1070 	const VectorHandler& XCurr)
1071 {
1072 	WorkMat.SetNullMatrix();
1073 
1074 	return WorkMat;
1075 }
1076 
1077 /**
1078  * See iGetInitialNumDof().
1079  */
1080 SubVectorHandler&
InitialAssRes(SubVectorHandler & WorkVec,const VectorHandler & XCurr)1081 asynchronous_machine::InitialAssRes(
1082 	SubVectorHandler& WorkVec,
1083 	const VectorHandler& XCurr)
1084 {
1085 	WorkVec.ResizeReset(0);
1086 
1087 	return WorkVec;
1088 }
1089 
1090 bool
asynchronous_machine_set(void)1091 asynchronous_machine_set(void)
1092 {
1093 #ifdef DEBUG
1094 	std::cerr << __FILE__ <<":"<< __LINE__ << ":"<< __PRETTY_FUNCTION__ << std::endl;
1095 #endif
1096 
1097 	UserDefinedElemRead *rf = new UDERead<asynchronous_machine>;
1098 
1099 	if (!SetUDE("asynchronous_machine", rf)) {
1100 		delete rf;
1101 		return false;
1102 	}
1103 
1104 	return true;
1105 }
1106 
1107 #ifndef STATIC_MODULES
1108 
1109 extern "C"
1110 {
1111 
1112 /**
1113  * \brief This function registers our user defined element for the math parser.
1114  *
1115  * It is called when the "module load" statement appears in the input file.
1116  */
1117 int
module_init(const char * module_name,void * pdm,void * php)1118 module_init(const char *module_name, void *pdm, void *php)
1119 {
1120 	if (!asynchronous_machine_set()) {
1121 		silent_cerr("asynchronous_machine: "
1122 			"module_init(" << module_name << ") "
1123 			"failed" << std::endl);
1124 		return -1;
1125 	}
1126 
1127 	return 0;
1128 }
1129 
1130 } // extern "C"
1131 
1132 #endif // ! STATIC_MODULES
1133 
1134