We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
json2xml needs to add support for the HiFa DTD
# Description

json2xml does not include the DTD statement in the XML outputs that it generates.

# Expected Behavior

Should include DTD statement.

# Actual Behavior

Does not include DTD statement.

# Checklist

- [x] Run `git fetch` to get the most up to date version of `master`
- [x] Searched through existing Issues to confirm this is not a duplicate issue
- [x] Filled out the Description, Expected Behavior, Actual Behavior, and Steps to Reproduce sections above or have edited/removed them in a way that fully describes the issue


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyhf/writexml.py`
Content:
```
1 import logging
2 
3 import os
4 import xml.etree.cElementTree as ET
5 import numpy as np
6 import uproot
7 from uproot_methods.classes import TH1
8 
9 _ROOT_DATA_FILE = None
10 
11 log = logging.getLogger(__name__)
12 
13 
14 def _make_hist_name(channel, sample, modifier='', prefix='hist', suffix=''):
15     return "{prefix}{middle}{suffix}".format(
16         prefix=prefix,
17         suffix=suffix,
18         middle='_'.join(filter(lambda x: x, [channel, sample, modifier])),
19     )
20 
21 
22 def _export_root_histogram(histname, data):
23     h = TH1.from_numpy((np.asarray(data), np.arange(len(data) + 1)))
24     h._fName = histname
25     # NB: uproot crashes for some reason, figure out why later
26     # if histname in _ROOT_DATA_FILE:
27     #    raise KeyError('Duplicate key {0} being written.'.format(histname))
28     _ROOT_DATA_FILE[histname] = h
29 
30 
31 # https://stackoverflow.com/a/4590052
32 def indent(elem, level=0):
33     i = "\n" + level * "  "
34     if elem:
35         if not elem.text or not elem.text.strip():
36             elem.text = i + "  "
37         if not elem.tail or not elem.tail.strip():
38             elem.tail = i
39         for elem in elem:
40             indent(elem, level + 1)
41         if not elem.tail or not elem.tail.strip():
42             elem.tail = i
43     else:
44         if level and (not elem.tail or not elem.tail.strip()):
45             elem.tail = i
46 
47 
48 def build_measurement(measurementspec):
49     config = measurementspec['config']
50     name = measurementspec['name']
51     poi = config['poi']
52 
53     # we want to know which parameters are fixed (constant)
54     # and to additionally extract the luminosity information
55     fixed_params = []
56     lumi = 1.0
57     lumierr = 0.0
58     for parameter in config['parameters']:
59         if parameter.get('fixed', False):
60             pname = parameter['name']
61             if pname == 'lumi':
62                 fixed_params.append('Lumi')
63             else:
64                 fixed_params.append(pname)
65         # we found luminosity, so handle it
66         if parameter['name'] == 'lumi':
67             lumi = parameter['auxdata'][0]
68             lumierr = parameter['sigmas'][0]
69 
70     # define measurement
71     meas = ET.Element("Measurement", Name=name, Lumi=str(lumi), LumiRelErr=str(lumierr))
72     poiel = ET.Element('POI')
73     poiel.text = poi
74     meas.append(poiel)
75 
76     # add fixed parameters (constant)
77     if fixed_params:
78         se = ET.Element('ParamSetting', Const='True')
79         se.text = ' '.join(fixed_params)
80         meas.append(se)
81     return meas
82 
83 
84 def build_modifier(modifierspec, channelname, samplename, sampledata):
85     if modifierspec['name'] == 'lumi':
86         return None
87     mod_map = {
88         'histosys': 'HistoSys',
89         'staterror': 'StatError',
90         'normsys': 'OverallSys',
91         'shapesys': 'ShapeSys',
92         'normfactor': 'NormFactor',
93         'shapefactor': 'ShapeFactor',
94     }
95 
96     attrs = {'Name': modifierspec['name']}
97     if modifierspec['type'] == 'histosys':
98         attrs['HistoNameLow'] = _make_hist_name(
99             channelname, samplename, modifierspec['name'], suffix='Low'
100         )
101         attrs['HistoNameHigh'] = _make_hist_name(
102             channelname, samplename, modifierspec['name'], suffix='High'
103         )
104         _export_root_histogram(attrs['HistoNameLow'], modifierspec['data']['lo_data'])
105         _export_root_histogram(attrs['HistoNameHigh'], modifierspec['data']['hi_data'])
106     elif modifierspec['type'] == 'normsys':
107         attrs['High'] = str(modifierspec['data']['hi'])
108         attrs['Low'] = str(modifierspec['data']['lo'])
109     elif modifierspec['type'] == 'normfactor':
110         attrs['Val'] = '1'
111         attrs['High'] = '10'
112         attrs['Low'] = '0'
113     elif modifierspec['type'] == 'staterror':
114         attrs['Activate'] = 'True'
115         attrs['HistoName'] = _make_hist_name(
116             channelname, samplename, modifierspec['name']
117         )
118         # need to make this a relative uncertainty stored in ROOT file
119         _export_root_histogram(
120             attrs['HistoName'], np.divide(modifierspec['data'], sampledata).tolist()
121         )
122     elif modifierspec['type'] == 'shapesys':
123         attrs['ConstraintType'] = 'Poisson'
124         attrs['HistoName'] = _make_hist_name(
125             channelname, samplename, modifierspec['name']
126         )
127         # need to make this a relative uncertainty stored in ROOT file
128         _export_root_histogram(
129             attrs['HistoName'],
130             [np.divide(a, b) for a, b in zip(modifierspec['data'], sampledata)],
131         )
132     else:
133         log.warning(
134             'Skipping {0}({1}) for now'.format(
135                 modifierspec['name'], modifierspec['type']
136             )
137         )
138 
139     modifier = ET.Element(mod_map[modifierspec['type']], **attrs)
140     return modifier
141 
142 
143 def build_sample(samplespec, channelname):
144     histname = _make_hist_name(channelname, samplespec['name'])
145     attrs = {
146         'Name': samplespec['name'],
147         'HistoName': histname,
148         'InputFile': _ROOT_DATA_FILE._path,
149         'NormalizeByTheory': 'False',
150     }
151     sample = ET.Element('Sample', **attrs)
152     for modspec in samplespec['modifiers']:
153         # if lumi modifier added for this sample, need to set NormalizeByTheory
154         if modspec['type'] == 'lumi':
155             sample.attrib.update({'NormalizeByTheory': 'True'})
156         modifier = build_modifier(
157             modspec, channelname, samplespec['name'], samplespec['data']
158         )
159         if modifier is not None:
160             sample.append(modifier)
161     _export_root_histogram(histname, samplespec['data'])
162     return sample
163 
164 
165 def build_data(dataspec, channelname):
166     histname = _make_hist_name(channelname, 'data')
167     data = ET.Element('Data', HistoName=histname, InputFile=_ROOT_DATA_FILE._path)
168     _export_root_histogram(histname, dataspec[channelname])
169     return data
170 
171 
172 def build_channel(channelspec, dataspec):
173     channel = ET.Element(
174         'Channel', Name=channelspec['name'], InputFile=_ROOT_DATA_FILE._path
175     )
176     if dataspec:
177         data = build_data(dataspec, channelspec['name'])
178         channel.append(data)
179     for samplespec in channelspec['samples']:
180         channel.append(build_sample(samplespec, channelspec['name']))
181     return channel
182 
183 
184 def writexml(spec, specdir, data_rootdir, resultprefix):
185     global _ROOT_DATA_FILE
186 
187     combination = ET.Element(
188         "Combination", OutputFilePrefix=os.path.join('.', specdir, resultprefix)
189     )
190 
191     with uproot.recreate(os.path.join(data_rootdir, 'data.root')) as _ROOT_DATA_FILE:
192         for channelspec in spec['channels']:
193             channelfilename = os.path.join(
194                 specdir, '{0:s}_{1:s}.xml'.format(resultprefix, channelspec['name'])
195             )
196             with open(channelfilename, 'w') as channelfile:
197                 channel = build_channel(channelspec, spec.get('data'))
198                 indent(channel)
199                 channelfile.write(
200                     ET.tostring(channel, encoding='utf-8').decode('utf-8')
201                 )
202 
203             inp = ET.Element("Input")
204             inp.text = channelfilename
205             combination.append(inp)
206 
207     for measurement in spec['toplvl']['measurements']:
208         combination.append(build_measurement(measurement))
209     indent(combination)
210     return ET.tostring(combination, encoding='utf-8')
211
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyhf/writexml.py b/pyhf/writexml.py
--- a/pyhf/writexml.py
+++ b/pyhf/writexml.py
@@ -1,6 +1,8 @@
 import logging
 
 import os
+import shutil
+import pkg_resources
 import xml.etree.cElementTree as ET
 import numpy as np
 import uproot
@@ -68,7 +70,13 @@
             lumierr = parameter['sigmas'][0]
 
     # define measurement
-    meas = ET.Element("Measurement", Name=name, Lumi=str(lumi), LumiRelErr=str(lumierr))
+    meas = ET.Element(
+        "Measurement",
+        Name=name,
+        Lumi=str(lumi),
+        LumiRelErr=str(lumierr),
+        ExportOnly=str(True),
+    )
     poiel = ET.Element('POI')
     poiel.text = poi
     meas.append(poiel)
@@ -115,6 +123,7 @@
         attrs['HistoName'] = _make_hist_name(
             channelname, samplename, modifierspec['name']
         )
+        del attrs['Name']
         # need to make this a relative uncertainty stored in ROOT file
         _export_root_histogram(
             attrs['HistoName'], np.divide(modifierspec['data'], sampledata).tolist()
@@ -184,6 +193,10 @@
 def writexml(spec, specdir, data_rootdir, resultprefix):
     global _ROOT_DATA_FILE
 
+    shutil.copyfile(
+        pkg_resources.resource_filename(__name__, 'data/HistFactorySchema.dtd'),
+        os.path.join(os.path.dirname(specdir), 'HistFactorySchema.dtd'),
+    )
     combination = ET.Element(
         "Combination", OutputFilePrefix=os.path.join('.', specdir, resultprefix)
     )
@@ -196,6 +209,9 @@
             with open(channelfilename, 'w') as channelfile:
                 channel = build_channel(channelspec, spec.get('data'))
                 indent(channel)
+                channelfile.write(
+                    "<!DOCTYPE Channel SYSTEM '../HistFactorySchema.dtd'>\n\n"
+                )
                 channelfile.write(
                     ET.tostring(channel, encoding='utf-8').decode('utf-8')
                 )
@@ -207,4 +223,6 @@
     for measurement in spec['toplvl']['measurements']:
         combination.append(build_measurement(measurement))
     indent(combination)
-    return ET.tostring(combination, encoding='utf-8')
+    return "<!DOCTYPE Combination  SYSTEM 'HistFactorySchema.dtd'>\n\n".encode(
+        "utf-8"
+    ) + ET.tostring(combination, encoding='utf-8')

{"golden_diff": "diff --git a/pyhf/writexml.py b/pyhf/writexml.py\n--- a/pyhf/writexml.py\n+++ b/pyhf/writexml.py\n@@ -1,6 +1,8 @@\n import logging\n \n import os\n+import shutil\n+import pkg_resources\n import xml.etree.cElementTree as ET\n import numpy as np\n import uproot\n@@ -68,7 +70,13 @@\n             lumierr = parameter['sigmas'][0]\n \n     # define measurement\n-    meas = ET.Element(\"Measurement\", Name=name, Lumi=str(lumi), LumiRelErr=str(lumierr))\n+    meas = ET.Element(\n+        \"Measurement\",\n+        Name=name,\n+        Lumi=str(lumi),\n+        LumiRelErr=str(lumierr),\n+        ExportOnly=str(True),\n+    )\n     poiel = ET.Element('POI')\n     poiel.text = poi\n     meas.append(poiel)\n@@ -115,6 +123,7 @@\n         attrs['HistoName'] = _make_hist_name(\n             channelname, samplename, modifierspec['name']\n         )\n+        del attrs['Name']\n         # need to make this a relative uncertainty stored in ROOT file\n         _export_root_histogram(\n             attrs['HistoName'], np.divide(modifierspec['data'], sampledata).tolist()\n@@ -184,6 +193,10 @@\n def writexml(spec, specdir, data_rootdir, resultprefix):\n     global _ROOT_DATA_FILE\n \n+    shutil.copyfile(\n+        pkg_resources.resource_filename(__name__, 'data/HistFactorySchema.dtd'),\n+        os.path.join(os.path.dirname(specdir), 'HistFactorySchema.dtd'),\n+    )\n     combination = ET.Element(\n         \"Combination\", OutputFilePrefix=os.path.join('.', specdir, resultprefix)\n     )\n@@ -196,6 +209,9 @@\n             with open(channelfilename, 'w') as channelfile:\n                 channel = build_channel(channelspec, spec.get('data'))\n                 indent(channel)\n+                channelfile.write(\n+                    \"<!DOCTYPE Channel SYSTEM '../HistFactorySchema.dtd'>\\n\\n\"\n+                )\n                 channelfile.write(\n                     ET.tostring(channel, encoding='utf-8').decode('utf-8')\n                 )\n@@ -207,4 +223,6 @@\n     for measurement in spec['toplvl']['measurements']:\n         combination.append(build_measurement(measurement))\n     indent(combination)\n-    return ET.tostring(combination, encoding='utf-8')\n+    return \"<!DOCTYPE Combination  SYSTEM 'HistFactorySchema.dtd'>\\n\\n\".encode(\n+        \"utf-8\"\n+    ) + ET.tostring(combination, encoding='utf-8')\n", "issue": "json2xml needs to add support for the HiFa DTD\n# Description\r\n\r\njson2xml does not include the DTD statement in the XML outputs that it generates.\r\n\r\n# Expected Behavior\r\n\r\nShould include DTD statement.\r\n\r\n# Actual Behavior\r\n\r\nDoes not include DTD statement.\r\n\r\n# Checklist\r\n\r\n- [x] Run `git fetch` to get the most up to date version of `master`\r\n- [x] Searched through existing Issues to confirm this is not a duplicate issue\r\n- [x] Filled out the Description, Expected Behavior, Actual Behavior, and Steps to Reproduce sections above or have edited/removed them in a way that fully describes the issue\r\n\n", "before_files": [{"content": "import logging\n\nimport os\nimport xml.etree.cElementTree as ET\nimport numpy as np\nimport uproot\nfrom uproot_methods.classes import TH1\n\n_ROOT_DATA_FILE = None\n\nlog = logging.getLogger(__name__)\n\n\ndef _make_hist_name(channel, sample, modifier='', prefix='hist', suffix=''):\n    return \"{prefix}{middle}{suffix}\".format(\n        prefix=prefix,\n        suffix=suffix,\n        middle='_'.join(filter(lambda x: x, [channel, sample, modifier])),\n    )\n\n\ndef _export_root_histogram(histname, data):\n    h = TH1.from_numpy((np.asarray(data), np.arange(len(data) + 1)))\n    h._fName = histname\n    # NB: uproot crashes for some reason, figure out why later\n    # if histname in _ROOT_DATA_FILE:\n    #    raise KeyError('Duplicate key {0} being written.'.format(histname))\n    _ROOT_DATA_FILE[histname] = h\n\n\n# https://stackoverflow.com/a/4590052\ndef indent(elem, level=0):\n    i = \"\\n\" + level * \"  \"\n    if elem:\n        if not elem.text or not elem.text.strip():\n            elem.text = i + \"  \"\n        if not elem.tail or not elem.tail.strip():\n            elem.tail = i\n        for elem in elem:\n            indent(elem, level + 1)\n        if not elem.tail or not elem.tail.strip():\n            elem.tail = i\n    else:\n        if level and (not elem.tail or not elem.tail.strip()):\n            elem.tail = i\n\n\ndef build_measurement(measurementspec):\n    config = measurementspec['config']\n    name = measurementspec['name']\n    poi = config['poi']\n\n    # we want to know which parameters are fixed (constant)\n    # and to additionally extract the luminosity information\n    fixed_params = []\n    lumi = 1.0\n    lumierr = 0.0\n    for parameter in config['parameters']:\n        if parameter.get('fixed', False):\n            pname = parameter['name']\n            if pname == 'lumi':\n                fixed_params.append('Lumi')\n            else:\n                fixed_params.append(pname)\n        # we found luminosity, so handle it\n        if parameter['name'] == 'lumi':\n            lumi = parameter['auxdata'][0]\n            lumierr = parameter['sigmas'][0]\n\n    # define measurement\n    meas = ET.Element(\"Measurement\", Name=name, Lumi=str(lumi), LumiRelErr=str(lumierr))\n    poiel = ET.Element('POI')\n    poiel.text = poi\n    meas.append(poiel)\n\n    # add fixed parameters (constant)\n    if fixed_params:\n        se = ET.Element('ParamSetting', Const='True')\n        se.text = ' '.join(fixed_params)\n        meas.append(se)\n    return meas\n\n\ndef build_modifier(modifierspec, channelname, samplename, sampledata):\n    if modifierspec['name'] == 'lumi':\n        return None\n    mod_map = {\n        'histosys': 'HistoSys',\n        'staterror': 'StatError',\n        'normsys': 'OverallSys',\n        'shapesys': 'ShapeSys',\n        'normfactor': 'NormFactor',\n        'shapefactor': 'ShapeFactor',\n    }\n\n    attrs = {'Name': modifierspec['name']}\n    if modifierspec['type'] == 'histosys':\n        attrs['HistoNameLow'] = _make_hist_name(\n            channelname, samplename, modifierspec['name'], suffix='Low'\n        )\n        attrs['HistoNameHigh'] = _make_hist_name(\n            channelname, samplename, modifierspec['name'], suffix='High'\n        )\n        _export_root_histogram(attrs['HistoNameLow'], modifierspec['data']['lo_data'])\n        _export_root_histogram(attrs['HistoNameHigh'], modifierspec['data']['hi_data'])\n    elif modifierspec['type'] == 'normsys':\n        attrs['High'] = str(modifierspec['data']['hi'])\n        attrs['Low'] = str(modifierspec['data']['lo'])\n    elif modifierspec['type'] == 'normfactor':\n        attrs['Val'] = '1'\n        attrs['High'] = '10'\n        attrs['Low'] = '0'\n    elif modifierspec['type'] == 'staterror':\n        attrs['Activate'] = 'True'\n        attrs['HistoName'] = _make_hist_name(\n            channelname, samplename, modifierspec['name']\n        )\n        # need to make this a relative uncertainty stored in ROOT file\n        _export_root_histogram(\n            attrs['HistoName'], np.divide(modifierspec['data'], sampledata).tolist()\n        )\n    elif modifierspec['type'] == 'shapesys':\n        attrs['ConstraintType'] = 'Poisson'\n        attrs['HistoName'] = _make_hist_name(\n            channelname, samplename, modifierspec['name']\n        )\n        # need to make this a relative uncertainty stored in ROOT file\n        _export_root_histogram(\n            attrs['HistoName'],\n            [np.divide(a, b) for a, b in zip(modifierspec['data'], sampledata)],\n        )\n    else:\n        log.warning(\n            'Skipping {0}({1}) for now'.format(\n                modifierspec['name'], modifierspec['type']\n            )\n        )\n\n    modifier = ET.Element(mod_map[modifierspec['type']], **attrs)\n    return modifier\n\n\ndef build_sample(samplespec, channelname):\n    histname = _make_hist_name(channelname, samplespec['name'])\n    attrs = {\n        'Name': samplespec['name'],\n        'HistoName': histname,\n        'InputFile': _ROOT_DATA_FILE._path,\n        'NormalizeByTheory': 'False',\n    }\n    sample = ET.Element('Sample', **attrs)\n    for modspec in samplespec['modifiers']:\n        # if lumi modifier added for this sample, need to set NormalizeByTheory\n        if modspec['type'] == 'lumi':\n            sample.attrib.update({'NormalizeByTheory': 'True'})\n        modifier = build_modifier(\n            modspec, channelname, samplespec['name'], samplespec['data']\n        )\n        if modifier is not None:\n            sample.append(modifier)\n    _export_root_histogram(histname, samplespec['data'])\n    return sample\n\n\ndef build_data(dataspec, channelname):\n    histname = _make_hist_name(channelname, 'data')\n    data = ET.Element('Data', HistoName=histname, InputFile=_ROOT_DATA_FILE._path)\n    _export_root_histogram(histname, dataspec[channelname])\n    return data\n\n\ndef build_channel(channelspec, dataspec):\n    channel = ET.Element(\n        'Channel', Name=channelspec['name'], InputFile=_ROOT_DATA_FILE._path\n    )\n    if dataspec:\n        data = build_data(dataspec, channelspec['name'])\n        channel.append(data)\n    for samplespec in channelspec['samples']:\n        channel.append(build_sample(samplespec, channelspec['name']))\n    return channel\n\n\ndef writexml(spec, specdir, data_rootdir, resultprefix):\n    global _ROOT_DATA_FILE\n\n    combination = ET.Element(\n        \"Combination\", OutputFilePrefix=os.path.join('.', specdir, resultprefix)\n    )\n\n    with uproot.recreate(os.path.join(data_rootdir, 'data.root')) as _ROOT_DATA_FILE:\n        for channelspec in spec['channels']:\n            channelfilename = os.path.join(\n                specdir, '{0:s}_{1:s}.xml'.format(resultprefix, channelspec['name'])\n            )\n            with open(channelfilename, 'w') as channelfile:\n                channel = build_channel(channelspec, spec.get('data'))\n                indent(channel)\n                channelfile.write(\n                    ET.tostring(channel, encoding='utf-8').decode('utf-8')\n                )\n\n            inp = ET.Element(\"Input\")\n            inp.text = channelfilename\n            combination.append(inp)\n\n    for measurement in spec['toplvl']['measurements']:\n        combination.append(build_measurement(measurement))\n    indent(combination)\n    return ET.tostring(combination, encoding='utf-8')\n", "path": "pyhf/writexml.py"}], "after_files": [{"content": "import logging\n\nimport os\nimport shutil\nimport pkg_resources\nimport xml.etree.cElementTree as ET\nimport numpy as np\nimport uproot\nfrom uproot_methods.classes import TH1\n\n_ROOT_DATA_FILE = None\n\nlog = logging.getLogger(__name__)\n\n\ndef _make_hist_name(channel, sample, modifier='', prefix='hist', suffix=''):\n    return \"{prefix}{middle}{suffix}\".format(\n        prefix=prefix,\n        suffix=suffix,\n        middle='_'.join(filter(lambda x: x, [channel, sample, modifier])),\n    )\n\n\ndef _export_root_histogram(histname, data):\n    h = TH1.from_numpy((np.asarray(data), np.arange(len(data) + 1)))\n    h._fName = histname\n    # NB: uproot crashes for some reason, figure out why later\n    # if histname in _ROOT_DATA_FILE:\n    #    raise KeyError('Duplicate key {0} being written.'.format(histname))\n    _ROOT_DATA_FILE[histname] = h\n\n\n# https://stackoverflow.com/a/4590052\ndef indent(elem, level=0):\n    i = \"\\n\" + level * \"  \"\n    if elem:\n        if not elem.text or not elem.text.strip():\n            elem.text = i + \"  \"\n        if not elem.tail or not elem.tail.strip():\n            elem.tail = i\n        for elem in elem:\n            indent(elem, level + 1)\n        if not elem.tail or not elem.tail.strip():\n            elem.tail = i\n    else:\n        if level and (not elem.tail or not elem.tail.strip()):\n            elem.tail = i\n\n\ndef build_measurement(measurementspec):\n    config = measurementspec['config']\n    name = measurementspec['name']\n    poi = config['poi']\n\n    # we want to know which parameters are fixed (constant)\n    # and to additionally extract the luminosity information\n    fixed_params = []\n    lumi = 1.0\n    lumierr = 0.0\n    for parameter in config['parameters']:\n        if parameter.get('fixed', False):\n            pname = parameter['name']\n            if pname == 'lumi':\n                fixed_params.append('Lumi')\n            else:\n                fixed_params.append(pname)\n        # we found luminosity, so handle it\n        if parameter['name'] == 'lumi':\n            lumi = parameter['auxdata'][0]\n            lumierr = parameter['sigmas'][0]\n\n    # define measurement\n    meas = ET.Element(\n        \"Measurement\",\n        Name=name,\n        Lumi=str(lumi),\n        LumiRelErr=str(lumierr),\n        ExportOnly=str(True),\n    )\n    poiel = ET.Element('POI')\n    poiel.text = poi\n    meas.append(poiel)\n\n    # add fixed parameters (constant)\n    if fixed_params:\n        se = ET.Element('ParamSetting', Const='True')\n        se.text = ' '.join(fixed_params)\n        meas.append(se)\n    return meas\n\n\ndef build_modifier(modifierspec, channelname, samplename, sampledata):\n    if modifierspec['name'] == 'lumi':\n        return None\n    mod_map = {\n        'histosys': 'HistoSys',\n        'staterror': 'StatError',\n        'normsys': 'OverallSys',\n        'shapesys': 'ShapeSys',\n        'normfactor': 'NormFactor',\n        'shapefactor': 'ShapeFactor',\n    }\n\n    attrs = {'Name': modifierspec['name']}\n    if modifierspec['type'] == 'histosys':\n        attrs['HistoNameLow'] = _make_hist_name(\n            channelname, samplename, modifierspec['name'], suffix='Low'\n        )\n        attrs['HistoNameHigh'] = _make_hist_name(\n            channelname, samplename, modifierspec['name'], suffix='High'\n        )\n        _export_root_histogram(attrs['HistoNameLow'], modifierspec['data']['lo_data'])\n        _export_root_histogram(attrs['HistoNameHigh'], modifierspec['data']['hi_data'])\n    elif modifierspec['type'] == 'normsys':\n        attrs['High'] = str(modifierspec['data']['hi'])\n        attrs['Low'] = str(modifierspec['data']['lo'])\n    elif modifierspec['type'] == 'normfactor':\n        attrs['Val'] = '1'\n        attrs['High'] = '10'\n        attrs['Low'] = '0'\n    elif modifierspec['type'] == 'staterror':\n        attrs['Activate'] = 'True'\n        attrs['HistoName'] = _make_hist_name(\n            channelname, samplename, modifierspec['name']\n        )\n        del attrs['Name']\n        # need to make this a relative uncertainty stored in ROOT file\n        _export_root_histogram(\n            attrs['HistoName'], np.divide(modifierspec['data'], sampledata).tolist()\n        )\n    elif modifierspec['type'] == 'shapesys':\n        attrs['ConstraintType'] = 'Poisson'\n        attrs['HistoName'] = _make_hist_name(\n            channelname, samplename, modifierspec['name']\n        )\n        # need to make this a relative uncertainty stored in ROOT file\n        _export_root_histogram(\n            attrs['HistoName'],\n            [np.divide(a, b) for a, b in zip(modifierspec['data'], sampledata)],\n        )\n    else:\n        log.warning(\n            'Skipping {0}({1}) for now'.format(\n                modifierspec['name'], modifierspec['type']\n            )\n        )\n\n    modifier = ET.Element(mod_map[modifierspec['type']], **attrs)\n    return modifier\n\n\ndef build_sample(samplespec, channelname):\n    histname = _make_hist_name(channelname, samplespec['name'])\n    attrs = {\n        'Name': samplespec['name'],\n        'HistoName': histname,\n        'InputFile': _ROOT_DATA_FILE._path,\n        'NormalizeByTheory': 'False',\n    }\n    sample = ET.Element('Sample', **attrs)\n    for modspec in samplespec['modifiers']:\n        # if lumi modifier added for this sample, need to set NormalizeByTheory\n        if modspec['type'] == 'lumi':\n            sample.attrib.update({'NormalizeByTheory': 'True'})\n        modifier = build_modifier(\n            modspec, channelname, samplespec['name'], samplespec['data']\n        )\n        if modifier is not None:\n            sample.append(modifier)\n    _export_root_histogram(histname, samplespec['data'])\n    return sample\n\n\ndef build_data(dataspec, channelname):\n    histname = _make_hist_name(channelname, 'data')\n    data = ET.Element('Data', HistoName=histname, InputFile=_ROOT_DATA_FILE._path)\n    _export_root_histogram(histname, dataspec[channelname])\n    return data\n\n\ndef build_channel(channelspec, dataspec):\n    channel = ET.Element(\n        'Channel', Name=channelspec['name'], InputFile=_ROOT_DATA_FILE._path\n    )\n    if dataspec:\n        data = build_data(dataspec, channelspec['name'])\n        channel.append(data)\n    for samplespec in channelspec['samples']:\n        channel.append(build_sample(samplespec, channelspec['name']))\n    return channel\n\n\ndef writexml(spec, specdir, data_rootdir, resultprefix):\n    global _ROOT_DATA_FILE\n\n    shutil.copyfile(\n        pkg_resources.resource_filename(__name__, 'data/HistFactorySchema.dtd'),\n        os.path.join(os.path.dirname(specdir), 'HistFactorySchema.dtd'),\n    )\n    combination = ET.Element(\n        \"Combination\", OutputFilePrefix=os.path.join('.', specdir, resultprefix)\n    )\n\n    with uproot.recreate(os.path.join(data_rootdir, 'data.root')) as _ROOT_DATA_FILE:\n        for channelspec in spec['channels']:\n            channelfilename = os.path.join(\n                specdir, '{0:s}_{1:s}.xml'.format(resultprefix, channelspec['name'])\n            )\n            with open(channelfilename, 'w') as channelfile:\n                channel = build_channel(channelspec, spec.get('data'))\n                indent(channel)\n                channelfile.write(\n                    \"<!DOCTYPE Channel SYSTEM '../HistFactorySchema.dtd'>\\n\\n\"\n                )\n                channelfile.write(\n                    ET.tostring(channel, encoding='utf-8').decode('utf-8')\n                )\n\n            inp = ET.Element(\"Input\")\n            inp.text = channelfilename\n            combination.append(inp)\n\n    for measurement in spec['toplvl']['measurements']:\n        combination.append(build_measurement(measurement))\n    indent(combination)\n    return \"<!DOCTYPE Combination  SYSTEM 'HistFactorySchema.dtd'>\\n\\n\".encode(\n        \"utf-8\"\n    ) + ET.tostring(combination, encoding='utf-8')\n", "path": "pyhf/writexml.py"}]}