<jupyter_start><jupyter_text>Z_ee Example for ATLAS xAOD FilesetThis notebook demonstrates the use of ServiceX and the _Coffea_ Local Executor to extract electrondata from an ATLAS Dataset and plot the mass of the Z Boson<jupyter_code>from servicex import ServiceXDataset
from coffea.processor.servicex import DataSource, FuncAdlDataset, Analysis
from coffea.processor.servicex import LocalExecutor 

import matplotlib.pyplot as plt

from coffea import hist, processor
from IPython.display import display, update_display, HTML<jupyter_output><empty_output><jupyter_text>Specify the Dataset IdentifierThe interface can easily process multiple datasets. Here we create a single one with the Rucio DID along with a backend specification that tells ServiceX these will be xAOD files<jupyter_code>dids = ['mc15_13TeV:mc15_13TeV.361106.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zee.merge.DAOD_STDM3.e3601_s2576_s2132_r6630_r6264_p2363_tid05630052_00']
datasets = [
    ServiceXDataset(did, backend_type='xaod', ignore_cache=False)
    for did in dids
]<jupyter_output><empty_output><jupyter_text>Specify The QueryWe will be using the [func_adl](https://github.com/iris-hep/func_adl_xAOD) language to specify the electron data wewant retrieved from the events along with a simple filter that only returns events with two electrons<jupyter_code>leptons_per_event_query = FuncAdlDataset() \
        .Select(lambda e: e.Electrons("Electrons")) \
        .Select(lambda eles: eles.Where(lambda e: e.pt()/1000.0 > 30.0)) \
        .Select(lambda eles: eles.Where(lambda e: abs(e.eta()) < 2.5)) \
        .Where(lambda eles: len(eles) == 2) \
        .Select(lambda ls: (ls.Select(lambda e: e.pt()/1000.0), ls.Select(lambda e: e.eta()), ls.Select(lambda e: e.phi()), ls.Select(lambda e: e.m()/1000.0), ls.Select(lambda e: e.charge()))) \
        .AsROOTTTree('data.root', 'mytree', ('electrons_pt', 'electrons_eta', 'electrons_phi', 'electrons_mass', 'electrons_charge'))
datasource = DataSource(query=leptons_per_event_query, metadata={}, datasets=datasets)<jupyter_output><empty_output><jupyter_text>The Physics CodeWe create a python function that simply accepts a _Coffea_ [NanoEvents](https://coffeateam.github.io/coffea/modules/coffea.nanoevents.html) instance that contains all of the events from a single root file out of the dataset. It returns a dict of histograms.In this case `sumw` is the total number of events and `mass` is a historgram of the dielectron mass<jupyter_code>class Z_EEAnalysis(Analysis):
    @staticmethod
    def process(events):
        import awkward as ak
        from collections import defaultdict

        sumw = defaultdict(float)
        mass_hist = hist.Hist(
            "Events",
            hist.Cat("dataset", "Dataset"),
            hist.Bin("mass", "$Z_{ee}$ [GeV]", 60, 60, 120),
        )

        dataset = events.metadata['dataset']
        electrons = events.electrons

        # Form the invar mass, plot.
        cut = (ak.num(electrons) == 2)
        diele = electrons[cut][:, 0] + electrons[cut][:, 1]

        sumw[dataset] += len(events)
        mass_hist.fill(
            dataset=dataset,
            mass=diele.mass,
        )
        
        return {
            "sumw": sumw,
            "mass": mass_hist
        }<jupyter_output><empty_output><jupyter_text>Create an ExecutorWe use an executor instance to receive the events streaming out of ServiceX and apply the analysis function. This is using the `LocalExecutor` which just runs on your local computer. There is support for Dask, FuncX, or Ray exectors.<jupyter_code>analysis = Z_EEAnalysis()
executor = LocalExecutor()<jupyter_output><empty_output><jupyter_text>Run the Analysis and Dynamically Plot the ResultsCreate a little asynchronous function that awaits results from the analysis and updates a histogram plot<jupyter_code>%matplotlib inline
async def plot_stream(accumulator_stream):
  global first
  fig, axes = plt.subplots()
  first = True


  count = 0
  async for coffea_info in accumulator_stream:
    hist.plot1d(coffea_info['mass'], ax=axes)

    count += 1
    plt.text(0.95, 0.8, f'Chunks of data: {count}', horizontalalignment='right', transform=axes.transAxes)

    # Either display it or update a previous version of the plot
    if first:
        display(fig, display_id='mass_update')
        first = False
    else:
        update_display(fig, display_id='mass_update')
  return coffea_info

await plot_stream(executor.execute(analysis, datasource))
plt.close()  # Prevents another copy of the plot showing up in the notebook<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09547738693467336, 'fraction_numerical': 0.019882018789600175, 'mean_word_length': 4.155405405405405, 'pattern_counts': {'":': 2, '<': 26, '<?xml version=': 0, '>': 26, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '267030', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1514, 'n_tokens_neox': 1429, 'n_words': 417, 'path': 'examples/ATLAS/Z_ee.ipynb', 'repo_name': 'fengpinghu/ServiceX', 'size': '4577'}