]> git.mxchange.org Git - flightgear.git/blob - src/Systems/electrical.cxx
Fix line endings
[flightgear.git] / src / Systems / electrical.cxx
1 // electrical.cxx - a flexible, generic electrical system model.
2 //
3 // Written by Curtis Olson, started September 2002.
4 //
5 // Copyright (C) 2002  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <simgear/structure/exception.hxx>
25 #include <simgear/misc/sg_path.hxx>
26
27 #include <Main/fg_props.hxx>
28 #include <Main/globals.hxx>
29
30 #include "electrical.hxx"
31
32
33 FGElectricalComponent::FGElectricalComponent() :
34     kind(-1),
35     name(""),
36     volts(0.0),
37     load_amps(0.0)
38 {
39 }
40
41
42 FGElectricalSupplier::FGElectricalSupplier ( SGPropertyNode *node ) {
43     kind = FG_SUPPLIER;
44
45     // cout << "Creating a supplier" << endl;
46     name = node->getStringValue("name");
47     string _model = node->getStringValue("kind");
48     // cout << "_model = " << _model << endl;
49     if ( _model == "battery" ) {
50         model = FG_BATTERY;
51         amp_hours = node->getFloatValue("amp-hours", 40.0);
52         percent_remaining = node->getFloatValue("percent-remaining", 1.0);
53         charge_amps = node->getFloatValue("charge-amps", 7.0);
54     } else if ( _model == "alternator" ) {
55         model = FG_ALTERNATOR;
56         rpm_src = node->getStringValue("rpm-source");
57         rpm_threshold = node->getFloatValue("rpm-threshold", 600.0);
58         ideal_amps = node->getFloatValue("amps", 60.0);
59     } else if ( _model == "external" ) {
60         model = FG_EXTERNAL;
61         ideal_amps = node->getFloatValue("amps", 60.0);
62     } else {
63         model = FG_UNKNOWN;
64     }
65     ideal_volts = node->getFloatValue("volts");
66
67     int i;
68     for ( i = 0; i < node->nChildren(); ++i ) {
69         SGPropertyNode *child = node->getChild(i);
70         // cout << " scanning: " << child->getName() << endl;
71         if ( !strcmp(child->getName(), "prop") ) {
72             string prop = child->getStringValue();
73             // cout << "  Adding prop = " << prop << endl;
74             add_prop( prop );
75             fgSetFloat( prop.c_str(), ideal_amps );
76         }
77     }
78
79     _rpm_node = fgGetNode( rpm_src.c_str(), true);
80 }  
81
82
83 float FGElectricalSupplier::apply_load( float amps, float dt ) {
84     if ( model == FG_BATTERY ) {
85         // calculate amp hours used
86         float amphrs_used = amps * dt / 3600.0;
87
88         // calculate percent of total available capacity
89         float percent_used = amphrs_used / amp_hours;
90         percent_remaining -= percent_used;
91         if ( percent_remaining < 0.0 ) {
92             percent_remaining = 0.0;
93         } else if ( percent_remaining > 1.0 ) {
94             percent_remaining = 1.0;
95         }
96         // cout << "battery percent = " << percent_remaining << endl;
97         return amp_hours * percent_remaining;
98     } else if ( model == FG_ALTERNATOR ) {
99         // scale alternator output for rpms < 600.  For rpms >= 600
100         // give full output.  This is just a WAG, and probably not how
101         // it really works but I'm keeping things "simple" to start.
102         float rpm = _rpm_node->getFloatValue();
103         float factor = rpm / rpm_threshold;
104         if ( factor > 1.0 ) {
105             factor = 1.0;
106         }
107         // cout << "alternator amps = " << amps * factor << endl;
108         float available_amps = ideal_amps * factor;
109         return available_amps - amps;
110     } else if ( model == FG_EXTERNAL ) {
111         // cout << "external amps = " << 0.0 << endl;
112         float available_amps = ideal_amps;
113         return available_amps - amps;
114     } else {
115         SG_LOG( SG_ALL, SG_ALERT, "unknown supplier type" );
116     }
117
118     return 0.0;
119 }
120
121
122 float FGElectricalSupplier::get_output_volts() {
123     if ( model == FG_BATTERY ) {
124         // cout << "battery amps = " << amps << endl;
125         float x = 1.0 - percent_remaining;
126         float tmp = -(3.0 * x - 1.0);
127         float factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
128         // cout << "battery % = " << percent_remaining <<
129         //         " factor = " << factor << endl;
130         // percent_remaining -= 0.001;
131         return ideal_volts * factor;
132     } else if ( model == FG_ALTERNATOR ) {
133         // scale alternator output for rpms < 600.  For rpms >= 600
134         // give full output.  This is just a WAG, and probably not how
135         // it really works but I'm keeping things "simple" to start.
136         float rpm = _rpm_node->getFloatValue();
137         float factor = rpm / rpm_threshold;
138         if ( factor > 1.0 ) {
139             factor = 1.0;
140         }
141         // cout << "alternator amps = " << amps * factor << endl;
142         return ideal_volts * factor;
143     } else if ( model == FG_EXTERNAL ) {
144         // cout << "external amps = " << 0.0 << endl;
145         return ideal_volts;
146     } else {
147         SG_LOG( SG_ALL, SG_ALERT, "unknown supplier type" );
148     }
149
150     return 0.0;
151 }
152
153
154 float FGElectricalSupplier::get_output_amps() {
155     if ( model == FG_BATTERY ) {
156         // cout << "battery amp_hours = " << amp_hours << endl;
157
158         // This is a WAG, but produce enough amps to burn the entire
159         // battery in one minute.
160         return amp_hours * 60.0;
161     } else if ( model == FG_ALTERNATOR ) {
162         // scale alternator output for rpms < 600.  For rpms >= 600
163         // give full output.  This is just a WAG, and probably not how
164         // it really works but I'm keeping things "simple" to start.
165         float rpm = _rpm_node->getFloatValue();
166         float factor = rpm / rpm_threshold;
167         if ( factor > 1.0 ) {
168             factor = 1.0;
169         }
170         // cout << "alternator amps = " << ideal_amps * factor << endl;
171         return ideal_amps * factor;
172     } else if ( model == FG_EXTERNAL ) {
173         // cout << "external amps = " << 0.0 << endl;
174         return ideal_amps;
175     } else {
176         SG_LOG( SG_ALL, SG_ALERT, "unknown supplier type" );
177     }
178
179     return 0.0;
180 }
181
182
183 FGElectricalBus::FGElectricalBus ( SGPropertyNode *node ) {
184     kind = FG_BUS;
185
186     name = node->getStringValue("name");
187     int i;
188     for ( i = 0; i < node->nChildren(); ++i ) {
189         SGPropertyNode *child = node->getChild(i);
190         if ( !strcmp(child->getName(), "prop") ) {
191             string prop = child->getStringValue();
192             add_prop( prop );
193         }
194     }
195 }  
196
197
198 FGElectricalOutput::FGElectricalOutput ( SGPropertyNode *node ) {
199     kind = FG_OUTPUT;
200     load_amps = 0.1;            // arbitrary default value
201
202     name = node->getStringValue("name");
203     SGPropertyNode *draw = node->getNode("rated-draw");
204     if ( draw != NULL ) {
205         load_amps = draw->getFloatValue();
206     }
207     // cout << "rated draw = " << output_amps << endl;
208
209     int i;
210     for ( i = 0; i < node->nChildren(); ++i ) {
211         SGPropertyNode *child = node->getChild(i);
212         if ( !strcmp(child->getName(), "prop") ) {
213             string prop = child->getStringValue();
214             add_prop( prop );
215         }
216     }
217 }  
218
219
220 FGElectricalSwitch::FGElectricalSwitch( SGPropertyNode *node ) :
221     switch_node( NULL ),
222     rating_amps( 0.0f ),
223     circuit_breaker( false )
224 {
225     bool initial_state = true;
226     int i;
227     for ( i = 0; i < node->nChildren(); ++i ) {
228         SGPropertyNode *child = node->getChild(i);
229         string cname = child->getName();
230         string cval = child->getStringValue();
231         if ( cname == "prop" ) {
232             switch_node = fgGetNode( cval.c_str(), true );
233             // cout << "switch node = " << cval << endl;
234         } else if ( cname == "initial-state" ) {
235             if ( cval == "off" || cval == "false" ) {
236                 initial_state = false;
237             }
238             // cout << "initial state = " << initial_state << endl;
239         } else if ( cname == "rating-amps" ) {
240             rating_amps = atof( cval.c_str() );
241             circuit_breaker = true;
242             // cout << "initial state = " << initial_state << endl;
243         }            
244     }
245
246     switch_node->setBoolValue( initial_state );
247     // cout << "  value = " << switch_node->getBoolValue() << endl;
248 }
249
250
251 FGElectricalConnector::FGElectricalConnector ( SGPropertyNode *node,
252                                                FGElectricalSystem *es ) {
253     kind = FG_CONNECTOR;
254     name = "connector";
255     int i;
256     for ( i = 0; i < node->nChildren(); ++i ) {
257         SGPropertyNode *child = node->getChild(i);
258         string cname = child->getName();
259         string cval = child->getStringValue();
260         // cout << "  " << cname << " = " << cval << endl;
261         if ( cname == "input" ) {
262             FGElectricalComponent *s = es->find( child->getStringValue() );
263             if ( s != NULL ) {
264                 add_input( s );
265                 if ( s->get_kind() == FG_SUPPLIER ) {
266                     s->add_output( this );
267                 } else if ( s->get_kind() == FG_BUS ) {
268                     s->add_output( this );
269                 } else {
270                     SG_LOG( SG_ALL, SG_ALERT,
271                             "Attempt to connect to something that can't provide an output: " 
272                             << child->getStringValue() );
273                 }
274             } else {
275                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
276                         << child->getStringValue() );
277             }
278         } else if ( cname == "output" ) {
279             FGElectricalComponent *s = es->find( child->getStringValue() );
280             if ( s != NULL ) {
281                 add_output( s );
282                 if ( s->get_kind() == FG_BUS ) {
283                     s->add_input( this );
284                 } else if ( s->get_kind() == FG_OUTPUT ) {
285                     s->add_input( this );
286                 } else if ( s->get_kind() == FG_SUPPLIER &&
287                             ((FGElectricalSupplier *)s)->get_model()
288                             == FGElectricalSupplier::FG_BATTERY ) {
289                     s->add_output( this );
290                 } else {
291                     SG_LOG( SG_ALL, SG_ALERT,
292                             "Attempt to connect to something that can't provide an input: " 
293                             << child->getStringValue() );
294                 }
295             } else {
296                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
297                         << child->getStringValue() );
298             }
299         } else if ( cname == "switch" ) {
300              // cout << "Switch = " << child->getStringValue() << endl;
301             FGElectricalSwitch s( child );
302             add_switch( s );
303         }
304     }
305 }  
306
307
308 // set all switches to the specified state
309 void FGElectricalConnector::set_switches( bool state ) {
310     // cout << "setting switch state to " << state << endl;
311     for ( unsigned int i = 0; i < switches.size(); ++i ) {
312         switches[i].set_state( state );
313     }
314 }
315
316
317 // return true if all switches are true, false otherwise.  A connector
318 // could have multiple switches, but they all need to be true(closed)
319 // for current to get through.
320 bool FGElectricalConnector::get_state() {
321     unsigned int i;
322     for ( i = 0; i < switches.size(); ++i ) {
323         if ( ! switches[i].get_state() ) {
324             return false;
325         }
326     }
327
328     return true;
329 }
330
331
332 FGElectricalSystem::FGElectricalSystem ( SGPropertyNode *node ) :
333     name("electrical"),
334     num(0),
335     path(""),
336     enabled(false)
337 {
338     int i;
339     for ( i = 0; i < node->nChildren(); ++i ) {
340         SGPropertyNode *child = node->getChild(i);
341         string cname = child->getName();
342         string cval = child->getStringValue();
343         if ( cname == "name" ) {
344             name = cval;
345         } else if ( cname == "number" ) {
346             num = child->getIntValue();
347         } else if ( cname == "path" ) {
348             path = cval;
349         } else {
350             SG_LOG( SG_SYSTEMS, SG_WARN,
351                     "Error in electrical system config logic" );
352             if ( name.length() ) {
353                 SG_LOG( SG_SYSTEMS, SG_WARN, "Section = " << name );
354             }
355         }
356     }
357 }
358
359
360 FGElectricalSystem::~FGElectricalSystem () {
361 }
362
363
364 void FGElectricalSystem::init () {
365     config_props = new SGPropertyNode;
366
367     _volts_out = fgGetNode( "/systems/electrical/volts", true );
368     _amps_out = fgGetNode( "/systems/electrical/amps", true );
369
370     // allow the electrical system to be specified via the
371     // aircraft-set.xml file (for backwards compatibility) or through
372     // the aircraft-systems.xml file.  If a -set.xml entry is
373     // specified, that overrides the system entry.
374     SGPropertyNode *path_n = fgGetNode("/sim/systems/electrical/path");
375     if ( path_n ) {
376         if ( path.length() ) {
377             SG_LOG( SG_ALL, SG_INFO,
378                     "NOTICE: System manager configuration specifies an " <<
379                     "electrical system: " << path << " but it is " <<
380                     "being overridden by the one specified in the -set.xml " <<
381                     "file: " << path_n->getStringValue() );
382         }
383
384         path = path_n->getStringValue();
385     }
386
387     if ( path.length() ) {
388         SGPath config( globals->get_fg_root() );
389         config.append( path );
390
391         // load an obsolete xml configuration
392         SG_LOG( SG_ALL, SG_ALERT,
393                 "Reading xml electrical system model from "
394                 << config.str() );
395         try {
396             readProperties( config.str(), config_props );
397
398             if ( build() ) {
399                 enabled = true;
400             } else {
401                 SG_LOG( SG_ALL, SG_ALERT,
402                         "Detected a logic error in the electrical system ");
403                 SG_LOG( SG_ALL, SG_ALERT,
404                         "specification file.  See earlier errors for " );
405                 SG_LOG( SG_ALL, SG_ALERT,
406                         "details.");
407                 exit(-1);
408             }        
409         } catch (const sg_exception& exc) {
410             SG_LOG( SG_ALL, SG_ALERT,
411                     "Failed to load electrical system model: "
412                     << config.str() );
413         }
414     } else {
415         SG_LOG( SG_ALL, SG_INFO,
416                 "No xml-based electrical model specified for this model!");
417     }
418
419     if ( !enabled ) {
420         _amps_out->setDoubleValue(0);
421     }
422
423     delete config_props;
424 }
425
426
427 void FGElectricalSystem::bind () {
428 }
429
430
431 void FGElectricalSystem::unbind () {
432 }
433
434
435 void FGElectricalSystem::update (double dt) {
436     if ( !enabled ) {
437         return;
438     }
439
440     // cout << "Updating electrical system, dt = " << dt << endl;
441
442     unsigned int i;
443
444     // zero out the voltage before we start, but don't clear the
445     // requested load values.
446     for ( i = 0; i < suppliers.size(); ++i ) {
447         suppliers[i]->set_volts( 0.0 );
448     }
449     for ( i = 0; i < buses.size(); ++i ) {
450         buses[i]->set_volts( 0.0 );
451     }
452     for ( i = 0; i < outputs.size(); ++i ) {
453         outputs[i]->set_volts( 0.0 );
454     }
455     for ( i = 0; i < connectors.size(); ++i ) {
456         connectors[i]->set_volts( 0.0 );
457     }
458
459     // for each "external" supplier, propagate the electrical current
460     for ( i = 0; i < suppliers.size(); ++i ) {
461         FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
462         if ( node->get_model() == FGElectricalSupplier::FG_EXTERNAL ) {
463             float load;
464             // cout << "Starting propagation: " << suppliers[i]->get_name()
465             //      << endl;
466             load = propagate( suppliers[i], dt,
467                               node->get_output_volts(),
468                               node->get_output_amps(),
469                               " " );
470
471             if ( node->apply_load( load, dt ) < 0.0 ) {
472                 cout << "Error drawing more current than available!" << endl;
473             }
474         }     
475     }
476
477     // for each "alternator" supplier, propagate the electrical
478     // current
479     for ( i = 0; i < suppliers.size(); ++i ) {
480         FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
481         if ( node->get_model() == FGElectricalSupplier::FG_ALTERNATOR) {
482             float load;
483             // cout << "Starting propagation: " << suppliers[i]->get_name()
484             //      << endl;
485             load = propagate( suppliers[i], dt,
486                               node->get_output_volts(),
487                               node->get_output_amps(),
488                               " " );
489
490             if ( node->apply_load( load, dt ) < 0.0 ) {
491                 cout << "Error drawing more current than available!" << endl;
492             }
493         }     
494     }
495
496     // for each "battery" supplier, propagate the electrical
497     // current
498     for ( i = 0; i < suppliers.size(); ++i ) {
499         FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
500         if ( node->get_model() == FGElectricalSupplier::FG_BATTERY ) {
501             float load;
502             // cout << "Starting propagation: " << suppliers[i]->get_name()
503             //      << endl;
504             load = propagate( suppliers[i], dt,
505                               node->get_output_volts(),
506                               node->get_output_amps(),
507                               " " );
508             // cout << "battery load = " << load << endl;
509
510             if ( node->apply_load( load, dt ) < 0.0 ) {
511                 cout << "Error drawing more current than available!" << endl;
512             }
513         }     
514     }
515
516     float alt_norm
517         = fgGetFloat("/systems/electrical/suppliers/alternator") / 60.0;
518
519     // impliment an extremely simplistic voltage model (assumes
520     // certain naming conventions in electrical system config)
521     // FIXME: we probably want to be able to feed power from all
522     // engines if they are running and the master-alt is switched on
523     float volts = 0.0;
524     if ( fgGetBool("/controls/engines/engine[0]/master-bat") ) {
525         volts = 24.0;
526     }
527     if ( fgGetBool("/controls/engines/engine[0]/master-alt") ) {
528         if ( fgGetFloat("/engines/engine[0]/rpm") > 800 ) {
529             float alt_contrib = 28.0;
530             if ( alt_contrib > volts ) {
531                 volts = alt_contrib;
532             }
533         } else if ( fgGetFloat("/engines/engine[0]/rpm") > 200 ) {
534             float alt_contrib = 20.0;
535             if ( alt_contrib > volts ) {
536                 volts = alt_contrib;
537             }
538         }
539     }
540     _volts_out->setFloatValue( volts );
541
542     // impliment an extremely simplistic amps model (assumes certain
543     // naming conventions in the electrical system config) ... FIXME:
544     // make this more generic
545     float amps = 0.0;
546     if ( fgGetBool("/controls/engines/engine[0]/master-bat") ) {
547         if ( fgGetBool("/controls/engines/engine[0]/master-alt") &&
548              fgGetFloat("/engines/engine[0]/rpm") > 800 )
549         {
550             amps += 40.0 * alt_norm;
551         }
552         amps -= 15.0;            // normal load
553         if ( fgGetBool("/controls/switches/flashing-beacon") ) {
554             amps -= 7.5;
555         }
556         if ( fgGetBool("/controls/switches/nav-lights") ) {
557             amps -= 7.5;
558         }
559         if ( amps > 7.0 ) {
560             amps = 7.0;
561         }
562     }
563     _amps_out->setFloatValue( amps );
564 }
565
566
567 bool FGElectricalSystem::build () {
568     SGPropertyNode *node;
569     int i;
570
571     int count = config_props->nChildren();
572     for ( i = 0; i < count; ++i ) {
573         node = config_props->getChild(i);
574         string name = node->getName();
575         // cout << name << endl;
576         if ( name == "supplier" ) {
577             FGElectricalSupplier *s =
578                 new FGElectricalSupplier( node );
579             suppliers.push_back( s );
580         } else if ( name == "bus" ) {
581             FGElectricalBus *b =
582                 new FGElectricalBus( node );
583             buses.push_back( b );
584         } else if ( name == "output" ) {
585             FGElectricalOutput *o =
586                 new FGElectricalOutput( node );
587             outputs.push_back( o );
588         } else if ( name == "connector" ) {
589             FGElectricalConnector *c =
590                 new FGElectricalConnector( node, this );
591             connectors.push_back( c );
592         } else {
593             SG_LOG( SG_ALL, SG_ALERT, "Unknown component type specified: " 
594                     << name );
595             return false;
596         }
597     }
598
599     return true;
600 }
601
602
603 // propagate the electrical current through the network, returns the
604 // total current drawn by the children of this node.
605 float FGElectricalSystem::propagate( FGElectricalComponent *node, double dt,
606                                      float input_volts, float input_amps,
607                                      string s ) {
608     s += " ";
609     
610     float total_load = 0.0;
611
612     // determine the current to carry forward
613     float volts = 0.0;
614     if ( !fgGetBool("/systems/electrical/serviceable") ) {
615         volts = 0;
616     } else if ( node->get_kind() == FGElectricalComponent::FG_SUPPLIER ) {
617         // cout << s << "is a supplier (" << node->get_name() << ")" << endl;
618         FGElectricalSupplier *supplier = (FGElectricalSupplier *)node;
619         if ( supplier->get_model() == FGElectricalSupplier::FG_BATTERY ) {
620             // cout << s << " (and is a battery)" << endl;
621             float battery_volts = supplier->get_output_volts();
622             if ( battery_volts < (input_volts - 0.1) ) {
623                 // special handling of a battery charge condition
624                 // cout << s << "  (and is being charged) in v = "
625                 //      << input_volts << " current v = " << battery_volts
626                 //      << endl;
627                 supplier->apply_load( -supplier->get_charge_amps(), dt );
628                 return supplier->get_charge_amps();
629             }
630         }
631         volts = input_volts;
632     } else if ( node->get_kind() == FGElectricalComponent::FG_BUS ) {
633         // cout << s << "is a bus (" << node->get_name() << ")" << endl;
634         volts = input_volts;
635     } else if ( node->get_kind() == FGElectricalComponent::FG_OUTPUT ) {
636         // cout << s << "is an output (" << node->get_name() << ")" << endl;
637         volts = input_volts;
638         if ( volts > 1.0 ) {
639             // draw current if we have voltage
640             total_load = node->get_load_amps();
641         }
642     } else if ( node->get_kind() == FGElectricalComponent::FG_CONNECTOR ) {
643         // cout << s << "is a connector (" << node->get_name() << ")" << endl;
644         if ( ((FGElectricalConnector *)node)->get_state() ) {
645             volts = input_volts;
646         } else {
647             volts = 0.0;
648         }
649         // cout << s << "  input_volts = " << volts << endl;
650     } else {
651         SG_LOG( SG_ALL, SG_ALERT, "unkown node type" );
652     }
653
654     int i;
655
656     // if this node has found a stronger power source, update the
657     // value and propagate to all children
658     if ( volts > node->get_volts() ) {
659         node->set_volts( volts );
660         for ( i = 0; i < node->get_num_outputs(); ++i ) {
661             FGElectricalComponent *child = node->get_output(i);
662             // send current equal to load
663             total_load += propagate( child, dt,
664                                      volts, child->get_load_amps(),
665                                      s );
666         }
667
668         // if not an output node, register the downstream current draw
669         // (sum of all children) with this node.  If volts are zero,
670         // current draw should be zero.
671         if ( node->get_kind() != FGElectricalComponent::FG_OUTPUT ) {
672             node->set_load_amps( total_load );
673         }
674
675         node->set_available_amps( input_amps - total_load );
676
677         // publish values to specified properties
678         for ( i = 0; i < node->get_num_props(); ++i ) {
679             fgSetFloat( node->get_prop(i).c_str(), node->get_volts() );
680         }
681
682         /*
683         cout << s << node->get_name() << " -> (volts) " << node->get_volts()
684              << endl;
685         cout << s << node->get_name() << " -> (load amps) " << total_load
686              << endl;
687         cout << s << node->get_name() << " -> (input amps) " << input_amps
688              << endl;
689         cout << s << node->get_name() << " -> (extra amps) "
690              << node->get_available_amps() << endl;
691         */
692
693         return total_load;
694     } else {
695         // cout << s << "no further propagation" << endl;
696         return 0.0;
697     }
698 }
699
700
701 // search for the named component and return a pointer to it, NULL otherwise
702 FGElectricalComponent *FGElectricalSystem::find ( const string &name ) {
703     unsigned int i;
704     string s;
705
706     // search suppliers
707     for ( i = 0; i < suppliers.size(); ++i ) {
708         s = suppliers[i]->get_name();
709         // cout <<  "    " << s << endl;
710         if ( s == name ) {
711             return suppliers[i];
712         }
713     }
714
715     // then search buses
716     for ( i = 0; i < buses.size(); ++i ) {
717         s = buses[i]->get_name();
718         // cout <<  "    " << s << endl;
719         if ( s == name ) {
720             return buses[i];
721         }
722     }
723
724     // then search outputs
725     for ( i = 0; i < outputs.size(); ++i ) {
726         s = outputs[i]->get_name();
727         // cout <<  "    " << s << endl;
728         if ( s == name ) {
729             return outputs[i];
730         }
731     }
732
733     // nothing found
734     return NULL;
735 }