• Halbwegs gute Nachrichten

    Eine Kurve mit einem breitem Minimum um die 8.5

    Aus dieser Kurve lässt sich ablesen, dass wir in acht Tagen knapp 3500 SARS-2-Fälle auf Intensivstationen haben werden. Wie, verrate ich in diesem Artikel.

    In meinen Corona-Überlegungen gestern habe ich mich mal wieder gefragt, wie lange wohl die „Intensiv-Antwort“ dem Inzidenzsignal nachläuft, wie viele Tage es also dauert, bis sich ein Anstieg in den Inzidenzen in der Intensivbelegung reflektiert. Diese Frage ist, wie ich unten ausführe, derzeit ziemlich relevant im Hinblick auf Überlegungen, wie lange wir eigentlich noch Zeit haben, um massenhafte Triage in (oder vor) unseren Intensivstationen abzuwenden.

    Meine Null-Annahme für diese Verzögerung war seit Mai 2020 – nach einer entsprechenden Ansage eines befreundeten Anästhesisten – „eher so drei Wochen“. Bei nährem Nachdenken ist mir gestern aber aufgefallen, dass er wohl eher „von Infektion bis Intensiv“ gemeint haben wird, und dann ist die Antwort sicher schneller, denn von Infektion bis Meldung vergeht normalerweise wohl mindestens eine Woche. Aber: Ich muss nicht raten. Wir spielen hier mit Kennzahlen, die vielleicht in der Realität nicht immer viel bedeuten, aber zumindest klar definiert sind. Daher lässt sich der Verzug auf der Basis von RKI- und DIVI-Zahlen nachrechnen.

    Ich verrate gleich mal das Ergebnis: ich komme für die zweite Welle auf gut fünf Tage Verzug der Intensiv-Antwort, für die dritte auf etwa sieben Tage, für die vierte Welle auf acht bis neun Tage. Wie ich unten ausführe, sind das relativ gute Nachrichten.

    Zeitreihen

    Wie habe ich das gerechnet? Nun, die Korrelation von Zeitreihen ist ein ganzer Satz von Wissenschaften, bei denen es meist darum geht, ungleiche Abdeckungen, unregelmäßig gesetzte Messpunkte sowie allerlei Rauschen und Schmutz weggefummelt zu kriegen, ohne allzu viele Informationen zu verlieren oder, vielleicht schlimmer, damit Artefakte einzubauen.

    Die so gereinigten Daten lassen sich dann zum Beispiel geeignet skaliert und verschoben übereinanderlegen. Tatsächlich sind die Parameter dieser Transformationen im Regelfall (und gewissermaßen auch hier) viel interessanter als die Zeitreihen selbst[1]. Es gibt daher zahlreiche mathematische Verfahren, die das von einer Augenmaß-Übung in etwas verwandeln, das reproduzierbar und auch quantifizierbar ist. Vorsicht: ich bin da kein Experte und gehe hier nur mit nicht allzu schwer erkranktem Menschenverstand ran, verwende also (zumindest in Summe) gerade kein wirklich wohldurchdachtes Verfahren. Wer sowas wie das hier für Hausaufgaben oder Hausarbeiten verwendet, tut das auf eigene Gefahr.

    Andererseits bin ich recht zuversichtlich, dass der Kram insgesamt schon stimmt.

    Ausgangsdaten sind meine aus RKI-Berichten gescrapten Intensivbelegungen und ein RKI-Sheet (in, ach weh, „Office Open XML“ a.k.a. XSLX – muss das sein?) mit den Inzidenzen. Dabei ist das erste Problem, dass ich aus verschiedenen Gründen nicht für jeden Tag Belegungszahlen habe, und so ist mein erster Schritt, fehlende Punkte zu interpolieren. Das Scipy-Paket macht es leicht, aus einem Satz von Zeit/Wert-Paaren (die Zeit wird in den Lesefunktionen auf Sekunden seit einer Epoche gewandelt) ein handliches Array zu rechnen:

    grid_points = numpy.arange(
      raw_crits[0][0],
      raw_crits[-1][0],
      86400)  # ein Tag in Sekunden
    critnums = interpolate.griddata(
      raw_crits[:,0], raw_crits[:,1], grid_points)
    

    Ich wollte diese Interpolation eigentlich visualisieren, habe aber keine gute Stelle gefunden, an der sie einen sichtbaren Unterschied gemacht hätte, und entscheidend ist eigentlich nur, dass ich ab diesem Schritt blind mit Arrays arbeiten kann; deren Index ist zunächst die Zahl der Tage seit dem ersten Tag mit Daten, hier speziell dem 11.8.2020, denn damals habe ich mit dem Screenscrapen der DIVI-Daten angefangen.

    Übergeplottete Kurven

    Wenn ich diese interpolierten Kurven übereinanderlege, kommt das hier heraus:

    Zwei nicht so arg gut aufeinanderpassende Kurven

    Weil das, wie gesagt, erst im August 2020 anfängt, fehlt die erste Welle.

    Das bloße Auge reicht für die Bestätigung der Erwartung, dass die Intensivbelegung der Inzidenzkurve meist etwas hinterherläuft, wenn auch nicht so, dass mensch hoffen könnte, die beiden durch etwas Schieben global übereinanderzubekommen. Die großen Zacken in den Inzidenzen durch Weihnachten und Ostern finden sich in der Intensivbelegung gar nicht wieder, was ein klares Zeichen ist, dass sie weitgehend Erfassungsartefakte sind. In der Hinsicht wären die „großen“ RKI-Zahlen mit Referenzdaten (vgl. die Film-Geschichte) bestimmt besser, aber ich wollte für dieses Ding nicht die 200 Megabyte durchkämmen, zumal die „kleinen“ RKI-Daten besser auf die Meldedaten aus den Tagesberichten passen, um die es mir hier ja geht.

    Vor allem fällt auf, dass meine Jammerei darüber, dass der Impffortschritt die Intensivantwort nicht wesentlich abgeflacht hat, unzutreffend ist: Mit der Skalierung aus dem Plot liegen Inzidenz und Belegung in der zweiten und dritten Welle ziemlich übereinander, während in der vierten Welle doch ein knapper Faktor zwei dazwischenliegt; da scheint die Impfung doch ein wenig gegenüber Delta zu gewinnen (aber, klar, nirgendwo hinreichend).

    Ich hatte erwartet, dass die abfallenden Flanken der Intensivkurven deutlich flacher sind als die der Inzidenzkurven, weil Leute unter Umständen lang auf Intensiv liegen und es entsprechend lang dauern sollte, bis sich die Stationen wieder leeren. Das sieht im Abfall der zweiten Welle auch ein wenig so aus, nicht jedoch bei dem der dritten Welle. Bei ihr fällt die Intensivbelegung sehr treu mit der Inzidenz. So viele LangzeitpatientInnen gibt es glücklicherweise wohl doch nicht.

    Nach Glätten differenzierbar

    Wie kann ich jetzt den Verzug zu quantifizieren? Mein (wie gesagt eher intuitiv gefasster) Plan ist, über die Ableitung der jeweiligen Kurven zu gehen, und zwar aus der Überlegung heraus, dass mich ja Veränderungen viel mehr als Pegel interessieren. Nun habe ich aber keine differenzierbaren Funktionen, sondern lediglich Arrays, bei denen ich Ableitungen allenfalls durch Subtrahieren benachbarter Elemente simulieren kann. Solche numerischen „Ableitungen“ reagieren ziemlich empfindlich auf das Gewackel („Rauschen“), das es in realen Daten immer gibt („Subtraktion ist in der Regel numerisch schlecht konditioniert“). Deshalb will ich meine rohen Daten glätten, bevor ich die „Ableitung“ ausrechne, sprich: das Rauschen rausnehmen, ohne das Signal wesentlich zu verzerren.

    Glättung heißt eigentlich immer, Kurvenpunkte in einem Zelle für Zelle über die Daten laufenden Fenster zu mitteln, also z.B., indem mensch je fünf Nachbarpunkte rechts und links auf den aktuellen Punkt draufaddiert und ihn dann durch die durch elf geteilte Summe ersetzt. Mit diesem ganz naiven Rezept bekommen relativ weit entfernte Werte aber genauso viel Einfluss auf den aktuellen Punkt wie die unmittelbaren Nachbarn. Das modelliert meist die sachlichen Grundlagen des Rauschens nur schlecht. Es ist auch aus theoretischeren Gründen normalerweise eher ungünstig.

    Der eher theoretische Hintergrund ist grob, dass bei der Glättung letztlich zwei Funktionen gefaltet werden. Das ist äquivalent dazu, die Spektren der beiden Signale (ihre Fouriertransformierten) zu multiplizieren und dann wieder zurückzutransformieren. Bei einem einfachen Fenster des oben skizierten Typs („Rechteckfunktion“) gibts nun sehr scharfe Kanten, die wiederum zu einem sehr breiten Spektrum führen, so dass auch das Spektrum der geglätteten Funktion allerlei unwillkommene Features bekommen kann (manchmal ist das aber auch genau das, was mensch haben will – hier nicht).

    Wie auch immer: scipy macht es einfach, mit einer lärmarmen Funktion – die sieht ein wenig gaußig aus und ist im nächsten Plot im kleinen Inset zu sehen – zu glätten, nämlich etwa so:

    import numpy
    from scipy import signal
    
    smoothing_kernel = numpy.kaiser(20, smoothing_width)
    smoothing_kernel = smoothing_kernel/sum(smoothing_kernel)
    convolved = signal.convolve(arr, smoothing_kernel, mode="same"
      )[smoothing_width:-smoothing_width]
    

    Die Division durch die Summe von smoothing_kernel in der zweiten Zeile sorgt dafür, dass sich an der „Höhe“ der geglätteten Funktion insgesamt nichts ändert: Sozusagen ausgeklammert ist die Faltung eine Multiplikation mit eins. Das Wegschneiden der Ränder in der letzten Zeile wiederum entfernt Punkte, bei denen fehlende Werte am Anfang und Ende der Zeitreihe im Fenster waren. Scipy ersetzt die durch Nullen, so dass die geglätteten Werte am Rand steil abfallen. Was ich hier mache, entspricht technisch dem valid-Mode der convolve-Funktion. Nur weiß ich hier zuverlässig, wie lang das Array am Schluss ist.

    Der Effekt, hier auf Indzidenzdaten der vierten Welle:

    Zwei Kurven, eng beieinander, aber eine viel rauschiger, mit einem glockenähnlichen Inset

    Damit kann ich jetzt meine numerische „Ableitung“ bilden, ohne dass mir das Ergebnis furchtbar rauscht:

    diff = convolved[1:]-convolved[:-1]
    

    Die nächsten beiden Grafiken zeigen diese Pseudo-Ableitungen für Inzidenz und Intensivbelegung. Ich habe jeweils in blau reingemalt, wie es ohne Glättung aussehen würde, um deutlich zu machen, warum diese eine gute Idee ist und was ich mit „furchtbar rauschen“ meine. Weiterverwendet werden natürlich die orangen Verläufe:

    Zwei Kurven, eine schlimm wackelnd, die andere ruhig
    Zwei Kurven, eine schlimm wackelnd, die andere ruhig

    In der Inzidenzkurve fallen wieder Weihnachten und Ostern besonders auf, weil sie selbst in den geglätteten Graphen noch wilde Ausschläge verursachen.

    In der Zeit verschieben

    Schon der optische Eindruck aus dem Rohdaten-Plot legt nahe, die einzelnen Wellen getrennt zu untersuchen, und das ist angesichts von über die Zeit stark veränderlichen Viren, Testverhältnissen, demographischen Gegebenheiten und nichtpharamzeutischen Maßnahmen sicher auch sachlich geboten.

    Deshalb hier zunächst der Verlauf der Ableitungen von Inzidenzen und Intensivbelegung in der aktuellen vierten Welle (das ist eine Kombination der rechten Enden der orangen Graphen der letzten beiden Plots):

    Zwei Kurven, die eine der anderen recht schön folgend

    In dem Graphen steckt noch eine weitere Normalisierung. Und zwar habe ich für beide Arrays etwas wie:

    incs /= sum(abs(incs))
    

    laufen lassen. Damit ist die Fläche zwischen den Kurven …

  • Corona: Neue Filme, Alte Zahlen

    Weil sich sowohl bei Inzidenz als auch bei Altersstruktur der Corona-Meldungen gerade viel tut, habe ich meine beiden Coronafilme neulich neu rechnen lassen. Dabei habe ich beim Inzidenzfilm noch darauf verzeichtet, den Wertebereich über die 350 hinaus zu erweitern, auch wenn das bewirkt, dass sowohl der Kreis mit der höchsten Inzidenz gestern (Miesbach mit 715 Fällen/100'000) als auch der Kreis mit der 33st-höchsten und mithin nur halb so hohen Inzidenz (Ostallgäu mit 350/100'000) saturiert erscheinen.

    Irgendwas werde ich da bei der nächsten Aktualisierung tun müssen, denn, wie z.B. ich im September ausgeführt habe: eine 300-er Inzidenz bedeutet, dass es sechs Jahre dauert, bis alle mal SARS-2 hatten. Es wird im anderen Worten Inzidenzen in den Tausendern brauchen, wenn SARS-2 in nächster Zeit zu einem der anderen humanen Coronaviren werden soll, mit denen zu leben wir alle schon als Kinder schniefend gelernt haben.

    XKCD-cartoon

    Randall Munroe hat mir in der Woche mal wieder aus dem Herzen gesprochen. CC-BY-NC xkcd

    Schniefend, so wie ich jetzt, denn seit letztem Freitag habe ich meine erste richtige Erklältung seit Corona. Ich hatte ganz vergessen, wie doof sowas ist (und nein, ausweislich zweier Antigentests gleich am Freitag und dann am Montag nochmal ist es kein SARS-2). Schon deshalb habe ich gestern mit viel Interesse den Wochenbericht des RKI gelesen, in dem ja immer die Ergebnisse der Influenzasurveillance (Seite 13) berichtet werden. Über die Proben von an respiratorischen Infekten erkrankten Personen steht dort gestern:

    In der virologischen Surveillance der AGI wurden in der 43. KW 2021 in insgesamt 118 von 204 eingesandten Proben (58 %) respiratorische Viren identifiziert. Darunter befanden sich 61 Proben mit Respiratorischen Synzytialviren (RSV) (30 %), 31 mit Rhinoviren (15 %), 20 mit humanen saisonalen Coronaviren (hCoV) (10 %), acht mit SARS-CoV-2 (4 %), sechs mit Parainfluenzaviren (3 %) sowie eine Probe mit humanen Metapneumoviren (0,5 %). Influenzaviren wurden in der 43. KW 2021 nicht nachgewiesen.

    Wenn das irgendwie repräsentativ ist, habe ich eine gute Chance, dass meine derzeitige Pest RSV ist und ich den Rhinoviren Unrecht getan habe, wenn ich sie schon am Freitag mit den saftigsten Flüchen belegt habe. Tatsächlich habe ich aber schon vor dem oben gezeigten XKCD 2535 überlegt, wer mich da wohl gerade quält. Ich glaube jedenfalls, Randall Munroe ist gerade auch erkältet.

    Aber zurück zu meinen Überlegungen vom September: Ich hatte damals ja bejammert, dass wir seit Anfang der Pandemie, von steilen Inzidenzflanken nach oben (etwas niedrigeres int/inc) und unten (deutlich höheres int/inc) abgesehen, eigentlich immer so 20 SARS-belegte Intensivbettern pro Inzidenzpunkt (int/inc) hatten und das grob bedeutet, dass Inzidenzen über 300 ein Gemetzel werden.

    Ich muss leider sagen, dass sich das nicht wesentlich geändert hat. An der stark steigenden Flanke am 2.11.2020 lag int/inc nach RKI-Zahlen bei 2243 ⁄ 120 ≈ 19, derzeit, ebenfalls an einer stark ansteigenden Flanke ist das 2226 ⁄ 155 ≈ 15. Seufz.

    Etwas einschränkend dazu zwei Punkte:

    • Mein Plot neulich hat, wo verfügbar, mit Referenzdaten gerechnet, also, wo rekonstruierbar, den Ansteckungs- und nicht den Meldedaten. Damit kommt mensch für Anfang November 2020 auch auf ein int/inc von rund 15; mit den aktuellen Daten geht das aber nicht, einfach weil von den jetzigen Daten viele Daten aus der Zukunft fehlen, deren Referenzdaten irgendwann mal heute sein werden. Deshalb vergleiche ich hier ganz blind in beiden Fällen die instantanen RKI-Meldezahlen und vergesse meine raffiniertere Technik vom September.
    • Ein wesentlicherer Einwand ist, dass wir in diesem Jahr von einem weitaus höheren Sockel kommen und deshalb in Wirklichkeit die Flanke in der Intensivantwort wesentlich weniger steil ist als im letzten Jahr und sie wahrscheinlich auch in Zukunft vermutlich nicht gleich auf 20 oder sowas zurücklaufen wird, wenn die Inzidenzentwicklung abflacht.

    Das mag so sein, aber qualitativ ändert das alles nicht viel: Unser int/inc ist um mindestens eine Größenordnung zu groß, als dass „wir“ entspannt auf 1000er-Inzidenzen hinlaufen könnten; ob bei 300 (sechs Jahre bis zur Endemisierung) oder bei 500 (vier Jahre) Schluss ist, ist in dieser Betrachtung eher nebensächlich.

    Mein told you so (auch schon im Juli, vierter Absatz) wäre vielleicht befriedigender, wenn das auch im September nicht eigentlich jedeR gesagt hätte, der/die nicht woandershin geschaut hat (was bis neulich sehr populär war, und nur so ist irgendwie plausibel zu machen, warum es ausgereicht jetzt hektische Krisentreffen gibt). Andererseits hatte ich damals auch gesagt:

    wir dürften also, wenn nicht ein Wunder geschieht, in sechs Wochen, Mitte Oktober, deutlich über 4000 liegen und damit in der Gegend der Notbremsenbelastung rund um Neujahr 2021.

    – und damals geschah ein Wunder, denn aus mit dem Sommerreiseverkehr endete auch die damalige exponentielle Füllung der Intensivstationen mit SARS-2-PatientInnen; in DIVI-Zahlen aus den RKI-Tagesberichten:

    Steigende Kurve mit langem Atemholen zwischen Mitte September und Mitte Oktober

    Was zwischen Mitte September und Mitte Oktober – oder, unter der Annahme, dass die Intensivantwort zwei, drei Wochen verzögert auf ihre Ursachen kommt, einfach im September – anders war als davor und danach, das würde mich wirklich interessieren.

  • Von der Gnade, ohne Bild zu sprechen

    Wenn ich „was mit Medien“ machen müsste, würde ich versuchen, irgendwas ohne Video zu erwischen. Und das nicht, weil ich nicht eitel wäre.

    Nein, die Überlegung ist eher verwandt mit meinen Ausführungen zum Panopticon Videokonferenz und werden, wie ich finde, schön illustriert durch einen Vergleich zwischen dem ruhigen, eleganten, fast schon heiteren Stolpern um Mitternacht des 3.7. im Deutschlandfunk und diesem Ausschnitt aus der Tagesschau von gestern; mein Rat wäre, das Video erst mit geschlossenen Augen laufen zu lassen:

    Wie üblich: Wenn euer Browser das nicht abspielt, beschwert euch bei dessen Hersteller. Wer schon überhaupt Videos im Browserfenster anzeigen will, kann zumindest auch webm unterstützen. Die Rechte liegen bei der ARD (also: Das Video ist nicht CC0).

    Gut: Ich habe das natürlich gleich mit all dem Gefummel mit Telefon und Brille wahrgenommen. Ich bin dennoch ziemlich sicher, dass Annette Dittert hier ohne Bild deutlich souveräner rübergekommen wäre.

  • A Local Search Engine for Pelican-based Blogs

    As the number of posts on this blog approaches 100, I figured some sort of search functionality would be in order. And since I'm wary of “free” commercial services and Free network search does not seem to go anywhere[1], the only way to offer that that is both practical and respectful of the digital rights of my readers is to have a local search engine. True, having a search engine running somewhat defeats the purpose of a static blog, except that there's a lot less code necessary for doing a simple search than for running a CMS, and of course you still get to version-control your posts.

    I have to admit that the “less code” argument is a bit relative given that I'm using xapian as a full-text indexer here. But I've long wanted to play with it, and it seems reasonably well-written and well-maintained. I have hence written a little CGI script enabling search over static collections of HTML files, which means in particular pelican blogs. In this post, I'll tell you first a few things about how this is written and then how you'd run it yourself.

    Using Xapian: Indexing

    At its core, xapian is not much more than an inverted index: Essentially, you feed it words (“tokens”), and it will generate a database pointing from each word to the documents that contain it.

    The first thing to understand when using xapian is that it doesn't really have a model of what exactly a document is; the example indexer code, for instance, indexes a text file such that each paragraph is treated as a separate document. All xapian itself cares about is a string („data“, but usually rather metadata) that you associate with a bunch of tokens. This pair receives a numeric id, and that's it.

    There is a higher-level thing called omega built on top of xapian that does identify files with xapian documents and can crawl and index a whole directory tree. It also knows (to some extent) how to pull tokens from a large variety of file types. I've tried it, and I wasn't happy; since pelican creates all those ancillary HTML files for tags, monthly archives, and whatnot, when indexing with omega, you get lots of really spurious matches as soon as people enter a term that's in an article title, and entering a tag or a category will yield almost all the files.

    So, I decided to write my own indexer, also with a view to later extending it to language detection (this blog has articles in German and English, and they eventually should be treated differently). The core is rather plain in Python:

    for dir, children, names in os.walk(document_dir):
      for name in fnmatch.filter(names, "*.html"):
        path = os.path.join(dir, name)
        doc = index_html(indexer, path, document_dir)
    

    That's enough for iterating over all HTML files in a pelican output directory (which document_dir should point to).

    In the code, there's a bit of additional logic in the do_index function. This code enables incremental indexing, i.e., only re-indexing a file if it has changed since the last indexing run (pelican fortunately manages the file timestamps properly).

    Nachtrag (2021-11-13)

    It didn't, actually; see the search engine update post for how to fix that.

    What I had to learn the hard way is that since xapian has no built-in relationship between what it considers a document and an operating system file, I need to explicitly remove the previous document matching a particular file. The function get_indexed_paths produces a suitable data structure for that from an existing database.

    The indexing also defines my document model; as said above, as far as xapian is concerned, a document is just some (typically metadata) string under user control (plus the id and the tokens, obviously). Since I want structured metadata, I need to structure that string, and these days, json is the least involved thing to have structured data in a flat string. That explains the first half of the function that actually indexes one single document, the path of which comes in in f_name:

    def index_html(indexer, f_name, document_dir):
      with open(f_name, encoding="utf-8") as f:
        soup = bs4.BeautifulSoup(f, "lxml")
      doc = xapian.Document()
      meta = {
        "title": soup_to_text(soup.find("title")),
        "path": remove_prefix(f_name, document_dir),
        "mtime": os.path.getmtime(f_name),}
      doc.set_data(json.dumps(meta))
    
      content = soup.find(class_="indexable")
      if not content:
        # only add terms if this isn't some index file or similar
        return doc
      print(f"Adding/updating {meta['path']}")
    
      indexer.set_document(doc)
      indexer.index_text(soup_to_text(content))
    
      return doc
    

    – my metadata thus consists of a title, a path relative to pelican's output directory, and the last modification time of the file.

    The other tricky part in here is that I only index children of the first element with an indexable class in the document. That's the key to keeping out all the tags, archive, and category files that pelican generates. But it means you will have to touch your templates if you want to adopt this to your pelican installation (see below). All other files are entered into the database, too, in order to avoid needlessly re-scanning them, but no tokens are associated with them, and hence they will never match a useful query.

    Nachtrag (2021-11-13)

    When you add the indexable class to your, also declare the language in order to support stemming; this would look like lang="{{ page.lang }} (substituting article for page as appropriate).

    There is a big lacuna here: the recall, i.e., the ratio between the number of documents actually returned for a query and the number of documents that should (in some sense) match, really suffers in both German and English if you don't do stemming, i.e., fail to strip off grammatical suffixes from words.

    Stemming is of course highly language-dependent. Fortunately, pelican's default metadata includes the language. Less fortunately, my templates don't communicate that metadata yet – but that would be quick to fix. The actual problem is that when I stem my documents, I'll also have to stem the incoming queries. Will I stem them for German or for English?

    I'll think about that problem later and for now don't stem at all; if you remember that I don't stem, you can simply append an asterisk to your search term; that's not exactly the same thing, but ought to be good enough in many cases.

    Using xapian: Searching

    Running searches using xapian is relatively straightforward: You open the database, parse the query, get the set of matches and then format the metadata you put in during indexing into links to the matches. In the code, that's in cgi_main; one could do paging here, but I figure spitting out 100 matches will be plenty, and distributing 100 matches on multiple HTML pages is silly (unless you're trying to optimise your access statistics; since I don't take those, that doesn't apply to me).

    The part with the query parser deserves a second look, because xapian supports a fairly rich query language, where I consider the most useful features:

    • Phrase searches ("this is a phrase")
    • Exclusions (-dontmatch)
    • Matches only when two words appear within 10 tokens of each other (matches NEAR appear)
    • Trailing wildcard as in file patterns (trail*)

    That last feature needs to be explicitly enabled, and since I find it somewhat unexpected that keyword arguments are not supported here, and perhaps even that the flag constant sits on the QueryParser object, here's how enabling wildcards in xapian looks in code:

    qp = xapian.QueryParser()
    parsed = qp.parse_query(query, qp.FLAG_WILDCARD)
    

    Deploying this on your Pelican Installation

    You can re-use my search script on your site relatively easily. It's one file, and if you're running an apache or something else that can run CGIs[2], making it run first is close to trivial: Install your equivalents of the Debian python3-xapian, python3-bs4, and python3-lxml packages. Perhaps you also need to explicitly allow CGI execution on your web server. In Debian's apache, that would be a2enmod cgi, elsewhere, you may need to otherwise arrange for mod_cgi or its equivalent to be loaded.

    Then you need to dump blogsearch somewhere in the file system.

    Nachtrag (2022-10-07)

    Don't take it from here; rather, see https://codeberg.org/AnselmF/pelican-ext

    While Debian has a default CGI directory defined, I'd suggest to put blogsearch somewhere next to your blog; I keep everything together in /var/blog (say), have the generated output in /var/blog/generated and would then keep the script in a directory /var/blog/cgi. Assuming this and apache, You'd then have something like:

    DocumentRoot /var/blog/generated
    ScriptAlias /bin /var/blog/cgi
    

    in your configuration, presumably in a VirtualHost definition. In addition, you will have to tell the script where your pelican directory is. It expects that information in the environment variable BLOG_DIR; so, for apache, add:

    SetEnv BLOG_DIR /var/blog/generated
    

    to the VirtualHost.

    After restarting your web server, the script would be ready (with the configuration above …

  • Erstaunlicher Klartext

    Kurve der EEG-Umlage: Aber 2009 gehts steil nach oben.

    Altmaier ist nicht alleine Schuld: Die stark steigenden EEG-Umlagen, die ihm den Vorwand für seinen Feldzug gegen erneuerbare Energien geliefert haben, folgten aus der Ausgleichsmechanismenverordnung (Grafik aus dem Artikel).

    Heute morgen lief im Deutschlandfunk ein recht bemerkenswertes Interview mit Claude Turmes, der für die Grünen in Luxemburg „Energieminister“ ist. Bemerkenswert finde ich dabei nicht seine Einsprüche gegen die ja recht offensichtlich unsinnigen Argumente der VerfechterInnen von Kernenergie – die nur im Zusammenhang mit monströsen Drohungen verstanden werden können, auch wenn, klar, zumindest für anlagenbauende Staaten industriepolitische Aspekte auch eine Rolle spielen.

    Nein, hörenswert ist der erstaunliche Klartext gegenüber einem (noch) wichtigen Mann einer befreundeten Nachbarregierung. Turmes hat bezüglich des Aufbaus einer umweltverträglichen Energieversorgung gesagt (bei Minute 2:30):

    Mit Peter Altmaier – das sind vier verlorene Jahre, und das muss man jetzt denke ich wieder aufholen.

    Nicht, dass dem zu widersprechen wäre, denn Altmaiers Versuche, mit marktwirtschaftlichen „Instrumenten“ die Erzeugung von Wind- und Solarstrom kleinzukriegen, waren wirklich erschreckend effektiv. Dennoch, solche Anwürfe über Staatsgrenzen hinweg (und nicht gegen „die Russen“): Ich bin ein wenig beeindruckt.

    Ich kann einen Post zur Altmaier'schen Klimakatastrophe nicht schließen, ohne darauf hinzuweisen, dass sie in gewissem Sinn Spätfolge einer anderen Mischung aus marktradikaler Verblendung und blanker Gier war. 2010 nämlich hatte eine Koalition aus FDP, den Stromunternehmen und der INSM die Ausgleichsmechanismenverordnung durchgedrückt. Dabei wurde der letztlich ohnehin fiktionale [1] Strom„markt“ weiter „liberalisiert“, mit den erwartbaren Auswirkungen drastisch steigender Kosten und Gewinne – siehe die Grafik oben.

    Das entschuldigt natürlich Altmaier nicht. Er hätte diese Verordnung ja auch kassieren können, statt den Ausbau von Wind- und Sonnenstromerzeugung in Grund und Boden zu bremsen. Aber gerade in Zeiten erneuter FDP-Regierungsbeteiligung schadet es vielleicht nicht, daran zu erinnern, mit welcher Kaltschnäuzigkeit dieses Personal die Welt für ein paar Handvoll Dollar weiter aufgeheizt hat.

    [1]Fiktional ist der Strom„markt“ vor allem, weil da jede Menge Physik und Echtzeit im Spiel sind. In so einem Geschehen müssen sich alle ziemlich eng an Spielregeln halten oder das ganze Netz kippt. Wenn mensch aber schon die Spielregeln vereinbart hat, braucht es eigentlich den Zirkus einer Strombörse nicht mehr. Oder halt nur, damit zwischendrin noch ein paar Leute reich werden können.
  • Whose Streets?

    Foto eines niederländischen Verkehrsschilds: Auto te Gast

    In den Niederlanden nicht überall Utopie: Der Mensch geht dem Auto vor.

    Neulich bin ich bei Dunkelheit über einen (relativ) einsamen Landwirtschaftsweg geradelt. Ein Auto kam mir entgegen und blendete irgendwann für einige Sekunden auf. Verunsichert habe ich nachgesehen, ob ich vielleicht vergessen hatte, mein Licht anzuschalten – aber nein, es war an.

    Als wir uns trafen, raunzte mich der Autofahrer – er hatte immerhin angehalten und sein Fenster heruntergelassen – an, mein Licht habe ihn geblendet. Nun bin ich recht pingelig, was meinen Lichtkegel angeht und bin daher sehr sicher, dass der Fahrer schlicht nicht an LED-Lichter gewöhnt war und er vergleichbar viel Streulicht aus Autoscheinwerfern als ganz natürlich hingenommen hätte.

    Aber das ist hier gar nicht mein Punkt. Mein Punkt ist, dass der Autofahrer zornig abgezischt ist, als ich ihm erklären wollte, wie er zu einem angemesseneren Ton finden könnte. Da das in den üblichen (un-)kommunikativen Situationen im motorisierten Individualverkehr auch sonst aus Zeit-, Geduld- und Hupgründen nicht klappt, will ich kurz eine kleine Fantasie schildern, die vielleicht road rage dieser Art mildern mag. Geheimplan: Die URL dieses Posts könnte ich vielleicht im Straßenverkehr schnell übergeben?

    Nur zu Gast

    Liebe AutofahrerInnen, stellt euch für einen Moment vor, ihr wärt in der Welt nur zu Gast. Wir anderen würden euch und eure tonnenschweren, lärmenden, rasenden und meist noch stinkenden Ungetüme nur aus freundlicher Nachsicht (und nicht wegen staatlicher Gewalt und lebenslanger Prägung) in unseren Städten akzeptieren. Stellt euch vor, ihr müsstet Danke sagen für unsere Bereitschaft, euch all den Platz in unseren Siedlungen einzuräumen, nur damit ihr rasen könnt. Stellt euch vor, ihr könntet nicht mit innerer Überzeugung mit euren parkenden Autos unsere Geh-, Fahr- und Spielfächen verstopfen und müsstet euch bei den Leuten entschuldigen, die den Platz eigentlich gerne nutzen würden.

    Könnt ihr das?

    Ihr sagt, das sei absurde Hippie-Träumerei? Nein, das ist es nicht. Die Auto-Gesellschaft ist nicht alt, und die ersten Autos hatten es nicht leicht auf Straßen, deren traditionelle NutzerInnen nicht immer einsahen, dass sie den Spielzeugen der Reichen auszuweichen hätten. Ohne ordentlich Nachhilfe durch die Vertreter der Reichen in der ersten Hälfte des 20. Jahrhunderts und ganz besonders geschickte Kampagnen der Auto-Industrie (cf. How the car industry outlawed crossing the road bei der BBC) hätte es so bleiben können wie zuvor.

    In Deutschland etwa haben Autos erst seit 1934 Vorrang auf den Straßen (cf. Geschichte der Straßenverkehrssicherheit in der Wikipedia). Noch so ein ärgerliches Erbe des Faschismus. Obwohl, na ja: an einem Gesetz von 1934 ist wohl wahrscheinlich schon vor der Machtübergabe gearbeitet worden. So oder so, die Straßen gehören hierzulande noch nicht mal 100 Jahre lang den Autos. Ob es eine große Kampagne „100 Jahre sind genug“ geben sollte?

    Während ich dann dem Autofahrer aus der Prä-LED-Zeit nachblickte, ist mir die andere Seite der Geschichte recht schlagartig bewusst geworden: Ich wurde von frühester Kindheit an darauf konditioniert, dass die Straße ein verbotener Raum ist, auf dem Menschen nichts verloren haben. Und so fahre ich auch jetzt noch so Fahrrad, als müsse ich mich entschuldigen, da zu sein. Konditioniert von Jahrzehnten wütenden Hupens suche ich Lücken, in denen ich den Autoverkehr möglichst wenig behindere, quetsche ich mich an Straßenränder, um Autos vorbeizulassen, husche ich über Bordsteine, um nur ja nicht „im Weg zu stehen“. Das Motto der Critical Mass, nach dem wir nicht den Verkehr blockeren, sondern wir der Verkehr sind, hatte ich oft im Mund. Aber, so ist mir an dem Abend klar geworden, im Kopf habe ich es nicht so recht.

    Ganz ehrlich: Ein wenig ärgert mich das schon, und ich bin offen neidisch auf die augenscheinlich felsenfeste Überzeugung des_der ideellen Gesamtautofahrenden, die Straße gehöre ihm_ihr.

    Fantasie, gefährlich und schön

    Will ich meine Hasen-Philosophe im Hinblick auf Straßen ändern? Sollte ich einfach auch das Gefühl entwickeln, die Straße gehöre mir?

    Mal ganz abgesehen von generellen Erwägungen zu ethischen Dimensionen von „gehören”: ich fürchte, auf den realen Straßen würden solche Ansprüche schnell im Krankenhaus oder auf dem Friedhof enden; die Macht kommt schon lange nicht mehr nur aus den Gewehrläufen, sie chromblitzt längst auch von Stoßstangen.

    Aber eine schöne Fantasie ist es schon: Die Straßen den Menschen, nicht den Autos. Und da, so ist mein Eindruck, derzeit immer mehr Menschen Träume dieser Art träumen, ist die Rückkehr auf die Straße vielleicht auf Dauer auch keine Gewaltfrage mehr, über die mit Gewehrläufen oder Stoßstangen entschieden wird.

    Vielleicht haben das sogar schon früher mal viele geträumt. Irgendwo glaube ich gelesen zu haben, die französische Volksfrontregierung von 1936/37 habe zumindest in Paris eine grundsätzliche Vorrangregelung für FußgängerInnen beschlossen, und das sei von der folgenden Daladier-Regierung oder den deutschen Nazis zurückgenommen worden. Ich habe dafür im Netz leider keine Belege mehr gefunden. Wenn wer etwas Näheres dazu weiß, wäre ich dankbar für einschlägige Hinweise.

  • Math with ReStructuredText and Pelican

    I recently wrote a piece on estimating my power output from CO₂ measurements (in German) and for the first time in this blog needed to write at least some not entirely trivial math. Well: I was seriously unhappy with the way formulae came out.

    Ugly math of course is very common as soon as you leave the lofty realms of LaTeX. This blog is made with ReStructuredText (RST) in pelican. Now, RST at least supports the math interpreted text role (“inline”) and directive (“block“ or in this case rather “displayed“) out of the box. To my great delight, the input syntax is a subset of LaTeX's, which remains the least cumbersome way to input typeset math into a computer.

    But as I said, once I saw how the formulae came out in the browser, my satifsfaction went away: there was really bad spacing, fractions weren't there, and things were really hard to read.

    In consequence, when writing the post I'm citing above, rather than reading the docutils documentation to research whether the ugly rendering was a bug or a non-feature, I wrote a footnote:

    Sorry für die hässlichen Formeln. Vielleicht schreibe ich mal eine Erweiterung für ReStructuredText, die die ordentlich mit TeX formatiert. Oder zumindest mit MathML. Bis dahin: Danke für euer Verständnis.

    (Sorry for the ugly formulae. Perhaps one of these days I'll write an RST extension that properly formats using TeX. Or at least MathML. Until then: thanks for your understanding.)

    This is while the documentation clearly said, just two lines below the example that was all I had initially bothered to look at:

    For HTML, the math_output configuration setting (or the corresponding --math-output command line option) selects between alternative output formats with different subsets of supported elements.

    Following the link at least would have told me that MathML was already there, saving me some public embarrassment.

    Anyway, when yesterday I thought I might as well have a look at whether someone had already written any of the code I was talking about in the footnote, rather than properly reading the documentation I started operating search engines (shame on me).

    Only when those lead me to various sphinx and pelican extensions and I peeked into their source code I finally ended up at the docutils documentation again. And I noticed that the default math rendering was so ugly just because I didn't bother to include the math.css stylesheet. Oh, the miracles of reading documentation!

    With this, the default math rendering suddenly turns from ”ouch” to “might just do”.

    But since I now had seen that docutils supports MathML, and since I have wanted to have a look at it at various times in the past 20 years, I thought I might as well try it, too. It is fairly straightforward to turn it on; just say:

    [html writers]
    math_output: MathML
    

    in your ~/.docutils (or perhaps via a pelican plugin).

    I have to say I am rather underwhelmed by how my webkit renders it. Here's what the plain docutils stylesheet works out to in my current luakit:

    Screenshot with ok formulae.

    And here's how it looks like via MathML:

    Screenshot with less ok formulae.

    For my tastes, the spacing is quite a bit worse in the MathML case; additionally, the Wikipedia article on MathML mentions that the Internet Explorer never supported it (which perhaps wouldn't bother me too much) and that Chromium withdrew support at some point (what?). Anyway: plain docutils with the proper css is the clear winner here in my book.

    I've not evaluated mathjax, which is another option in docutils math_output and is what pelican's render_math plugin uses. Call me a luddite, but I'll file requiring people to let me execute almost arbitrary code on their box just so they see math into the big folder labelled “insanities of the modern Web”.

    So, I can't really tell whether mathjax would approach TeX's quality, but the other two options clearly lose out against real TeX, which using dvipng would render the example to:

    Screenshot with perfect formulae

    – the spacing is perfect, though of course the inline equation has a terrible break (which is not TeX's fault). It hence might still be worth hacking a pelican extension that collects all formulae, returns placeholder image links for them and then finally does a big dvipng run to create these images. But then this will mean dealing with a lot of files, which I'm not wild about.

    What I'd like to ideally use for the small PNGs we are talking about here would be inline images using the data scheme, as in:

    <img src="data:image/png;base64,AAA..."/>
    

    But since I would need to create the data string when docutils calls my extension function, I in that scheme cannot collect all the math rendering for a single run of LaTeX and dvipng. That in turn would mean either creating a new process for TeX and dvipng each for each piece of math, which really sounds bad, or hacking some wild pipeline involving both, which doesn't sound like a terribly viable proposition either.

    While considering this, I remembered that matplotlib renders quite a bit of TeX math strings, too, and it lets me render them without any fiddling with external executables. So, I whipped up this piece of Python:

    import base64
    import io
    import matplotlib
    from matplotlib import mathtext
    
    matplotlib.rcParams["mathtext.fontset"] = "cm"
    
    def render_math(tex_fragment):
        """returns self-contained HTML for a fragment of TeX (inline) math.
        """
        res = io.BytesIO()
        mathtext.math_to_image(f"${tex_fragment}$",
          res, dpi=100, format="png")
        encoded = base64.b64encode(res.getvalue()).decode("ascii")
        return (f'<img src="data:image/png;base64,{encoded}"'
            f' alt="{tex_fragment}" class="math-png"/>')
    
    if __name__=="__main__":
        print(render_math("\int_0^\infty \sin(x)^2\,dx"))
    

    This prints the HTML with the inline formula, which with the example provided looks like this: \int_0^\infty \sin(x)^2\,dx – ok, there's a bit too much cropping, I'd have to trick in transparency, there's no displayed styles as far as I can tell, and clearly one would have to think hard about CSS rules to make plausible choices for scale and baseline – but in case my current half-satisfaction with docutils' text choices wears off: This is what I will try to use in a docutils extension.

  • Kohlendioxid und die thermische Leistung von Menschen

    Mit meinem zyTemp-Kohlendioxid-Messgerät – das, für das ich neulich Software gebastelt habe – bin ich natürlich gleich in die Welt gezogen, um mal zu sehen, wo es überall CO2 (und mithin plausiblerweise Corona-Aerosol) geben wird.

    Der Wind und die Seuche

    Nachdem mich ja zunächst sehr überrascht hat, wie deutlich sich die Anwesenheit von Menschen in rasch steigendem CO2-Gehalt der Luft in Innenräumen niederschlägt, war meine zweite große Überraschung, dass sich umgekehrt im Freien nichts tut. Also, richtig gar nichts. Selbst Autos, die mindestens eine Größenordnungen mehr CO2 emittieren als Menschen (vgl. unten), fallen schon aus ein paar Metern Entfernung im Wesentlichen nicht mehr auf. Ich musste mich schon an Kreuzungen neben die Ampeln stellen, um überhaupt ein schwaches Signal zu bekommen. Und das war kaum mehr als ein leichtes Oszillieren um ein paar ppm, während die wartenden Autos vor sich hinstanken und dann losbrausten. So sehr es nach Abgasen stank – CO2 ist im Nahbereich von Autos kein Problem.

    Die gute Nachricht ist also: Wenn CO2 ein guter Indikator ist, wie schlimm es mit Aerosol sein kann – real verschwindet Aerosol im Regelfall aus hinreichend ruhiger Luft durch Niederschlag, was CO2 nicht tut – ist praktisch sicher, dass an der frischen Luft bei nicht völlig irren Wetterlagen SARS-2 allenfalls durch Tröpfchen übertragen wird.

    Umgekehrt war meine Sorge ja immer der öffentliche Verkehr, und so habe ich mit Hingabe in verschiedenen Zügen gemessen. Als Referenz: Frischluft liegt derzeit hier irgendwo zwischen 280 und 350 ppm CO2. In einem halb vollen ICE habe ich zwischen 800 und 1400 ppm gemessen (interessanterweise nicht so ganz korreliert mit den Bahnhofsstopps; die Bahn kennend, vermute ich eine Nicht-so-ganz-wie-gedacht-Funktion der Lüftung in dem Wagen, in dem ich saß). Ein vollbesetzter IC-Zug der SBB war zwischen 800 und 1050 dabei, ein leerer Nahverkehrszug bei etwa 400, ein halb voller eher bei 700.

    Bei solchen Dynamiken ist wohl klar, dass hinreichend viel frisches Aerosol in der Luft sein wird, jedenfalls, solange nicht alle Passagiere mit richtig sitzenden FFP2-Masken dahocken, und sowas habe ich noch nicht mal dort gesehen, wo es wie in Berlin und Bayern gesetzlich gefordert ist oder war. Es muss also im letzten Winter weit mehr Ansteckungen in Zügen gegeben haben als das RKI in seinen Ausbruchshistogrammen (z.B. S. 12 am 9.3.2021) mit den kleinen roten Säulen andeutet. Aber ok, sie haben ja immer dazugesagt, „Clustersituationen in anonymen Menschengruppen (z.B. ÖPNV, Kino, Theater)“ seien fast sicher unterrepräsentiert.

    Atmende Blumen

    Aber ich hatte auch anderweitig viel Spaß mit dem Gerät. So war ich neulich verreist, und in der Wohnung verlief die CO2-Konzentration so:

    Graph mit Periodizitäten

    CO2-Konzentrationen in meinem Wohnzimmer während der Abwesenheit aller BewohnerInnen. Zeiten sind in UTC.

    Wohlgemerkt: Da war niemand. Es könnte sein, dass sich hier einfach Schwankungen in der Außenluft reflektieren; aber ich glaube zunächst mal, dass wir hier einer Birkenfeige beim Stoffwechsel zusehen; 6 Uhr UTC, wenn die Kurve sich nach unten wendet, entspricht 8 Uhr Lokalzeit und damit wohl der Zeit, in der es in dem Zimmer hell genug für Photosynthese werden dürfte; der große Peak rund um 18 Uhr am 28.9. wäre schön konsistent damit, dass die Pflanze sich zur Ruhe setzt und dazu kurz mal ihre Mitochondrien anwirft; der folgende Abfall wäre dann wieder ein Mischungseffekt, denn der Sensor lag (mehr zufällig) praktisch in den Zweigen des Ficus. Warum er, das angenommen, den Peak am 29.9. doch deutlich früher gemacht hat? Nun, vielleicht war ja Mistwetter? Oder vielleicht ist das auch alles ganz anders: das bräuchte definitiv mehr Forschung.

    Rauchmelder diagnostizieren Blasenschwäche

    Graph mit einigen Spitzen

    CO2-Konzentrationen in meiner Diele. Zeiten sind in UTC.

    Wenig überraschend zeigt sich, dass die CO2-Konzentrationen dramatisch personenbezogene Daten sind. Der zweite Graph illustriert das an einem relativ harmlosen Beispiel: Der Sensor steht jetzt in der Diele, vor meiner Zimmertür. Deutlich zu sehen ist, dass ich an dem Tag gegen 23 Uhr geschlafen habe, oder jedenfalls, dass meine Schlafzimmertür dann zu war. Und dann bin ich kurz vor zwei mal wach gewesen, weil ich am Abend etwas viel Tee getrunken hatte. am Morgen aufgestanden bin ich um sieben, kurz vor acht habe ich mal gelüftet, und um halb neun bin ich gegangen.

    Wer da etwas länger auf diese Weise zuschaut, findet viel über die BewohnerInnen von Wohungen heraus – angefangen davon, wann wie viele Menschen wo in der Wohnung sind –, und das im Zweifelsfall auch automatisiert unter vielen Menschen. Ich habe dabei lediglich zwei Messwerte pro Minute genommen. Das ginge, so würde ich schätzen, für eine ganze Weile mit den zumindest hier im Haus recht verbreiteten per Funk auslesbaren Rauchmeldern ganz gut, ohne dass ihre Batterien gleich alle wären – für die Betreiber und, weil die Krypto von den Teilen schon aus Stromspargründen sehr wahrscheinlich lausig ist, vermutlich auch ungefähr für jedeN, der/die sich hinreichend intensiv für das Leben der Anderen interessiert.

    Nachdenken nur für 50W?

    Schließlich bin ich jeden Tag wieder aufs Neue fasziniert, wie schnell ich in meinem Büro schlechte Luft verbreite.

    Graph mit zwei recht gleichmäßigen Anstiegen

    CO2-Konzentrationen in meinem Büro; ich komme von der Mittagspause zurück, arbeite, und lüfte ein Mal. Zeiten sind in UTC.

    An dieser Kurve ist viel zu sehen, unter anderem, dass sich offenbar die Luft in dem Raum doch recht schnell mischt; das würde jedenfalls schön erklären, warum es beim Lüften kurz nach 12 Uhr UTC so eine Delle nach unten gibt: Das ist die Frischluft von außen, die ziemlich direkt an den Sensor weht, sich dann aber innerhalb von fünf Minuten mit meinen im Raum gebliebenen Abgasen mischt.

    Diese schnelle Homogenisierung ist wesentlich für eine Überlegung, die sich für mich da aufdrängt: Wie viel CO2 mache ich eigentlich? Das geht so:

    In den 96 Minuten von 10:30 bis 12:06 habe ich die Konzentration von 808 auf 1245 ppm erhöht, was einer Rate von

    ((1245 − 808)  ppm)/((96⋅60)  s) = 0.077  ppm ⁄ s

    entspricht[1] (ich habe das nicht aus dem PNG, sondern noch im Plotprogramm selbst abgelesen). Ein zweiter Datenpunkt ist nach Lüften und Mischen: Da ging es von 12:17 bis 14:08 von 837 auf 1288 ppm, was auf eine Rate von 0.068 ppm/s führt.

    Aus den beiden Werten würde ich grob schätzen, dass ich die CO2-Konzentration in meinem Büro so etwa mit 0.07 ppm/s erhöhe, wenn ich normal arbeite; ich nenne diese Rate hier kurz δ. Unter der sicher falschen, aber vielleicht noch hinnehmbaren Annahme, dass kein CO2 entweicht und der nach den Beobachtungen plausiblen Annahme voller Durchmischung im Raum kann ich nun abschätzen, was mein Stoffwechsel so tut.

    Was es dazu braucht, ist das Wissen, dass bei einem idealen Gas (was die Luft nicht ist, aber für die Abschätzung reicht es) bei „Normalbedingungen“ (die bei mir im Zimmer glücklicherweise auch nicht ganz perfekt realisiert sind) ein Mol 22.4 Liter Volumen einnimmt[2]. Unter Kopfzahl-Aspekten kann ich nicht genau sagen, warum ich mir da drei Stellen gemerkt habe. In Wirklichkeit sind 20 l/mol natürlich genau genug als Kopfzahl. Ich nenne das unten Vm.

    Das ist eine Aussage über die Zahl der Gasmoleküle in einem Volumen V, denn ein Mol sind ungefähr 6e23 (so schreibe ich wieder kurz für 6⋅1023) Teilchen („Avogadro-Konstante“; außerhalb von Kopfzahlen ist die inzwischen exakt festgelegt und definiert das Mol). Mein Büro ist so in etwa fünf Meter lang, 2.5 Meter breit und drei Meter hoch, hat also V = 40 Kubikmeter Rauminhalt. Das heißt, dass sich darin

    (40  m3)/(0.0224  m3 ⁄  mol) ≈ 1800  mol

    befinden. Das sind gegen 1e27 oder 1000000000000000000000000000[3] Moleküle. Diese Zahl hat einen Namen: das ist eine Quadrillarde. Aber klar: der Name ist selbstverständlich Quatsch. Ich musste ihn selbst nachsehen. Der wissenschaftliche Fachbegriff für solche Zahlen ist Gazillion. Für alle davon. Weshalb wir eben immer zehn hoch siebenundzwanzig sagen, was viel nützlicher ist.

    Und dann brauche ich noch die Energie (oder hier genauer die Enthalpie, normalerweise geschrieben als ΔH) die bei der Bildung eines Moleküls CO2 auch C und O₂ frei wird; konventionell geben die Leute das nicht für ein Molekül, sondern für ein ganzes Mol (ihr erinnert euch: ganz platt 6e23 Moleküle statt nur eins) an, und die Wikipedia verrät, dass das 394 kJ/mol sind.

    Jetzt baue ich das zusammen, nämlich die Erzeugungsrate von CO2 in physikalischen Einheiten (statt in ppm/s), δV ⁄ Vm, auf der einen Seite, und mein ΔH auf der anderen Seite. Es ergibt sich für meine Leistung:

    P = ΔHδV ⁄ Vm

    Wenn mensch da die Einheiten glattzieht und bedenkt, dass ppm/s eigentlich 1e-6/s ist, muss mensch was rechnen wie:

    P = 394⋅103  J ⁄ mol⋅0.07⋅10 − 6 ⁄  s⋅40  m3 ⁄ (0.0224  m3 ⁄  mol)

    (ich schreibe das so ausführlich, weil ich mich beim schnellen Hinschreiben prompt verrechnet habe), was rund 50 J/s (also 50 W) ergibt.

    Ich habe von irgendwoher im Kopf, dass ein …

  • Kopfzahlen 2: Wir fressen der Welt die Haare vom Kopf

    Im Hintergrund Politik vom 9.9.2021 habe ich die schockierendste Zahl dieses Jahres gehört. Das Thema wird dort so eröffnet:

    1,6 Milliarden Hektar Ackerflächen gibt es auf der Erde. Nach den Zahlen des Umweltbundesamtes wächst nur etwa auf einem Fünftel davon Nahrung für die menschliche Ernährung. Auf den übrigen vier Fünfteln wird dagegen Tierfutter und Biosprit produziert.

    Auch wenn mir klar war, dass bei der „Wandlung“ von Soja und Getreide in Steaks oder Eier zwischen 70% und 95% des Nährwerts verlorengehen[1] – „Wandlung“ ist hier eingestandenermaßen ein wenig euphemistisch –, hatte ich diese danach relativ naheliegende Konsequenz nicht auf dem Schirm: 80% der Äcker auf diesem Planeten gehen im Groben in „unsere“ (also die der reichen Länder) Völlerei. Derweil:

    Und trotzdem gehen immer noch mehr als 800 Millionen Menschen hungrig zu Bett. Und viele davon sind selbst Kleinbauern – und Kleinbäuerinnen. Sie erwirtschaften auf einem Bruchteil der weltweiten Ackerfläche bis zu 70 Prozent der menschlichen Nahrung, gesicherte Zahlen gibt es hier nicht […]

    Also: unsere tolle, marktgestählte, industrielle Landwirtschaft trägt plausiblerweise nur zu 30% zur Welternährung bei, obwohl sie den Großteil der intensiv zur Nahrungsmittelproduktion nutzbaren Landfläche in Anspruch nimmt (und um die 10% der CO2-Emissionen verursacht). Das war, was mich wirklich schockiert hat.

    Wenn „unsere“ Firmen im globalen Süden für die Produktion unseres Tierfutters und, schlimmer noch, unseres „Biosprits“ Land requirieren und den dortigen Kleinbauern wegnehmen („Landgrabbing“), ist das schlicht ein Hungerprogramm. Wieder mal frage ich mich, wie die Leute in, sagen wir, hundert Jahren auf uns zurückblicken werden.

    Kopfzahlen für große Flächen

    Unterdessen sind die 1.6 Milliarden Hektar von oben ein guter Anlass, meine Kopfzahlen für „große“ Flächen loszuwerden. Erstmal: Ein Hektar ist ein Quadrat, dessen Diagonale ein Mensch in etwa zwei Minuten durchläuft. Nämlich: Ein nicht allzu hektischer Mensch läuft so gegen 4 km/h oder etwas mehr als einen Meter pro Sekunde. Die Diagonale eines Quadrats mit 100 Meter Kantenlänge (eben ein Hektar) sind 100 Meter mal √2 oder rund 150 Meter[2]. Bei einem guten Meter pro Sekunde dauert es also rund 120 Sekunden von Ecke zu Ecke durch die Mitte.

    Ansonsten ist die bessere Einheit für große Flächen der Quadratkilometer, also 100 ha. Mithin: 1.6e9 ha (1.6 ⋅ 109 ist in meinem Eingabeformat ReStructuredText echt lästig zu schreiben, weshalb ich mir hier die in eigentlich allen nichtantiken Programmiersprachen übliche Schreibweise genehmige) sind 1.6e7 oder 16 Millionen km².

    Wie viel ist das? Etwas albernerweise weiß ich immer noch die Fläche der alten Sowjetunion, des in meiner Kindheit größten Staats der Erde: das waren 22 Millionen km². Es gibt also global etwas weniger Acker als das, was mal alles zur Sowjetunion gehörte. Ich denke, der zeitgemäße Ersatz sind einerseits Afrika[3] mit 30 Millionen km² und andererseits die USA und China mit jeweils rund 10 Millionen km². Ich finde, auf diese Weise kriegt mensch schon mal ein Gefühl, wie die global genutzte Ackerfläche auf einer Weltkarte aussähe – und wie viele Wüsten, Tundren, Hochgebirge, Wildnisse oder Parkplätze es so geben wird.

    A propos Welt: Die Erde hat bei einem Radius von etwa 6000 km eine Fläche von 4 π (6000 km)² oder 450 Millionen km² oder auch 15 Mal Afrika, wobei mensch nicht vergessen sollte, dass im Fall der Erde 70% der Fläche von Meeren eingenommen wird und also Afrika deutlich über 20% der Landfläche ausmacht.

    Zwecks Gefühl und Abschätzung merke ich mir noch, dass die BRD eine Fläche von rund 350'000 km² hat (oder, für Leute, die sich das so leichter merken können: eine Drittelmillion). Kombiniert mit der Kopfzahl für Afrika heißt das: die BRD passt 100 Mal in Afrika rein. Von der Fläche her. Vom Ressourcenverbrauch her… eher so zwei Mal[4].

    Die Fläche von Baden-Württemberg ist 35'000 km². Das ist bequem zu merken, weil es einen Faktor zehn kleiner ist als die BRD, und ist als Größenordnung für ernstzunehmende Bundesländer auch sonst ganz brauchbar (die Spanne reicht von 71'000 km² für Bayern bis 16'000 km² für Schleswig-Holstein, also alles grob innerhalb von einem Faktor zwei, was für viele Abschätzungen überallhin reicht).

    So eine Zahl im Kopf ist beispielsweise praktisch, wenn mensch am Neckar steht und abschätzen will, wie viel Wasser da wohl fließt: Sein Einzugsgebiet wird so etwa die Hälfte des Landes sein (fürs einfache Rechnen: 20'000 km²), und es wird da zwischen 500 und 1000 mm pro Jahr regnen (800 mm Niederschläge pro Jahr sind keine unvernünftige Schätzung für normale Gebiete in der BRD). Wegen einfacher Rechnung nehmen wir hier mal 1000 mm, und dann müssten über dem Einzugsgebiet jedes Jahr 20e3 km² ⋅ 1e6 m²/km² ⋅ 1 m an Wasser runterkommen, also irgendwas wie 20e9 m³. Einiges davon wird, gerade im Sommer, verdunsten oder auch von Planzen in Sauerstoff verwandelt, bevor es in Heidelberg vorbeikommt; das Problem verschiebe ich aufs Ende des übernächsten Absatzes.

    20 Milliarden Kubikmeter pro Jahr sind etwas unhandlich, wenn mensch gerade auf den Fluss guckt. Wie sieht es pro Sekunde aus? Nun, ein Jahr hat etwas wie 3e7 Sekunden (vgl. die klassische Kopfzahl in Kopfzahlen 1), also wird der mittlere Abfluss vom Neckar was wie 20/3 ⋅ 10², also rund 670 m³/s sein. Tatsächlich gibt die Wikipedia 145 m³/s für den Pegel Mannheim an.

    Gut: wir liegen einen Faktor vier zu hoch, aber das ist für so eine Abschätzung schon ok, zumal der tatsächliche Abfluss übers Jahr hinweg um weit mehr als so einen Faktor schwanken wird. Wers genauer haben will: das wirkliche Einzugsgebiet sind nur 13'934 km², und dann ist da der Evapotranspirationswert (vgl. Erläuterung in der Wikipedia), dessen Einfluss nicht ganz einfach anzubringen ist, schon, weil einiges des verdunsteten Wassers ja noch im Einzugsgebiet wieder abregnet oder -taut. Egal: Ein Faktor vier ist oft gut genug.

    Letzte Kopfzahlen in dieser Rubrik: Größere europäische Städte wie Berlin oder Hamburg haben so gegen 1000 km² (in Wirklichkeit: 892 bzw. 755), kleinere Städte wie Heidelberg eher so 100 km² (in Wirklichkeit: 109).

    [Alle Flächenangaben hier aus den jeweils erwartbaren Wikipedia-Artikeln]

    [1]Das sind FAO-Schätzungen, die z.B. die gesetzliche Unfallversicherung grafisch schön aufgemacht hat. Die FAO-Quellen habe ich auf die Schnelle nicht im Netz gefunden.
    [2]Eine nicht direkt flächige Kopfzahl obendrauf: √2 ist ungefähr 1.44, und der Kehrwert ungefähr 0.7. Ein wenig flächig ist das aber auch: Die Fläche eines A4-Blatts ist (theoretisch genau) (√2)-(2⋅4) m², für A3 ist sie (√2)-(2⋅3) m² usf. Messt und rechnet selbst oder lest einfach im Wikipedia-Artikel zu Papierformaten nach, warum das so ist.
    [3]Asien (55 Millionen km²) eignet sich nicht so gut als Referenz, weil zu viele Leute Europa als separaten Kontinent abrechnen und zumindest ich mir nie sicher wäre, ob meine Fläche mit oder ohne Europa zählt (die 55 Millionen zählen den ganzen Kontinent, also mit den Gebieten westlich von Ural und kaspischem Meer).
    [4]CO2-Emission der BRD aus Kopfzahlen 1: 2/3 Gigatonnen; für Afrika insgesamt gibt das dort zitierte Our World in Data-CSV 1.4 Gigatonnen.
  • Patriotische Raison

    Die heutige Presseschau im Deutschlandfunk war mal wieder niederschmetternd.

    Zum Afghanistan-„Zapfenstreich“ gestern fällt der taz gerade noch ein, es sei „eher“ ein „Kuriosum“, während die anderen Blätter sich noch fester hinter ihrer Armee versammeln:

    • Es wäre in Afghanistan ja sonst noch viel schlimmer gewesen (Volksstimme); angesichts der tatsächlichen Verhältnisse ist das nur schwer vorstellbar: Geringer als in Afghanistan, 51.3 Jahre laut CIA World Factbook von 2016, ist die Lebenserwartung derzeit nur noch in zwei Staaten.
    • Soldaten – und nicht etwa ihre Opfer – seien „traumatisiert und physisch verletzt“ (Mitteldeutsche; den naheliegenden Schluss, die Bundeswehr, die dafür ja verantwortlich ist, aufzulösen, zieht sie natürlich nicht).
    • „gut gemeint“ (Nürnberger Nachrichten).
    • „Außen- und Sicherheitspolitik den nötigen Raum“ geben (Märkische Oderzeitung).
    Strammstehende und taktstockschwingene Soldaten

    Screenshots vom Zapfenstreich, Rechte fürs Rohmaterial hat die ARD.

    Au weia. Niemand möchte sich durch Stellen der offensichtlichen Frage als vaterlandsloser Geselle outen: „Was war denn das für ein bizarres Spektakel?“

    Strammstehen, Tschingdarassabum, bunte Kappen, Orden? Für Leute, die für die Regierung töten? Leute, es ist 2021. Es hat zwischendurch ein 20. Jahrhundert gegeben. Erinnert ihr euch?

    Den Auweia-Preis für den fürchterlichsten Kommentar räumt aber erneut die FAZ ab, wenn sie fordert:

    Nie wieder darf die ‚Parlamentsarmee‘ in eine Mission geschickt werden, in der sie mit ihrem Blut für die Unschärfe des Mandats und die Inkonsequenz, um nicht zu sagen: Feigheit der politischen Entscheider büßen muss.

    Das ist nur noch ein paar Millimeter von „im Felde ungeschlagen“ und dem Narrativ hinter der Dolchstoßlegende („die tapferen Jungs hätten den Feind schon platt gemacht, aber die Politiker...!“) entfernt. Wenn in dieser Umgebung dann noch Testosteron-Vokabeln wie „feige“ und „Entscheider“ auftauchen, schlägt mein persönliches Jetzt-Flucht-planen-o-Meter schon ziemlich deutlich aus.

  • Ach, Bahn, Teil 1: Captchas und Bodensee

    Alte Waggons und eine Dampflok

    Einen Vorteil hat die dysfunktionale Bahn: Mensch sieht etwas von der Welt, für mich und heute etwa dieses Stück Bahnromantik am Bahnhof von Rottweil.

    Ja, klar, es ist wohlfeil, über die Bahn zu ranten. Andererseits ist es auch unmöglich, mit der Bahn zu reisen und es nicht zu tun. Drum genehmige ich mir das gleich mal in mehreren Teilen. Wobei: Ich bin die Segnungen der Privatisierung nach meinen coronabedingten Zugpausen auch einfach nicht mehr gewöhnt. Vielleicht gibt es gar nicht mehr so viele weitere Teile von „Ach, Bahn“, wenn ich mich erstmal wieder in die Realitäten privatisierter Infrastruktur gefunden habe (vgl. auch Post-Wettbewerb).

    Sprinter und Antisprinter

    Ich fahre gerade von Heidelberg auf die Insel Reichenau. Das sind so etwa 200 Kilometer. Weil ich um 12 Uhr angekommen sein soll, musste ich in Heidelberg um 7:02 losfahren, fünf Stunden vorher. Ok, vielleicht sollen ja Leute eh nicht so viel durch die Gegend fahren, aber wenn ich gestern bei heise lese, die Bahn wolle „mit Inlandsflügen konkurrieren“ und hole künftig zwischen Berlin und Köln eine halbe Stunde raus: Nun, so sehr Flugzeuge viel Dreck und großflächig Lärm machen, das weit größere Problem sind immer noch Autos. In Konkurrenz zu denen werden netto fünf Stunden für 200 km, zumindest solange es Autobahnen gibt, nicht viele Leute auf die Schiene ziehen.

    Oder, in den Worten der taz zu den neuen Sprintern aus dem Heise-Artikel: „Dreimal täglich je Richtung fährt dann zum Beispiel ein Zug ohne Unterbrechung direkt von Berlin nach Köln. Pech gehabt, wenn man in einen Ort dazwischen will.“

    Ja, klar, die komplizierte und langsame Verbindung für mich und heute liegt bestimmt auch daran, dass derzeit die Schwarzwaldbahn nicht fährt. Oh Verzeihung, dass da Schienenersatzverkehr ist. Tja. Ich würde, dies bedenkend, als ein Infrastrukturziel vor dem Ausbau auf 350 km/h-Strecken vorschlagen: Möglichst viele Strecken so ausbauen, dass weitere Bauarbeiten ohne Schienenersatzverkehr und Zugverlegungen durchgeführt werden können. Das würde mir gerade als wichtiger Punkt erscheinen, um die Bahn als Autoalternative glaubhaft zu machen. Zugegeben, vielleicht erscheint mir dieser Punkt wichtiger als er ist, weil ich öfters in der weiteren Umgebung der Stuttgart 21-Katastrophe unterwegs bin. Dort nämlich litt tatsächlich die Mehrheit meiner Zugfahrten in den letzten 10 Jahre darunter, dass Züge, die meine Verbindung viel angenehmer gemacht hätten, baustellenbedingt nicht fuhren.

    Erlaubt mir kurz einen Extra-Nostalgie-Jammer bezüglich der speziellen Heidelberg-Bodensee-Relation: Ich bin alt genug, um mich an den Interregio zu erinnern, der einstmals direkt von Heidelberg nach Konstanz fuhr; ich weiß zwar nicht mehr, wie lang der wirklich brauchte, aber es wäre ja schon mal großartig, wenn ich nicht alle halbe Stunde wieder alles zusammenpacken und umsteigen müsste.

    Ach, Bahn. Sollten die Leute, die damals das Einstampfen der Interregios zu verantworten hatten, schon tot sein, hat der Teufel hoffentlich noch ein paar Grad draufgelegt in dem Teil der Hölle, in den sie gefahren sind.

    Von einem, der mit der Bahn-Webseite interagierte

    Ähnliche Wünsche hege ich im Hinblick auf die Leute, die derzeit an der Bahn-Webseite schrauben. Beim Buchen gestern: Ich logge mich auf bahn.de ein, gebe meine richtigen Credentials ein. Ein Captcha poppt auf. Ich soll nacheinander Fahrräder und Boote markieren. Ich überlege schon, einfach zu Hause zu bleiben, tue der Bahn aber trotzdem den Gefallen. Ich klicke mich zum Bezahlen durch, lasse dort meine gewohnte SEPA-Lastschrift angeklickt.

    Doch irgendwie mag die Bahn das nach nochmaligem Überlegen nicht mehr und schreibt auf der nächsten Seite in Rot: „Die folgenden Zahlungsmöglichkeiten bestehen für diese Reise“ (oder so ähnlich). Lastschrift ist jetzt ohne weitere Erklärung verschwunden. Nach einem tiefen Durchatmen wähle ich Kreditkarte. Ein paar Felder poppen auf, doch kann ich dort nichts eingeben (und, wohlgemerkt, das ist ein hundsordinärer Firefox, nicht mein üblicher Luakit; ich erwarte von der Bahn ja schon gar nicht mehr Browserunabhängigkeit). In einem Versuch, dem Bahn-Code eine zweite Chance zu geben, mache einen Reload. Eine kurze Fehlermeldung, und dann loggt mich der Bahn-Kram aus.

    Ich brülle laut und fluche, knirsche mit den Zähnen und fantasiere von Teufeln an großen Schaltpulten, die die Temperatureinstellung von „fies“ auf „jenseits der Spezifikation“ drehen. Ich wechsele auf einen fast unkonfigurierten, erweiterungslosen Chromium. N-n-n-n-och ein Captcha! Lokomotiven und Lkws dieses Mal. Ich bin jetzt noch heiser von der Fluchkaskade, die sich an dem Punkt meiner Kehle entrang. Immerhin ging vom Chromium aus die SEPA-Lastschrift. Warum auch immer.

    Captchas! C-a-p-t-c-h-a-s!

    For the record: Wenn du wem was verkaufen willst, dann verschwende nicht seine/ihre Zeit mit Captchas. Das ist wirklich ein No-no. Niemand will Objekt verhaltenspsychologischer Experimente sein, und auch nicht von Dingen, die so aussehen. Vielleicht kannst du deine NutzerInnen dazu bringen, da mitzumachen, wenn du so tust, als würdest du ihnen was schenken. Aber wenn sie dir Geld geben, dann musst du im Zweifelsfall Leute bezahlen, um Robots rauszufiltern (wenn es das schon brauchen sollte, was ich in den meisten Fällen bestreiten würde); zur allergrößten Not ist vielleicht noch eine knappe Texteingabe zur Robot-Abwehr („Textcha“) statthaft.

    Völlig absurd ist es natürlich, ein Captcha bei korrekt eingegebenen Credentials zu verlangen. Woher bitte sollten Robots die haben? Was denken sich diese Leute? Wenn das eine Abwehr von Clients sein sollte, die die (eingestanderenmaßen etwas mühsame) Interaktion mit der bahn.de-Webseite automatisieren: Öhm… Was genau wäre das Problem mit denen?

    Oh: Und wenn dich jemand auf so einen solchen Fehler hinweist (das habe ich in diesem Fall schon vor zwei Wochen per Mail an die Bahn-Kontaktadressee gemacht): Reagiere irgendwie. Ein „nur ein wenig Geduld, wir arbeiten an einer Fehlerlösung“ wäre zwar nicht optimal, ist aber immerhin besser als gar nichts. Überflüssig zu erwähnen, dass die Bahn für „gar nichts“ optiert hat.

    Auf der positiven Seite: Die Strecke zwischen Stuttgart und Singen ist hübsch, gerade hier in der Gegend von Horb. Ohne die Schließung der Schwarzwaldbahn hätte ich das wahrscheinlich nie gesehen.

  • Ella: Fast ein Jahr im Gefängnis

    Gerade an dem Tag, an dem in meinen Überlegungen zu Wahlen und Informationstheorie anmerkte, bedeutender als Wahlen sei für die politische Partizipation „eine Justiz, die es häufig genug doch noch schafft, diesen wenigstens dann und wann ein wenig Schutz vor den Übergriffen der Exekutive zu geben“, fand eine verteilte Uraufführung eines Films statt, der Zweifel am „häufig genug“ ziemlich nachhaltig vertieft.

    Es geht darin um den Fall von „Ella“ oder auch „UP1“ für „Unbekannte Person 1“, die seit der brutalen Räumung des Dannenröder Forsts im letzten November im Gefängnis sitzt. Der Fall folgt dem von den Rondenbarg-Prozessen allzu bekannten Muster, bei dem die Polizei lebensgefährliche Einsatzmethoden – im Dannenröder Forst insbesondere das Durchtrennen lebenswichtiger Seile – durch absurd aufgeblasene Vorwürfe gegen die Opfer dieser Einsätze in irgendeinem Sinne zu rechtfertigen versucht; in Ellas Fall kommt sicher noch einiger Zorn über ihre erfolgreiche Personalienverweigerung dazu.

    Einsatzszene

    Ein Sequenz vom Anfang des Ella-Films: Ein Polizist wirft einen Aktivisten von einem Baum runter. Die öffentliche Zurückhaltung angesichts erschreckend gewalttätiger Räumungstechniken im Wald (na gut, inzwischen: Autobahnbaustelle) ist jedenfalls im Hinblick auf künftige Möglichkeiten politischer Partizipation schon ziemlich beunruhigend.

    Die erste Gerichtsinstanz hat dabei mitgemacht, und nun soll sie noch weitere 16 Monate im Gefängnis schmoren. Ohne große Öffentlichkeit wird das wohl auch so kommen, denn ein Landgerichtsprozess geht normalerweise nicht im Eiltempo. Und dann hilft auch ein Freispruch nichts mehr.

    Sowohl im Hinblick auf Ellas Schicksal als auch auf die Diskussion indiskutabler Polizeitaktiken finde ich den Film also höchst verdienstvoll. Wer ihn verbreiten kann, möge das tun, z.B. von youtube; wer das Ding ohne google bekommen will, möge sich per Mail rühren, dann lege ich es auf von mir kontrollierten Webspace (ich spare mir die 800 MB in der Erwartung, dass eh alle zu youtube gehen).

    Nachtrag (2022-05-10)

    Meine Spekulation, es sei bei der ganzen Einsperrerei im Wesentlichen um Ellas Weigerung gegangen, ihre Personalien abzugeben, gewinnt an Substanz. Die Staatsgewalt lässt Ella jetzt, da sie ihre Identität preisgegeben hat, frei, wie die taz berichtet. Die Entscheidung wird den mitredenden Behörden leicht gefallen sein, denn sie werden alle nicht wild sein auf eine weitere Klärung der inzwischen offizell gewordenen Tatsache, dass „die Beamten die Unwahrheit gesagt hatten“ (so die taz sehr staatsfreundlich) und des offensichtlichen Desinteresses beider Gerichtsinstanzen, die Polizei beim Lügen zu erwischen. Zumindest die zweite Instanz kannte ja (vermutlich) den hier besprochenen Film.

  • Wahlen und Informationstheorie

    Ich hatte neulich versprochen, ein paar Worte zu Zweifeln am repräsentativen Modell zu sagen, die sich aus der Informationstheorie speisen. Dazu braucht es zunächst einen Begriff von Information, und um den definieren zu können, ein Modell von Nachrichtenübertragung, in diesem Fall etwa: eine Wahl überträgt die Wünsche zur Organisation der Gesellschaft von Wählenden an die Macht.

    Information: Nachrichten in Bits gemessen

    Wie viel Information steckt nun in den Wunschlisten dieses Modells? Nun, Information – gemessen in Bit – lässt sich recht anschaulich definieren als die Zahl der ja/nein-Fragen, die mensch bei optimaler Fragestrategie im schlimmsten Fall stellen muss, um einer Menge verschiedener Nachrichten eine ganz bestimmte Nachricht rauszufiltern.

    Wenn die Wahl heißt „Parkplätze zu Parks?“ und sonst nichts, reicht eine solche Frage, und mithin wird ein Bit Information übertragen. Kommt als zweite Frage hinzu „Lebkuchen subventionieren?“, braucht es zwei Fragen und mithin Bit, um die kompletten Wünsche zu übertragen.

    Wenn mensch das fortführt, ergibt sich: Für ein komplettes Programm mit n binären Entscheidungen braucht es naiv erstmal n bit Information. Diese n bit reichen aus, um 2n Programme zu kodieren, nämlich alle Kombinationen von ja/nein Entscheidungen über die n Fragen hinweg. Wenn es nur die Parkplätze und die Lebkuchen von oben gäbe, wären das beispielsweise:

    • für Parkplätze/für Lebkuchen
    • für Parkplätze/gegen Lebkuchen
    • gegen Parkplätze/für Lebkuchen
    • gegen Parkplätze/gegen Lebkuchen

    Nochmal: Mit n bits kann ich 2n verschiedene Nachrichten (hier also Programme oder Wunschzettel) auseinanderhalten.

    Das kann mensch jetzt rückwärts aufziehen. Um den Informationsgehalt einer Nachricht herauszubekommen, muss mensch sehen, wie viele verschiedene Nachrichten es gibt und diese Zahl dann als 2x darstellen. Das x in diesem Ausdruck ist der Informationsgehalt in Bit. Das x braucht mensch nicht zu raten, denn es ist nichts anderes als der Logarithmus der Zahl der verschiedenen Nachrichten, genauer der Zweierlogarithmus (meist als ld geschrieben). Wenn euer Taschenrechner den nicht kann: ld x = ln x/ln 2 – aber letztlich kommts nicht so drauf an, denn ln 2 ist nicht viel was anderes als eins. Profis schenken sich sowas.

    Pop Quiz: Wie viele Bits braucht ihr, um eine von 1000 Nachrichten rauszufummeln? (Ungefähr 10). Wie viele, um eine von 1'000'000'0000 zu kriegen? (Ungefähr 30; ihr seht, der Logarithmus wächst sehr langsam).

    Nicht gleichverteilt, nicht unabhängig

    In Wahrheit ist das mit der Information etwas komplizierter. Stellt euch vor, zur Parkplatz-Lebkuchen-Programmatik käme jetzt die Frage „Vorrang für FußgängerInnen auf der Straße?“. Wer die Antwort einer Person auf die Parkplatz-Frage kennt, dürfte recht zuverlässig vorhersagen können, wie ihre Antwort auf die Vorrang-Frage aussehen wird.

    Mathematisch gesprochen sind die beiden Entscheidungen nicht unabhängig, und das führt dazu, dass mensch durch geschicktes Fragen im Schnitt deutlich weniger als drei Fragen brauchen wird, um das komplette Programm mit den drei Antworten rauszukriegen, etwa, indem mensch zusammen nach Parkplätzen und Vorrang fragt. Dieser Schnitt liegt irgendwo zwischen 2 und 3 – für die Mathematik (und den Logarithmus) ist es kein Problem, Fragen auch hinter dem Komma zu zählen: 2.3 bit, vielleicht (ich bin immer wieder erstaunt, wie viele Menschen noch gewillt sind, Parkplätze hinzunehmen, während der Vorrang für FußgängerInnen doch hoffentlich unbestrittener Konsens in der zivilisierten Welt ist[1]).

    Ein ähnlicher Effekt ergibt sich, wenn bestimmte Antworten viel wahrscheinlicher sind als andere. Wenn es z.B. zwei Texte A und B gibt, die jeweils 45% der Nachrichten ausmachen, bekomme ich in 90% der Fälle die Nachricht in nur zwei Fragen raus („Eins von A oder B?“, worauf zu 90% schlicht „A?“ reicht, um die gewählte Nachricht rauszukriegen), ganz egal, ob es noch 10 oder 10'000'000'000 andere Nachrichten gibt.

    Die Sache mit „Information in bit rechnest du als den Logarithmus der Zahl der verschiedenen Nachrichten aus“ gibt also eine Obergrenze für den Informationsgehalt. Sie wird erreicht wenn die Nachrichten gleichverteilt sind (und in gewissem Sinn in sich unabhängig; besser verständlich wird der Unabhängigkeits-Teil, wenn mensch nicht eine Nachricht, sondern eine Folge von Nachrichten betrachtet). Wer wissen will, wie das richtig geht, sei auf die Wikipedia verwiesen.

    Das ganz einfache Modell unabhängiger, gleichverteilter Nachrichten von oben gilt in der Regel nicht – in natürlichsprachigen Texten sind z.B. die Buchstabenhhäufigkeiten drastisch verschieden (Scrabble-SpielerInnen kennen das), und es gibt allerlei Regeln, in welchen Reihenfolgen Buchstaben kommen können. Eine erstaunlich effektive Schätzung für den Informationsgehalt von Nachrichten ist übrigens, einfach mal gzip laufen zu lassen: Für diesen Text bis hierher kommt da 2090 Bytes (á 8 bit) raus, während er auf der Platte 4394 Bytes braucht: Was gzip da geschluckt hat, sind die Abweichungen von Gleichverteilung und Unabhängigkeit, die so ein dummes Computerprogramm leicht finden kann.

    Klar: auch die 2090 ⋅ 8 bit sind höchst fragwürdig als Schätzung für den Informationsgehalt bis hier. Wenn die Nachrichtenmenge „alle bisherigen Blogposts hier“ wäre (davon gibt es etwas weniger als 100), wären es nur sechseinhalb Bit, ist sie „Zeug, das Anselm Flügel schreibt“, wäre es zwar mehr, aber immer noch klar weniger als die 16720 Bit, trotz aller Exkurse über Information und Logarithmen[2]. Informationsgehalt ist nur im Kontext aller anderen möglichen Nachrichten gut definiert. Und dem, was bei EmpfängerInnen ankommt, was bei diesem Post für SchurkInnen auch nur ein Bit sein kann: „Alles Mist“.

    Wie viele bit in einem Wahlzettel?

    Euer Wahlzettel bei der Bundestagswahl neulich dürfte so um die zwei Mal sechzehn Möglichkeiten gehabt haben, etwas anzukreuzen. Im besten Fall – unabhängige Parteien mit gleichen Erfolgschancen – könntet ihr also 8 bit übertragen mit euren zwei Kreuzen. In Wahrheit sorgt schon die 5%-Hürde dafür, dass es allenfalls 8 Listen gibt, die in der Logik repräsentativer Regierungsbildung wählbar sind, und dann noch vielleicht eineN von vier DirektkandidatInnen, die auch nur irgendeine Chance haben. Zusammen, schätze ich (immer noch optimistisch), vielleicht drei Bit.

    Vergleicht das mit den Nachrichten, die so eine Regierung aussendet: So redundant und erwartbar da auch viel sein mag, kein gzip dieser Welt wird die Gesetze, Verordnungen und Exekutivakte von Regierung und Parlament in der letzten Legislaturperiode auf irgendwas unter 100 Megabyte bringen können, selbst wenn es, das Kompressionsprogramm, Politik und Jura schon kann. Gesetze wie das zur Bestandsdatenauskunft etwa sind völlig beliebig: sie setzen einfach Wünsche der Polizeien um und kümmern sich weder um Verfassungen noch um Sinn, und sie würden deutlich anders aussehen, wenn bei BKA, Innenministerium und Polizeiverbänden gerade andere Individuen am Werk gewesen wären. Beliebigkeit ist aber nur ein anderes Wort für Unabhängigkeit und Gleichverteilung. Die 100 Megabyte werden also eine harte untere Grenze sein.

    Bei einem Verhältnis von rund drei Bit rein zu mindestens 100 Megabyte raus (in Worten: eins zu zweihunderfünfzig Millionen, weit unter der Gewinnchance beim 6 aus 49-Lotto) ist evident, dass Wahlen gewiss kein „Hochamt der Demokratie“ sind; ihr Einfluss auf konkrete Entscheidungen wäre auch dann minimal, wenn bei realen Wahlen viel entschieden würde.

    Was natürlich nicht der Fall ist. Niemand erwartet ernsthaft, dass eine Wahl irgendetwas ändert an wesentlichen Politikfragen, hierzulande beispielsweise Reduzierung des Freihandels, Zurückrollen von Privatisierungen, Abschaffung des Militärs, Befreiung der Menschen von der Autoplage, weniger autoritäres Management sozialer Spannungen (z.B. durch weniger übergriffige Polizeigesetze), weniger blutige Staatsgrenzen, weniger marktförmige Verteilung von Boden, kein Wachstum bis zum Kollaps und so weiter und so fort; praktisch die gesamte Bevölkerung hat in allen diesen Punkten die bestehende Regierungspolitik bestätigt, obwohl sie manifest ihren Interessen oder zumindest ihrem moralischen Empfinden widerspricht.

    Warum Wahlen wichtig sind

    Entsprechend tut in den gegenwärtigen Koalitionsverhandlungen nicht mal wer so, als ginge es um mehr als um Selbstverständlichkeiten wie Tempolimits auf Autobahnen (stellt euch mal kurz vor, wie unfassbar bizarr das auf in 100 Jahren eventuell noch lebende Menschen wirken muss).

    Was nicht heißt, dass Wahlen nicht wichtig sind. Die ganz zentrale Funktion von Wahlen dieser Art hat neulich im Deutschlandfunk ein gewisser Andrej Kolesnikow am Beispiel Russland erläutert:

    Die Wahl soll vor allem das Staatsmodell legitimieren, das sich in Russland entwickelt hat. Sie ist deshalb für die Staatsmacht wichtiger als für die Bürger. Die Wahl soll den Menschen auch vor Augen führen, dass die Staatsmacht weiterhin über eine Mehrheit verfügt und dass es besser ist, sich dieser Mehrheit anzuschließen, oder, wenn jemand unzufrieden ist, wenigstens ruhig zu bleiben und seine Unzufriedenheit für sich zu behalten.

    Wer aus ein paar Schritt Entfernung auf die hiesigen Verhältnisse blickt, wird diese Beobachtung auch hierzulande im Wesentlichen bestätigt sehen. Versteht mich nicht falsch: Das ist durchaus wichtig. Ein delegitimierter Staat geht schnell in eine kaputte Gesellschaft über, solange wir es nicht hinbekommen, Menschen auch ohne Nationalgeklingele zu rationalem, sprich kooperativem Verhalten zu bekommen (nicht, dass ich glaube, dass das sehr schwer wäre; es würde aber jedenfalls andere Schulen brauchen). Etwas von dieser Delegitimation sehen wir schon hier, verglichen mit den 1980er Jahren jedenfalls, etwas mehr in den USA, und noch viel mehr im, sagen wir, Libanon. Und etwas weniger als hier in Dänemark oder Schweden. Ich mache kein Geheimnis daraus, wo auf diesem Spektrum ich lieber leben will.

    Allerdings: diese Legitimationsfunktion der Wahl funktioniert weitgehend unabhängig von politischer Partizipation. Auch die finstersten autoritären Regimes halten Wahlen ab und wollen diese in aller Regel auch recht ehrlich …

  • Fortgesetzte Missachtung

    Ich versuche gerade erneut, die Neuregelung (oder eher: Wiederregelung oder vielleicht auch Widerregelung) der Bestandsdatenauskunft nachzuvollzielen und schmökere dazu im letzten einschlägigen Urteil des Bundesverfassungsgerichts vom 27.5.2020 (1 BvR 1873/13). Darin findet das Gericht zunächst, dass die Anfechtungen von Regelungen im BND-Gesetz und im Zollfahndungsgesetz wegen Verfristung unzulässig sind, da der Bundestag in der Zwischenzeit bereits wieder Verschärfungen der entsprechenden Gesetze abgenickt hatte (Rn. 66ff).

    Das ist schon mal sportlich: Die Regierung beschließt neuen Menschenrechtsabbau, bevor das Verfassungsgericht den alten beanstanden kann. Das ist nicht die ungeschickteste Art, auf die Beschränkungen zu reagieren, die so eine lästige Verfassung mit sich bringt.

    Noch bemerkenswerter finde ich allerdings folgende, für höchstreichterliche Verhältnisse doch sehr klaren Worte des Gerichts (Rn. 80):

    Auch § 113 Abs. 1 Satz 2 TKG konnte fristgerecht angegriffen werden. Zwar hat die Norm gegenüber der Vorgängerregelung vom 22. Juni 2004 (BGBl I S. 1190) ‒ trotz geänderten Wortlauts und neuer Regelungsstruktur ‒ für sich genommen keinen grundsätzlich neuen Gehalt. Die Vorgängerregelung wurde jedoch für verfassungswidrig erklärt (BVerfGE 130, 151). Wenn der Gesetzgeber nunmehr eine Regelung mit im Wesentlichen gleichem Inhalt wiederholt, stellt diese einen neuen verfassungsrechtlichen Prüfungsgegenstand dar (vgl. dazu BVerfGE 96, 260 <263>; 102, 127 <141>; vgl. auch BVerfGE 135, 259 <281 Rn. 36>).

    Mit anderen Worten: Das Gericht hat den alten 113er für menschrechtswidrig erklärt, woraufhin die Regierung das Ding einfach ein wenig umformuliert und ganz offenbar im Wissen um seine Menschenrechtswidrigkeit völlig unverfroren wieder beschließen lässt. Dass keineR der betroffenen ParlamentarierInnen den Mut hatte, Einspruch gegen diesen doch besonders offensichtlichen Fall von Grundrechtsfeindlichkeit einzulegen, könnte in Hinblick auf die Funktionsfähigkeit des Parlaments ernüchtern. Andererseits ist das Muster autoritärer Problembewältigung (kein Mitglied der Regierungsparteien darf öffentlich gegen Wünsche von Polizei und Militär sprechen; privat tun sie das übrigens durchaus) leider allzu vertraut, und zwar von allen deutschen Regierungen zumindest in diesem Jahrtausend.

    Wobei: Immerhin hat die Regierung nicht (erkennbar) die vom BVerfG beanstanden Passagen noch wesentlich weiter getrieben (die Passwortabfrage ist wohl eher ein sachlicher Unkenntnis geschuldetes Gimmick). So ein Weitertreiben gab es durchaus schon, vielleicht am eklatantesten bei den Terrordateien (ATD und RED), deren Verschärfungen von 2015 vorgaben, durch recht fundamentale Beanstandungen des BVerfG motiviert zu sein, aber in Wirklichkeit die Menschenrechtsverstöße noch zuspitzten. Oder, wie Michael Plöse richtig feststellt (Vorgänge 208, 4/2014):

    [Der Regierungsentwurf zum ATDG-ÄG kann] kaum mehr nur als eine Enttäuschung bezeichnet werden – er ist vielmehr eine dreiste Provokation des Karlsruher Verfassungskompromisses.
  • Foced https Redirects Considered Harmful

    I don't remember where I first saw the admontion that “not everything that does HTTP is a browser“ – but I'd like to underscore this here. One corollary to this is:

    Please do not unconditionally redirect to https!

    People may have good reasons to choose unencrypted http, and sometimes they don't get to choose, in particular in embedded systems (where https may be prohibitively large) or when you cannot upgrade the ssl libraries and sooner or later the server no longer considers any of the ciphers you know safe.

    Case in point: I have a command line program to query bahn.de (python3 version)…

    Nachtrag (2022-09-04)

    after many years of relative stability, the Bahn web page has significantly changed their markup, which broke this script. There is a new bahnconn now.

    …which screen-scrapes the HTML pages that Deutsche Bahn's connection service hands out. I know bahn.de has a proper API, too, and I'm sure it would be a lot faster if I used it, but alas, my experiments with it were unpromising, with what's on the web working much better; perhaps I'll try again next time they change their HTML. But that's beside the point here.

    The point is: In contrast to browsers capable of rendering bahn.de's HTML/javascript combo, this script runs on weak hardware like my Nokia N900. Unfortunately, the N900 is more or less frozen at the state of something like Debian Lenny, because its kernel has proprietary components that (or so I think) deal with actually doing phone calls, and hence I can't upgrade it beyond 2.6.29. And that means more or less (sure, I could start building a lot of that stuff from source, but eventually the libc is too old, and newer libcs require at least kernel 2.6.32) that I'm stuck with Python 2.5 and an OpenSSL of that time. Since about a year ago, these have no ciphers any more that the bahn.de server accepts. But it redirects me to https nevertheless, and hence the whole thing breaks. For no good reason at all.

    You see, encryption buys me nothing when querying train connections. The main privacy breach here is bahn.de storing the request, and there I'm far better off with my script, as that (at least if more people used it) is a lot more anonymous than my browser with all the cookies I let Deutsche Bahn put into it and all the javascript goo they feed it. I furthermore see zero risk in letting random people snoop my train routes individually and now and then. The state can, regrettably, ask Deutsche Bahn directly ever since the Ottokatalog of about 2002. There is less than zero risk of someone manipulating the bahn.de responses to get me on the wrong trains.

    Now, I admit that when lots of people do lots of queries in the presence of adversarial internet service providers and other wire goblins, this whole reasoning will work out differently, and so it's probably a good idea to nudge unsuspecting muggles towards https. Well: That's easy to do without breaking things for wizards wishing to do http.

    Doing it right

    The mechanism for that is the upgrade-insecure-requests header that essentially all muggle browsers now send (don't confuse it with the upgrade-insecure-requests CSP). This does not lock out old clients while still giving muggles some basic semblance of crypto.

    And it's not hard to do, either. In Apache, you add:

    <If "%{req:Upgrade-Insecure-Requests} == '1'">
      Header always set Vary Upgrade-Insecure-Requests
      Redirect 307 "/" "https://<your domain>/"
    </If>
    

    rather than the unconditional redirect you'd otherwise have; I suppose you can parameterise this rule so you don't even have to edit in your domain, but since I'm migrating towards nginx on my servers, I'm too lazy to figure out how. Oh, and you may need to enable mod_headers; on Debian, that would be a2enmod headers.

    In nginx, you can have something like:

    set $do_http_upgrade "$https$http_upgrade_insecure_requests";
    location / {
    
      (whatever you otherwise configure)
    
      if ($do_http_upgrade = "1") {
         add_header Vary Upgrade-Insecure-Requests;
         return 307 https://$host$request_uri;
      }
    }
    

    in your server block. The trick with the intermediate do_http_upgrade variable makes sure we don't redirect if we already are on https; browsers shouldn't send the header on https connections, but I've seen redirect loops without this trick (origin).

    Browser considerations

    Me, I am by now taking it as a sign of quality if a server doesn't force https redirects and instead honours upgrade-insecure-requests. For instance, that way I can watch what some server speaks with the Javascript it executes on my machine without major hassle, and that's something that gives me a lot of peace of mind (but of course it's rather rare these days). In celebration of servers doing it right, I've configured my browser – luakit – to not send upgrade-insecure-requests; where I consider https a benefit rather than a liability for my privacy, I can remember switching to it myself, thank you.

    The way to do that is to drop a file no_https_upgrade_wm.lua into .config/luakit containing:

    local _M = {}
    
    luakit.add_signal("page-created",
        function(page)
            page:add_signal("send-request", function(p, _, headers)
                if headers["Upgrade-Insecure-Requests"] then
                    headers["Upgrade-Insecure-Requests"] = nil
                end
            end)
    end)
    

    (or fetch the file here). And then, in your rc.lua, write something like:

    require_web_module("no_https_upgrade_wm")
    

    ...and for bone-headed websites?

    In today's internet, it's quite likely that a given server will stink. As a matter of fact, since 1995, the part of the internet that stinks has consistently grown 20 percentage points[1] faster than the part that doesn't stink, which means that by now, essentially the entire internet stinks even though there's much more great stuff in it than there was in 1995: that's the miracle of exponential growth.

    But at least for escaping forced https redirects, there is a simple fix in that you can always run a reverse proxy to enable http on https-only services. I'm not 100% sure just how legal that is, but as long as you simply hand through traffic and it's not some page where cleartext on the wire can realistically hurt worse than the cleartext on the server side, I'd claim you're ethically in the green. So, to make the Deutsche Bahn connection finder work with python 2.5, all that was necessary was a suitable host name, an nginx, and a config file like this:

    server {
      listen 80;
      server_name bahnauskunft.tfiu.de;
    
      location / {
        proxy_pass https://reiseauskunft.bahn.de;
        proxy_set_header Host $host;
      }
    }
    
    [1]This figure is of course entirely made up<ESC>3bC only a conservative guess.
  • Bingo zur Wahl

    Bullshit Bingo-Karten zur Wahl

    Wer solche Bingo-Karten haben will: Das Wahlbingo-CGI macht sie euch gerne. Jeder Reload macht neue Karten!

    Auch wenn verschiedene Posts der letzten Zeit etwas anderes suggerieren mögen: Ich will gewiss nicht in die von Plakatwänden und aus Radiolautsprechern quellende Wahlaufregung einstimmen. Aber ein wenig freue ich mich doch auf die Bundestagswahl am nächsten Sonntag: ich kann nämlich wieder mein Wahlbingo spielen. Ihr kriegt bei jedem Reload der Bingo-Seite andere Karten, und zumindest Webkit-Browser sollten das auch so drucken, dass auf einer A4-Seite zwei Bingokarten rauskommen.

    Die Regeln sind dabei: Wer in der Wahlkampfberichterstattung eine hinreichend ähnliche Phrase aufschnappt, darf ein Feld abkreuzen (die Hälfte vom Spaß ist natürlich die lautstarke Klärung der Frage, ob eine Phrase ähnlich genug war). Wer zuerst vier…

    Nachtrag (2022-03-07)

    Die Erfahrung zeigt, dass es mit drei mehr Spaß macht.

    …Felder in horizonaler, vertikaler, oder diagonaler Richtung hat, hat gewonnen. Dabei gelten periodische Randbedingungen, mensch darf also über den Rand hinaus verlängern, als würde die eigene Karte die Ebene parkettieren.

    Das Ganze ist übrigens ein furchtbar schneller Hack an irgendeinm Wahlabend gewesen. Ich habe den gerade noch geschwinder für das Blog in ein CGI gewandelt. Wer darauf etwas Aufgeräumteres aufbauen will: die Quellen. Spenden für den Phrasenkorpus nehme ich sehr gerne per Mail.

  • Ad hominem

    Die Ideenwelt der repräsentativen Demokratie hat etliche Ungereimtheiten – was an sich nicht notwendig katastrophal[1] ist, siehe RiwaFiw. Speziell zu Wahlkampfzeiten muss ich aber doch manchmal meinen Kopf bis an die Grenze zum Schleudertrauma schütteln.

    Derzeit ist in Heidelberg etwa das hier plakatiert:

    Wahlplakat mit eigenartigem Brustportrait und einem Slogan

    Klar, mensch könnte das einfach mit „selbst schuld“ wegnicken und weiterfahren. Aber für mich will diese Sorte Plakat einfach nicht zusammengehen mit der öffentlichen Ächtung von Attacken auf die Person von KandidatInnen und PolitikerInnen („ad hominem“), und das nicht nur, weil das Plakat recht unbestreitbar eine ad hominem-Selbstattacke ist.

    Schön, das ad hominem-Tabu war schon immer mehr deklariert als gelebt (was gerade die zugeben müssen, die sich gerne öffentlich nach Herbert Wehner und Franz Josef Strauß sehnen), aber als normative Richtschnur des Handelns ist es im Wesentlichen unbestritten. Wenn also Kritik ad hominem nicht statthaft ist: Warum zeigt dann die Mehrheit der Wahlplakate „ernstzunehmender“ Parteien die Portraits der KandidatInnen und nicht etwa, sagen wir, eine politische Position oder wenigstens ein hübsches Bild zur Aufwertung des Straßenraums, Dinge jedenfalls, die anzugreifen nicht Tabu ist?

    Der „Digitalturbo“ im Plakat oben zählt übrigens mangels Bedeutung nicht als politische Position. „Digitalisierung“ ist Antisprache, versucht also aktiv, nichts zu sagen. „Turbo“ hingegen ist eine dämliche Autometapher, der positiv nur die radikale Selbstentlarvung zuzurechnen ist. Der Kluge führt unter dem Lemma Turbine aus:

    1. turbo (-inis) m. „Wirbel; alles, was sich im Kreis dreht“

    Ich erfinde das nicht.

    Eine politische Position, die tatsächlich etwas bedeutet, wäre etwa public money, public code gewesen, oder vielleicht „hohe Hürden bei Zugriff auf Tk-Bestandsdaten“ (cf. Post vom 2021-01-31). Doch, sowas passt auf ein Plakat, und mit etwas Mühe kriegt mensch auch Muggels erklärt, was das jeweils bedeutet. Allerdings müsste ich bei einem „Master of Public Policy“ (was Nusser ausweislich seiner Online-Biografie ist) zunächst noch überzeugt werden, dass der Kandidat tatsächlich Einsicht hätte in das, was er da sagen würde.

    Da seine Parole leer ist: was eigentlich soll mensch kritisieren als das Restplakat, also das Bild? Wenn das Bild nur die Person zeigt, wird die Kritik notwenig ad hominem. Das ist besonders bitter, wenn der Kandidat aussieht, als habe er starke Schwierigkeiten bei der Ablösung von der Mutter (oder jedenfalls bei der Impulskontrolle). Hand aufs Herz: Wer hatte bei Nussers Foto nicht gleich das Bild im Kopf vom pummeligen Einzelgänger in der Schule, der Verachtung und Hänselei der Mitschülis jetzt durch Dampfplaudern im Machoclub FDP kompensiert? [Mitschülis von Nusser: wie irrig ist diese Fantasie?]

    Wer solche, eingestandermaßen üblen, Reflexe nicht haben will: Wie gesagt, thematische Bilder statt Köpfe auf Plakaten würden sich anbieten, bei der selbsternannten Wachstumspartei FDP vielleicht viele Autos und viel Beton oder so. Weniger ansprechend als die Portraits der KandidatInnen wird das in der Regel auch nicht sein, solange nicht gerade Rana Plaza oder Union Carbide in Bhopal als Symbole für die Segnungen des Freihandels herhalten müssten.

    Besser wärs aber wahrscheinlich, ganz auf Fotos zu verzichten, etwa nach US-Vorbild:

    Vorgarten mit Wahlschildern, auf denen nur Namen stehen

    Um euch die Arbeit zu ersparen, anhand der Namen herauszubekommen, wann und wo die Szene spielt: Das Foto entstand 2002 in Massachussetts, und die schon etwas extreme Botschaftsdichte mag damit zusammenängen, dass der Vorgartenbesitzer im liberalen Jamaica Plain Werbung für alle möglichen Kandidaten der Republicans machte. Mensch sieht: Rechte Trolle sind keine Erfindung des facebook-Zeitalters.

    So oder so: Wahlwerbung in den USA ist, soweit ich das sehe, immer noch, wenn Leute die Namen ihrer LieblingskandidatInnen in den Vorgarten stellen (ok, und am Straßenrand mit Namenschildern winken). Keine Fotos, keine leeren Slogans.

    Ich glaube ja, das ist weit mehr im Geist der repräsentativen Demokratie, bei der Menschen ja genau nicht etwa die wählen sollen, die aussehen wie sie selbst; von Lookismus-Prävention will ich gar nicht anfangen. Der größte Vorteil aber: ästhetische Tiefschläge wie der folgende aus dem Jahr 1998 unterbleiben:

    Wahlplakat von Karl A. Lamers
    [1]Nur zur Vorsicht sollte ich wohl sagen, dass ich damit natürlich mitnichten repräsentative Demokratie befürwortet haben will; eine dahingehende Beurteilung aus informationstheoretischer Sicht verspreche ich schon mal für demnächst.
  • Reading a zyTemp Carbon Dioxide Monitor using Tkinter on Linux

    Last weekend I had my first major in-person conference since the SARS-2 pandemic began: about 150 people congregated from all over Germany to quarrel and, more importantly, to settle quarrels. But it's still Corona, and thus the organisers put in place a whole bunch of disease control measures. A relatively minor of these was monitoring the CO2 levels in the conference hall as a proxy for how much aerosol may have accumulated. The monitor devices they got were powered by USB, and since I was sitting on the stage with a computer having USB ports anyway, I was asked to run (and keep an eye on) the CO2 monitor for that area.

    A photo of the CO2 meter

    The CO2 sensor I got my hands on. While it registers as a Holtek USB-zyTemp, on the back it says “TFA Dostmann Kat.Nr. 31.5006.02“. I suppose the German word for what's going on here is “Wertschöpfungskette“ (I'm not making this up. The word, I mean. Why there are so many companies involved I really can only guess).

    When plugging in the thing, my syslog[1] intriguingly said:

    usb 1-1: new low-speed USB device number 64 using xhci_hcd
    usb 1-1: New USB device found, idVendor=04d9, idProduct=a052, bcdDevice= 2.00
    usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    usb 1-1: Product: USB-zyTemp
    usb 1-1: Manufacturer: Holtek
    usb 1-1: SerialNumber: 2.00
    hid-generic 0003:04D9:A052.006B: hiddev96: USB HID v1.10 Device [Holtek USB-zyTemp] on usb-0000:00:14.0-1/input0
    hid-generic 0003:04D9:A052.006C: hiddev96: USB HID v1.10 Device [Holtek USB-zyTemp] on usb-0000:00:14.0-1/input0
    

    So: The USB is not only there for power. The thing can actually talk to the computer. Using the protocol for human interface devices (HID, i.e., keyboards, mice, remote controls and such) perhaps is a bit funky for a measurement device, but, on closer reflection, fairly reasonable: just as the mouse reports changes in its position, the monitor reports changes in CO2 levels and temperatures of the air inside of it.

    Asking Duckduckgo for the USB id "04d9:a052" (be sure to make it a phrase search with the quotes our you'll be bombarded by pages on bitcoin scams) yields a blog post on decrypting the wire protocol and, even better, a github repo with a few modules of Python to read out values and do all kinds of things with them.

    However, I felt like the amount of code in that repo was a bit excessive for something that's in the league of what I call a classical 200 lines problem – meaning: a single Python script that works without any sort of installation should really do –, since all I wanted (for now) was a gadget that shows the current values plus a bit of history.

    Hence, I explanted and streamlined the core readout code and added some 100 lines of Tkinter to produce co2display.py3, showing an interface like this:

    A co2display screenshot

    This is how opening a window (the sharp drop of the curve on the left), then opening a second one (the even sharper drop following) and closing it again while staying in the room (the gentle slope on the right) looks like in co2display.py. In case it's not obvious: The current CO2 concentration was 420 ppm, and the temperature 23.8 degrees Centigrade (where I'm sure the thing doesn't measure to tenths of Kelvins; but then who cares about thenths of Kelvins?) when I took that screenshot.

    If you have devices like the zyTemp yourself, you can just download the program, install the python3-hid package (or its equivalent on non-Debian boxes) and run it; well, except that you need to make sure you can read the HID device nodes as non-root. The easiest way to do that is to (as root) create a file /etc/udev/rules.d/80-co2meter.rules containing:

    ATTR{idVendor}=="04d9", ATTR{idProduct}=="a052", SUBSYSTEM=="usb", MODE:="0666"
    

    This udev rule simply says that whenever a device with the respective ids is plugged in, any device node created will be world-readable and world-writable (and yeah, it does over-produce a bit[2]).

    After adding the rule, unplug and replug the device and then type python3 co2display.py3. Ah, yes, the startup (i.e., the display until actual data is available) probably could do with a bit of extra polish.

    First Observations

    I'm rather intrigued by the dynamics of CO2 levels measured in that way (where I've not attempted to estimates errors yet). In reasonably undisturbed nature at the end of the summer and during the day, I've seen 250 to 280 ppm, which would be consistent with mean global pre-industrial levels (which Wikipedia claims is about 280 ppm). I'm curious how this will evolve towards winter and next spring, as I'd guess Germany's temporal mean will hardly be below the global one of a bit more than 400 (again according to Wikipedia).

    In a basically empty train I've seen 350 ppm yesterday, a slightly stuffy train about 30% full was at 1015 ppm, about as much as I have in my office after something like an hour of work (anecdotically, I think half an hour of telecon makes for a comparable increase, but I can hardly believe that idle chat causes more CO2 production than heavy-duty thinking. Hm).

    On a balcony 10 m above a reasonably busy road (of order one car every 10 seconds) in a lightly built-up area I saw 330 ppm under mildly breezy conditions, dropping further to 300 as the wind picked up. Surprisingly, this didn't change as I went down to the street level. I can hardly wait for those winter days when the exhaust gases are strong in one's nose: I cannot imagine that won't be reflected in the CO2.

    The funkiest measurements I made on the way home from the meeting that got the device into my hands in the first place, where I bit the bullet and joined friends who had travelled their in a car (yikes!). While speeding down the Autobahn, depending on where I measured in the small car (a Mazda if I remember correctly) carrying four people, I found anything from 250 ppm near the ventilation flaps to 700 ppm around my head to 1000 ppm between the two rear passengers. And these values were rather stable as long as the windows were closed. Wow. Air flows in cars must be pretty tightly engineered.

    Technics

    If you look at the program code, you'll see that I'm basically polling the device:

    def _update(self):
      try:
        self._take_sample()
        ...
      finally:
        self.after(self.sample_interval, self._update)
    

    – that's how I usually do timed things in tkinter programs, where, as normal in GUI programming, there's an event loop external to your code and you cannot just say something like time.wait() or so.

    Polling is rarely pretty, but it's particularly inappropriate in this case, as the device (or so I think at this point) really sends data as it sees fit, and it clearly would be a lot better to just sit there and wait for its input. Additionally, _take_sample, written as it is, can take quite a bit of time, and during that time the UI is unresponsive, which in this case means that resizes and redraws don't take place.

    That latter problem could easily be fixed by pushing the I/O into a thread. But then this kind of thing is what select was invented for, or, these days, wrappers for it (or rather its friends) usually subsumed under “async programming“.

    However, marrying async and the Tkinter event loop is still painful, as evinced by this 2016 bug against tkinter. It's still open. Going properly async on the CO2monitor class in the program will still be the next thing to do, presumably using threads.

    Ah, that, and recovering from plugging the device out and in again, which would also improve behaviour as people suspend the machine.

    Apart from that, there's just one detail I should perhaps highlight in the code: The

    self.bind("<Configure>", lambda ev: self._update_plot())
    

    in the constructor. That makes the history plot re-scale if the UI is re-sized, and I've always found it a bit under-documented that <Configure> is the event to listen for in this situation. But perhaps that's just me.

    Nachtrag (2021-10-19)

    I've updated co2display.py3 as published here, since I've been hacking on it quite a bit in the meantime. In particular, if you rename the script co2log.py (or anything else with “log” in it), this will run as a plain logger (logging into /var/log/co2-levels by default), and there's a systemd unit at the end of the script that lets you run this automatically; send a HUP to the process to make it re-open its log; this may be useful together with logrotate if you let this run for weeks your months.

    You can also enable logging while letting the Tk UI run by passing a -d option …

  • Ehrlichkeit in Parteiprogrammen

    Über einen Artikel in der Wochenzeitung Kontext bin ich auf eine Kleinstudie des Zentrums für europäische Wirtschaftsforschung (ZEW) gestoßen, auf die ich vor allem im Hinblick auf eine spätere Nutzung zur Mythenstörung kurz eingehen möchte.

    Zu bedenken ist zunächst, dass das ZEW gewiss in keinem Verdacht steht, irgendwelche fortschrittlichen Ideen zu hegen. So gehört zu deren aktuellen „Empfehlungen für die Wirtschaftspolitik“ offensichtlicher Quatsch der Art „Standardisierte Altersvorsorgeprodukte einführen“ oder Deflektorschilde gegen eine entspanntere, arbeitsärmere und umweltfreunlichere Gesellschaft des Typs „Kostengerechte Aufteilung der CO2-Reduktionen zwischen den Sektoren“; aktuelle Pressemitteilungen beten marktradikale Wirrnisse herunter wie „Lokale Preisanreize im Strommarkt setzen“ (Faustregel: „Anreiz“ heißt im Klartext: Geld von unten nach oben umverteilen) oder „Bundesnetzagentur sollte weiterhin auf Auktionen setzen“, ganz als sei 1998.

    Ausgerechnet diese Leute haben in ihrer Kleinstudie zu den fiskalischen und ökonomischen Folgen der aktuellen Parteiprogramme herausgefunden, dass das Programm der Linken den Staathaushalt um 37 Milliarden Euro entlasten würde, während die FDP den Haushalt mit 88 Millarden Euro belasten würde und die CDU immer noch 33 Millarden Euro mehr Staatsdefizit ansagt.

    So viel zur Frage fiskalischer Verantwortung in der Theorie, soweit also mensch Wahlprogramme ernstnehmen will. Zumindest sollte die Studie traugen, um die Erzählungen von „wirtschaftlicher Vernunft“ bei den Rechtspartien als Legenden zu entlarven.

    Die Studie liefert weiter Hinweise zum Thema „Turkeys voting for Christmas“, also Armen, die Rechtsparteien wählen (davon gibt es augenscheinlich einen ganzen Haufen): Das VEW hat nämlich auch die Folgen der Programme für den Gini-Koeffizienten abgeschätzt, also einer Metrik für die Ungleichheit der Einkommen in einer Gesellschaft, der mit wachsender Ungleichheit wächst. Souveränder Spitzenreiter dabei ist die AfD, die die Ungleichheit in dieser Metrik um 3.8% verschärfen würde, wo selbst die FDP die Dinge nur 3.4% schlimmer machen will (und die CDU um 1.6%). Demgegenüber würde das Wahlprogramm der Linken – take this, ExtremismustheoretikerInnen! – die Ungleichheit um knapp 15% reduzieren. Wenn denn irgendwas davon umgesetzt würde, käme sie an die Regierung; spätestens nach der Katastrophe der Schröder-Administration dürfte klar sein, dass das ungefähr so wahrscheinlich ist wie ein ernsthaftes Aufbegehren gegen den permanenten Bürgerrechtsabbau durch eine FDP in der Regierung. Die Grünen würden laut VEW die Ungleichheit um 6.5% reduzieren, und selbst die vom für Cum Ex und Hartz IV bekannten Scholz geführte SPD wäre noch mit -4.3% dabei.

    Klar: das Ganze ist eine stark zirkuläre Argumentation, denn Programme der Parteien haben viel mit den Methoden des ZEW und Metriken wie dem Gini-Koeffizienten gemein, alle drei aber sicher nicht viel mit irgendwelchen Realitäten. In der gemeinsamen, wenn auch hermetischen Logik bleibt festzuhalten: die bekennenden Rechtspartien betreiben unzweifelhaft die Umverteilung von unten nach oben auf Kosten des Staatshaushalts.

    Und das taugt ja vielleicht später nochmal für ein Argument innerhalb dieser hermetischen Welt.

  • Vom Töten und Massenschlachten

    Heute morgen hat der Deutschlandfunk das IMI-Urgestein Tobias Pflüger interviewt (Informationen am Morgen, 14.9.), und die Art, in der der Interviewende versucht hat, Tobias dazu zu bringen, sich für einen „Mangel“ an Bellizismus zu entschuldigen, war erwartbar empörend. Umgekehrt aber war Tobias schon sehr zahm, verglichen jedenfalls mit dem militanten Pazifismus, den ich von ihm eigentlich kenne; nun ja, er trat auch als stellvertretender Vorsitzender der Linken auf, und die will erkennbar regieren[1].

    Das – wie natürlich auch das ultrazynische Rührstück um die „Ortskräfte“, die die Bundeswehr aus dem gleichen Afghanistan „rettete“, in das die Regierung in den Vormonaten noch mindestens 167 Menschen abschieben hat lassen – wiederum gibt mir den Vorwarnd, endlich ein paar politische Gegenstücke zum Tucholsky-Klassiker „Soldaten sind [meinethalben potenzielle] Mörder“ zu formulieren, die ich schon lang irgendwo unterbringen wollte (auch wenn ich anerkenne, dass sie vermutlich nicht sehr originell sind und bestimmt schon oft ganz ähnlich von PazifistInnen, AnarchistInnen und anarchistischen PazifistInnen formuliert wurden; ich sollte vermutlich mehr von solchen Leuten lesen).

    Erste Behauptung: Eine Regierung, die sich ein Militär hält, will für die eigene Macht Menschen töten.

    Für den Fall, dass jemand das nicht unmittelbar offensichtlich findet, will ich ein paar Ableitungsschritte nennen. Erstens ist nämlich Militär schicht dafür da, Krieg zu führen oder Aufstände zu unterdrücken. Ich gebe zu, dass die Bundeswehr auch schon Dämme ausgebessert, Brunnen gebohrt, und in Impfzentren ausgeholfen hat. Sie war dabei aber immer ausnehmend schlecht, bis hin zur Unfähigkeit, die Impfunterlagen korrekt und halbwegs gestapelt zusammenzutackern. Das ist soweit erwartbar, denn sowohl das Rumgeballer als auch die Gehorcherei sind bei nichttödlichen Einsätzen klar störend. Wer Personal für „humanitäre“ Einsätze vorhalten will, würde selbstverständlich keine Gewehre und Waffen kaufen und viel Geld dafür ausgeben, den Leuten den Umgang damit (statt mit Baggern, Bohrern und Büroklammern) beizubringen.

    Mithin geht es beim Militär um Personal zum Bedienen von Kriegswaffen, und das heißt zum Führen von Krieg (bei der Aufstandsbekämpfung ist das der Sonderfall des Bürgerkriegs).

    Was aber ist Krieg? Krieg ist auf der einen Seite der Versuch einer Regierung, eine andere Regierung zu ersetzen, entweder durch sich selbst („Eroberungskrieg“) oder durch eine der eigenen Machtausübung weniger hinderliche („Nation Building“). Und entweder komplett oder nur in einem Teil des Machtbereichs der anderen Regierung.

    Auf der anderen Seite ist Krieg der Versuch einer Regierung, die eigene Macht gegen eine andere Regierung oder Teile der Bevölkerung (beim Bürgerkrieg) zu halten. Wie herum es im Einzelfall auch sein mag: Es geht allein darum, Macht auszuweiten oder zu erhalten.

    Selbst wenn mensch der eigenen Regierung wider jede Evidenz (die Bundeswehr hat derzeit in, wievielen?, zwanzig oder so, anderen Ländern Waffen) unterstellt, sie sei dabei in der Rolle der machterhaltenden, quasi verteidigenden Regierung: Sie könnte jede Menge Blutvergießen verhindern, wenn sie einfach zurücktreten würde und sagen, die „angreifende“ Regierung könne ja gerne versuchen, ob sie es besser kann. Es gäbe dann keinen Krieg, und ob die Regierungsführung am Boden wirklich wesentlich schlechter wäre, ist überhaupt nicht ausgemacht. Ich z.B. würde es wahrscheinlich begrüßen, wenn die Schweiz die Regierung in Baden übernehmen würde. Oder Luxemburg: soweit es mich betrifft, könnten die mich schon erobern, denn sooo viel unethischer und steuerparadiesiger als meine gegenwärtige Regierung sind die auch nicht, aber ich glaube, deren Sozialsystem macht schon ein wenig mehr her.

    Ach, wenn nicht gerade Macron regiert, würde jetzt auch ein Überfall aus Frankreich nicht offensichtlich zu einem Rückschritt führen, wenn die Machtübergabe hinreichend friedlich passiert. Ich versuche ohnehin im Augenblick, ein wenig Französisch zu lernen.

    Also: Regierungen, die ein Militär unterhalten, sagen damit klar an, dass sie für ihre Macht töten wollen. Auf jeden Fall mal die Soldaten der anderen Regierungen.

    Es kommt aber noch schlimmer: Wie ich in meinem Furor über die Weigerung der deutschen Regierung, dem Atomwaffenverbotsvertrag TPNW beizutreten, argumentiert habe, sind Kernwaffen nur einsetzbar, um Hunderttausende oder Millionen von Untertanen einer (na ja: in der Regel) anderen Regierung zu töten. Es gibt schlicht keine anderen glaubhaften Einsatzszenarien.

    Mithin ist, wer die Bombe werfen will, gewillt, für die eigene Macht Städte in Schlachthäuser zu verwandeln. Alle deutschen Regierungen meiner Lebenszeit waren ganz wild auf die „nukleare Teilhabe“ und hatten damit diesen Willen. Die zweite Behauptung, die ich hier machen will, ergibt sich damit unmittelbar: Wer in der BRD lebt, wird regiert von Menschen, die für ihre Macht Städte ausradieren werden.

    Es wäre also schon ein großer zivilisatorischer Fortschritt, wenn sich die nächste Regierung durchringen könnte zum Statement, sie könne sich schon vorstellen, zwecks Machterhalt ein paar hundert, tausend, oder zehntausend Menschen zu töten (also: sie löst die Bundeswehr nicht einfach auf, was natürlich der erfreulichste Ausgang wäre); der eigene Machterhalt würde aber doch nicht rechtfertigen, dutzendweise Städte einzuäschern (weshalb sie den Spuk der nuklearen Teilhabe beenden und dem TPNW beitreten würde).

    Ich wette dagegen.

    [1]Da ich wild entschlossen bin, niemals mit meiner Zustimmung regiert zu werden und also nie der künftigen Regierung meine Stimme geben will – das ist mir bisher auch nicht schwer gefallen –, konnte ich daher leider den Linken nicht meine Stimme geben. Repräsentative Demokratie ist schon manchmal kompliziert, denn im Parlament will ich die Linke selbstverständlich schon haben: Wo wären wir heute ohne ihre parlamentarischen Anfragen?
  • Kopfzahlen: Kohlendioxid

    Es gibt eine Handvoll Zahlen, die ich versuche, so ungefähr im Kopf zu haben. Ich nenne die gerne Kopfzahlen, auch wenn das Wort laut Duden was ganz anderes bedeutet.

    Die klassische Kopfzahl ist natürlich die Zahl der Sekunden in einem Jahr, mit guter Genauigkeit π ⋅ 1e7 (ich schreibe hier aus Bequemlichkeit und Computergewohnheit 107 als 1e7). Überlegungen des Typs „ich habe eine Milliarde Datensätze und schaffe 3000 pro Sekunde, also brauche ich für das ganze Ding ungefähr 300000 Sekunden oder halt 3e5/3e7, also ein hundertstel Jahr oder vier Tage“ finde ich sehr hilfreich. Würde ich Mathematik an einer Schule unterrichten, würde ich Depressionen kriegen, wenn meine Schülis sowas nicht hinkriegen würden, nachdem ich ein Jahr auf sie eingeredet habe. Gut, dass ich kein Schullehrer bin.

    Heute nun hat mich die Deutsche Umwelthilfe (DUH) angeschrieben, weil sie Spenden haben will für Klagen gegen BMW, Daimler und Wintershall[1]. Die Brücke zu den Kopfzahlen: sie sagen darin, Wintershall sei allein für rund 80 Megatonnen CO2 im Jahr verantwortlich.

    Um solche Angaben ein wenig einordnen zu können, hatte ich mir vor zehn Jahren zwei Kopfzahlen zurechtgelegt: Die BRD emittierte rund eine Gigatonne, die Welt rund dreißig Gigatonnen CO2. Demnach macht Wintershall rund 8% des CO2-Ausstoßes der BRD aus; das ist nicht unplausibel, aber ich dachte mir, das ist ein guter Anlass, meinen schon etwas länger gehegten Plan umzusetzen, solche Kopfzahlen im Blog zu notieren und zu aktualisieren. Wie steht es nun also, zehn Jahre nach 1 und 30 Gigatonnen?

    Für halbwegs überschaubare Infos zu den BRD-Emissionen hatte ich ganz naiv auf die Publikationen zum Emissionshandel des UBA gehofft, aber zumindest der Juli-Bericht ist in der Hinsicht nicht sehr ergiebig: Was genau heißt es, dass in der Kalenderwoche 27 Emissionsrechte über 280 Millionen Tonnen gehandelt wurden (außer, dass ein Haufen Leute nicht sehr nützliche Arbeit hatten)?

    Aber das Umweltbundesamt hat glücklicherweise eine andere Seite mit nicht marktkontaminierten Informationen. Demnach wären 2020 in der BRD 644 Millionen Tonnen CO2 emittiert worden. Auf drei Stellen genau ist das natürlich lächerlicher Unfug; ich wäre überrascht, wenn das auf 10% irgendeiner sinnvollen Definition von Emission entspräche. Aber dennoch: 2/3 Gigatonnen klingen nach einer brauchbaren Kopfzahl für die aktuelle Kohlendioxid-Emission direkt aus der BRD. Natürlich wären dazu die importierten Emissionen (z.B. aus brasilianischem Sojaanbau für „unser“ Fleisch oder chinesischer Zement- und Stahlproduktion für „unsere“ Häuser und Elektronik) zu rechnen, aber das wird dann kompliziert und letztlich spekulativ.

    Neu merken sollte ich mir den Quellen-Split, den sie dort aufmachen, wieder bereinigt um allzu mutige Claims bezüglich Genauigkeit: Energie 1/3, Haushalte 1/5, Straßenverkehr ein gutes Fünftel, Industrie (wozu dann auch z.B. Hausbau gehört) 1/4.

    Für die globale Emission gibt der einschlägige Artikel der Wikipedia 57 Gigatonnen CO2-Äquivalent an, wo dann allerdings jede Menge tiefes Voodoo reingerechnet ist („including 5 Gt due to land use change“).

    Um dem tiefen Voodoo zu entkommen (und nur das Einsteiger-Voodoo der Emissionsschätzung zu behalten), kann mensch zum aus meiner Sicht relativ vertrauenswürdigen Our World in Data gehen, das eine Kohlendioxid-Seite anbietet, die wiederum auf github-Daten verweist, wo schließlich CO2 und Treibhausgas-Äquivalente separat ausgewiesen sind. Deutschland hat dabei für 2019 700 Mt; das passt im Rahmen meiner Erwartungen gut zu unseren 2/3 Gt für 2020 von oben. Für die Welt (freundlicherweise gibt es die in dem Datensatz schon vorkumuliert unter „World“) und 2019 stehen da mit absurden acht Stellen 36441.388 Mt. Schon zwei Stellen Genauigkeit sind da sicher überoptimistisch, und so wäre meine Kopfzahl für die Weltemission jetzt „um die 35 Gt“.

    Mein Spickzettel für diese Runde Kopfzahlen ist also:

    • Emission in der BRD: 2/3 Gt, davon anteilig 1/3 Energie, 1/4 Industrie, je rund 1/5 Verkehr und Haushalte.
    • Emission in der Welt: 35 Gt, womit die BRD direkt ungefähr 1/50 der Welt-Emission ausmacht.
    [1]Dass mich die DUH immer noch in ihrem CRM-System hat, obwohl ich ihnen eigentlich nur vor Jahren mal gespendet habe – die erfolgreichen Fahrverbots-Klagen haben mich schon sehr begeistert – finde ich jetzt nicht so großartig, aber wer sich so effektiv dem Autowahnsinn entgegenstellt, darf in menem Buch auch mal nicht ganz so korrekt mit Daten umgehen.
  • Die Intensiv-Antwort

    Ich habe ja immer noch nicht so recht meinen Frieden gemacht mit dem Schluss von 66 ist das neue 50 vom letzten Freitag, dass nämlich die Impfkampagne bisher fast keinen Unterschied macht für unsere Fähigkeit, hohe Inzidenzen – die wir praktisch sicher bald haben werden, wenn wir nicht wieder viel weiter zumachen als derzeit – zu ertragen, ohne dass die Intensivstationen zu einem Schlachtfeld werden.

    Die Frage lässt sich zurückführen auf: „Wie viele Intensivbetten waren eigentlich relativ zur jeweiligen Inzidenz durch SARS-2-Erkrankte belegt?“ (bei allen berechtigten Zweifeln an der zuverlässigen Messung letzterer). Diese Antwort der Intensivbelegung auf das Infektionsgeschehen – ich taufe es mal int/inc, weil ich zu faul bin, nachzusehen, wie die Profis das nennen – kriege ich mit dem Besteck vom Coronafilm-Post und meiner DIVI-Auswertung relativ schnell ausgerechnet, und wenn ich dann das Verhältnis von DIVI-Belegung (gezogen jeweils aus den RKI-Berichten der Zeit) zur bundesweiten Inzidenz (auf der Basis der Referenz-Daten des RKI) plotte, sieht das so aus:

    Ein großes Maximum im Juli 2021

    Ich habe die y-Achse wieder logarithmisch eingeteilt, in dem Fall nicht, weil ich irgendwelche exponentiellen Verläufe erwarten würde, sondern weil mir im Wesentlichen der Verlauf im unteren Teil des Wertebereichs am Herzen liegt.

    Das dominierende Feature Anfang Juli 2021 nämlich ist schnell erklärt: Hier waren die Inzidenzen sehr niedrig, die langwierigen Fälle blieben aber auf den Intensivstationen. Und so ging es bis zu 120 IntensivpatientInnen pro Punkt Inzidenz. Ähnlich reflektiert das zweithöchste Feature rund um den Februar 2021 lediglich die fallende Inzidenz und also quasi vorauseilend kleine Nenner zwischen zweiter und dritter Welle.

    Wenn mensch sich diese großen Features mal rausdenkt, kommt als Basis etwas heraus zwischen 20 und 40 Intensivpatient_innen pro Inzidenzpunkt während der Wellen. Mitte August, als die Inzidenzen kräftig anzogen, haben wir vielleicht auch mal 10 touchiert, was zu erwarten wäre, wenn die Basisrate 30 wäre und um die zwei Drittel der Leute geimpft sind. Angesichts der dramatischen Altersabhängigkeit der Hospialisierung von SARS-2 sind solche Überlegungen aber ohne demographische Betrachtungen unsinnig.

    Der Anstieg ganz am rechten Rand des Graphen dürfte übrigens zumindest in diesem Ausmaß ein Artefakt sein, denn ich rechne hier wie gesagt mit dem, was das RKI als Referenzdatum angibt (wo das Infektionsdatum das Meldedatum ersetzt, wenn das rauszukriegen ist), und es kann so sein, dass aus den letzten zwei Wochen Fälle in die weitere Vergangenheit gewandert sind, die, die sie in Zukunft aus der Gegenwart bekommen werden, aber noch nicht da sind; das würde die Inzidenz unterschätzen und mein int/inc überschätzen.

    Nehmen wir trotzdem an, wir hätten mit der derzeitigen Virenpopulation, Infektionsdemographie und Impfquote im Gleichgewicht 20 Intensivpatient_innen pro Indizenzpunkt. Wenn wir dann unter 6000 Intensivpatient_innen bleiben wollen, ist eine 300er-Inzidenz das äußerste, was mit viel Glück zu stemmen wäre. Wenn also nicht mein int/inc drastisch runtergeht – als Faustregel dürfte taugen, dass jede Halbierung des Anteils der ungeimpften Erwachsenen auch eine Halbierung von int/inc nach sich ziehen würde –, wird es noch lang dauern, bis die (relativ) strikte Kontrolle von SARS 2 aufhören kann.

    Eine 300er-Inzidenz in der RKI-Rechnung heißt ja, dass jede Woche 300 von 100000 Menschen SARS-2 hinter sich bringen. Bis dann alle mal SARS-2 hatten (und alle werden es irgendwann gehabt haben, dauerhaft sterile Impfungen gibts bei Atemwegserkrankungen nicht), dauert es logischerweise 100000/300, also gut 300 Wochen. Oder grob sechs Jahre.

    Sorry, liebe Ungeimpfte: so lange will ich echt nicht warten, bis ich nicht mehr an jeder Ecke damit rechnen muss, Namen und Geburtsdatum (im Impfzertifkat) von mir nicht annähernd kontrollierbaren Rechnern überantworten zu müssen.

    Technics

    Der Code, der das erzeugt, ist nicht spannend; mit corona.py sieht die Inzidenzberechnung so aus:

    def get_incidences(tuples):
        """returns a mapping of dates to 7-day incidences from RKI tuples
        as yielded by corona.iter_counts.
        """
        tuples.sort(key=lambda r: r[0])
        incidences = {}
        queue = collections.deque([0], maxlen=7)
        cur_date = None
    
        for tup in tuples:
            if tup[0]!=cur_date:
                if len(queue)==7:
                    incidences[cur_date] = sum(queue)/830
                queue.append(0)
                cur_date = tup[0]
    
            queue[-1] += tup[2]
    
        incidences[cur_date] = sum(queue)/83e4
        return incidences
    
       with open(corona.DATA_DIR
              +"Aktuell_Deutschland_SarsCov2_Infektionen.csv") as f:
          all_rows = list(corona.iter_counts(f, True))
      incidences = get_incidences(all_rows)
    

    Das Zusammenfummeln mit den DIVI-Daten nimmt meine aus den RKI-Berichten gescreenscrapten Daten. Das ist an sich Quatsch, und ihr solltet die Daten einfach von der DIVI selbst ziehen, wenn ihr das reproduzieren wollt.

    Mit dem zusammengefummelten Daten (Spalten: Datum, int/inc, Intensivbelegung und referenzdatenbasierte Inzidenz) könnt ihr aber selbst Plots machen. Das Bild oben kommt aus diesem Gnuplot-Skript:

    set size ratio 0.375
    set key inside left top
    set xdata time
    set yrange [5:200]
    set ytics (10,22,47,100)
    set timefmt "%Y-%m-%d"
    set xrange ['2020-08-01':'2021-08-31']
    set logscale y
    set xtics format "%Y-%m-%d" time
    set term svg size 800,300
    set out "int-vs-inc.svg"
    plot "int-vs-inc.txt" using 1:2 with lines title "int/inc"
    

    ElektronikbastlerInnen werden die Ticks auf der y-Achse wiedererkennen. Logarithmen sind überall.

  • 66 ist das neue 50

    Ich sage es nicht gerne, aber: Entweder wir machen im Winter wieder einen Lockdown oder wir kriegen ein übles Gemetzel.

    Ich hatte ja schon im Januar erzählt, warum ich die Intensivbelegung für die aussagekräftigste Zahl halte, die in den RKI-Berichten vorkommt (natürlich mit dem Nachteil, dass sie für Echtzeitsteuerung nicht taugt, weil sie so weit nachläuft). Und die sieht seit ein paar Wochen übel aus, nämlich praktisch so wie im letzten Oktober:

    Im September 2021 siehts aus wie im Oktober 2020

    SARS 2-Intensivbelegung seit Mitte letzten Jahres in einem Log-Plot (also: exponentielle Entwicklung ist eine Gerade). Zur Verdeutlichung habe ich die Kurve seit diesem Juli nochmal weiß unterlegt neben die Gesamtkurve seit August 2020 gestellt: mensch sieht, wie sich die Verläufe ähneln, abgesehen von der höheren Ausgangsbasis in diesem Jahr. Die grüne Kurve ist übrigens die Zahl der invasiv Beatmeten. Sie hört mittendrin auf, weil der RKI-Tagesbericht die entsprechenden Daten nicht mehr berichtet hat.

    Die Verdopplungszeit ist derzeit so rund drei Wochen; wir dürften also, wenn nicht ein Wunder geschieht, in sechs Wochen, Mitte Oktober, deutlich über 4000 liegen und damit in der Gegend der Notbremsenbelastung rund um Neujahr 2021.

    In der Tat scheint es, als habe die Impfkampagne enttäuschend wenig Einfluss auf diese Zahlen. Heute ist die SARS 2-Intensivbelegung laut RKI-Bericht 1169 bei einer Inzidenz von 80. Eine vergleichbare Belegung war am 23.10.2020 mit 1121 bei einer Inzidenz von 60. Von einer Entkopplung der Intensivbelegung von der Inzidenz kann also keine Rede sein, und schon gar nicht vom „200 ist das neue 50“, das Gesundheitsminister Spahn vor ein paar Wochen wagte. Dazu kommt, dass die Dunkelziffer angesichts des immer noch relativ engen Testregimes in diesem Jahr vermutlich niedriger ist als letztes Jahr und mithin die damalige Inzidenz nach oben korrigiert werden müsste, um mit aktuellen Zahlen vergleichbar zu sein. Selbst ohne so eine Korrektur ist allenfalls 50 ⋅ (80/60) = 66 das neue 50.

    Warum kaum ein Impfeffekt sichtbar ist in diesen Zahlen: Tja, das ist eine spannende Frage, und ich bin neugierig auf Christian Drostens Kommentare dazu, wenn es heute abend wieder ein neues Coronavirus-Update gibt (endlich!). Mein persönlicher Verdacht ist etwas gruselig. Wahrscheinlich stimmen die anekdotischen Berichte, dass aus den Alten- und Pflegeheimen in den ersten drei Wellen kaum Menschen in Intensivstationen überwiesen wurden und sie in aller Regel einfach an Ort und Stelle starben. Die jüngeren Menschen hingegen, die es jetzt vermehrt erwischt (auch wenn der Altersmedian weniger schwankt als ich glaubte), erhalten vermutlich erheblich häufiger Intensivpflege. Ich bin nicht sicher, ob ich da ein „hoffe ich“ dazu schreiben möchte.

    Ohne einen „Delta-Effekt“ wird diese Erklärung jedoch nicht auskommen, denn von den rund 1000 relativ jungen Menschen, denen im letzten Juni bei Tönnies SARS-2 reingedrückt wurde, landeten, soweit ich weiß, keine auf Intensivstationen – und jedenfalls starben keine. Bei vor drei Wochen (als sich heute eingelieferte so plusminus angesteckt haben werden) rund 30000 RKI-aktiven SARS-2-Fällen und einer derzeit vermutlich mit der Tönnies-Belegschaft vergleichbaren Demographie dürften die Intensivstationen eigentlich nicht volllaufen, wenn das Virus nicht eine ganze Ecke fieser geworden wäre.

    Wie dem auch sei: Meine Hoffnung, dass mit jüngeren Kranken und vielen Geimpften höhere Inzidenzen ohne Gemetzel durchhaltbar sind, war klar unbegründet. Das aber macht erneute Notbremsen ab Oktober (und vielleicht schon früher) zu einer praktischen Gewissheit. Ich vermute stark, dass diese Geimpfte anfangs ausnehmen werden. Ich nehme keine Wetten an zur Frage, ob das ausreichen wird. So'n Mist.

  • Wenn LektorInnen schlafen

    Als jemand, dem es schon in der BRD zu frömmelnd zugeht, bin ich ziemlich sicher nicht in Gefahr, allzu große Sympathien für die neue afghanische Regierung („Taliban“) zu hegen. Und trotzdem schmerzt so gut wie die gesamte Berichterstattung über das Regime, gerade im Vergleich zum doch recht gemäßigten Umgang mit Saudi-Arabien. Dort nämlich geht es nach innen hin wohl ziemlich ähnlich zu, doch die Taliban führen immerhin keinen Krieg nach außen, schon gar nicht einen in der Liga des Gemetzels im Jemen.

    Aber selbst wenn sich JournalistInnen in patriotischer[1] Botmäßigkeit verpflichtet fühlen, den Krieg zu rechtfertigen, den unsere Regierungen mit breiter publizistischer Unterstütztung in den letzten zwanzig Jahren „am Hindukusch“ geführt haben: Wenigstens etwas Sorgfalt sollten sie walten lassen. Auf Seite 9 der taz vom 24.8.2021 ist das ganz offenbar nicht passiert. Während Finn Mayer-Kuckuk vierspaltig einen „Rückfall in den Handel mit Drogen“ drohen sieht, stellt der Agenturartikel in der fünften Spalte klar, dass bereits das von „uns“ befreite Afghanistan 85% des Weltbedarfs an Opiaten gedeckt hat:

    Oben: „Es droht ein Rückfall in den Handel mit Drogen“.  Unten: „Afghanistan produziert UN-Angaben zufolge rund 85 Prozent des weltweit hergestellten Opiums“

    Rechte bei der taz; hier verwendet unter… Satireprivileg?

    EinE gutE LektorIn hätte es gemerkt.

    Wo ich schon über Afghanistan spreche, möchte ich noch kurz ein Standbild aus der Tagesschau vom 28.8.2021 zeigen:

    Eingang zum „Hamid Karzai International Airport“

    Rechte wahrscheinlich bei der Tagesschau.

    Das entscheidende Detail hier ist: Das alte Regime war unbescheiden genug, den Flughafen nach dem immerhin noch lebenden und (z.B. nach der Lebenserwartung der Untertanen bemessen) gewiss nicht sehr erfolgreichen Ex-Präsidenten Hamid Karzai zu benennen. Nur mal so als ein Datenpunkt zur Erklärung, warum die Regierung, die „wir“ dort 20 Jahre lang installiert hatten, noch vor „unserem“ Abzug so ostentativ kollabiert ist.

    Zum Abschluss hätte ich einen Buchtipp in dem Zusammenhang: Cockburn, A. und St. Clair, J., 1998: „Whiteout: The CIA, Drugs, and the Press“, London, New York: Verso (verfügbar in der Imperial Library of Trantor). Der Guardian hat es damals rezensiert mit:

    A history of hypocrisy and political interference the like of which only Frederick Forsyth in a dangerous caffeine frenzy could make up.

    Dem kann ich zustimmen.

    [1]Wo ich es schon von Patriotismus habe, will ich einen Aufkleber lobend erwähnen, den ich neulich in Hamburg-Wilhelmsburg gesehen habe: „Wer als Patriot*in losläuft, kommt als Faschist*in an“. Wenn nur das Wetter nicht so lausig wäre in der Gegend – mensch könnte da fast hinziehen wollen.

« Seite 14 / 18 »

Letzte Ergänzungen