void ftof_volumes_xml(xml_document& doc, const ForwardTOF& ftof)
{
    // start building up the XML document
    xml_node geom_node = doc.child("geometry");
    if (!geom_node)
    {
        geom_node = doc.append_child("geometry");
    }
    xml_node dc_node = geom_node.child("forward_tof");
    if (!dc_node)
    {
        dc_node = geom_node.append_child("forward_tof");
    }
    xml_node vol_node = dc_node.child("volumes");
    if (!vol_node)
    {
        vol_node = dc_node.append_child("volumes");
    }

    for (auto k1 : ftof_volumes_map(ftof))
    {
        xml_node n1 = vol_node.append_child(k1.first.c_str());

        for (auto k2 : k1.second)
        {
            xml_node n2 = n1.append_child(k2.first.c_str());
            n2.append_child(node_pcdata).set_value(k2.second.c_str());
        }
    }
}
void dc_core_params_xml(xml_document& doc, const DriftChamber& dc, const string& units="cm")
{
    double lconv = length_conversion(units);

    // start building up the XML document
    xml_node geom_node = doc.child("geometry");
    if (!geom_node)
    {
        geom_node = doc.append_child("geometry");
    }

    xml_node dc_node = geom_node.child("drift_chamber");
    if (!dc_node)
    {
        dc_node = geom_node.append_child("drift_chamber");
    }

    xml_node params_node = dc_node.append_child("core_params");

    for (int nreg : vector<int>{0, 1, 2})
    {
        xml_node reg_node = params_node.append_child("region");
        reg_node.append_attribute("index") = nreg;

        for (int nslyr : vector<int>{0, 1})
        {
            xml_node slyr_node = reg_node.append_child("superlayer");
            slyr_node.append_attribute("index") = nslyr;

            slyr_node.append_attribute("wpdist") = dc.sector(0).region(nreg).superlayer(nslyr).wpdist() * lconv;
        }
    }
}
/// Read & assign the object's fields from an XML DOM structure.
void stream_info_impl::read_xml(xml_document &doc) {
	try {
		xml_node info = doc.child("info");
		// name
		name_ = info.child_value("name");
		if (name_.empty())
			throw std::runtime_error("Received a stream info with empty <name> field.");
		// type
		type_ = info.child_value("type");
		// channel_count
		channel_count_ = lexical_cast<int>(info.child_value("channel_count"));
		if (channel_count_ < 0)
			throw std::runtime_error("The channel count of the given stream info is smaller than 0.");
		// nominal_srate
		nominal_srate_ = lexical_cast<double>(info.child_value("nominal_srate"));
		if (nominal_srate_ < 0.0)
			throw std::runtime_error("The sampling rate of the given stream info is negative.");
		// channel_format
		channel_format_ = cf_undefined;
		string fmt(info.child_value("channel_format"));
		if (fmt == "float32")
			channel_format_ = cf_float32;
		if (fmt == "double64")
			channel_format_ = cf_double64;
		if (fmt == "string")
			channel_format_ = cf_string;
		if (fmt == "int32")
			channel_format_ = cf_int32;
		if (fmt == "int16")
			channel_format_ = cf_int16;
		if (fmt == "int8")
			channel_format_ = cf_int8;
		if (fmt == "int64")
			channel_format_ = cf_int64;
		// source_id
		source_id_ = info.child_value("source_id");
		// version
		version_ = (int)(lexical_cast<double>(info.child_value("version"))*100.0);
		if (version_ <= 0)
			throw std::runtime_error("The version of the given stream info is invalid.");
		// created_at
		created_at_ = lexical_cast<double>(info.child_value("created_at"));
		// uid
		uid_ = info.child_value("uid");
		if (uid_.empty())
			throw std::runtime_error("The UID of the given stream info is empty.");
		// session_id
		session_id_ = info.child_value("session_id");
		// hostname
		hostname_ = info.child_value("hostname");
		// address
		v4address_ = info.child_value("v4address");
		// data_port
		v4data_port_ = lexical_cast<int>(info.child_value("v4data_port"));
		// service_port
		v4service_port_ = lexical_cast<int>(info.child_value("v4service_port"));
		// address
		v6address_ = info.child_value("v6address");
		// data_port
		v6data_port_ = lexical_cast<int>(info.child_value("v6data_port"));
		// service_port
		v6service_port_ = lexical_cast<int>(info.child_value("v6service_port"));
	} catch(std::exception &e) {
		// reset the stream info to blank state
		*this = stream_info_impl();
		name_ = (string("(invalid: ") += e.what()) += ")";
	}
}
void ftof_panels_parms_xml(xml_document& doc, const ForwardTOF& ftof, const string& coordsys="clas", const string& units="cm")
{
    double lconv = length_conversion(units);

    if (coordsys != "sector" && coordsys != "clas")
    {
        throw runtime_error(string("can not generate data in ") + coordsys + " coordinates");
    }
    coordsys_t coord = str2coord(coordsys);

    // start building up the XML document
    xml_node geom_node = doc.child("geometry");
    if (!geom_node)
    {
        geom_node = doc.append_child("geometry");
    }

    xml_node ftof_node = geom_node.child("forward_tof");
    if (!ftof_node)
    {
        ftof_node = geom_node.append_child("forward_tof");
    }

    xml_node panels_node = ftof_node.child("panels_parms");
    if (!panels_node)
    {
        panels_node = ftof_node.append_child("panels_parms");
        panels_node.append_attribute("length_units");
        panels_node.append_attribute("coordinate_system");
    }

    panels_node.attribute("length_units") = units.c_str();
    panels_node.attribute("coordinate_system") = coordsys.c_str();

    for (int sec=0; sec<ftof.sectors().size(); sec++)
    {
        const forward_tof::Sector& ftof_sector = ftof.sector(sec);

        for (int pan=0; pan<ftof_sector.panels().size(); pan++)
        {
            const forward_tof::Panel& panel = ftof_sector.panel(pan);

            xml_node panel_node = panels_node.append_child("panel");
            panel_node.append_attribute("sector") = sec;
            panel_node.append_attribute("panel") = ftof_sector.panel_name(pan).c_str();
            panel_node.append_attribute("npaddles") = int(panel.npaddles());
            panel_node.append_attribute("dist2tgt") = panel.dist2tgt() * lconv;

            direction_vector<double,3> panel_norm = panel.panel_normal(coord);
            panel_node.append_attribute("norm_phi") = panel_norm.phi();
            panel_node.append_attribute("norm_theta") = panel_norm.theta();

            direction_vector<double,3> paddle_dir = panel.paddle_direction(coord);
            panel_node.append_attribute("paddle_phi") = paddle_dir.phi();
            panel_node.append_attribute("paddle_theta") = paddle_dir.theta();

            panel_node.append_attribute("paddle_width") = panel.paddle_width() * lconv;
            panel_node.append_attribute("paddle_thickness") = panel.paddle_thickness() * lconv;

            euclid_vector<double,3> paddle_extent = panel.paddle_extent(COORDSYS::SECTOR);
            panel_node.append_attribute("paddle_extent_x") = paddle_extent.x() * lconv;
            panel_node.append_attribute("paddle_extent_z") = paddle_extent.z() * lconv;

            xml_node center_node = panel_node.append_child("paddle_centers");
            vector<xml_node> paddle_centers_node;
            paddle_centers_node.push_back(center_node.append_child("x"));
            paddle_centers_node.push_back(center_node.append_child("y"));
            paddle_centers_node.push_back(center_node.append_child("z"));

            vector<stringstream> centers(3);
            for (auto cntr : panel.paddle_centers(coord))
            {
                centers[0] << " " << cntr.x() * lconv;
                centers[1] << " " << cntr.y() * lconv;
                centers[2] << " " << cntr.z() * lconv;
            }
            for (int i=0; i<paddle_centers_node.size(); i++)
            {
                paddle_centers_node[i].append_child(pugi::node_pcdata).set_value(
                    centers[i].str().erase(0,1).c_str());
            }

            xml_node length_node = panel_node.append_child("paddle_lengths");
            stringstream lengths;
            for (auto lngth : panel.paddle_lengths())
            {
                lengths << " " << lngth;
            }
            length_node.append_child(pugi::node_pcdata).set_value(
                lengths.str().erase(0,1).c_str());
        }
    }
}