Commit eb131e63aa1cdc9f2b451e5f2da95f4af7adfc49

Authored by Jürgen Knödlseder
2 parents 76e21e25 6f4762d7

Merge branch '1039-allow-energy-thresholds-via-IRF-and-XML' into integration

ChangeLog
1 -2014-10-31 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 1 +2014-11-07 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
2 2
3 * Version 0.9.0 released 3 * Version 0.9.0 released
4 ======================== 4 ========================
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 Introduce class codes to avoid dynamic type casting 34 Introduce class codes to avoid dynamic type casting
35 Remove error computation from optimization 35 Remove error computation from optimization
36 Add GOptimize::errors() method for error computation 36 Add GOptimize::errors() method for error computation
  37 + Add support for user and save CTA energy thresholds
37 Several bug corrections 38 Several bug corrections
38 39
39 40
@@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 00-09-00 @@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 00-09-00
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -31 October 2014 5 +7 November 2014
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -307,6 +307,11 @@ Events are now only loaded when needed into GCTAObservation. This reduces memory @@ -307,6 +307,11 @@ Events are now only loaded when needed into GCTAObservation. This reduces memory
307 the usage. The method dispose_events() has been added to explicitely allow the 307 the usage. The method dispose_events() has been added to explicitely allow the
308 disposal of the events. 308 disposal of the events.
309 309
  310 +Support for user provided energy thresholds and save energy thresholds has been
  311 +implemented. Save energy thresholds will be extracted from the LO_THRES and
  312 +HI_THRES keywords if they are present in the effective area response FITS
  313 +file. The user may supply energy thresholds in the XML file.
  314 +
310 315
311 21. COMPTEL interface 316 21. COMPTEL interface
312 --------------------- 317 ---------------------
inst/cta/include/GCTAObservation.hpp
@@ -120,6 +120,8 @@ public: @@ -120,6 +120,8 @@ public:
120 void eventfile(const std::string& filename); 120 void eventfile(const std::string& filename);
121 const std::string& eventfile(void) const; 121 const std::string& eventfile(void) const;
122 void dispose_events(void); 122 void dispose_events(void);
  123 + const double& lo_user_thres(void) const;
  124 + const double& hi_user_thres(void) const;
123 125
124 protected: 126 protected:
125 // Protected methods 127 // Protected methods
@@ -131,17 +133,19 @@ protected: @@ -131,17 +133,19 @@ protected:
131 void set_event_type(void); 133 void set_event_type(void);
132 134
133 // Protected members 135 // Protected members
134 - std::string m_instrument; //!< Instrument name  
135 - std::string m_eventfile; //!< Event filename  
136 - std::string m_eventtype; //!< Event type (for XML file)  
137 - GCTAResponse* m_response; //!< Pointer to instrument response functions  
138 - GCTAPointing m_pointing; //!< Pointing direction  
139 - int m_obs_id; //!< Observation ID  
140 - double m_ontime; //!< Ontime (seconds)  
141 - double m_livetime; //!< Livetime (seconds)  
142 - double m_deadc; //!< Deadtime correction (livetime/ontime)  
143 - double m_ra_obj; //!< Right Ascension of object (degrees)  
144 - double m_dec_obj; //!< Declination of object (degrees) 136 + std::string m_instrument; //!< Instrument name
  137 + std::string m_eventfile; //!< Event filename
  138 + std::string m_eventtype; //!< Event type (for XML file)
  139 + GCTAResponse* m_response; //!< Pointer to instrument response functions
  140 + GCTAPointing m_pointing; //!< Pointing direction
  141 + int m_obs_id; //!< Observation ID
  142 + double m_ontime; //!< Ontime (seconds)
  143 + double m_livetime; //!< Livetime (seconds)
  144 + double m_deadc; //!< Deadtime correction (livetime/ontime)
  145 + double m_ra_obj; //!< Right Ascension of object (degrees)
  146 + double m_dec_obj; //!< Declination of object (degrees)
  147 + double m_lo_user_thres; //!< User defined lower energy threshold
  148 + double m_hi_user_thres; //!< User defined upper energy boundary
145 149
146 // Special protected member for GCTAModelCubeBackground friend 150 // Special protected member for GCTAModelCubeBackground friend
147 std::string m_bgdfile; //!< Background filename 151 std::string m_bgdfile; //!< Background filename
@@ -387,4 +391,30 @@ const std::string&amp; GCTAObservation::eventfile(void) const @@ -387,4 +391,30 @@ const std::string&amp; GCTAObservation::eventfile(void) const
387 return m_eventfile; 391 return m_eventfile;
388 } 392 }
389 393
  394 +
  395 +/***********************************************************************//**
  396 + * @brief Return user low energy threshold
  397 + *
  398 + * @return User low energy threshold.
  399 + ***************************************************************************/
  400 +inline
  401 +const double& GCTAObservation::lo_user_thres(void) const
  402 +{
  403 + return m_lo_user_thres;
  404 +}
  405 +
  406 +/***********************************************************************//**
  407 + * @brief Return user high energy threshold
  408 + *
  409 + * @return User high energy threshold.
  410 + ***************************************************************************/
  411 +inline
  412 +const double& GCTAObservation::hi_user_thres(void) const
  413 +{
  414 + return m_hi_user_thres;
  415 +}
  416 +
  417 +
  418 +
  419 +
390 #endif /* GCTAOBSERVATION_HPP */ 420 #endif /* GCTAOBSERVATION_HPP */
inst/cta/include/GCTAResponse.hpp
@@ -93,6 +93,7 @@ protected: @@ -93,6 +93,7 @@ protected:
93 const GObservation& obs) const; 93 const GObservation& obs) const;
94 const GCTAInstDir& retrieve_dir(const std::string& origin, 94 const GCTAInstDir& retrieve_dir(const std::string& origin,
95 const GEvent& event) const; 95 const GEvent& event) const;
  96 +
96 }; 97 };
97 98
98 #endif /* GCTARESPONSE_HPP */ 99 #endif /* GCTARESPONSE_HPP */
inst/cta/include/GCTAResponseIrf.hpp
@@ -127,6 +127,8 @@ public: @@ -127,6 +127,8 @@ public:
127 void edisp(GCTAEdisp* edisp); 127 void edisp(GCTAEdisp* edisp);
128 const GCTABackground* background(void) const; 128 const GCTABackground* background(void) const;
129 void background(GCTABackground* background); 129 void background(GCTABackground* background);
  130 + const double& lo_save_thres(void) const;
  131 + const double& hi_save_thres(void) const;
130 132
131 // Low-level response methods 133 // Low-level response methods
132 double aeff(const double& theta, 134 double aeff(const double& theta,
@@ -177,6 +179,8 @@ private: @@ -177,6 +179,8 @@ private:
177 GCTAEdisp* m_edisp; //!< Energy dispersion 179 GCTAEdisp* m_edisp; //!< Energy dispersion
178 GCTABackground* m_background; //!< Energy dispersion 180 GCTABackground* m_background; //!< Energy dispersion
179 mutable bool m_apply_edisp; //!< Apply energy dispersion 181 mutable bool m_apply_edisp; //!< Apply energy dispersion
  182 + double m_lo_save_thres; //!< Save low energy threshold
  183 + double m_hi_save_thres; //!< Save high energy threshold
180 184
181 // XML response filename 185 // XML response filename
182 std::string m_xml_caldb; //!< Calibration database string in XML file 186 std::string m_xml_caldb; //!< Calibration database string in XML file
@@ -398,4 +402,26 @@ void GCTAResponseIrf::background(GCTABackground* background) @@ -398,4 +402,26 @@ void GCTAResponseIrf::background(GCTABackground* background)
398 return; 402 return;
399 } 403 }
400 404
  405 +/***********************************************************************//**
  406 + * @brief Return low energy threshold from IRF
  407 + *
  408 + * @return Low energy threshold from IRF.
  409 + ***************************************************************************/
  410 +inline
  411 +const double& GCTAResponseIrf::lo_save_thres(void) const
  412 +{
  413 + return m_lo_save_thres;
  414 +}
  415 +
  416 +/***********************************************************************//**
  417 + * @brief Return high energy threshold from IRF
  418 + *
  419 + * @return High energy threshold from IRF.
  420 + ***************************************************************************/
  421 +inline
  422 +const double& GCTAResponseIrf::hi_save_thres(void) const
  423 +{
  424 + return m_hi_save_thres;
  425 +}
  426 +
401 #endif /* GCTARESPONSEIRF_HPP */ 427 #endif /* GCTARESPONSEIRF_HPP */
inst/cta/pyext/GCTAObservation.i
@@ -93,6 +93,8 @@ public: @@ -93,6 +93,8 @@ public:
93 void eventfile(const std::string& filename); 93 void eventfile(const std::string& filename);
94 const std::string& eventfile(void) const; 94 const std::string& eventfile(void) const;
95 void dispose_events(void); 95 void dispose_events(void);
  96 + const double& lo_user_thres(void) const;
  97 + const double& hi_user_thres(void) const;
96 }; 98 };
97 99
98 100
inst/cta/src/GCTAObservation.cpp
@@ -638,7 +638,10 @@ GEbounds GCTAObservation::ebounds(void) const @@ -638,7 +638,10 @@ GEbounds GCTAObservation::ebounds(void) const
638 * ... 638 * ...
639 * </observation> 639 * </observation>
640 * 640 *
641 - * for a binned observation. 641 + * for a binned observation. Optional user energy thresholds can be
  642 + * specified by adding the emin and emax attributes:
  643 + *
  644 + * <observation name="..." id="..." instrument="..." emin="..." emax="...">
642 * 645 *
643 * The method does no load the events into memory but stores the file name 646 * The method does no load the events into memory but stores the file name
644 * of the event file. The events are only loaded when required. This reduces 647 * of the event file. The events are only loaded when required. This reduces
@@ -653,6 +656,14 @@ void GCTAObservation::read(const GXmlElement&amp; xml) @@ -653,6 +656,14 @@ void GCTAObservation::read(const GXmlElement&amp; xml)
653 // Extract instrument name 656 // Extract instrument name
654 m_instrument = xml.attribute("instrument"); 657 m_instrument = xml.attribute("instrument");
655 658
  659 + // Read in user defined energy boundaries of this observation
  660 + if (xml.attribute("emin") != "") {
  661 + m_lo_user_thres = gammalib::todouble(xml.attribute("emin"));
  662 + }
  663 + if (xml.attribute("emax") != "") {
  664 + m_hi_user_thres = gammalib::todouble(xml.attribute("emax"));
  665 + }
  666 +
656 // Determine number of parameter nodes in XML element 667 // Determine number of parameter nodes in XML element
657 int npars = xml.elements("parameter"); 668 int npars = xml.elements("parameter");
658 669
@@ -793,7 +804,12 @@ void GCTAObservation::read(const GXmlElement&amp; xml) @@ -793,7 +804,12 @@ void GCTAObservation::read(const GXmlElement&amp; xml)
793 * ... 804 * ...
794 * </observation> 805 * </observation>
795 * 806 *
796 - * for a binned observation. 807 + * for a binned observation. If user energy threshold are defined (i.e.
  808 + * threshold values are >0) the additional emin and emax attributes will
  809 + * be written to the file:
  810 + *
  811 + * <observation name="..." id="..." instrument="..." emin="..." emax="...">
  812 + *
797 ***************************************************************************/ 813 ***************************************************************************/
798 void GCTAObservation::write(GXmlElement& xml) const 814 void GCTAObservation::write(GXmlElement& xml) const
799 { 815 {
@@ -813,6 +829,14 @@ void GCTAObservation::write(GXmlElement&amp; xml) const @@ -813,6 +829,14 @@ void GCTAObservation::write(GXmlElement&amp; xml) const
813 throw GException::invalid_value(G_WRITE, msg); 829 throw GException::invalid_value(G_WRITE, msg);
814 } 830 }
815 831
  832 + // Add user energy threshold attributes (if set)
  833 + if (m_lo_user_thres > 0.0) {
  834 + xml.attribute("emin", gammalib::str(m_lo_user_thres));
  835 + }
  836 + if (m_hi_user_thres > 0.0) {
  837 + xml.attribute("emax", gammalib::str(m_hi_user_thres));
  838 + }
  839 +
816 // If XML element has 0 nodes then add the required parameter nodes 840 // If XML element has 0 nodes then add the required parameter nodes
817 if (xml.elements() == 0) { 841 if (xml.elements() == 0) {
818 std::string text = "parameter name=\""+m_eventtype+"\""; 842 std::string text = "parameter name=\""+m_eventtype+"\"";
@@ -1033,6 +1057,7 @@ void GCTAObservation::load(const std::string&amp; cntcube, @@ -1033,6 +1057,7 @@ void GCTAObservation::load(const std::string&amp; cntcube,
1033 ***************************************************************************/ 1057 ***************************************************************************/
1034 void GCTAObservation::save(const std::string& filename, const bool& clobber) const 1058 void GCTAObservation::save(const std::string& filename, const bool& clobber) const
1035 { 1059 {
  1060 +
1036 // Create FITS file 1061 // Create FITS file
1037 GFits fits; 1062 GFits fits;
1038 1063
@@ -1173,6 +1198,28 @@ std::string GCTAObservation::print(const GChatter&amp; chatter) const @@ -1173,6 +1198,28 @@ std::string GCTAObservation::print(const GChatter&amp; chatter) const
1173 result.append("\n"+gammalib::parformat("Deadtime correction")); 1198 result.append("\n"+gammalib::parformat("Deadtime correction"));
1174 result.append(gammalib::str(m_deadc)); 1199 result.append(gammalib::str(m_deadc));
1175 1200
  1201 + // Append user energy threshold information
  1202 + result.append("\n"+gammalib::parformat("User energy range"));
  1203 + if (m_lo_user_thres > 0.0 && m_hi_user_thres) {
  1204 + result.append(gammalib::str(m_lo_user_thres));
  1205 + result.append(" - ");
  1206 + result.append(gammalib::str(m_hi_user_thres));
  1207 + result.append(" TeV");
  1208 + }
  1209 + else if (m_lo_user_thres > 0.0) {
  1210 + result.append("> ");
  1211 + result.append(gammalib::str(m_lo_user_thres));
  1212 + result.append(" TeV");
  1213 + }
  1214 + else if (m_hi_user_thres > 0.0) {
  1215 + result.append("< ");
  1216 + result.append(gammalib::str(m_hi_user_thres));
  1217 + result.append(" TeV");
  1218 + }
  1219 + else {
  1220 + result.append("undefined");
  1221 + }
  1222 +
1176 // Append detailed information 1223 // Append detailed information
1177 GChatter reduced_chatter = gammalib::reduce(chatter); 1224 GChatter reduced_chatter = gammalib::reduce(chatter);
1178 if (reduced_chatter > SILENT) { 1225 if (reduced_chatter > SILENT) {
@@ -1221,12 +1268,14 @@ void GCTAObservation::init_members(void) @@ -1221,12 +1268,14 @@ void GCTAObservation::init_members(void)
1221 m_bgdfile.clear(); 1268 m_bgdfile.clear();
1222 m_response = NULL; 1269 m_response = NULL;
1223 m_pointing.clear(); 1270 m_pointing.clear();
1224 - m_obs_id = 0;  
1225 - m_ontime = 0.0;  
1226 - m_livetime = 0.0;  
1227 - m_deadc = 0.0;  
1228 - m_ra_obj = 0.0;  
1229 - m_dec_obj = 0.0; 1271 + m_obs_id = 0;
  1272 + m_ontime = 0.0;
  1273 + m_livetime = 0.0;
  1274 + m_deadc = 0.0;
  1275 + m_ra_obj = 0.0;
  1276 + m_dec_obj = 0.0;
  1277 + m_lo_user_thres = 0.0;
  1278 + m_hi_user_thres = 0.0;
1230 1279
1231 // Return 1280 // Return
1232 return; 1281 return;
@@ -1241,17 +1290,19 @@ void GCTAObservation::init_members(void) @@ -1241,17 +1290,19 @@ void GCTAObservation::init_members(void)
1241 void GCTAObservation::copy_members(const GCTAObservation& obs) 1290 void GCTAObservation::copy_members(const GCTAObservation& obs)
1242 { 1291 {
1243 // Copy members 1292 // Copy members
1244 - m_instrument = obs.m_instrument;  
1245 - m_eventfile = obs.m_eventfile;  
1246 - m_eventtype = obs.m_eventtype;  
1247 - m_bgdfile = obs.m_bgdfile;  
1248 - m_pointing = obs.m_pointing;  
1249 - m_obs_id = obs.m_obs_id;  
1250 - m_ontime = obs.m_ontime;  
1251 - m_livetime = obs.m_livetime;  
1252 - m_deadc = obs.m_deadc;  
1253 - m_ra_obj = obs.m_ra_obj;  
1254 - m_dec_obj = obs.m_dec_obj; 1293 + m_instrument = obs.m_instrument;
  1294 + m_eventfile = obs.m_eventfile;
  1295 + m_eventtype = obs.m_eventtype;
  1296 + m_bgdfile = obs.m_bgdfile;
  1297 + m_pointing = obs.m_pointing;
  1298 + m_obs_id = obs.m_obs_id;
  1299 + m_ontime = obs.m_ontime;
  1300 + m_livetime = obs.m_livetime;
  1301 + m_deadc = obs.m_deadc;
  1302 + m_ra_obj = obs.m_ra_obj;
  1303 + m_dec_obj = obs.m_dec_obj;
  1304 + m_lo_user_thres = obs.m_lo_user_thres;
  1305 + m_hi_user_thres = obs.m_hi_user_thres;
1255 1306
1256 // Clone members 1307 // Clone members
1257 m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL; 1308 m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL;
inst/cta/src/GCTAResponseIrf.cpp
@@ -948,15 +948,47 @@ void GCTAResponseIrf::load_aeff(const std::string&amp; filename) @@ -948,15 +948,47 @@ void GCTAResponseIrf::load_aeff(const std::string&amp; filename)
948 // If file contains an "EFFECTIVE AREA" extension then load it 948 // If file contains an "EFFECTIVE AREA" extension then load it
949 // as CTA response table 949 // as CTA response table
950 if (file.contains("EFFECTIVE AREA")) { 950 if (file.contains("EFFECTIVE AREA")) {
  951 +
  952 + // Get HDU
  953 + const GFitsHDU* hdu = file.at("EFFECTIVE AREA");
  954 +
  955 + // Read save energy thresholds if available
  956 + if (hdu->has_card("LO_THRES")) {
  957 + m_lo_save_thres = hdu->real("LO_THRES");
  958 + }
  959 + if (hdu->has_card("HI_THRES")) {
  960 + m_hi_save_thres = hdu->real("HI_THRES");
  961 + }
  962 +
  963 + // Close file
951 file.close(); 964 file.close();
  965 +
  966 + // Allocate Aeff from file
952 m_aeff = new GCTAAeff2D(filename); 967 m_aeff = new GCTAAeff2D(filename);
  968 +
953 } 969 }
954 970
955 // ... else if file contains a "SPECRESP" extension then load it 971 // ... else if file contains a "SPECRESP" extension then load it
956 // as ARF 972 // as ARF
957 else if (file.contains("SPECRESP")) { 973 else if (file.contains("SPECRESP")) {
  974 +
  975 + // Get HDU
  976 + const GFitsHDU* hdu = file.at("SPECRESP");
  977 +
  978 + // Read save energy thresholds if available
  979 + if (hdu->has_card("LO_THRES")) {
  980 + m_lo_save_thres = hdu->real("LO_THRES");
  981 + }
  982 + if (hdu->has_card("LO_THRES")) {
  983 + m_hi_save_thres = hdu->real("HI_THRES");
  984 + }
  985 +
  986 + // Close file
958 file.close(); 987 file.close();
  988 +
  989 + // Allocate Aeff from file
959 m_aeff = new GCTAAeffArf(filename); 990 m_aeff = new GCTAAeffArf(filename);
  991 +
960 } 992 }
961 993
962 } 994 }
@@ -1221,6 +1253,28 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const @@ -1221,6 +1253,28 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1221 } 1253 }
1222 } 1254 }
1223 1255
  1256 + // Append save energy threshold information
  1257 + result.append("\n"+gammalib::parformat("Save energy range"));
  1258 + if (m_lo_save_thres > 0.0 && m_hi_save_thres) {
  1259 + result.append(gammalib::str(m_lo_save_thres));
  1260 + result.append(" - ");
  1261 + result.append(gammalib::str(m_hi_save_thres));
  1262 + result.append(" TeV");
  1263 + }
  1264 + else if (m_lo_save_thres > 0.0) {
  1265 + result.append("> ");
  1266 + result.append(gammalib::str(m_lo_save_thres));
  1267 + result.append(" TeV");
  1268 + }
  1269 + else if (m_hi_save_thres > 0.0) {
  1270 + result.append("< ");
  1271 + result.append(gammalib::str(m_hi_save_thres));
  1272 + result.append(" TeV");
  1273 + }
  1274 + else {
  1275 + result.append("undefined");
  1276 + }
  1277 +
1224 // Append calibration database 1278 // Append calibration database
1225 result.append("\n"+m_caldb.print(chatter)); 1279 result.append("\n"+m_caldb.print(chatter));
1226 1280
@@ -2905,11 +2959,13 @@ void GCTAResponseIrf::init_members(void) @@ -2905,11 +2959,13 @@ void GCTAResponseIrf::init_members(void)
2905 // Initialise members 2959 // Initialise members
2906 m_caldb.clear(); 2960 m_caldb.clear();
2907 m_rspname.clear(); 2961 m_rspname.clear();
2908 - m_aeff = NULL;  
2909 - m_psf = NULL;  
2910 - m_edisp = NULL;  
2911 - m_background = NULL;  
2912 - m_apply_edisp = false; //!< Switched off by default 2962 + m_aeff = NULL;
  2963 + m_psf = NULL;
  2964 + m_edisp = NULL;
  2965 + m_background = NULL;
  2966 + m_apply_edisp = false; //!< Switched off by default
  2967 + m_lo_save_thres = 0.0;
  2968 + m_hi_save_thres = 0.0;
2913 2969
2914 // XML response filenames 2970 // XML response filenames
2915 m_xml_caldb.clear(); 2971 m_xml_caldb.clear();
@@ -2938,9 +2994,11 @@ void GCTAResponseIrf::init_members(void) @@ -2938,9 +2994,11 @@ void GCTAResponseIrf::init_members(void)
2938 void GCTAResponseIrf::copy_members(const GCTAResponseIrf& rsp) 2994 void GCTAResponseIrf::copy_members(const GCTAResponseIrf& rsp)
2939 { 2995 {
2940 // Copy members 2996 // Copy members
2941 - m_caldb = rsp.m_caldb;  
2942 - m_rspname = rsp.m_rspname;  
2943 - m_apply_edisp = rsp.m_apply_edisp; 2997 + m_caldb = rsp.m_caldb;
  2998 + m_rspname = rsp.m_rspname;
  2999 + m_apply_edisp = rsp.m_apply_edisp;
  3000 + m_lo_save_thres = rsp.m_lo_save_thres;
  3001 + m_hi_save_thres = rsp.m_hi_save_thres;
2944 3002
2945 // Copy response filenames 3003 // Copy response filenames
2946 m_xml_caldb = rsp.m_xml_caldb; 3004 m_xml_caldb = rsp.m_xml_caldb;
src/obs/GObservations.cpp
@@ -646,7 +646,7 @@ void GObservations::write(GXml&amp; xml) const @@ -646,7 +646,7 @@ void GObservations::write(GXml&amp; xml) const
646 646
647 // Write all observations into library 647 // Write all observations into library
648 for (int i = 0; i < size(); ++i) { 648 for (int i = 0; i < size(); ++i) {
649 - 649 +
650 // Initialise pointer on observation 650 // Initialise pointer on observation
651 GXmlElement* obs = NULL; 651 GXmlElement* obs = NULL;
652 652
@@ -670,7 +670,7 @@ void GObservations::write(GXml&amp; xml) const @@ -670,7 +670,7 @@ void GObservations::write(GXml&amp; xml) const
670 obs->attribute("id", m_obs[i]->id()); 670 obs->attribute("id", m_obs[i]->id());
671 obs->attribute("instrument", m_obs[i]->instrument()); 671 obs->attribute("instrument", m_obs[i]->instrument());
672 } 672 }
673 - 673 +
674 // Write now observation 674 // Write now observation
675 m_obs[i]->write(*obs); 675 m_obs[i]->write(*obs);
676 676