/[drupal]/contributions/modules/carbon/carbon_calculate.inc
ViewVC logotype

Contents of /contributions/modules/carbon/carbon_calculate.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.43 - (show annotations) (download) (as text)
Sun Jul 5 10:47:40 2009 UTC (4 months, 3 weeks ago) by johnackers
Branch: MAIN
CVS Tags: HEAD
Changes since 1.42: +32 -45 lines
File MIME type: text/x-php
bug fix for sharing/adjustment changes in the middle of accounting period
1 <?php
2 /**
3 * $Id: carbon_calculate.inc,v 1.42 2009/07/03 12:21:29 johnackers Exp $
4 *
5 * Drupal carbon module (calculations)
6 *
7 * Created on 18-Oct-2006
8 *
9 * @file
10 * The calculations used to build carbon footprint.
11 *
12 * Different carbon_stamps have a different scope, some declare
13 * emissions on one date, others declare emissions over a period
14 * of time.
15 *
16 * Some sources of emissions e.g. from air travel need special
17 * formulas for calculating resulting CO2 because fuel used in
18 * aircraft takeoff has to be taken into consideration.
19 */
20
21
22 /*
23 * Describes the footprint results for a single account.
24 *
25 * used to pass results information between main functions
26 * including the reporting functions and the CRAG settlement functions.
27 */
28
29 class GroupResult
30 {
31 var $account ; // account
32 var $group ; // group name
33 var $start ;
34 var $end ;
35
36 var $sector_kwh0 = array();
37 var $sector_kwh1 = array() ;
38 var $sector_co2 = array() ;
39 var $grand_kwh0 = 0 ;
40 var $grand_kwh1 = 0 ;
41 var $grand_co2 = 0 ;
42 var $C02_above_target = 0 ;
43 var $emissions = array();
44 }
45
46 /**
47 * Describes a single emission source e.g. a plane trip
48 *
49 */
50
51 class Emission
52 {
53 var $start ; // stamp use for starting point
54 var $end ; // stamp used for end point
55 var $error ;
56 var $notes ; // additional information to present to user
57
58 var $select ; // array of stamps
59 var $co2 ; // co2 emission
60 var $kwh0 ; // energy 0
61 var $kwh1 ; // energy 1
62
63 function Emission($select)
64 {
65 $this->select = $select;
66 }
67 }
68
69
70 class CarbonStamp
71 {
72
73 }
74
75 /**
76 * work out where to place the boundary between years
77 * use months and days from any user preference
78 * otherwise use Jan 1 in year of first stamp
79 *
80 * @param $dateRange first and last enddate omn stamps
81 * @param $firstDate as specified in account settings, year ignored
82 * @param $period as specified in account settings
83 * @return unknown_type
84 */
85 function carbon_calculate_reporting_dates(&$dateRange, $firstDate, $period)
86 {
87 $earliestStampDate = $dateRange['first'];
88 if (!empty($firstDate))
89 {
90 $y = date('Y', $firstDate);
91 $m = date('m', $firstDate);
92 $d = date('d', $firstDate);
93 }
94 else
95 {
96 $y = date('Y',$earliestStampDate);
97 $m = 1 ; // jan
98 $d = 1 ; // 1st
99 }
100 $earliestDate = mktime(0, 0, 0, $m, $d, $y);
101
102 // the purpose of the $maxReports integer is only to prevent
103 // any software bug from printing numerous reports
104
105 $dates = array();
106
107 for ($i = 0 ; $i < ($maxReports=20) ; $i++)
108 {
109 $dates[] = $firstdayinyear = _add_months($earliestDate, $i * $period);
110 $firstdayinyear_s = CarbonDate::asYYYYMMDD($firstdayinyear); // to help debug
111
112
113 // break out when last date reached
114 if ($firstdayinyear >= $dateRange['last'])
115 break ;
116 }
117 return $dates;
118 }
119
120
121
122 /**
123 * Create a set of results for a given period (usually a year)
124 * based on on all the available carbon_stamps. Maintain
125 * sector (e.g. home, tranport) and grand totals.
126 *
127 * @param $account passed to $results but only nid used
128 * @param $stamps array of all carbon_stamps (with associated carbon_source fields)
129 * @param $start first date the generated carbon footprint
130 * @param $end last date of the generated carbon footprint
131 *
132 * @return a complex structure that has the carbon footprint for this period
133 */
134
135
136 function carbon_calculate_total(&$account, &$stamps, $start, $end)
137 {
138 $results = new GroupResult();
139 $results->account = $account ;
140 $results->start = $start ;
141 $results->end = $end ;
142 $results->emissions = _calculate($stamps, $start, $end);
143
144 foreach ($results->emissions as $emission)
145 {
146 $sector = $emission->select[0]->sector ;
147 if (isset($emission->kwh0))
148 {
149 //aacc
150 if (!isset($results->sector_kwh0[$sector]))
151 $results->sector_kwh0[$sector] = 0 ;
152
153 $results->sector_kwh0[$sector] += $emission->kwh0 ;
154 $results->grand_kwh0 += $emission->kwh0 ;
155 }
156 if (isset($emission->kwh1))
157 {
158 //aacc
159 if (!isset($results->sector_kwh1[$sector]))
160 $results->sector_kwh1[$sector] = 0 ;
161
162 $results->sector_kwh1[$sector] += $emission->kwh1 ;
163 $results->grand_kwh1 += $emission->kwh1 ;
164 }
165 if (isset($emission->co2))
166 {
167 if (!isset($results->sector_co2[$sector]))
168 $results->sector_co2[$sector] = 0 ;
169
170 $results->sector_co2[$sector] += $emission->co2 ;
171 $results->grand_co2 += $emission->co2 ;
172 }
173 // need to execute this once.
174 $results->enablekwh0 = $emission->select[0]->enablekwh0 && (0 < strlen(variable_get("carbon_energy_units_0", "")));
175 $results->enablekwh1 = $emission->select[0]->enablekwh1 && (0 < strlen(variable_get("carbon_energy_units_1", "")));
176 $results->enableco2 = $emission->select[0]->enableco2 ;
177 }
178 return $results ;
179 }
180
181
182
183 /**
184 * Aggregate all similar carbon stamps and build an array
185 * of results of CO2 calculations.
186 *
187 * @param $ar array of all carbon_stamps (with associated carbon_source fields)
188 * @param $start first date the generated carbon footprint
189 * @param $end last date of the generated carbon footprint
190 *
191 * @return an array of emissions. Each result contains text and/or value.
192 */
193
194
195 function _calculate(&$ar, $start, $stop)
196 {
197 $dgroup = array();
198 foreach ($ar as $node)
199 {
200 $dgroup[] = _concat_same_value($node);
201 }
202
203 // each key represents a group of readings
204 // that can be considered identical.
205
206 $keys = array_unique($dgroup);
207 $emissions = array();
208 foreach ($keys as $subgroup)
209 {
210 // pull out the first group and
211 // select all the nodes that match that group
212
213 $select = array();
214 $column_notes = "" ;
215 foreach ($ar as $node2)
216 {
217 if (_concat_same_value($node2) != $subgroup)
218 continue ;
219 $select[] = $node2;
220 $node2->total = null ;
221 }
222 $fname = '_scope_'.$select[0]->scope;
223
224 if (function_exists($fname))
225 {
226 $fname($emissions, $select, $start, $stop);
227 }
228 else
229 {
230 drupal_set_message("Invalid or missing function ". $fname, "error");
231 }
232 }
233 return $emissions ;
234 }
235
236 /**
237 * identify stamps that are identical so that they can be
238 * group together.
239 *
240 * meter readings (scope == 1) are handled as a group, so stamps
241 * with a different value should be group together.
242 *
243 * ja 2-jan-2009 use _concat() function
244 */
245
246
247 function _concat_same_value($node)
248 {
249 $hashcode = _concat($node);
250 switch ($node->scope)
251 {
252 case 0 :
253 case 2 :
254 return $hashcode.'.'.$node->reading.'.'.$node->enddate.'.'.$node->startdate;
255 case 1 : return $hashcode ;
256 }
257 }
258
259 /**
260 * identify stamps that are identical that can presented in a group on a form
261 * and the only the field that needs to be changed is enddate and reading.
262 *
263 * ja 2-jan-2009 ignore adjustment and code fields. The side effect is that
264 * the title alone of each meter reading determines which sequence of readings it
265 * belongs to.
266 */
267
268 function _concat($node){
269 return $node->title.'.'.$node->scope.'.'.$node->sourceclass;
270 }
271
272
273 /**
274 * todo check that no ; or : present
275 */
276
277 function _evaluate($expression)
278 {
279 $expression = trim($expression);
280 $exp1 = substr($expression,0,1);
281 if (strcmp($exp1,"=")==0)
282 $expression = substr($expression,1);
283
284 return eval('return '.$expression.';');
285 }
286
287
288
289 function _stamp_map_sorted_by_enddate($ar)
290 {
291 $ar1 = array();
292 foreach ($ar as $node)
293 {
294 $k = $node->enddate;
295 while (isset($ar1[$k]))
296 $k++ ;
297 $ar1[$node->enddate] = $node ;
298 }
299 ksort($ar1);
300 return $ar1;
301 }
302
303
304 function _sort_by_valuedate($ar)
305 {
306 $ar1 = array();
307 foreach ($ar as $node)
308 {
309 $k = $node->valuedate;
310 while (isset($ar1[$k]))
311 $k++ ;
312 $ar1[$node->valuedate] = $node ;
313 }
314 ksort($ar1);
315 return array_values($ar1);
316 }
317
318
319
320
321 /**
322 * There are two scenarios.
323 * 1. the emissions occur on a single date (e.g. air travel)
324 * 2. the emissions are spread over a month , year or more.
325 *
326 * @return false if no stamp falls outside date range
327 */
328
329 function _scope_0(&$emissions, $select, $start, $stop)
330 {
331 $emission = new Emission($select);
332 _duplicate_check($emission->select);
333
334 $stamp = $emission->select[0] ;
335 if (empty($stamp->startdate) || $stamp->startdate == $stamp->enddate)
336 {
337 // this is the simple case where all the emissions
338 // were released on the same date
339 // (take care with < and <= etc to include all stamps in
340 // at least one accounting period (a year)
341 //
342 if ($stamp->enddate < $start) return false ;
343 if ($stamp->enddate >= $stop) return false ;
344 _invoke_calculate($emission, $stamp, 1);
345 }
346 else
347 {
348 // this is the case where the emissions spread across a long
349 // period of time e.g. the carbon stamp might refer to emissions
350 // from june 05 to june 06 and so would appear in two separate years.
351
352 // explanation of below:
353 // Let's say user inputs carbon stamp of 1000 Kg of CO2 over 10 years.
354 // We want to allocate the 1000Kg over the ten years as evenly as possible.
355 // So we calculate an adjustment which equals
356 // number of emitting days inside our period of interest (i.e between $start and $end) /
357 // total number of emitting days.
358
359 if ($stamp->startdate <= $end) return false ;
360
361 $begin = max($stamp->startdate, $start);
362 $end = min($stamp->enddate, $stop);
363 if ($end <= $begin) return false ;
364
365 $adjust_for_period = ($end - $begin) / ($stamp->enddate - $stamp->startdate);
366 _invoke_calculate($emission, $stamp, $adjust_for_period);
367
368 }
369 $emission->valuedate = $stamp->enddate ;
370
371 $emissions[] = $emission ;
372 }
373
374 /**
375 * meter readings
376 *
377 * Use linear interpolation to calculate meter readings for the
378 * start and end times below then calculate emissions by creating
379 * a scope==0 stamp and calculating emissions in the usual way.
380 *
381 * The resulting emission is added to the emissions array.
382 *
383 *
384 * @param $emissions array of results of each calculation
385 * @param $select one of more scope==1 stamps to process
386 * @param $start start and
387 * @param $end end time of the accounting period we are interested in
388 * @return unknown_type
389 */
390 function _scope_1(&$emissions, &$select, $start, $end)
391 {
392 $meterStamps = _stamp_map_sorted_by_enddate($select);
393
394 $usefulStamps = array(); // the stamps that we will use to calculate emissions
395
396 // add the first real stamp or calculated estimate stamp
397 // at the start of the period that we are interested in.
398
399 $usefulStamps[] = $first = _estimate_meter_reading($meterStamps, $start) ;
400
401 // add all the intermediate stamps
402 foreach ($meterStamps as $stamp) // sorted by ascending date
403 {
404 if ($stamp->enddate <= $start)
405 continue ; // meter reading too early
406
407 if ($stamp->enddate >= $end)
408 continue ; // meter reading too late
409
410 $usefulStamps[] = $stamp ;
411 }
412 // add the last stamp of the period we are interested in
413 $usefulStamps[] = _estimate_meter_reading($meterStamps, $end) ;
414
415 // look for other stamps between $start and $end where the $sourcecode
416 // or the sharing adjustment has been changed. If the adjustment
417 // hasn't changed, remove them from the map. This doesn't make
418 // any difference to the calculation but it is confusing to the
419 // user to itemise emissions between each reading.
420
421 $first = $second = null ;
422 $usefulStampsMap = _stamp_map_sorted_by_enddate($usefulStamps);
423 foreach ($usefulStampsMap as $stamp)
424 {
425 $date_s = _format_date_only($stamp->enddate);
426 if (isset($first) && isset($second))
427 {
428 if ( ($stamp->adjustment == $second->adjustment)
429 && ($stamp->sourcecode == $second->sourcecode))
430 {
431 unset($usefulStampsMap[$second->enddate]); // remove the middle stamp
432 }
433 }
434 $first = $second ; $second = $stamp ;
435 }
436
437 $usefulStamps = array_values($usefulStampsMap);
438
439
440 // there will only be two stamps here, unless sourcecode or
441 // adjustment changes have been uncovered.
442
443 foreach ($usefulStamps as $current)
444 {
445 if ($current == null)
446 continue ; // no estimate available
447
448 if (empty($previous)) // skip first meter reading
449 {
450 $previous = $current ; continue ;
451 }
452
453 // $emissions = _create_emissions_record($previousStamp, $stamp);
454
455 $stamp = clone($current); /// use adjust and sourcecode from last reading
456 $stamp->scope = 0 ;
457 $stamp->reading = $current->reading - $previous->reading ;
458
459 $e0 = new Emission($usefulStamps);
460 $e0->start = $previous ; $e0->end = $current ;
461 $e0->notes = "" ;
462
463 _invoke_calculate($e0, $stamp, 1) ;
464
465 if ($e0->start->estimate)
466 {
467 $e0->notes .= $e0->start->notes ;
468 }
469
470 if ($e0->end->estimate)
471 {
472 $e0->notes .= $e0->end->notes ;
473 }
474
475 if (isset($e0->end->adjustment))
476 if (_evaluate($e0->end->adjustment) != 1.0)
477 {
478 $e0->notes .= " Adjusted by ". $e0->end->adjustment. "." ;
479 }
480
481 $e0->valuedate = $e0->current->enddate;
482
483 $emissions[] = $e0;
484 $previous = $current ;
485 }
486 }
487
488 /**
489 * estimate a meter reading for a given date and return the value
490 * in a new temporary carbon stamp
491 *
492 * @param $meter_readings an array of carbon_stamps
493 * @param $date the date of the desired new fillin stamp
494 * @return calculated timestamp
495 */
496
497 function _estimate_meter_reading($meter_readings, $date)
498 {
499 $date_s = _format_date_only($date);
500 $i = 0 ;
501 foreach ($meter_readings as $mr) // assume meter readings are sorted by date
502 {
503 if ($mr->enddate == $date)
504 return $mr ; // got an actual reading for that date
505
506 if ($mr->enddate > $date)
507 break ;
508
509 $i++; // push pointer above all the earlier dates.
510 }
511 $i-- ;
512 // now, $i is the index of the meter reading just below
513 // where we want to calculate our estimate (and if there isn't
514 // one -1.
515
516 // special case, we want an estimate before the first meter reading
517 // shift up and use the first two meter readings
518
519
520 if ($i <= 0)
521 $i++ ;
522
523 // special case, we want to estimate after the last meter reading
524 // shift down and use the last two meter readings
525
526 if ($i >= count($meter_readings) - 1)
527 $i-- ;
528
529 // now pick out the meter readings we are going to use
530 $meter_readings_array = array_values($meter_readings);
531 $below = $above = null ;
532 if ($i >= 0 && $i <= count($meter_readings) - 2)
533 {
534 $below = $meter_readings_array[$i];
535 $above = $meter_readings_array[$i+1];
536 }
537 else
538 return null ;
539
540 $stamp = clone($above); // assume source code and adjustment of later stamp
541 $stamp->estimate = true ;
542 $stamp->enddate = $date ;
543
544 // use linear interpolation to estimate value between the dates that we have
545
546 $adjust = ($date - $below->enddate) / ($above->enddate - $below->enddate);
547
548 // If the estimate required falls between 2 meter readings then 0.0 < $adjust < 1.0.
549
550 // We try to provide estimates before the first or after the last meter reading,
551 // however this is a big assumption as the person may have moved.
552
553 // the margin of 1/2 below is equivalent to creating estimates up to month before
554 // the first meter reading or 1 month after the last meter reading, if the
555 // meter readings are 2 months apart.
556
557 $margin = 1/2 ;
558
559 if ( $adjust < -$margin // we are estimating before the first meter reading
560 || $adjust > 1+ $margin) // we are estimating after the last meter reading
561 {
562 return null ;
563 }
564
565
566 $stamp->reading = $below->reading + $adjust * ($above->reading - $below->reading) ;
567
568 $stamp->notes = "Using estimated reading of "
569 . _format_float($stamp->reading) . " on " . _format_date_only($stamp->enddate)
570 . " interpolated from "
571 . _format_float($below->reading) . " read on " . _format_date_only($below->enddate)
572 . " and "
573 . _format_float($above->reading) . " read on " . _format_date_only($above->enddate)
574 . ". ";
575
576 return $stamp ;
577 }
578
579
580 /**
581 * daily event
582 * The difference betweeen a scope_0 and scope_2 event is that
583 * the scope_2 event repeats the same level of emissions every
584 * year and the emissions are not dispersed between start and
585 * date.
586 */
587
588
589 function _scope_2(&$emissions, &$select, $start, $stop)
590 {
591 $emission = new Emission($select);
592
593 _duplicate_check($emission->select);
594
595 $stamp = $emission->select[0] ;
596
597 if (empty($stamp->startdate))
598 {
599 if ($stamp->enddate > $start && $stamp->enddate <= $stop)
600 $begin = $start ;
601 else
602 return false;
603 }
604 else
605 {
606 // find out the range of dates when this event occurred
607 // and that fall inside the reporting period that we are
608 // interested in.
609
610 $begin = max($stamp->startdate, $start);
611 }
612 $end = min($stamp->enddate, $stop);
613
614 if ($end <= $begin)
615 return false ;
616 // assuming annual repetition
617 $adjust_for_repetition = ($end - $begin) / (60*60*24*365);
618 _invoke_calculate($emission, $stamp, $adjust_for_repetition);
619 $emission->valuedate = $end;
620
621 $emissions[] = $emission; // process only 1 stamp, ignore duplicates
622 }
623
624 /**
625 * We had expected to be passed an array of just 1 carbon_stamp.
626 * Log an error if there is more than one.
627 * @param $ar an array of carbon stamps
628 */
629
630
631 function _duplicate_check($ar)
632 {
633 if (count($ar) > 1)
634 {
635 $text = "These identical carbon stamps treated as one:";
636 foreach ($ar as $stamp)
637 {
638 $text .= " ".l("node ".$stamp->nid,"/node/".$stamp->nid);
639 }
640 drupal_set_message($text, "warning");
641 }
642 }
643
644 /**
645 *
646 * At the moment the classname is simply mapped to a function name.
647 *
648 * @param $emission structure into which results are place
649 * @param $node a carbon stamp
650 */
651
652
653 function _invoke_calculate(&$emission, $node, $adjust)
654 {
655 if (!isset($node->sourcecode))
656 {
657 $emission->sourceError = t("Unknown source code: %code", array('%code'=>$node->code)) ;
658 return ;
659 }
660 if ($node->sourcestatus == 0)
661 {
662 $emission->sourceError = t("Source not or no longer published: %code", array('%code'=>$node->code)) ;
663 return ;
664 }
665
666 if (empty($node->sourceclass))
667 {
668 $emission->sourceError = t("Source class not defined: %class", array('%class'=>$node->sourceclass)) ;
669 return ;
670 }
671
672 $fn = '_'.$node->sourceclass ;
673 if (!function_exists($fn))
674 {
675 $emission->sourceError = t("Calculator function not defined: ").$fn ;
676 return ;
677 }
678 $fn($emission, $node, $adjust);
679 }
680
681 /**
682 * The simple class calculates emissions with the formula
683 *
684 * emissions = r*m*a
685 *
686 * where r is the value spcified by the user
687 * m is the multiplier used to convert units of r
688 * a is the adjustment specified by the user
689 * which might be 48/52 to allow for holidays
690 * from a daily commute.
691 * (this can be a string and must be evaluated)
692 *
693 * to the corresponding CO2 value.
694 *
695 * @param $node a carbon stamp
696 */
697
698 function _simple(&$emission, $node, $adjust)
699 {
700 $emission->units = $node->reading * _evaluate($node->adjustment) * $adjust;
701 $emission->co2 = $emission->units * $node->toco2 ;
702 $emission->kwh0 = $emission->units * $node->tokwh0 ;
703 $emission->kwh1 = $emission->units * $node->tokwh1 ;
704 }
705
706 /**
707 * This formula comes from http://www.chooseclimate.org
708 *
709
710 Fuel per passenger (Data for B-747).
711
712 Fuel = [7840 + 10.1 * (distance-250)] (*2 if return)
713 (7840 kg take off-climb-descent, 10.1 kg/km cruising.
714 Passengers, = 370 * [occupancy] (/1.5 if business)
715 Greenhouse warming:
716 CO2 = fuel * (44/12 * 156/184) (molecular masses)
717
718 Total warming effect of CO2,Ozone (made by NOx), water vapour and
719 contrails is about three times greater than effect of CO2 alone
720 */
721
722
723 /**
724 * $multiplier should only be used to convert distance from some
725 * arbitrary unit to Km.
726 **/
727
728 function _chooseclimate(&$emission, $node, $adjust)
729 {
730 $model = array(
731 "aircraft" => "B747-400",
732 "fuel_for_takeoff" => 7840,
733 "fuel_cruising_per_km" => 10.1,
734 "climb_distance" => 250,
735 "capacity" => 370,
736 "occupancy" => .80,
737 "fuel_conversion" => (44/12 * 156/184),
738 "radiative_forcing_index" => 2.7
739 );
740 return _flight_calculate($emission, $node, $adjust, $model);
741 }
742
743 /**
744 Climate care model
745
746 This model is based on the information in a report written
747 by the Environmnetal Change Institute for climate care
748 and is provided on the climate care website.
749 http://www.climatecare.org/_media/documents/pdf/Aviation_Emissions_&_Offsets.pdf
750
751 the raw data refrred to on aircraft fuel consumption
752 http://reports.eea.europa.eu/EMEPCORINAIR3/en/B851vs2.4.pdf
753
754 my version of this with average fuel consumption added:
755 http://cvs.drupal.org/viewcvs/ checkout /drupal/contributions/modules/carbon/air_fuel_use.html
756
757 note the climate care assume 100% occupancy and that on long
758 haul flights the 10% of fuel can be be attributed to freight.
759 */
760
761 function _climatecare(&$emission, $node, $adjust)
762 {
763 $distance_km = $node->reading * $node->toco2 ;
764
765 // switch planes depending on distance
766
767 $model = $distance_km < 3500 ? _climatecareB737() : _climatecareB747();
768
769 return _flight_calculate($emission, $node, $adjust, $model);
770 }
771
772 function _climatecareB747() {
773 return array(
774 "aircraft" => "B747-400",
775 "fuel_for_takeoff" => 3402,
776 "fuel_cruising_per_km" => 10.1, // from average kg/mile in EU table
777 "climb_distance" => 0,
778 "fuel_conversion" => 3.15, // http://www.climatecare.org/_media/documents/pdf/Aviation_Emissions_&_Offsets.pdf
779 "capacity" => 400, // ditto
780 "occupancy" => 1.00, // ditto
781 "radiative_forcing_index" => 2.0 // ditto & http://iacweb.ethz.ch/tradeoff/
782 );
783 }
784
785 function _climatecareB737() {
786 return array(
787 "aircraft" => "B737-400",
788 "fuel_for_takeoff" => 825.4,
789 "fuel_cruising_per_km" => 3.01, // from average kg/mile above
790 "climb_distance" => 0,
791 "fuel_conversion" => 3.15, // http://www.climatecare.org/_media/documents/pdf/Aviation_Emissions_&_Offsets.pdf
792 "capacity" => 180, // ditto
793 "occupancy" => 1.00, // ditto
794 "radiative_forcing_index" => 2.0 // diito & http://iacweb.ethz.ch/tradeoff/
795 );
796 }
797
798 function _climatecareA340() {
799 return array(
800 "aircraft" => "Airbus-A340",
801 "fuel_for_takeoff" => 2019,
802 "fuel_cruising_per_km" => 6.6, // from average kg/mile in EU table
803 "climb_distance" => 0,
804 "fuel_conversion" => 3.15, // http://www.climatecare.org/_media/documents/pdf/Aviation_Emissions_&_Offsets.pdf
805 "capacity" => 295, // ditto
806 "occupancy" => 1.00, // ditto
807 "radiative_forcing_index" => 2.0 // diito & http://iacweb.ethz.ch/tradeoff/
808 );
809 }
810
811
812 /**
813 * the choose climate model is basically a simple
814 * first order polynomial and I have used it for
815 * the climate care model as well.
816 */
817
818
819 function _flight_calculate(&$emission, $node, $adjust, $model)
820 {
821 $distance = $node->reading * $node->toco2 ;
822
823 $flight_takeoff = $model["fuel_for_takeoff"] ;
824
825 if ($distance > $model["climb_distance"])
826 $flight_cruising += ($model["fuel_cruising_per_km"] * ($distance-$model["climb_distance"])); // Kg
827
828 $fuel = ($flight_takeoff + $flight_cruising)/($model["capacity"] * $model["occupancy"]) ; //.Kg
829
830 $co2 = $fuel * $model["fuel_conversion"]; // convert fuel to CO2
831
832 // the radiative_forcing_index below (expected to be between
833 // 1.8 and around 2.7) includes effect of the non CO2 gasses
834
835 $co2_and_other_gasses = $co2 * $model["radiative_forcing_index"] ;
836
837 $emission->notes = sprintf("Assumptions: aircraft is a %s" .
838 " which uses %s Kg of fuel on takeoff/landing, ".
839 " and %s Kg/Km while cruising; ".
840 " %s%% of %s seats occupied," .
841 " radiative forcing index is %s." .
842 " Calculation: CO2 per passenger is %2d Kg," .
843 " all gasses (expressed as CO2 equivalent) is %2d Kg.",
844 $model["aircraft"], $model["fuel_for_takeoff"], $model["fuel_cruising_per_km"],
845 100 * $model["occupancy"],$model["capacity"], $model["radiative_forcing_index"],
846 $co2, $co2_and_other_gasses);
847
848 $emission->units = $node->reading ; // this was previously set !
849 $emission->co2 = $co2_and_other_gasses * _evaluate($node->adjustment) * $adjust ; // Kg
850 }
851
852
853 /**
854 * Create a printable carbon footprint for a given period (usually a year)
855 * based on on all the available carbon_stamps.
856 *
857 * @param $results a structure containing all results of calculations
858 * @param $json set to true if want table as raw data
859 * @return a string containing a table that shows the carbon footprint.
860 */
861
862
863 function carbon_calculate_present_results(&$group_result)
864 {
865 $rows = array();
866 $grand_total = 0 ;
867 $numupdates = 0 ;
868 $notes = array();
869 $show_model = false ;
870
871
872 // now go through the results and create a table
873 // containing the results and then add the notes
874 // to the end of the table.
875
876 foreach ($group_result->emissions as $emission)
877 {
878 $result_as_text = null ;
879 $column_notes = "" ;
880 $showRow = false ;
881
882 if (!empty($emission->notes))
883 {
884 $notes[] = $emission->notes;
885 $column_notes = " [".count($notes)."]" ;
886 }
887
888 if (isset($emission->sourceError) || isset($emission->error) || isset($emission->co2))
889 {
890 $showRow = true ;
891 }
892 if ($showRow)
893 {
894 $select = $emission->select ;
895 $daterange = "" ;
896 if ($select[0]->scope == 1)
897 {
898 if ($emission->start->enddate != $emission->end->enddate)
899 $daterange = _format_date_only($emission->start->enddate)." and " ;
900 $daterange .= _format_date_only($emission->end->enddate);
901 }
902 else
903 {
904 if (!empty($select[0]->startdate) && ($select[0]->startdate != $select[0]->enddate))
905 $daterange = _format_date($select[0]->startdate)." to ";
906 $daterange .= _format_date($select[0]->enddate);
907 $emission->end = $select[0] ;
908 }
909 if (isset($emission->sourceError))
910 {
911 $units_col = $emission->sourceError ;
912 }
913 else // ja 2-1-2008 replace select[0] with emission->end
914 {
915 if (isset($emission->units))
916 $units = is_numeric($emission->units) ? _format_float($emission->units) : $emission->units ;
917 else
918 $units = $emission->end->amount ; // is this line used?
919
920 $model = $show_model ? $emission->end->sourcemodel."/" : "" ;
921 $units_col = isset($units) ? $units." ".$emission->end->units." of " : "" ;
922
923 $units_col .= l($model.$emission->end->sourcetitle, 'node/'.$emission->end->sourceid);
924 }
925 $row = array(
926 $daterange,
927 carbon_link_from_title($emission->end,$group_result->account,"")." ".$column_notes,
928 $units_col);
929 if ($group_result->enablekwh0)
930 $row[] = _format_float($emission->kwh0);
931 if ($group_result->enablekwh1)
932 $row[] = _format_float($emission->kwh1);
933 if ($group_result->enableco2)
934 $row[] = empty($emission->error) ? _format_float($emission->co2) : $emission->error;
935 // really need to redirect to original page rather than hard coded page
936 $row[] = carbon_access("update", $group_result->account) ?
937 carbon_format_edit_ops($emission->end, "destination=node/".$group_result->account->nid) : "" ;
938 $rows[] = $row ;
939 $numupdates++ ;
940 }
941 }
942 if (!empty($numupdates))
943 {
944 $out = "";
945 $row = array("total", null, null);
946
947 if ($group_result->enablekwh0)
948 $row[] = _format_float($group_result->grand_kwh0);
949
950 if ($group_result->enablekwh1)
951 $row[] = _format_float($group_result->grand_kwh1);
952
953 if ($group_result->enableco2)
954 $row[] = _format_float($group_result->grand_co2);
955
956 // pass the footprint and see whether we are allowed to update
957 // it rather than asking if we can crate a new stamp as the former
958 // check makes sure we own the existing footprint.
959 $row[] = "" ; // carbon_format_add_new_op($group_result->account, "destination=node/".$group_result->account->nid);
960 $rows[] = $row ;
961
962 $header = array(t("date"),t("title"),t("source"));
963 if ($group_result->enablekwh0)
964 $header[] = variable_get("carbon_energy_units_0", "");
965 if ($group_result->enablekwh1)
966 $header[] = variable_get("carbon_energy_units_1", "");
967 if ($group_result->enableco2)
968 $header[] = variable_get("carbon_units", "Kg of C02") ;
969 $header[] = t("Operations");
970
971 $out .= theme('table', $header, $rows);
972
973 $i = 1 ; foreach ($notes as $note)
974 {
975 $out .= "<p class='link'>[".$i++."] ".$note."</p>" ;
976 }
977 }
978 return $out ;
979 }
980
981
982 function carbon_calculate_empty_table($footprint)
983 {
984 $row = array(t("There are no carbon stamps attached to this carbon account. <br/>" .
985 "Select <i>Meters</i> or <i>Travel</i> tabs and add new carbon stamps."), null);
986 $out .= "<h1></h1>".theme('table', array("","",""), array($row));
987 return $out ;
988 }
989
990 /**
991 * this routine borrowed from php.net
992 * @param $amount value to print
993 * @return unknown_type
994 */
995
996 function _format_float($amount)
997 {
998 $precision = 2 ; // number of significant digits
999 $logval = log10(abs($amount));
1000 $decimals = min(0, max(0, $precision - 1 - intval($logval)));
1001 $format = "%." . $decimals . "f";
1002 return sprintf($format, $amount);
1003 }
1004
1005
1006 ?>

  ViewVC Help
Powered by ViewVC 1.1.2