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