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