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;
        }
    }
}
/// Initialize the XML DOM structure (leaving .desc unchanged) from the data.
void stream_info_impl::write_xml(xml_document &doc) {
	const char *channel_format_strings[] = {"undefined","float32","double64","string","int32","int16","int8","int64"};
	xml_node info = doc.append_child("info");
	info.append_child("name").append_child(node_pcdata).set_value(name_.c_str());
	info.append_child("type").append_child(node_pcdata).set_value(type_.c_str());
	info.append_child("channel_count").append_child(node_pcdata).set_value(lexical_cast<string>(channel_count_).c_str());
	info.append_child("nominal_srate").append_child(node_pcdata).set_value(lexical_cast<string>(nominal_srate_).c_str());
	info.append_child("channel_format").append_child(node_pcdata).set_value(channel_format_strings[channel_format_]);
	info.append_child("source_id").append_child(node_pcdata).set_value(source_id_.c_str());
	info.append_child("version").append_child(node_pcdata).set_value(lexical_cast<string>(version_/100.0).c_str());
	info.append_child("created_at").append_child(node_pcdata).set_value(lexical_cast<string>(created_at_).c_str());
	info.append_child("uid").append_child(node_pcdata).set_value(uid_.c_str());
	info.append_child("session_id").append_child(node_pcdata).set_value(session_id_.c_str());
	info.append_child("hostname").append_child(node_pcdata).set_value(hostname_.c_str());
	info.append_child("v4address").append_child(node_pcdata).set_value(v4address_.c_str());
	info.append_child("v4data_port").append_child(node_pcdata).set_value(lexical_cast<string>(v4data_port_).c_str());
	info.append_child("v4service_port").append_child(node_pcdata).set_value(lexical_cast<string>(v4service_port_).c_str());
	info.append_child("v6address").append_child(node_pcdata).set_value(v6address_.c_str());
	info.append_child("v6data_port").append_child(node_pcdata).set_value(lexical_cast<string>(v6data_port_).c_str());
	info.append_child("v6service_port").append_child(node_pcdata).set_value(lexical_cast<string>(v6service_port_).c_str());
	info.append_child("desc");
}
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());
        }
    }
}