• Dickens und die Gründerzeit – Pro und Contra

    Ich bin kein besonderer Freund von Pro/Contra-Formaten wie der Streitkultur am Deutschlandfunk. Warum? Nun, ich denke, die dabei diskutierbaren Fragen lassen sich in drei Gruppen einteilen:

    1. Es gibt keine klare Antwort, und mensch muss sich auch nicht einigen. Beispiel: „Ist die Musik von Richard oder einem der Johann Strauße besser oder die von den Rolling Stones?“ – natürlich braucht das nur unter der Annahme keine Einigung, dass die Beteiligten sich aktustisch aus dem Weg gehen können; aber können sie das nicht, sollte vielleicht das repariert werden.
    2. Es gibt keine klare Antwort, und mensch muss sich irgendwie einigen, z.B. „Wollen wir hier im Flur noch ein Regal aufstellen?“ Das braucht eine Einigung unter der Annahme, dass die Beteiligten sich den Flur teilen, und es ist nicht klar entscheidbar unter der Annahme, dass der Flur auch mit Regal noch grob benutzbar bleibt.
    3. Es gibt eine ethisch oder sachlich klar gebotene Antwort, und es ist allenfalls statthaft, noch nachzusehen, wie viel Bequemlichkeit mensch sich gegen das gebotene Verhalten rausnehmen kann („können wir nicht 22 Grad machen in der Wohnung?“; „kann ich mir eine Flasche Kokos-Ananas-Saft kaufen?“).

    Beim ersten und dritten Typ hat so ein Pro/Contra-Format keinen Sinn; im ersten Fall ist der Streit Zeitverschwendung, weil weder Notwendigkeit noch Möglichkeit einer Einigung besteht. Im dritten Fall hingegen muss mindestens ein Part in der Diskussion entweder ethischen Bankrott oder Realitätsverweigerung erklären, wenn die ganze Veranstaltung nicht eine rhetorische Übung werden soll[1] (was mithin fast notwendig passiert).

    Nur bei Fragen vom Typ 2 hat so ein Format eigentlich Sinn. Es ist aber gar einfach, nichttriviale Beispiele für solche Fragen zu finden. Die in der Streitkultur vom 13.11. debattierte war jedenfalls nicht von diesem Typ: „Brauchen wir ein Einwanderungsrecht nach kanadischem Vorbild?“ Ethisch ist völlig klar, dass es nicht angeht, Menschen nach ihrer Herkunft zu sortieren, und so wäre das kanadische Modell vielleicht schon ein wenig besser als das, was wir jetzt haben, aber immer noch ein Tiefschlag gegen Ethik und Menschenrecht.

    Natürlich wurde das in der Sendung nicht so analysiert. Wie auch, wenn sich ein „Migrationsforscher“ (Dietrich Thränhardt, der immerhin schon seit den 1980er Jahren das „Gastarbeiter“-Narrativ bekämpft hat) mit einem VWL-Prof aus dem FDP-Sumpf (Karl-Heinz Paqué) herumschlagen muss. Dennoch fand ich die Sendung bemerkenswert, und zwar aus drei Gründen.

    Erstens gab es mal wieder so einen Dickens-Moment, also eine Aussage, mit der ganz analog die Reichen in der Zeit von Charles Dickens das katastrophale Elend gleich nebenan wegignorieren konnten. Wer sich fragt, wie Verhältnisse aus Oliver Twist (oder meinethalben dem kleinen Lord; Weihnachten steht ja vor der Tür) und weit Schlimmeres für die Menschen damals ertragbar waren: Nun, wir haben ganz ähnliche Verhältnisse immer noch, nur eben nicht mehr zwischen dem Manor House und der Taglöhnersiedlung, sondern zwischen globalem Norden und Süden. Und Paqué ist völlig klar, dass „wir“ diese schockierende Ungleichheit mit Gewalt aufrechterhalten und vertritt nonchalant, dass die Ausübung dieser Gewalt unser Recht, ja unsere Pflicht ist (ab Minute 9:12):

    Die Freizügigkeit innerhalb Europas kann man nicht mit einer globalen Freizügigkeit vergleichen. Wir haben hier eine gänzlich andere Situation, wie wir übrigens auch bei dem Migrationsstrom im Zusammenhang mit unserem Asylrecht sehen, das man natürlich ganz scharf von einem Zuwanderungspunktesystem unterscheiden muss. [...] Das ist nicht auf die Welt übertragbar. Hier muss man eine Auswahl treffen [... Minute 13:30] Bis wir diesen Punkt erreichen, eines globalen Arbeitsmarkts, den ich mir natürlich auch wünschen würde, als Liberaler, werden noch einige Jahrzehnte vergehen, da brauchen Sie eine Übergangslösung.

    Was ist der Unterschied zwischen Europa und der Welt? Nun, woanders sind die Leute noch schlimmer dran als in Rumänien oder Polen. Das damit verbundene Elend hat Paqué ganz offenbar völlig wegabstrahiert, als einen quasi naturgesetzlich „noch einige Jahrzehnte“ fortbestehenden Sachzwang. Genau so muss das die viktorianische Gentry auch gedacht haben, als sie die Kinder ihrer ArbeiterInnen zu 14-Stunden-Schichten in die Minen einfahren ließen.

    Immerhin, und das ist mein zweiter Punkt, hat Paqué eine realistische Selbsteinschätzung (Minute 14:05).

    Ich wäre als habilitierter Volkswirt völlig ungeeignet, um eine technische Aufgabe in einem Unternehmen zu lösen.

    Wenn das die anderen Volks- und Betriebswirte auch einsehen und aufhören würden, die Leute zu „managen“, die nachher die „technischen Aufgaben“ erledigen: Das wäre ein klarer Fortschritt.

    Und schließlich verriet Paqué, in welcher Zeit er sich sieht:

    Deutschland war zwar immer faktisch zu einem relativ hohen Grad ein Einwanderungsland, auch schon am Ende des letzten Jahrhunderts, im wilhelminischen Boom…

    Der „wilhelminische Boom“ heißt unter Nichtvolkswirten „Gründerzeit“. Beide Bezeichnungen sind etwa gleich dämlich, um so mehr angesichts des oben für die entsprechende Epoche in England diskutierten himmelschreienden Massenelends. Großzügig gerechnet geht es um ein paar Jahre um 1870 herum. Der Boom war, das nur nebenbei, schnell vorbei und mündete in rund zwanzig Jahre Krise.

    Diese Zeit noch im zweiten Jahrzehnt des dritten Jahrtausends als „letztes Jahrhundert“ zu bezeichnen: Nun, das passt gut zu jemand, der erkennbar immer noch Marktwirtschaft pfundig findet und möglicherweise sogar die ökonomischen Rezepte von damals für im Prinzip richtig, aber schlecht umgesetzt hält.

    [1]Damit will ich nicht gesagt haben, eine gemeinsame Klärung der Verhältnisse und Verhältnismäßigkeiten sei nicht sinnvoll. Das wären dann Fragen wie: was müssen wir noch rauskriegen, um eine verlässliche Antwort zu finden? Wie viel subjektiven Gewinn habe ich von einer Verletzung der physisch („Newton war ein Schwätzer: 10 m über dem Boden hört die Gravitation auf”) oder ethisch („Bei den Temperaturen setz ich mich nicht aufs Fahrrad.“) gebotenen Antwort? Aber genau dieser zweifellos sinnvolle Diskurs findet in Pro/Contra-Formaten konzeptionell und in aller Regel auch real nicht statt.
  • Fernseh-Livestreams mit Python und mpv

    Nachtrag (2022-10-11)

    Ich habe das Programm jetzt auf codeberg untergebracht

    Die Unsitte, alles in den Browser zu verlegen, ist ja schon aus einer Freiheitsperspektive zu verurteilen – „die Plattform“ gibt die Benutzerschnittstelle vor, kann die Software jederzeit abschalten und sieht (potenziell) noch den kleinsten Klick der NutzerIn (vgl. WWWorst App Store). Bei Mediatheken und Livestreams kommt noch dazu, dass videoabspielende Browser jedenfalls in der Vergangenheit gerne mal einen Faktor zwei oder drei mehr Strom verbraucht haben als ordentliche Videosoftware, von vermeidbaren Hakeleien aufgrund von schlechter Hardwarenutzung und daraus resultierendem Elektroschrott ganz zu schweigen.

    Es gibt also viele Gründe, speziell im Videobereich den Web-Gefängnissen entkommen zu wollen. Für übliche Videoplattformen gibt es dafür Meisterwerke der EntwicklerInnengeduld wie youtube-dl oder streamlink.

    Für die Live-Ströme der öffentlich-rechtlichen Anstalten hingegen habe ich zumindest nichts Paketiertes gefunden. Vor vielen Jahren hatte ich von einem Freund ein paar screenscrapende Zeilen Python erledigt dazu. In diesen Zeiten müssen allerdings Webseiten alle paar Monate komplett umgeschrieben („jquery ist doch total alt, angular.js hat auch schon bessere Tage gesehen“) und regelauncht werden, und das Skript ging mit einem Relaunch ca. 2015 kaputt. Als ich am Freitag die Tagesschau ansehen wollte, ohne DVB-Hardware zu haben, habe ich mich deshalb nach einer Neufassung des Skripts umgesehen.

    Das Ergebnis war eine uralte Seite mit mpv-Kommandozeilen und ein Verweis auf ein von den MediathekView-Leuten gepflegtes Verzeichnis von Live-Strömen. Da stehen zwar oben alt aussehende Timestamps drin, das Log aber zeigt, dass der Kram durchaus gepflegt wird.

    Aus letzterem habe ich livetv.py (ja, das ist ein Download-Link) gestrickt, ein weiteres meiner Ein-Datei-Programme. Installiert mpv (und Python, klar), macht chmod +x livetv.py und sagt dann ./livetv.py ARD Livestream – fertig. Bequemer ist es natürlich, das Skript einfach irgendwo in den Pfad zu legen.

    Das Argument, das das Programm haben will, kann irgendeine Zeichenfolge sein, die eindeutig einen Sender identifiziert, also nur in einem Sendernamen vorkommt. Welche Sender es gibt, gibt das Programm aus, wenn es ohne Argumente aufgerufen wird:

    $ livetv.py
    3Sat Livestream
    ...
    PHOENIX Livestream
    

    Mit der aktuellen Liste könnt ihr z.B. livetv.py Hamburg sagen, weil „Hamburg“ (auch nach Normalisierung auf Kleinbuchstaben) nur in einer Stationsbezeichnung vorkommt, während „SWR“ auf eine Rückfrage führt:

    $ livetv.py SWR
    SWR BW Livestream? SWR RP Livestream?
    

    „SWR BW“ (mit oder ohne Quotes auf der Kommandozeile) ist dann eindeutig, woraufhin livetv an den mpv übergibt.

    Ich gehe davon aus, dass die Anstalten die URLs ihrer Streams auch weiterhin munter verändern werden. Deshalb kann sich das Programm neue URLs von den MediathekView-Leuten holen, und zwar durch den Aufruf:

    $ livety.py update
    

    Das schreibt, wenn alles gut geht, die Programmdatei neu und funktioniert mithin nur, wenn ihr Schreibrechte auf das Verzeichnis habt, in dem livetv.py liegt – was besser nicht der Fall sein sollte, wenn ihr es z.B. nach /usr/local/bin geschoben habt.

    A propos Sicherheitsüberlegungen: Der update-Teil vertraut gegenwärtig ein wenig den MediathekView-Repo – ich entschärfe zwar die offensichtlichsten Probleme, die durch Kopieren heruntergeladenen Materials in ausführbaren Code entstehen, aber ich verspreche nicht, raffinierteren Angriffen zu widerstehen. Abgesehen vom update-Teil halte ich das Programm für sicherheits-unkritisch. Es redet selbst auch nicht mit dem Netz, sondern überlässt das dem mpv.

    Livetv.py sagt per Voreinstellung dem mpv, es solle einen „vernünftigen“ Stream aussuchen, was sich im Augenblick zu „2 Mbit/s oder weniger“ übersetzt. Wer eine andere Auffassung von „vernünftig“ hat, kann die --max-bitrate-Option verwenden, die einfach an mpvs --hls-bitrate weitergereicht wird. Damit könnt ihr

    $ livetv.py --max-bitrate min arte.de
    

    für etwas sagen, das für die Sender, die ich geprüft habe, auch auf sehr alten Geräten noch geht,

    $ livetv.py --max-bitrate max arte.fr
    

    für HD-Wahnsinn oder

    $ livetv.py --max-bitrate 4000000 dw live
    

    für einen Stream, der nicht mehr als 4 MB/s verbraucht.

    Technics

    Die größte Fummelei war, die Kanalliste geparst zu bekommen, denn aus Gründen, für die meine Fantasie nicht ausreicht (MediathekView-Leute: Ich wäre echt neugierig, warum ihr das so gemacht habt), kommen die Sender in einem JSON-Objekt (statt einer Liste), und jeder Sender hat den gleichen Schlüssel:

    "X" : [ "3Sat", "Livestream", ...
    "X" : [ "ARD", "Livestream", ...
    

    – ein einfaches json.loads liefert also ein Dictionary, in dem nur ein Kanal enthalten ist.

    Auch wenn ich sowas noch nie gesehen habe, ist es offenbar nicht ganz unüblich, denn der json-Parser aus der Python-Standardbibliothek ist darauf vorbereitet. Wer ein JSONDecoder-Objekt konstruiert, kann in object_pairs_hook eine Funktion übergeben, die entscheiden kann, was mit solchen mehrfach besetzen Schlüsseln pasieren soll. Sie bekommt vom Parser eine Sequenz von Schlüssel-Wert-Paaren übergeben.

    Für meine spezielle Anwendung will ich lediglich ein Mapping von Stationstiteln (in Element 3 der Kanaldefinition) zu Stream-URLs (in Element 8) rausziehen und den Rest der Information wegwerfen. Deshalb reicht mir Code wie dieser:

    def load_stations():
      channels = {}
      def collect(args):
        for name, val in args:
          if name=="X":
            channels[val[2]] = val[8]
    
      dec = json.JSONDecoder(object_pairs_hook=collect)
      dec.decode(LIST_CACHE)
    
      return channels
    

    – das channels-Dictionary, das collect nach und nach füllt, ist wegen Pythons Scoping-Regeln das, das load_stations definiert. Die collect-Funktion ist also eine Closure, eine Funktion, die Teile ihres Definitionsumfelds einpackt und mitnehmt. So etwas macht das Leben von AutorInnen von Code sehr oft leichter – aber vielleicht nicht das Leben der späteren LeserInnen. Dass die collect-Funktion als ein Seiteneffekt von dec.decode(...) aufgerufen wird und dadurch channels gefüllt wird, braucht jedenfalls erstmal etwas Überlegung.

    Der andere interessante Aspekt am Code ist, dass ich die Liste der Live-Streams nicht separat irgendwo ablegen wollte. Das Ganze soll ja ein Ein-Datei-Programm sein, das einfach und ohne Installation überall läuft, wo es Python und mpv gibt. Ein Blick ins Commit-Log der Kanalliste verrät, dass sich diese allein im letzten Jahr über ein dutzend Mal geändert hat (herzlichen Dank an dieser Stelle an die Maintainer!). Es braucht also eine Möglichkeit, sie aktuell zu halten, wenn ich die Liste nicht bei jedem Aufruf erneut aus dem Netz holen will. Das aber will ich auf keinen Fall, weniger, um github zu schonen, mehr, weil sonst github sehen kann, wer so alles wann livetv.py verwendet.

    Ich könnte die Liste beim ersten Programmstart holen und irgendwo im Home (oder gar unter /var/tmp) speichern. Aber dann setzt zumindest der erste Aufruf einen Datenpunkt bei github, und zwar für neue NutzerInnen eher überraschend. Das kann ich verhindern, wenn ich die Liste einfach im Programm selbst speichere, also selbstverändernden Code schreibe.

    Das ist in interpretierten Sprachen eigentlich nicht schwierig, da bei ihnen Quellcode und ausgeführtes Programm identisch sind. Zu den großartigen Ideen in Unix gehört weiter, dass (das Äquivalent von) sys.argv[0] den Pfad zur gerade ausgeführten Datei enthält. Und so dachte ich mir, ich ziehe mir einfach den eigenen Programmcode und ersetze die Zuweisung des LIST_CACHE (das json-Literal von github) per Holzhammer, also regulärem Ausdruck. In Code:

    self_path = sys.argv[0]
    with open(self_path, "rb") as f:
      src = f.read()
    
    src = re.sub(b'(?s)LIST_CACHE = """.*?"""',
      b'LIST_CACHE = """%s"""'%(in_bytes.replace(b'"', b'\\"')),
      src)
    
    with open(self_path, "wb") as f:
      f.write(src)
    

    Dass das Schreiben ein eigener, fast atomarer Schritt ist, ist Vorsicht: Wenn beim Ersetzen etwas schief geht und das Programm eine Exception wirft, ist das open(... "wb") noch nicht gelaufen. Es leert ja die Programmdatei, und solange es das nicht getan hat, hat mensch eine zweite Chance. Ähnlich übrigens meine Überlegung, das alles in Binärstrings zu bearbeiten: beim Enkodieren kann es immer mal Probleme geben, die am Ende zu teilgeschriebenen Dateien führen können. Vermeide ich Umkodierungen, kann zumindest die Sorte von Fehler nicht auftreten.

    Wie dem auch sei: Dieser Code funktioniert nicht. Und zwar in recht typischer Weise innerhalb der Familie von Quines und anderen Selbstanwendungsproblemen: das re.sub erwischt auch seine beiden ersten Argumente, denn beide passen auf das Muster LIST_CACHE = """.*?""". Deshalb würden von livetv.py update auch diese beiden durch das json-Literal mit den Senderdefinitionen ersetzt. Das so geänderte Programm hat zwei Syntaxfehler, weil das json natürlich nicht in die String-Literale passt, und selbst wenn es das täte, gingen keine weiteren Updates mehr, da die Such- und Ersatzpatterns wegersetzt wären.

    Eine Lösung in diesem Fall ist geradezu billig: in Python kann mensch ein Leerzeichen auch als '\x20' schreiben (das ASCII-Zeichen Nummer 0x20 oder 32), und schon matcht der reguläre Ausdruck nicht mehr sich selbst:

    re.sub(b'(?s)LIST_CACHE\x20= """.*?"""',
      b'LIST_CACHE\x20= """%s"""'...
    

    Sicherheitsfragen

    Ein Programm, das Daten aus dem Netz in sich selbst einbaut, muss eigentlich eine Ecke vorsichtiger vorgehen als dieses hier. Stellt euch vor, irgendwer bekommt etwas wie:

    { "Filmliste": [....,
      "X": ["...
      "Igore": ['"""; os.system("rm -r ~"); """']
    }
    

    in das MediathekView-Repo committet; das würde für die MediathekView immer noch prima funktionieren, das Objekt mit dem Schlüssel Ignore würde fast sicher tatsächlich einfach ignoriert.

    Wer dann allerdings livetv.py update laufen lässt, bekommt den ganzen Kram in Python-Quelltext gepackt, und der Inhalt des Ignore-Schlüssels wird vom Python-Parser gelesen. Der sieht, wie der lange String mit den drei Anführungszeichen geschlossen wird. Danach kommt eine normale Python-Anweisung. Die hier das Home-Verzeichnis der NutzerIn löscht. Python wird die treu ausführen. Bumm.

    So funktioniert das in Wirklichkeit zum Glück nicht, denn ich escape im realen Code Anführungszeichen (das .replace(b'"', b'\\"')). Damit …

  • Antisprache: Chancengleichheit

    Foto: Schriftzug

    Freiheit, Gleichheit, Brüderlichkeit am Rathaus des fünften Pariser Arrondissements. Ok, die „Brüderlichkeit“ sollte heute besser „Solidarität“ sein. Klar ist aber: Mit „Chancengleichheit“ in der Losung wäre das 1789 nichts geworden.

    Ein Klassiker der Antisprache – Wörtern, die von normalen Wörtern übertragene Information zerstrahlen wie Antimaterie normale Materie – ist „Chancengleichheit”. Seit meinem Schurken und Engel-Post, der eine reale Chancengleichheit modelliert hat, hatte ich immer mal diskutieren wollen, warum das eine sehr unrealistische Annahme war. Jetzt ist ein guter Anlass dafür da – siehe unten.

    Zunächst aber will ich meine Fantasie loswerden, wie zwei gewissenlose Werbefuzzis die Chancengleichheit ausgekocht haben:

    A: „Die Leute finden es irgendwie doof, dass ein Vorstand so viel kostet wie 1000 Leute, die die Arbeit machen [ok: Als dieses Gespräch stattgefunden haben könnte, war der Faktor vielleicht 100]. Wir müssen was tun.“

    B: „Da springen wirklich ein paar Knallköpfe rum, die finden, alle sollten gleich viel verdienen. Haha. Wo kämen wir denn da hin? Also: Erstmal müssen wir denen klarmachen, dass Gleichheit totaler und fieser Quatsch ist.”

    A: „Das ist einfach: Lass uns von Mao-Einheitskleidung erzählen. Das wird unser claim: Gleichheit ist eintönig!“

    B: „Genau! Und nicht die Gewalt, die die krassen Eigentumsunterschiede aufrechterhält, ist Zwang, nein, es wäre total fieser Zwang, wenn die Reichen nicht mehr die anderen für sich arbeiten lassen dürften. Gleichmacherei! Das wäre die üble Bevormundung, das ist mal klar.“

    A: „Aber… die Leute werden es immer noch doof finden, dass der Vorstand hässliche Protzbauten bauen lässt und endlos in der Welt rumjettet, während die Zeitungsausträger noch nicht mal Mindestlohn [ups: den hat es in der Geburtsstunde der Chancengleichheit in der BRD noch nicht gegeben] kriegen.“

    B: „Ah bah. Denen müssen nur eben bescheidstoßen, dass sie selbst schuld sind. Wäre der Zeitungsausträger halt auch Vorstand geworden. Er hätte ja die Chance gehabt. Vor allem die Chance, sich andere Eltern auszusuchen.“

    A: „Chance… Chance… Ich habs: Wir sagen »Chancengleichheit«. Damit ist ein bisschen von der Gleichheit drin, die die Leute ja immer irgendwie gerecht finden, aber noch der letzte Hirni versteht, dass es ihm nur deshalb dreckig geht, weil er seine Chancen verplempert hat.“

    B: „Brilliant. Mit ein bisschen Glück glauben sie sogar so arg dran, dass sie versuchen, ihren Kindern die Chancen, haha, wirklich zu geben, und wir können noch einen 1a Bildungsmarkt aufmachen. Heia Bruttosozialprodukt!“

    Also gut, ich kann jetzt nicht versprechen, dass das Gespräch genau so stattgefunden hat, vielleicht irgendwann in den 70ern. Ich finde das aber ziemlich plausibel. „Chancengleichheit“ ist jedenfalls ein zutiefst reaktionäres Konzept, das einerseits Ungleichheit legitimiert und andererseits Armen die Schuld an ihrer Situation zuschiebt.

    Ein Moment der Überlegung entlarvt diesen Begriff sofort: Niemand kann sich irgendwelche Verhältnisse vorstellen, in denen die Tochter eines armen, strukturell analphabetischen Paars auch nur irgendwie vergleichbare „Chancen“ hat wie der Sohn des Klischee-Paares aus Prof und Anwältin. Nicht auf Bildung, nicht auf ordentlich bezahlte oder gar halbwegs befriedigende Jobs, nicht auf eine Machtposition und auch nicht auf einen Sitzplatz in der Oper. Und selbst wenn sie die Chance hätte: würde das die dramatischen Unterschiede bei Existenzsicherheit, Lebenserwartung, Stinkigkeit der Jobs rechtfertigen, die wir in unserer Gesellschaft haben?

    Für mich ganz zweifellos: Würde es nicht. Die Ungleichheit ist das Problem, nicht irgendwelche Gemeinheiten, die ein paar Privilegierten ein Abo auf die Sonnenseiten (für irgendwelche vielleicht seltsamen Begriffe von Sonne) geben. Die zentrale Antisprachlichkeit von Chancengleichheit besteht im Versuch, das Gleichheitsgebot zu diskreditieren und zu demontieren. Demgegenüber fast schon verzeihlich wäre der innere Widerspruch, dass eine Gleichheit von Chancen überhaupt nur bei halbwegs gleicher gesellschaftlicher Teilhabe, also mindestens sozialer Gleichheit, vorstellbar ist. Etwas gedrechselt: die Chancengleichheit hat paradoxerweise die Gleichheit, deren Fehlen sie legitimieren würde, zur Voraussetzung.

    Natürlich bin ich nicht der erste, dem das auffällt, und so ist übers letzte Jahrzehnt oder so die „Chancengerechtigkeit“ populär geworden, vermutlich zunächst aus dem Gedanken heraus, Arme müssten gerechtigkeitshalber halt mehr Chancen bekommen als Reiche, damit es am Schluss „gerecht“ zugeht, ein wenig im Stil der affirmative action.

    Leider hilft das fast nichts. Auch die Chancengerechtigkeit legitimiert jede Ungleichheit („du hattest deine gerechte Chance und hast sie nicht genutzt“) und schiebt die Schuld für individuelles Elend nur noch mehr den Armen zu („wir haben dich ja sogar extra gefördert, aber dann hast du nur handygedaddelt und RTL 2 geschaut“).

    Nein: die richtige Forderung ist die nach Gleichheit, und erfreulicherweise hat die GEW im Gastkommentar der Erziehung und Wissenschaft 11/2021 Christian Baron das auf den Punkt bringen lassen:

    Die Formel muss also lauten: Erst die Umverteilung, dann die Bildung. Oder anders gesagt: Ein Goethe-Gedicht kann Wunder bewirken. Essen kann man es aber nicht. Anstatt Bildung als Weg aus der Armut zu verkaufen und ihr damit einen rein nutzenmaximierenden Ballast aufzuladen, sollte die Politik lieber dafür sorgen, dass kein Mensch mehr in Armut leben muss.

    Nur wäre die GEW nicht die GEW, wenn nicht in der gleichen Ausgabe das kritisierte Konzept im Titel eines Artikels auftauchen würde: „20 Jahre PISA: Schlusslicht in Sachen Chancengleichheit“. Nee: Nur, weil ein Hochamt der Metrik- und Marktreligion wie PISA für eine Weile mal Argumente für eigenen Interessen liefert (oder zu liefern scheint), ist das noch lang kein Grund, den Mist zu verwenden. Oder gar, die damit transportierte Antisprache gegen die eigenen LeserInnen einzusetzen.

  • Fortschritt statt Demokratie

    Demo-Szene: Die Polizei kickt Leute weg

    Meine bisher engste Begegnung mit Olaf Scholz: Ich bin der Mensch in Gelb mit dem Knüppel im Rücken. Die Herren mit den Helmen hat Olaf Scholz geschickt. Bildrechte: ARD.

    Der Vertrag, über den die künftigen Koalitionsparteien derzeit befinden, verballhornt das (ohnehin mit Glaubwürdigkeitsproblemen behaftete) Brandt-Motto „Mehr Demokratie wagen“ zu „Mehr Fortschritt wagen“ – das ist ohne großen Zwang zusammenziehbar zu „Fortschritt statt Demokratie“ als Motto der künftigen Scholz-Regierung.

    Das finde ich angesichts meiner bisherigen Erfahrung mit Scholz sehr naheliegend. Der körperlich eindrücklichste, quasi tuchfühlendste Teil dieser Erfahrungen ist im Bild oben zu sehen. Wir sind im Juli 2017, Hamburg wird von Olaf Scholz regiert. Im Wesentlichen die ganze Stadt ist gegen den von Scholz eingefädelten Gipfel der G20. Mag sein, dass er in dieser Situation keine andere Wahl hatte, als die willkürlichen und teilweise erschreckend gewalttätigen Einsätze der Polizei unter seinem Innensenator Andy Grote[1] bedingungslos zu unterstützen. Aber wahrscheinlich fand er sie gut. Bis heute jedenfalls war von ihm keine Distanzierung oder gar Entschuldigung zu hören.

    Bevor ich wie auf dem Bild oben Bekanntschaft mit einigen Scholz'schen Knüppeln machte, hatte die Polizei ein Protestcamp in Entenwerder geräumt, und zwar trotz eines diese Räumung untersagenden Gerichtsurteils. Gegen diesen dicken Stinkefinger in Richtung der G20-GegenerInnen ebenso in Richtung dessen, was sonntags als Rechtsstaat gelobt wird, hatten sich vielleicht tausend Leute auf einer Wiese versammelt, darunter ich und auch ein paar der aus Entenwerder Vertriebenen mitsamt ihren Zelten.

    Dann kam Polizei. Viel Polizei. Und prügelte die Leute vom Platz, ohne jeden erkennbaren Grund, sieht mensch davon ab, dass Scholz und Grote schlicht keine Störung ihrer Machtdemonstration dulden wollten. Und was als eine Machtdemonstration soll so ein Gipfel gleich neben einer, ach ja, Herzkammer des Linksradikalismus in der BRD – der Austragungsort Messehallen liegt gleich neben dem Karo-, und das wiederum gleich neben dem Schanzenviertel – denn wohl sein?

    So ging es weiter: Die Eröffnungsdemo („Welcome to Hell“) hat die Polizei von vorne in einer Straßenschlucht angegriffen – mit dem spätestens nach über anderthalb Jahren Maskenpflicht bei Versammlungen schier unfassbar dämlichen Vorwand, ein paar der TeilnehmerInnen hätten sich vermummt. Dass die zwischen drei Meter hohen Mauern und dem Rest der Demo eingeklemmten Menschen nicht angefangen haben, im Love-Parade-Stil panisch zu fliehen, finde ich bis heute bemerkenswert. Die Besonnenheit der Demonstrierenden hat, rückblickend betrachtet, die Köpfe von Scholz und Grote gerettet, denn Dutzende Zertrampelte wären nach dieser katastrophalen Polizeitaktik dann noch nicht durchgegangen.

    Diese Rettung dankten sie, indem sie am Folgetag am Rondenbarg nicht nur einen Demozug mit wirklich bemerkenswert brutaler Gewalt plattmachen ließen, sondern die Opfer des Einsatzes auch noch unter haarsträubenden Vorwürfen verfolgten und verfolgen – inklusive der Schikane, die auf viele Termine angelegten Verfahren selbst für Minderjährige in Hamburg laufen zu lassen, so dass Leute, die gerade noch in die Schule gegangen wären, mehrmals wöchentlich etwa auch aus Baden-Württemberg dorthin hätten fahren müssen. Immerhin ist aus der Schikane nicht viel geworden, wenn auch vor allem, weil die Justiz mit Corona nicht gut zurecht gekommen ist. Sollten die Rondenbarg-Prozesse nun doch wieder aufgenommen werden, dürften wohl auch die jüngsten Angeklagten mit der Schule fertig sein. Immerhin.

    Und damit sind wir zurück in der Gegenwart. In der ein Soldat den Corona-Krisenstab führen soll. Das hat, nach dem eben erzählten, aus meiner Sicht dem jede Menge innerer Logik. Der Menschenrechts-Record der kommenden Regierung wird jedenfalls absehbar kaum besser werden als der der Schröder-Administration.

    [1]Ja, genau der Grote, der neulich auch Menschen willkürlichen Hausdurchsuchungen unterworfen hat, ganz offenbar nur, um sein Mütchen zu kühlen („Pimmelgate“).
  • Trifft die Menschen hart

    Viele Kurven mit Lebenserwartungen

    Die SARS-2-Pandemie ist historisch: relative Änderungen der Lebenserwartungen nach Jahren für Männer, soweit doi:10.1016/S2214-109X(21)00386-7 brauchbare Daten hatte. In Blau ist die Veränderung 2020 (also vor allem durch SARS-2) markiert. Es lohnt sich, die Abbildung detailliert in einem eigenen Browserfenster anzusehen: Von den demographischen Folgen des Zusammenbruchs der alten Ordnung in vielen Ex-Ostblockstaaten über die Spanische Grippe und die verschiedenen Kriege bis hin zum Rauschen der kleinen Zahlen in Island ist viel zu entdecken. CC-BY Aburto et al.

    In den Informationen am Morgen im Deutschlandfunk hat der Moderator Rainer Brandes heute berichtet, dass die deutsche Regierung nun Einreisesperren für Menschen aus dem südlichen Afrika verhängt hat und fuhr fort mit dem Satz: „Das trifft die Menschen dort natürlich hart“.

    Wenn das ein Versuch von Empathie war, ist der ziemlich misslungen. Einerseits, weil „die Menschen“ in der Region im Schnitt sicher nicht gerade jetzt (es ist eiskalt!) dringend in die BRD wollen. Tatsächlich wäre ich überrascht, wenn das Land als Reise- oder Fluchtdestination überhaupt schon in vielen Köpfen aufgetaucht wäre, schon aus Sprachgründen.

    Weiter geht aus der Übersicht zur Visumspflicht des Auswärtigen Amts hervor, dass die BewohnerInnen aller Staaten des südlichen Afrikas (Südafrika/Azania, Eswatini, Lesotho, Simbabwe, Botsuana, Angola, Mosambik und sogar die unseres alten Schlachtfeldes Namibia) ohne Visum nicht reinkommen. Wie groß sind wohl die Chancen eines Durchschnittsmenschen aus, sagen wir, Namibia ohne bereits bestehende Kontakte hierher, so ein Visum zu bekommen?

    Der wirklich wesentliche Punkt in Sachen Empathie ist aber: Für fast die gesamte EinwohnerInnenschaft des südlichen Afrika stellt sich die Visafrage nicht, und auch nicht die coronabedingter Reisebeschränkungen: Die Leute sind schlicht zu arm, und bevor sie darüber nachdenken, wo sie nächste Woche hinfliegen könnten[1], müssen sie erstmal klarkriegen, was sie morgen zu beißen haben.

    Angesichts der oft wirklich schreienden Armut in der weiteren Region (und auch unserer eigenen Visapolitiken) ausgerechnet die coronabedinge Einreisesperre in die BRD als „hart“ zu bezeichnen – nun, das ist entweder verwegen oder ignorant.

    Etwas Ähnliches ist mir neulich beim Hören eines Interviews mit Arne Kroidl vom Tropeninstitut der GSU LMU München zu einer Corona-Seroprävalenzstudie in Äthiopien durch den Kopf gegangen. Hintergrund ist das Paper doi:10.1016/S2214-109X(21)00386-7, in dem berichtet wird, dass es zwischen August 2020 und und Februar 2021 im eher ländlich geprägten Jimma Inzidenzen im Bereich von im Schnitt 1600/100000/Woche gegeben haben muss, in Addis Abeba sogar über 4500; was das für Inzidenzen während der tatsächlichen Ausbrüche bedeutet, ist unschwer vorstellbar.

    Das ist dort offenbar nicht besonders aufgefallen, es hat ein Forschungsprojekt gebraucht, um es zu merken. In einer im Wesentlichen völlig ungeimpften Bevölkerung.

    Das ist kein Argument dafür, dass SARS-2 doch harmlos ist. Es ist ein Symptom der Nonchalance, mit der „wir“ Verhältnisse hinnehmen, in denen Menschen an einem Fleck recht normal finden, was woanders (zu recht) als wirklich ganz schlimme Gesundheitskrise empfunden würde. Bei aller Reserviertheit gegenüber Metriken und Zweifeln am Meldewesen: Laut CIA World Factbook ist die Lebenserwartung in Äthiopien 68 Jahre. Die Vergleichszahl für die BRD sind 81 Jahre.

    Aburto et al, doi:10.1093/ije/dyab207, schätzen, dass Corona, wo es wirklich schlimm durchgelaufen ist (Spanien, Belgien), etwa anderthalb Jahre Lebenserwartung gekostet hat (in der BRD: ca. 6 Monate). Wie viel schlimmer das ohne Lockdown geworden wäre, ist natürlich Spekulation, aber da es gerade die besonders verwundbaren Bevölkerungsgruppen ohnehin besonders schlimm erwischt hat, dürfte ein Faktor fünf zwischen dem realen Verlauf und dem schlimmsten Szenario eine sehr plausible Obergrenze geben, oder etwa eine um acht Jahre reduzierte Lebenserwartung. Auch damit wäre die BRD immer noch fünf Jahre über den offiziösen Zehlen in Äthiopien.

    Was in dieser Metrik[2] hier im Land ein unvorstellbares Gemetzel ist (denn fünf Mal Belgien wäre hier bundesweit Bergamo), ist dort Normalzustand, und zwar zu guten Stücken aus völlig vermeidbaren Gründen, wie beispielsweise unserer Völlerei; vgl. dazu How food and water are driving a 21st-century African land grab aus dem Guardian von 2010. Oder den IWF-Strukturanpassungsmaßnahmen, die, wo immer sie zuschlugen, das öffentliche Gesundheitswesen ruinierten und die Menschen Evangelikalen und anderen Hexendoktoren in die Arme trieben. Am Beispiel Peru illustriert zwangen „wir“ mit unseren marktradikalen Zivilreligion zwischen 1981 und 1990 die dortige Regierung zur Senkung der Gesundheitsausgaben um 75%.

    Verglichen mit solchen Totalabrissen sind unsere Gesundheitsreformen kaum mehr als das Niederlegen einer Hälfte der Doppelgarage vor der Villa. Dass „wir“ bei sowas dezent in die andere Richtung schauen, das ist ein noch größeres Empathieversagen als das vom Anfang dieses Posts.

    Nachtrag (2022-03-28)

    Der Hintergrund Politik vom 11. März wirft weitere Blicke auf die SARS-2-Situation in Afrika. In der Sendung berichtet Kondwani Jambo beispielsweise, dass BlutspenderInnen in Malawi im Februar 2022 bereits zu 80% SARS-2-positiv waren; auch in einem Land mit einem – laut Angaben der Sendung – Durchschnittsalter von knapp 18 hätte eine derart hohe Welle eigentlich stark auffallen müssen. Die Vermutung, Kreuzimmunitäten mit lokal verbreiteten anderen Coronavieren könnten geholfen haben, findet Jambo nicht bestätigt. Seine in der Sendung unverbindlich angebotene Erklärung über „schnellere“ Monozyten in Malawi gegenüber einer britischen Vergleichsgruppe finde ich allerdings spontan auch nicht allzu überzeugend.

    [1]Wie der BUND nicht ganz unplausibel behauptet: 90 Prozent der Weltbevölkerung haben noch nie ein Flugzeug von innen gesehen.
    [2]Wie immer sollte die Metrik nicht überbewertet werden; metriktheoretisch lesenswert ist in diesem Zusammenhang das Methoden-Kapitel der Aburto-Arbeit. Mensch sollte insbesondere klar haben, dass sich ein Tod weniger junger Menschen in der Lebenserwartung bei Geburt nicht von einem Tod vieler alter Menschen (wie bei SARS-2, wo der Verlust von Lebenserwartung bei Männern bei Aburto et al fast überall durch Tode in der Altersgruppe 60-79 dominiert ist) unterscheiden lässt. Mensch muss nicht Boris Palmer sein, um zwischen diesen Situationen unterscheiden zu wollen. Aber schon meine Erfahrungen mit Notaufnahmen in den USA (näher bin ich, eingestandenermaßen, Krankenhäusern im globalen Süden nie gekommen) sagen mir, dass die Lebenserwartungs-Zahlen eben doch oft sehr konkrete Not beim Zugang zu medizinischer Versorgung spiegeln.
  • Wasch mir den Pelz

    In der Forschung aktuell-Sendung vom 3.11. am Deutschlandfunk liefen zwei ziemlich bemerkenswerte Beiträge, die sich beide ein wenig im Umfeld von XKCD 1301 bewegen:

    Fake-Balkengrafik

    Nun: Ich würde XLS deutlich weniger trauen als der Autor dieser Grafik, Randall Munroe. Argumente dafür folgen unten. CC-BY-NC XKCD.

    Erstens gab es ein Interview mit Regina Riphan von der Uni Erlangen (nun: sie ist an deren WISO-Fakultät, sitzt also in Wirklichkeit in Nürnberg), in dem sie zur Nutzung wissenschaftlicher Erkenntnisse durch die Politik ab Minute 2:20 berichtet,

    dass die wissenschaftlichen Analysen am häufigsten verwendet werden, wenn sie thematisch und redaktionell aufbereitet sind

    und dann weiter ab 3:35:

    damit die Nutzung von wissenschaftlichen Erkenntnissen steigen kann, müssen die Texte gut verständlich sein und kurz zusammengefasst sein.

    Übersetzt: Wissenschaft bitte nur in Powerpoint. Eine Implikation dieser Erwartung zeigt der XKCD oben.

    Allein: Wenn etwas eindeutig ist, leicht konsumierbar runtergekocht und kurz zusammengefasst werden kann, ist es im besten Fall Lehrbuchwissen, aber jedenfalls nicht mehr Wissenschaft.

    Wissenschaft im Sinne von „was wir gerade erforschen” hat immer Voraussetzungen, Fehlerbetrachtungen und Einschränkungen, ohne die die Aussage nicht sinnvoll eingeordnet werden kann. Natürlich können auch wissenschaftliche Aussagen schon mal auf einen Satz zusammenschnurren („Cygnus X-3 enthält ein schwarzes Loch von 17 Sonnenmassen.”), aber der ist fast immer zu ergänzen mit einem „…wenn A, B und C so stimmen“. Ohne solche Einschränkungen wird es meist mehr oder weniger falsch („…aber wenn das so wäre, könnten wir das nicht im Röntgen sehen, und deshalb kann es gut sein, dass da stattdessen nicht mal ein weniger exotisches schwarzes Loch ist.“).

    Wer sich fragt, warum auch weit über den Umgang mit SARS-2 hinaus politisches Handeln oft ziemlich plemplem wirkt, könnte hier die Antwort finden. Wer Entscheidungen auf wissenschaftlicher Evidenz basieren will, muss sich auf Wisschenschaft einlassen, und das bedeutet in aller Regel, Papers zu lesen. Das dauert auch mit fachkundiger Erläuterung zumindest im Bereich der Naturwissenschaften Stunden. Für ein erstes Verständnis. Wer das nicht will, sollte vielleicht lieber nicht so viel entscheiden. Oder jedenfalls nicht sagen, seine/ihre Politik sei irgendwie anders als durch soziale Zwänge, Interessen, Fast Talk, Loyalität und Bauchgefühl geleitet.

    Dabei bleibt einzuräumen, dass ein großer Teil von Wissenschaft am Ende schlicht gar nicht hinhaut – wenn es einfach wäre, bräuchte es keine Forschung. Und gelegentlich ist Kram auch nicht nur falsch, weil er sackschwierig ist. Eine erstaunlich irre Geschichte in dieser Abteilung wird in einem zweiten Beitrag der Sendung erzählt: Da nutzen Leute ernsthaft Excel für Wissenschaft, etwas, das mir selbst in der Astronomie immer wieder mit fatalen Ergebnissen begegnet[1]. Wo Leute über Genetik reden, hat das besonders lachhafte Folgen:

    Der Name des Gens Septin 4, abgekürzt Sept4, wird automatisch in den vierten September umgewandelt.

    Das ist auch Mark Ziemann und KollegInnen von der Deakin University in Melbourne aufgefallen, die daraufhin nachgesehen haben, wie groß das Problem wohl in publizierten Arbeiten sein mag (PLOS Comput. Biol. 17(7), e1008984, doi:10.1371/journal.pcbi.1008984). Im DLF-Beitrag:

    [Ziemann:] „Die Ergebnisse waren kurz gesagt viel schlechter als bei unserer ersten Analyse 2016.“ [...] In fast jeder dritten Studie war ein Gen-Name in ein Datum gewandelt worden. [... Ziemann:] „Zunächst sollte Genomik nicht in eine Tabellenkalkulation aufgenommen werden. Es ist viel besser, Software zu nehmen, die für umfangreiche Datenanalysen geeignet ist.“

    Dem Appell am Ende des Zitats kann ich mal mit ganzem Herzen zustimmen, und zwar wie gesagt weit über das Feld der Genetik hinaus. Eine so klare und offensichtlich wahre Aussage verlässt das Feld der Wissenschaft. Ich kanonisiere sie hiermit zu Lehrbuchwissen.

    [1]Richtig schräg wird es, wenn Leute in Tabellenkalkulationen mit vorzeichenbehafteten sexagesimalen Koordinaten wie -80° 14' 27" rechnen. Klar, das sollten sie auch ohne Excel nicht tun, aber Leute, die immer noch Excel verwenden, haben offensichtlich besonders große Schwierigkeiten, sich von problematischen Traditionen zu lösen.
  • Wieder falsch vorhergesagt

    Also gut. Ich sehe es ein. Und gebe es auf. Vor neun Tagen hatte ich vorhergesagt, heute müssten so in etwa 4700 Intensivbetten mit SARS-2-PatientInnen belegt sein. In Wahrheit liegt die DIVI-Zahl im RKI-Bericht von heute bei 3987, also gut 15% darunter. Das wäre bei meinen sonstigen Handwerks-Abschätzungen kein Drama. Hier aber sagt es klar: Meine Methode taugt (erstmal) nicht (mehr).

    Hintergrund war mein Artikel von vor 18 Tagen, in dem ich (für Verhältnisse dieses Blogs sorgfältig) die Verzögerung zwischen steigenden Inzidenzen und in der Folge steigender Intensivbelegung abgeschätzt habe. Das Ergebnis für die vierte Welle waren neun Tage. Doch schon die daraus folgende Abschätzung der Intensivbelegung vor neun Tagen lag weit daneben.

    Und nun liege ich eine weitere Verzögerungsperiode später wieder falsch. Das ist also ganz offenbar alles Quatsch. Während ich mich bei der letzten falschen Vorhersage noch mit einer Fehlanwendung des heuristischen Modells herausreden konnte, ist das bei zwei falschen Vorhersagen nicht mehr drin. Nein. Die Prämisse ist falsch. Die Instensivbelegung folgt nicht mehr, wie noch in den zweiten und dritten Wellen, ganz brauchbar der Inzidenz. Die beiden Kurven haben sich inzwischen sehr deutlich entkoppelt:

    Graph: Entkoppelte Entwicklungen

    Meldezahlen des RKI vs. DIVI-Zahlen (Quellen vgl. Halbwegs gute Nachrichten). Auf der Zeitachse Sekunden seit 1.1.2020; 5⋅107 entspricht dabei dem 1.8.2021. Die Intensivbelegung ist um neun Tage nach vorne gezogen, um den wahrscheinlichsten Verzug auszugleichen und die Kurven übereinanderzubringen. Die y-Achse ist wie immer bei solchen Wachstumsplots von mir logarithmisch (also: exponentielles Wachstum ist eine Gerade). Die Skalierung der Intensivbelegung ist frei Auge, aber egal, wie mensch das macht: die Kurven passen nicht übereinander.

    Ganz offensichtlich reagiert die Intensivbelegung „weicher“ als die Inzidenz, und zwar nicht nur, wie aufgrund längerer Liegezeiten zu erwarten, nach unten, sondern auch nach oben. Es ist eben derzeit nicht so, dass aus einer gegebenen Zahl von Infizierten eine leicht vorhersehbare Zahl von IntensivpatientInnen wird. Daher ist vorläufig jede Vorhersage, die von einem konstanten Verhältnis von Intensivbelegung zu Inzidenz ausgeht, eine schlechte und ziemlich sicher falsche Vorhersage.

    Das richtige Vorgehen wäre jetzt, nachzusehen, was eigentlich diese Annahme kaputt macht (wobei: wie ich im September herausgefunden habe, war sie so ganz richtig ohnehin nie). Leider gibt es eine große Zahl möglicher Gründe, allen voran ist das natürlich die Demographie. Solange sie nicht ihre Eltern und Großeltern anstecken, können sich sehr viele Kinder mit SARS-2 infizieren, bevor das irgendwo in Intensivstatistiken sichtbar wird, während umgekehrt ein einziger Ausbruch in einem Pflegeheim mit gebrechlichen Menschen einige dutzend Intensivbetten belegen mag, was auch bundesweit schon eine Veränderung im einstelligen Prozentbereich ausmachen würde.

    Dazu kommen dann regional und nach Altersgruppen recht deutlich schwankende Impfquoten: Rasant steigende Inzidenzen in Bremen mit einer relativ stark durchimpften Bevölkerung geben ziemlich sicher ein deutlich schwächeres Signal auf Intensiv als eine rollende Welle in Sachsen, wo immer noch viele Menschen im mittleren Altersbereich ungeimpft sind und damit weit eher langwierige und kritische Verläufe nehmen werden

    Nachtrag (2021-11-25)

    Zum Thema Sachsen ist in der taz vom 25.11. zu lesen, von den dortigen 14000 PolizistInnen seien derzeit 519 SARS-2-positiv. Das ist eine 100000er-Wocheninzidenz zwischen 1500 und 4000, je nach dem, wie die zählen, und damit selbst für sächsische Verhältnisse (RKI-Inzidenz heute 1075) ziemlich sportlich.

    Mein int/inc-Maß (IntensivpatientInnen pro Inzidenzpunkt) ist aber auch empfindlich für Auswahleffekte. So wird es immer dann stark sinken, wenn systematisch getestet wird: Wenn die Dunkelziffer unerkannt Infizierter runtergeht, geht die Inzidenz im Hellfeld und damit mein Nenner hoch, ohne dass sich an der im Zähler reflektierten Realität etwas ändert. Besonders verzerrend werden sich solche Effekte auswirken, wenn systematische Tests nur demographisch oder impfstatistisch sehr auffällige Teile der Bevölkerung erreichen (sagen wir: SchülerInnen).

    In Summe: Wer derzeit aus der Inzidenzkurve Vorhersagen über die Intensivbelegung machen will, musss Impfquoten und Demographie, und damit auch die geographische Verteilung der Inzidenz, berücksichtigen, wenn das irgendwie hinkommen soll. Und das mutiert zu mehr Arbeit als ich in der Kategorie handwerk tun will.

    Bestimmt macht das irgendwer auch richtig. Aber dann: die Zahlenspielereien ändern nichts daran, dass wir Inzidenzen um 5000 haben müssten, wenn wir im nächsten Frühling durch sein wollen (100000/(5000 pro Woche) entspricht 20 Wochen oder einem knappen halben Jahr, mit Dunkelziffer also vielleicht einem Vierteljahr oder so), und auch nicht daran, dass das mit unseren augenblicklichen Techniken und Politiken ein furchtbares Gemetzel werden würde. Seufz.

  • Kohlendioxid auf dem Balkon

    Nicht offensichtlich korrelierte Kurven von CO_2, Windgeschwindigkeit und Temperatur

    CO2-Konzentrationen auf meinem Straßenbalkon, zusammen mit Windgeschwindigkeiten und Temperaturen. Das ist ein SVG, es lohnt sich also durchaus, in einem separaten Browserfenster in den Plot zu zoomen.

    Ich habe neulich eine längere Zeitreihe mit CO2-Konzentrationen auf meinem „vorderen” Balkon genommen. Zur Einordnung: Das Messgerät steht so etwa 10 Meter über und 15 Meter neben einer halbwegs viel befahrenen Straße. Ob das wohl etwas mit den wilden Schwankungen zu tun hat, die in der Kurve oben vor allem um den 9.11. herum zu sehen sind? Muss ich meine Einschätzung von neulich, einzelne Autos seien selbst im mittleren Nahbereich im CO2 kaum nachzuweisen (nun: an der frischen Luft, natürlich), revidieren?

    Verheizt jemand 100000 Tonnen Kohlenstoff am Tag?

    Wer die Kurven von Windgeschwindigkeit[1] und CO2-Konzentration vergleicht, könnte schon glauben wollen, ohne externe Frischluftzufuhr (also bei niedrigen Windgeschwindigkeiten) gehe das CO2 lokal merklich nach oben. Wirklich überzeugen kann mich aber keine Korrelation zwischen den verschiedenen geplotteten Größen.

    Darum gehe ich die Frage zunächst deduktiv an: woher könnten die enormen Schwankungen der CO2-Konzentration wohl kommen? Wir reden hier von einer Spanne zwischen 260 ppm und über 400 ppm, wobei es vorkommen kann, dass ich innerhalb von wenigen Stunden 100 ppm mehr CO2 sehe. Der langfristig ansteigende Trend macht mir übrigens weniger Sorgen: Wenn die Photosyntheserate Richtung Winter dramatisch sinkt, die Emission aber z.B. wegen Heizung eher zunimmt, ist das angesichts der beschränkten globalen Durchmischung der Atmosphäre auf der Erde zu erwarten[2], auch wenn das vielleicht nicht gerade innerhalb von zwei Wochen vonstatten gehen sollte.

    Mit den Werkzeugen aus dem Artikel zu meiner Heizleistung von neulich kann mensch abschätzen, was so eine Konzentrationsschwankung in einer lokal gut durchmischten Atmosphäre in, sagen wir, verbranntem Kohlenstoff bedeuten würde.

    Dafür muss ich erst überlegen, wie viele CO2-Teilchen ΔNCO2, oder der Bequemlichkeit halber eher welche CO2-Stoffmenge ΔnCO2 = NCO2 ⁄ A („in mol”) es braucht, um die Konzentration (in ppm, also CO2-Molekülen pro Million Teilchen insgesamt) innerhalb eines angenommenen Volumens V um das Δcppm zu erhöhen, das ich aus dem Plot ablese. Gemäß meinen Rezepten von neulich ist das:

    ΔnCO2 = (V)/(Vm)⋅Δcppm⋅106, 

    wobei Vm wieder das Normvolumen ist (22.4 Liter pro mol); das A von oben war die Avogadro-Konstante. Um herauszukriegen, wie viel Kohlenstoff (sagen wir, in Kilogramm) ich verbrennen muss, um diese Änderung quasi durch „frisches“ CO2 hinzukriegen, muss ich das nur noch mit dem Atomgewicht von Kohlenstoff uC multiplizieren.

    Das Atomgewicht ist, weil Kohlenstoffkerne meist 6 Protonoen und 6 Neutronen enthalten, mit 12 g/mol gut abgeschätzt (ganz genau ist das nicht, vor allem weil in der Atmosphäre auch etwas C-13 und sogar ein wenig C-14 herumschwebt). In dieser Kopfzahl steht das Gramm aus historischen Gründen. Das Mol wurde so definiert, dass die Zahl der Nukleonen im Kern so in etwa das Atomgewicht liefert, als in der Wissenschaft das cgs-System (aus Zentimeter, Gramm und Sekunde) seine große Zeit hatte. Würde mensch das Mol in den heutigen SI-Zeiten (na gut: die meisten AstronomInnen bleiben dem cgs verhaftet und reden zum Beispiel über Energien in erg) definieren, wäre die Avogadro-Konstante um einen Faktor 1000 (nämlich den Faktor zur SI-Einheit Kilogramm) größer.

    Wie auch immer: Wenn ich mir mal vorstelle, dass das, was ich da auf meinem Balkon messe, repräsentativ für den Umkreis von 10 km und bis in eine Höhe von 2 km wäre (mensch ahnt schon: Ich eröffne hier eine Reductio ad absurdum), komme ich auf ein Volumen von

    V = 2⋅π⋅(10  km)2⋅(2  km) ≈ 1.3⋅1012  m3

    was mit Vm ≈ 0.02 m3 ⁄  mol, einer Änderung von 100 ppm, die mensch als Sprung am 9. und 10.11. sehen kann, sowie der Formel oben auf

    ΔmC  = uC(V)/(Vm)⋅Δcppm⋅106  ≈ 0.012 kg ⁄ mol(1.3⋅1012  m3)/(0.02 m3 ⁄  mol)⋅100⋅10 − 6  ≈ 8⋅107  kg

    oder achzigtausend Tonnen verbrannten Kohlenstoff führt. Das klingt nach richtig viel und ist es auch. Aber das Volumen, das ich hier betrachte, sind eben auch 1200 Kubikkilometer, und wer sich erinnert, dass ein Kubikmeter eines normalen Gase bei Normalbedingungen um die 1 kg wiegt, kann leicht ausrechnen, dass die Luft in diesem Volumen 1.2⋅1012  kg (oder 1.2 Milliarden Tonnen – Luft in großen Mengen ist überhaupt nicht leicht) wiegen wird. Dieser ganze Kohlenstoff macht also ungefähr 0.07 Promille (oder 70 Milionstel) der Masse der Atmosphäre aus, was ganz gut mit den 100 ppm in Teilchen zusammengeht, die wir in die ganze Rechnung reingesteckt haben.

    Andersrum gerechnet

    Tatsächlich kann mensch die Kohlenstoffmasse, die eine Erhöhung der Teilchenkonzentration in einem Gasvolumen bewirkt, auch so herum abschätzen. Der Umrechnungsfaktor von Teilchen- zu Massenkonzentration ist der Faktor zwischen den Dichten von CO2 und Luft. Das Verhältnis dieser Dichten ist wiederum das der jeweiligen Atommassen, solange jedes Teilchen das gleiche Volumen einnimmt; das schließlich folgt aus der Annahme, dass die Gase ideal sind, was wiederum für unsere Abschätzungen überallhin gut genug ist.

    Für CO2 ist das mit den überwiegend vorkommenden Isotopen von Sauerstoff und Kohlenstoff 16 + 16 + 12 = 44, für Luft, wenn wir nur auf den Stickstoff N2 schauen, 14 + 14 = 28. Demnach macht 1 ppm in der Teilchenzahl von CO2 44 ⁄ 28 ≈ 1.6 ppm in der Masse aus, solange die CO2-Konzentration so gering ist, dass tatsächlich N2 die Dichte dominiert.

    Andererseits macht Kohlenstoff nur 12 ⁄ 44 ≈ 0.3 an der Masse im CO2 aus, die Zunahme an Kohlenstoff ist demnach nur ein Drittel von dem gerade berechneten 1.6, also etwas wie 0.5. Folglich werden aus 100 ppm Änderung in der Teilchenzahl etwas wie 100⋅0.5 = 50  ppm Änderung in der Masse; wer das genauer rechnet, bekommt auf diese Weise natürlich das gleiche Resultat wie oben raus.

    Wie herum mensch das auch rechnet, es ist klar, dass niemand in der kurzen Zeit so viel Kohlenstoff verbrennt. Ein schneller Reality Check: Meine Kohlendioxid-Kopfzahl war, dass die BRD 2/3 Gigatonnen im Jahr emittiert, was mit dem C/CO2-Verhältnis von 0.3 von oben ungefähr 200 Megatonnen Kohlenstoff entspricht, oder irgendwas wie gut 500000 Tonnen am Tag. Damit wäre die Zunahme, die ich hier sehe, rund ein Sechstel des gesamten Kohlenstoffbudgets der BRD, und mehr, wenn der Anstieg schneller als in einem Tag vonstatten geht: Das ist (fast) natürlich Quatsch.

    Aber was ist es dann? Noch immer gefällt mir die These ganz lokaler Schwankungen nicht. Wenn hier wirklich nur das CO2 von Autos und Heizungen nicht mehr weggepustet würde, müsste die Korrelation zwischen CO2 und Wind viel deutlicher sein.

    Ist es eine die Abgasfahne des GKM?

    Nächster Versuch: Rund 12 km westlich von meiner Wohnung läuft das Großkraftwerk Mannheim („GKM“). Wenn das Volllast fährt und meine Wohnung in seine Abgasfahne kommt, könnte das so ein Signal geben?

    Nun, so ein Kraftwerk liefert ungefähr 1 Gigawatt elektrische Leistung (wie mir der Wikipedia-Artikel dazu gerade verrät: darunter 15% des deutschen Bahnstroms), was bei einem Wirkungsgrad von 1/3 (ok, bei modernen Kohlekraftwerken ist das noch ein wenig mehr, aber als Kopfzahl taugt es) auf 3 Gigawatt thermische Leistung führt (tatsächlich nennt die Wikpedia eine Bruttoleistung von 2146 MW für das GKM).

    Aus den 394 kJ/mol, die bei der Verbrennung von Kohlenstoff frei werden (vgl. den Artikel zu meiner thermischen Leistung) könnte mensch jetzt die CO2-Emission aus der Bruttoleistung ableiten, aber ich bin mal faul und sehe beim WWF nach, der für Kraftwerke dieser Größenordnung ansagt, für eine Kilowattstunde Strom (wir sind dann also wieder bei der Nutzleistung) werde rund ein Kilogramm CO2 emittiert.

    Wenn das Kraftwerk also Volldampf (rund ein GW netto) macht, wird es etwa

    109  W⋅0.001 kg ⁄ Wh = 106 kg ⁄ h

    CO2 emittieren, also etwa 1000 Tonnen, was wiederum mit unserem 0.3-Faktor zwischen Kohlenstoff und CO2 zu einem Kohleverbrauch von 300 Tonnen pro Stunde führt.

    Damit leert das Kraftwerk unter Vollast ein Großes Rheinschiff in zehn Stunden – das scheint mir zwar schon sehr schnell zu gehen, ist aber auch nicht gänzlich unplausibel. Gegenrechnung: Das WWF-Dokument von oben nennt 7.7⋅109  kg ⁄ a als CO2-Emission des GKM im Jahr 2006. Mit der Ur-Kopfzahl π ⋅ 1e7 Sekunden pro Jahr übersetzt sich das in eine mittlere Emission von etwa 200 kg pro Sekunde oder gut 1000 Tonnen pro Stunde. Das passt fast zu gut, denn als jemand, der das Kraftwerk von seiner Leseecke aus sehen kann, kann ich zuverlässig sagen, dass das Ding keineswegs durchläuft. Andererseits hatte das Kraftwerk 2006 auch noch einen Block weniger, und überhaupt ist in der Rechnung genug Luft für Stillstandszeiten.

    Nehmen wir …

  • Sicherheit, die wirklich niemand will

    Ich habe nie viel von dem Gerede von der „Balance von Sicherheit und Freiheit“ gehalten – so würde ich etwa behaupten, dass ohne eine gewisse soziale Sicherheit Freiheit ein recht hohler Begriff wird. Wer, sagen wir, unter permanenter Drohung durch die Hartz IV-Kautelen lebt, hat zumindest nicht mehr die Freiheit, sinnlose und miese Arbeit (Call Center, Lieferdienste, Burgerflippen) abzulehnen. Wenn nun die Gesellschaft auf absehbare Zeit nicht vom Arbeitszwang wegkommt, sind vermutlich nicht viele Zwänge (ja: Einschränkungen von Freiheit) demütigender als eine Lohnarbeit tun zu müssen, ohne einen Sinn in ihr zu sehen oder wenigestens Spaß an ihr zu haben.

    Aber gut: Die Leute, die gerne vom Widerspruch zwischen Freiheit und Sicherheit reden, haben sicher keine Freiheit zur Faulheit im Sinn, sondern eher die Freiheit, sich anderer Menschen zur eigenen Bereicherung zu bedienen. Auch ihre Sicherheit ist eine ganz andere als die von Existenz und Obdach. Ihre Sicherheit ist ziemlich genau das, das von Polizei, Militär und Überwachungstechnologie hergestellt, genauer: erzwungen werden kann. Erst bei diesem Erzwingen wird der Widerspruch von Freiheit und Sicherheit unausweichlich; er hängt damit aber klar an einem genz spezifischen Begriff von Sicherheit, den, wird er explizit gemacht, wohl nicht viele Menschen teilen werden.

    Ein gutes Beispiel, dass häufig gerade die „Geschützten“ diese Sorte Sicherheit gar nicht haben wollen, gab es am 24. Oktober im Hintergrund Politik des DLF: Jedenfalls offiziell zum „Schutz“ der auf Samos gestrandeten Geflüchteten findet im dort neu errichteten Lager eine strikte Eingangskontrolle statt. Die ist aber nur bis 20 Uhr besetzt. Das Lager ist außerdem am Ende der Welt, so dass Stadtausflüge am Nachmittag riskant werden. Ein Geflüchteter berichtet in der Sendung:

    Ich brauche [für den Weg zurück aus der Stadt] eine Stunde und 20 Minuten. Aber wenn du es nicht rechtzeitig zurückschaffst, lassen sie dich nicht mehr rein. Das ist mir schon passiert. Ich musste die ganze Nacht draußen verbringen. Im alten Camp haben wir zwar im Zelt gelebt, aber wir hatten unsere Freiheit.

    Grob in den Bereich passt etwas, auf das ich seit Wochen linken wollte, weil es wirklich lesenswert ist, nämlich die Stellungnahme von Amnesty International zum neuen Versammlungsgesetz in NRW. Ich glaube zwar nicht, dass irgendwer ernsthaft versucht, diesen Gesetzentwurf mit „Sicherheit” zu begründen. Es geht recht offensichtlich durchweg nur um autoritären Durchgriff („öffentliche Ordnung“). Dafür ist der Abbau von Grundrechten, die die Voraussetung von „Freiheit“ in jedem nicht völlig verdrehtem Sinn sind, hier aber auch besonders greifbar.

    Das sage nicht nur ich aus meiner linksradikalen Ecke. Selbst die sonst ja eher zurückhaltenden Leute von ai reden Klartext:

    Mit der Distanzierung von der Brokdorf-Entscheidung distanziert sich der Gesetzentwurf daher nicht nur von der Rechtsprechung des Bundesverfassungsgerichts, sondern auch von international verbindlichen Menschenrechtsstandards.

    Wie gesagt: Lohnende Lektüre für alle, die noch gerne einen Unterschied hätten zwischen den viel geschmähten „autokratischen Regimes“ und ihren eigenen Regierungen. Oder, sagen wir, den Verhältnissen in Spanien. Oder denen in Hessen.

  • Keine guten Nachrichten

    Und wieder muss ich meinen Hut essen im Zusammenhang mit meinen Corona-Zahlenspielen. Ich hatte nämlich vor neun Tagen zuversichtlich vorhergesagt, so etwa jetzt sollten knapp 3500 Intensivbetten in der BRD mit SARS-2-PatientInnen belegt sein, mit dem Argument, dass sich die entsprechenden Zahlen derzeit neun Tage hinter der Inzidenz herbewegen. Da (und das war, wie unten diskutiert, ein Fehlschluss) die Inzidenz in den neun Tagen vor dem 6.11. um 44% gestiegen war, sah ich die Intensivbelegung heute bei 2332⋅1.44 ≈ 3350. Tatsächlich aber berichtet das RKI heute von nur 3034 SARS-2 IntensivpatientInnen, also um die 10% weniger als meine Vorhersage – oder 30% weniger Anstieg, um die Fehleinschätzung mal deutlicher zu machen.

    Ein Metafehler und einige Nicht-Fehler

    Es war schon ein paar Tage abzusehen, dass ich falsch liegen würde, und ich habe mir bereits letzte Woche ein paar lose Gedanken gemacht, wo wohl mein Fehler liegen könnte. Nicht angreifen konnte ich meine Argumentation aus dem Artikel, nach der die Leute, die in den vergangenen neun Tagen intensivpflichtig geworden sind, damals bereits krank waren und in diesem Sinn nicht mehr viel zu ändern sein würde.

    Ich hatte dann kurz überlegt, ob vielleicht bei der Normalisierung der Ableitungen (das incs /= sum(abs(incs)) irgendwas schief gegangen sein kann. Aber nein, eine Angabe wie „44%“ ist natürlich selbst normalisiert („pro hundert“). Der Verdacht jedoch führte schon mal in die richtige Richtung: Nachdenken über die Ableiterei und was dabei so passiert.

    Bevor ich da weiterknoble, zunächst die eigenliche Selbstbezichtung, denn was ich vor neun Tagen zumindest hätte tun sollen, wäre eine simple Validierung an den bestehenden Daten, nämlich am unmittelbar vorhergehenden 9-Tage-Intervall. Am 27.10. war die Intensivbelegung bei 1707, in den neun Tagen vor dem 6.11. war die Intensivbelegung also um 37% gestiegen. Es wäre ganz leicht gewesen, gleich nachzusehen, ob auch die Meldezahlen des RKI in den neun Tagen davor um etwas wie 37% gestiegen sind. Ich hätte festgestellt, dass sie das nicht sind – am 27.10. lag die RKI-Meldeinzidenz bei 118, am 18.10. bei 74, ein Anstieg also um satte 59% –, und das hätte mir gesagt, dass ich einen Fehler gemacht habe.

    Auch dann hätte ich vermutlich, wie heute auch, den nächsten Verdacht auf die heftige Kontamination der tageweisen Inzidenzschätzungen des RKI durch Wochenenden und Co gelenkt – schon in meinem allerersten Corona-Post hatte ich die bejammert. Vielleicht ist es ja das? Im Programm von neulich glätte ich deshalb vor der Ableitung. Die geglättete Kurve kommt am 18.10. auf 75, am 27.10. auf 120, und für den 6.11. habe ich noch keine geglätteten Daten, weil da noch zu viele Randeffekte dabei sind. Das ist sehr nah an den ungeglätteten Daten. Also, nein: Das macht repariert meine Fehlvorhersage nicht.

    Der wirkliche Fehler

    Das tatsächliche Problem liegt in der Methode, und zwar nicht in dem komplizierten Teil. Die Berechnung des Verzuges mit all dem Glätten und Ableiten ist völlig in Ordnung. Das Problem ist vielmehr, und ein wenig Nachdenken über Schulmathematik hätte mich darauf bringen können, in der Natur der Ableitung. Bei der gehen Konstanten nämlich verloren: (d)/(dx)(f(x) + C) = (d)/(dx)f(x). Ein hoher Sockel von Langzeit-IntensivpatientInnen wird bei meiner Verzögerungsrechnung einfach wegdifferenziert. Das ist ja sogar der Sinn der Differenziererei.

    Nur: Wenn ich am Schluss blind „44% mehr“ rechne, wird der Sockel (das C) mitmultipliziert, und genau da wird es falsch. Die richtige Rechnung wäre gewesen, die Differenz der Inzidenzen über die neun Tage vor dem 27.10. (von 74 auf 118) zu vergleichen mit der Differenz der Intensivbelegung der neun Tage vor dem 6.11 (von 1707 auf 2332) – dabei geht der Verzug ein, irgendwelche konstanzen Sockel spielen aber keine Rolle.

    Dieser Vergleich ergibt einen, sagen wir, 9-Tage-Übersetzungfaktor von 625 ⁄ 44 ≈ 14. In diesem stecken die Demographie der Erkrankten, die Eigenschaften des Virus, das Verhalten der Bevölkerung, und alles andere, was die mittlere Wahrscheinlichkeit bestimmt, mit einer SARS-2-Infektion intensivpflichtig zu werden. Unter der Annahme jedoch, dass der Übersetzungsfaktor über kurze Zeiten in etwa kontant ist, kann mensch jetzt die Entwicklung korrekt vorhersagen. Und zwar übersetzt sich demnach die Inzidenzentwicklung zwischen 27.10. und 6.11. (von 118 auf 164) 14-fach in die Intensivbelegung der jetzt gerade vergangenen neun Tage (das ist letztlich etwas wie ein Momentanwert von meiner int/inc-Metrik aus dem September).

    Ich hätte damit am 6.11. vorhergesagt, die Intensivbelegung würde um 46⋅16 = 644 zunehmen oder eben auf 2332 + 644 = 2976, in guter Übereinstimmung mit dem berichteten Wert von 3034.

    Blöd, dass ich nach meinen Zahlen- und Interpolationsspielen beim Zusammenbau der Vorhersage nicht aufgepasst habe. Aber es zeigt mal wieder, dass Mathe voll ist mit Fallen und ein Moment der Unaufmerksamkeit ziemlich unausweichlich zu zwanghaftem Vertilgen von Hüten führt. Und dabei hätte ich mir durch einfache Versuche, die Zukunft der Verangenheit vorherzusagen – ein sehr probates Mittel, wann immer mensch Zeitreihen analysiert – diese wenig erfreuliche Mahlzeit sparen können. Rülps.

    Aus eine physikalischen Betrachtung heraus ist diese Methode auch nicht so arg befriedigend, denn natürlich gibts bei den Meldezahlen keinen Sockel. Die sind ja selbst schon Ableitungen[1], nämlich die der Gesamtzahl der Infizierten. Die Intensivbelegung ist von der Genese her noch komplexer, da dort Zu- wie Abgänge eingehen. Insofern ist die Sache mit dem Übersetzungsfaktor zutiefst phänomenologisch und kann also aus vielen Gründen brechen.

    Schauen wir also mal, wie es in neun Tagen, am 24.11., aussieht. Meine Vorhersage wäre 3034 + (303 − 184)⋅14 = 4700. Das ist auch von der Dynamik her nicht mehr weit weg von der Höchstbelegung am 3.1.2021 (5762), und ohne ziemlich deutliche „Maßnahmen” werden wir wohl recht bald an der vorbeirauschen.

    [1]Wobei: Solange die Entwicklung exponentiell ist, ist das mit der Ableitung in diesem Kontext quasi wurst, denn die Exponentialfunktion ex ist ihre eigene Ableitung. Reale Wachstumsfunktionen über der Zeit t sehen aus wie N(1 + r)t = Neln(1 + r)⋅t, wobei r die Wachstumsrate ist (mit RKI-Zahlen R-Wert minus 1). Die Ableitung solcher Funktionen sind sie selbst mal einem konstanten Faktor, und der würde bequem in unserem Übersetzungfaktor 14 aufgehen. Wie gesagt: alles erstmal phänomenologisch.
  • Fixing "No sandbox user" the Right Way

    I'm setting up an ancient machine – a Pentium M box with a meme 256 MB of RAM – with current Debian bullseye, and I'm impressed that that still works: this machine is almost 20 years old. Hats off to the Debian folks.

    But that's not really my story. Instead, this is about fixing what's behind the message:

    No sandbox user '_apt' on the system, can not drop privileges
    

    from apt. As you probably have just done, my first reaction was to feed that message to a search engine.

    Quite a few pages were returned, and all I looked at suggested to simply create the user using one of the many ways a Debian box has for that. That is not totally unreasonable, but it does not really address the underlying cause, and hence I thought I should do better.

    The immediately underlying cause is that for whatever deeper reason a maintainer script – shell scripts that Debian packages run after installing packages or before removing them – has not properly run; that is usually the place where packages create users and do similar housekeeping. Just creating the user may or may not be enough, depending on what else the maintainer script would have done.

    Hence, the better way to fix things is to re-run the maintainer script, as that would either run the full routine or at least give an error message that lets you figure out the deeper cause of the problem. Dpkg runs the maintainer script(s) automatically when you re-install the package in question.

    But what is that “package in question” that should have created the user? You could guess, and in this particular case your guess would quite likely be right, but a more generally applicable technique is to simply see what script should have created the user. That's not hard to do once you know that the maintainer scripts are kept (next to other package metadata) in /var/lib/dpkg/info/; so, with GNU grep's -r (recursive) option, you can run:

    grep -lr "_apt" /var/lib/dpkg/info/
    

    which gives the names of all files containing _apt in files below that directory. On my box, that is:

    /var/lib/dpkg/info/python3-apt.md5sums
    /var/lib/dpkg/info/libperl5.32:i386.symbols
    /var/lib/dpkg/info/apt.postinst
    /var/lib/dpkg/info/python3-apt.list
    

    Ah-ha! The string is mentioned in the post-installation script of the apt package. Peeking inside this file, you see:

    if [ "$1" = 'configure' ]; then
            # add unprivileged user for the apt methods
            adduser --force-badname --system --home /nonexistent  \
                --no-create-home --quiet _apt || true
    fi
    

    So: this really tries to create the user when the package is being configured, but it ignores any errors that may occur in the process (the || true). That explains why the system installation went fine and I got the warnings later (rather than a hard error during the installation).

    Just re-configuring the apt package would therefore be enough to either fix things or at least see an error message. But really, unless it's a huge package I tend to save on brain cycles and just run apt reinstall, which in this particular case leads to the somewhat funky command line:

    apt reinstall apt
    

    For me, this fixed the problem – and I've not bothered to fathom why the user creation failed during initial system setup. If you've seen the same problem and still have a record of the installation, perhaps you could investigate and file a bug if necessary?

  • Stemming for the Search Engine

    First off, here is a quick reference for the search syntax on this site (the search form links here):

    • 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*)
    • Searches don't use stemming by default, but stem for German when introduced with l:de and for English when introduced with l:en
    • See also the Xapian syntax.

    If you only came here for the search syntax, that's it, and you can stop reading here.

    Otherwise, if you have read the previous post on my little search engine, you will remember I was a bit unhappy that I completely ignored the language of the posts and had wanted to support stemming so that you can find, ideally, documents containing any of "search", "searches", "searching", and "searched" when searching for any of these. Being able to do that (without completely ruining precision) is obviously language-dependent, which means the first step to make it happen is to properly declare the languague of your posts.

    As discussed in the previous post, my blogsearch script only looks at elements with the CSS class indexable, and so I decided to have the language declaration there, too. In my templates, I hence now use:

    <div class="indexable" lang="{{ article.lang }}">
    

    or:

    <div class="indexable" lang="{{ page.lang }}">
    

    as appropriate.

    This is interpreted by the indexer rather straightforwardly by pulling the value out of the attribute and asking xapian for a stemmer for the named language. That works for at least most European two-letter country codes, because those happen to coincide with what's legal in HTML's lang universal attribute. It does not work for the more complex BCP 47 language tags like de-AT (where no actually existing stemmer would give results different from plain de anyway) or even sr-Latn-RS (for which, I think, no stemmer exists).

    On searching, I was worried that enabling stemming would blow unstemmed searches, but xapian's indexes are clever enough that that's not a problem. But I still cannot stem queries by default, because it is hard to guess their language from just a word or two. Hence, I have defined a query syntax extension: If you prefix your query with l:whatever, blogsearch will try to construct a xapian stemmer from whatever. If that fails, you'll get an error, if it succeeds, it will stem the query in that language.

    As an aside, I considered for a moment whether it is a terribly good idea to hand through essentially unfiltered user input to a C++ API like xapian's. I eventually settled for just making it a bit harder to craft buffer overflows by saying:

    lang = parts[0][2:30]
    

    – that is, I'm only allowing through up to 28 characters of language code. Not that I expect that anything in between my code and xapian's core has an overflow problem, but this is a cheap defensive measure that would also limit the amount of code someone could smuggle in in case some vulnerability did sneak in. Since it's essentially free, I'd say that's reasonable defensive programming.

    In closing, I do not think stemmed searches will be used a lot, and as usual with these very simple stemmers, they leave a lot to be desired from a linguistic point of view. Compare, for instance, a simple search for going with the result l:en going to see where this is supposed to go (and compare with the result when stemming as German). And then compare with l:en went, which should return the same as l:en going in an ideal world but of course doesn't: Not with the simple snowball stemmer that xapian employs.

    I'm still happy the feature's there, and I'm sure I'll need it one of these days.

    And again, if you need a CGI that can index and query your static HTML collection with low deployment effort: you're welcome.

  • Ad hominem 2

    Panorama des Stuttgarter Schlosses

    Einst die Residenz des Herrn Schmid, der nun die Ukraine[1] als Zwischenlager ihm unwillkommener Menschen nutzen möchte: Das Stuttgarter Schloss (CC-BY-SA Grossmummrich).

    Wie ich neulich schon betonte, ist mir selbstverständlich klar, dass im politischen Diskurs Attacken auf fragwürdige charakterliche oder physische Eigenschaften von MachthaberInnen sowie Menschen, die es werden wollen, als unfein gelten.

    Andererseits gehen die guten Sitten™ offensichtlich auch ohne mich vor die Hunde. Aktuelles Beispiel: Der „SPD-Außenpolitiker“ (Deutschlandfunk) Nils Schmidt hat gestern im DLF gefordert, die Menschen, die gerade über Belarus in die EU fliehen, sollten doch in der Ukraine quasi zwischengelagert (eingestanden: das ist nicht Schmids Wort, aber doch nach Bedeutung und Stil das, was er sagt) werden, und zwar im Wesentlichen aus Gründen des Prinzips.

    Er hat sich so nicht nur an dem furchtbaren Diskurs beteiligt, die Fliehenden seien irgendwie eine Waffe oder eine Bedrohung, sondern macht auch die Ansage, er halte die Ukraine – ganz wie die russische Regierung übrigens – für so eine Art Kolonie der EU, über die diese nach ihrem Belieben verfügen kann. Und so fühle ich mich aufgerufen, an eine Geschichte zu erinnern, bei der Schmid vor seiner Zeit als „Außenpolitiker“ in meiner weiteren Umgebung unangenehm aufgefallen ist.

    Bevor er nämlich „Außenpolitiker“ wurde, war er Finanzminister der ersten Grün-Roten Regierung hier in Baden-Württemberg. Als solcher residierte er im Stuttgarter Schloss. Wenn ihr dem Wikipedia-Link folgt, lest ihr dort:

    Seit dem Auszug des Ministeriums für Kultus, Jugend und Sport Anfang 2012 ist es heute noch Sitz des Finanzministeriums [...]

    Der Auszug des Kultusministeriums hat mit Schmid zu tun – seine Amtszeit als Finanzminister begann 2011. Zumindest im Umfeld der Verdrängten ging damals die Geschichte um, das Schloss sei zu CDU-Zeiten zwar groß genug für zwei Ministerien gewesen, für Schmid, sein Ego und das Kultusministerium reiche es aber nicht. Da die CDU-Ära Kultusministerinnen wie Dr. Annette Schavan und Finanzminister wie Gerhard Mayer-Vorfelder – beide gewiss keine Kinder der Bescheidenheit – kannte, fällt es nicht leicht, das zu glauben, aber es ist zweifellos plausibel, dass Schmid nach seiner Wahlniederlage gegen die Grünen einiges zu kompensieren hatte und sich das ganze Schloss sozusagen zum Trost gönnte.

    Ja, das ist alles etwas ad hominem, aber wer im Namen von „Recht und Ordnung“ austeilt wie Schmid in dem DLF-Interview, sollte auf Rückfragen zur eigenen Charakterfestigkeit zumindest vorbereitet sein.

    Bei alldem gestehe ich gerne, dass mich der Furor des Herrn Schmid besonders befremdet, weil er gerade jetzt kommt. Lukaschenko ist sicher neben vielem anderen vorzuwerfen, dass er viele Jahre lang in das schmutzige Grenzregime der EU verstrickt war; im 2019er-Bericht zu Abschiebungen aus der EU heißt es etwa zu Weißrussland:

    A total of 18 Member States reported having approached the authorities of Belarus for readmission matters [das ist hier Schurkensprache für „Abschiebung“] related to its nationals in 2019.

    All of them assessed the overall cooperation with Belarus in the identification procedure as good or very good (except one which rated it as average).

    This is reflected in 13 Member States having a functioning established routine [für Abschiebungen] with Belarus diplomatic missions, with only one informing that it is not effective.

    Gut: die da abgeschoben wurden, waren Menschen mit belarusischen Pässen, aber auch die sollte mensch wohl nicht in ein Land abschieben, das im öffentlichen Diskurs recht konsistent als „letzte Diktatur Europas“ gehandelt wurde (if only it were true). Jedenfalls nicht, wenn mensch anschließend irgendwelche menschenrechtlichen Standards an andere anlegen will.

    Angesichts dieser Geschichte der Kollusion beim Grenzregime Lukaschenko ausgerechnet dann Verfehlungen vorzuwerfen, wenn er sein Land erstmal nicht mehr als extrabreite Grenzmauer der EU zur Verfügung stellt, das lässt tief blicken im Hinblick auf die Umsetzung eines Ziels, das in der DLF-Presseschau vom 10.12. mit wirklich schockierender Ehrlichkeit aus der Nordwest-Zeitung zitiert wurde:

    Viertens ist auf EU-Ebene eine kollektive Einwanderungspolitik, die sich ausschließlich an den Interessen Europas ausrichtet, noch immer überfällig.

    Raubt nur mir den Atem, wie komplett der Kommentator hier vergessen hat, was das Wort „Asylrecht“ bedeutet[2]? Dass es darum ging, Menschen, die vor Krieg und Verfolgung fliehen müssen, eine Möglichkeit dazu zu geben?

    Wer so etwas schreibt, ist sich offensichtlich sehr sicher, niemals fliehen zu müssen. Diese Sicherheit sei solchen Leuten gegönnt, und wer mit so viel Patriotismus und Staatsraison gesegnet ist, mag mit der Zuversicht auch richtig liegen. Daraus aber zu schließen, dass auch alle anderen nicht fliehen dürfen, das ist, ich kann es nicht anders sagen, ein klares Merkmal von Schurken.

    Doch ich will versöhnlich enden, denn die Presseschau am nächsten Tag schloss mit folgender Einsicht aus dem Freitag (den natürlich wieder niemand liest):

    Was bei aller Lukaschenko-Verteufelung in vielen Zeitungen vergessen wird: Das ist nicht der Grund, warum diese Leute aus dem Nahen Osten kommen. Sie fliehen vor Kriegen und kriegerischen Folgeschäden aus dem Irak, Syrien oder Afghanistan. Die zugrunde liegenden Konflikte, die ihre Heimat destabilisierten, fanden ohne Beteiligung von Belarus, sehr wohl aber unter maßgeblichem Mitmischen des Westens, darunter EU-Mitgliedern, statt.

    Das hätte ich nicht schöner sagen können.

    [1]Das gerade, während in der Ukraine Corona durch schwindelerregend schnell durch eine offenbar nicht gut immunisierte Bevölkerung läuft: der z-Score (in etwa: Wie viele Standardabweichungen liegt die derzeitige Sterblichkeit über dem langjährigen Mittel?) war dort laut Euromomo vor drei Wochen bei fast 23, in Worten „extraordinarily high“. Aktuellere Zahlen sind wohl wegen Meldeverzug noch sehr unzuverlässig.
    [2]Na gut: Das ist, was Asylrecht bedeutet hat, bevor der Bundestag am schicksalsschweren 26.5.1993 mit dem Artikel 16a Grundgesetz das Asylrecht in der BRD in eine hohle, winddurchpfeifte Ruine verwandelt hat. Zur Erinnerung: Damit wurde die „Drittstaatenregelung“ eingeführt, die, auf EU-Ebene skaliert (u.a. „Dublin II“), letztlich der rechtliche Hintergrund der gespenstischen Vorgänge an der Grenze zwischen Belarus und Polen ist. Aber das ist nochmal eine ganz andere Geschichte.
  • Moving Clipboard Content Between Displays and Machines with Xclip

    Since Corona started, I've had to occasionally run zoom and other questionable telecon software. I don't want that proprietary junk on my main machine, partly because I'm a raving Free software lunatic, partly because binary packages from commercial vendors outside of the Debian main repository have a way of blowing up things years after one has put them on a box. I take some pride in never having re-installed my primary machine since 1996, so there would have been lots of opportunity for binary junk to accumulate.

    Hence I took a spare box I had sitting around idly, quickly put a simple Debian on its disk and then dumped all the questionable proprietary code next to its systemd and pulseaudio, reckoning that shredding that file system once the zoom pandemic is over will give me a lot of satisfaction.

    But now the various links, room ids and whatnot come in on the proper machine. Until a few days ago, I used to move them over to the zoom machine by having a screen open there, ssh-ing in from my main box, running screen -x to attach the screen that is already running in the ssh session, and then pasting the link into that shared screen. It works, but it feels clunky.

    The other day, I finally realised there's a better way using a nifty thing called xclip. I had already used xclip for ages whenever I have two displays running on a single box and I need to copy and paste between the two displaye; that happens when I'm at work. Then, I use the following key bindings (in this case for sawfish) on both ends:

    (bind-keys global-keymap "M-C-v"
            '(system "xclip -in < ~/.current-clipboard"))
    (bind-keys global-keymap "M-C-c"
            '(system "xclip -out > ~/.current-clipboard"))
    

    This lets me hit Alt-Ctrl-C on the first display and Alt-Ctrl-V on the second, and I'll then have what was in the primary selection on the first in the primary selection on the second.

    When later webkit on gtk3 started to copy links into the X11 clipboard rather than the primary selection and I wanted a quick way to get them to where I can middle-mouse them in again, I added another xclip binding to my sawfshrc:

    (bind-keys global-keymap "M-RET"
      '(system "xclip -out -selection clipboard | xclip -in"))
    

    – that's Meta-Return copying the content of the clipoard to the primary selection, and I've come to use that one quite extensively after initially piling quite a bit of abuse on the gtk3 policy of using the clipboard.

    What I noticed the other day was that xclip also lets me conveniently transport the telecon links. I've created an alias for that:

    alias zoomclip='xclip -o | ssh zoom "DISPLAY=:0 xclip -r -l 1 -i"'
    

    (zoom here is the name of the target machine). My new workflow is: select the string to transmit, run zoomclip in a terminal, hit the middle mouse button on the target machine to paste what I selected on the source machine. I'm not sure if it saves a lot of time over the old screen-based method, but it sure feels niftier, and I'd say that's reason enough for that alias.

    Note that the DISPLAY=:0 in the remote command is necessary because xclip of course is a normal X client and needs to know what display to talk to; and you want the local display on the target machine, not the display on the source machine. The -l 1, on the other hand, makes the xclip on the remote machine exit once you have pasted the content. Leave the option out if you expect to need to paste the thing multiple times. But without the -l 1, due to the way the selections are built on X11 (i.e, the system doesn't store selection content, you're always directly sending stuff between clients), xclip runs (and hence the ssh connection is being maintained) until some other client takes over the selection.

  • Der hundertste Post

    Vor 10 Monaten habe ich den ersten Artikel für dieses Blog geschrieben, und siehe da: Mit diesem sind es jetzt 100 Posts geworden.

    Das wäre ein guter Vorwand für ein paar Statistiken, aber da ich ja generell ein Feind von Metriken bin, die mensch ohne konkrete Fragestellung sammelt (das ist ein wenig wie beim statistischen Testen: Wenn du nicht von vorneherein weißt, worauf du testest, machst du es falsch), bestätige ich mir nur, dass meine Posts viel länger sind als ich das eigentlich will. Insgesamt nämlich habe ich nach Zählung von wc -l auf den Quelldateien fast 93000 Wörter in diesen Artikeln. Zur Fehlerabschätzung: xapian (vgl. unten) zählt nur 89000.

    Die Länge der Artikel ist nach wc-Wörtern so verteilt:

    Histogramm mit einem Klumpen zwischen 200 und 1000 und einem Outlier bei 3000

    Ich weiß auch nicht recht, warum ich mich nicht kürzer fassen kann. Oder will. Der überlange Post mit 3244 Wörtern ist übrigens der über die Konfiguration eines Mailservers – und das ist wieder ein gutes Beispiel für die Fragwürdigkeit von Metriken, denn erstens hat Englisch fast keine Komposita und ist von daher im Nachteil beim Wörterzählen und zweitens ist in dem Artikel ziemlich viel Material, das in Wirklichkeit Rechner lesen, und das sollte wirklich anders zählen als natürlichsprachiger Text.

    Na gut, und einem Weiteren kann ich nicht widerstehen: Wie viele verschiedene Wörter („Paradigmata“) kommen da eigentlich vor? Das ist natürlich auch Mumpitz, denn die Definition, wann zwei Wörter verschieden sind („die Token verschiedenen Paradigmata angehören“), ist alles andere als tivial. So würde ich beispielsweise behaupten, dass die Wörter Worte und Wörter praktisch nichts miteinander zu tun haben, während im Deuschen z.B. auf, schaute und aufschauen besser alle zusammen ein einziges Paradigma bilden sollten (zusammen mit allerlei anderem).

    Aber ist ja egal, sind ja nur Metriken, ist also eh Quatsch. Und es gibt die Daten auch schon, was für die Nutzung von und die Liebe zu Kennzahlen immer ein Vorteil ist. Ich habe nämlich den xapian-Index über dem Blog, und mit dem kann ich einfach ein paar Zeilen Python schreiben:

    import xapian
    db = xapian.Database("output/.xapian_db")
    print(sum(1 for w in db.allterms()))
    

    (Beachtet die elegante Längenbestimmung mit konstantem Speicherbedarf – db.allterms() ist nämlich ein Iterator).

    Damit bekomme ich – ich stemme nach wie vor nicht – 16540 raus. Klar, diese 16540 für die Zahl der verschiedenen Wörter ist selbst nach den lockeren Maßstäben von Metriken ganz besonders sinnlos, weil es ja eine wilde Mischung von Deutsch und Englisch ist.

    Um so mehr Spaß macht es, das mit den 100'000 Wörtern zu vergleichen, die schließlich mal im Goethe-Wörterbuch sein sollen, wenn es fertig ist. Eine schnelle Webrecherche hat leider nichts zur Frage ergeben, wie entsprechende Schätzungen für Thomas Mann aussehen. Einmal, wenn ich gerne Kennzahlen vergleichen würde…

  • Tatort und Menschenrechte

    Ich bekenne, regelmäßiger Tatort-Konsument zu sein und rechtfertige das gerne mit einem Interesse an der Öffentlichkeitsarbeit der Polizei, auch wenn die Danksagungen an diverse Polizeien in den Abspannen seltener geworden sind (oder jedenfalls scheint mir das so). Aber lasst mir mal die Annahme, dass bei den Drehbüchern polizeiliche PressesprecherInnen dann und wann mitreden.

    Mit dieser Annahme verblüfft mich, in welchem Maße die Filme zu einer Demo für Überwachungstechnologie verkommen sind. Seht euch mal im Vergleich irgendeinen alten Derrick an, oder wegen mir auch einen Felmy-Tatort aus den 70ern: Nicht, dass die plausibler oder näher an der damaligen Realität gewesen sein werden, aber das äußerste, was dort an Technik aufscheint, ist vielleicht ein mal ein Fingerabdruck, und wenns ganz scary werden soll, ein Nachschlagen in einer Straftäterdatei über ein grün flimmerndes Terminal. Ansonsten: Verhöre, Zeugengespräche, ernst guckende Beamte in braunen Anzügen.

    Filmszene: ein eine gelbe Spur auf einem Stadtplan

    Aus dem Tatort „Dreams“ vom letzten Sonntag: die Ermittler diskutieren ein in der Erzählung retrograd (nämlich etwas wie drei Tage nach der mutmaßlichen Tat) abgerufenes „Bewegungsprofil“ einer Verdächtigen. Wie sich Tatort-AutorInnen das halt so vorstellen. Und vielleicht auch die Münchner Polizei? (Bildrechte bei der ARD)

    Natürlich ist in der Zwischenzeit auch der reale Polizeialltag deutlich techniklastiger geworden, und das muss ein Fernsehkrimi nicht unbedingt ignorieren. Aber: Im Wesentlichen jeden Fall um DNA-Abgleiche oder -Analysen, Telefon-Verbindungs- und Standortdaten, haufenweise Videodaten (übrigens: im Hinblick auf private Kameras hat das erst seit 2017 überhaupt eine Rechtsgrundlage in der StPO), mehr oder weniger legale Zugriffe auf allerlei weiteren Computerkram (den nächsten Tatort, in dem die Kommissäre scharfsinnig Passwörter raten, schalte ich ab) und etliche weitere forensische Methoden aus der dystopischen Zukunft herumzustricken, das reflektiert sicher keine Realität. Das kann ich schon allein in Kenntnis aktueller Klageschriften und den in (wenn auch vor allem politischen) Verfahren tatsächlich verwendeten Beweismitteln zuversichtlich sagen.

    Vor allem verblüfft dabei, was für völlig bürgerrechtsfeindliche Vorstellungen der durchschnittliche Tatort inzwischen transportiert. Da hängen überall aufzeichnende Kameras rum, die auch noch fröhlich öffentliche Räume abbilden[1] , da reicht ein Blick in den Computer für eine halbwegs vollständige Biographe offenbar beliebiger Personen, und dann gibts den ganzen Mythos rund um das, worum es bei der Vorratsdatenspeicherung wirklich geht.

    Der Screenshot oben aus dem Tatort vom Sonntag ist ein besonderes absurdes Beispiel. Da haben die Ermittler ein „Bewegungsprofil“ bestellt – wo genau, bleibt unklar – und bekommen dann tatsächlich etwas, das die Qualität eines GPS-Tracks hat. Das ist glücklicherweise Unfug. Noch nicht mal bei einem Live-Abruf von Standortdaten nach §100i StPO kämen Geodaten dieser Sorte heraus; im GSM-Netz schon gar nicht, und selbst mit LTE und 5G sind Zellen mindestens einige Hektar groß und eine Triangulation – so sie überhaupt gemacht wird – ist in der Stadt Glückssache. Klar, mit einem GPS-Tracker (für den gibt es, im Gegensatz zum Zugriff auf die GPS-Daten des Mobiltelefons außerhalb der „Online-Durchsuchung“, eine Rechtsgrundlage) ginge das, aber den gab es am Auto der Verdächtigen in dieser Geschichte nicht.

    Im Nachhinein, also wie in diesem Fall sagen wir nach drei Tagen, kann die Polizei allenfalls auf Verkehrsdaten nach §96 TKG hoffen. Da sind zwar auch Standortdaten dabei, aber eben nur zu „Beginn und […] Ende der jeweiligen Verbindung“ – die Herstellung einer solchen Verbindung war Zweck der „Ortungspulse“ der stillen SMS. Nun ist das mit der „Verbindung“ im Zeitalter von paketvermittelten Diensten ein etwas schwieriger Begriff, und ich gestehe, auch nicht aus erster Hand zu wissen, wie das in der Praxis interpretiert wird. Klar ist aber, dass nicht alle paar Sekunden so ein §96 TKG-Record entsteht, um so mehr, als die der Norm zugrundeliegende Vorratsdatenspeicherung ja ausgesetzt ist[2].

    Also: Warum erfinden die Tatort-AutorInnen eine Aluhut-Welt und warum rät ihnen die Polizei nicht davon ab, rechnend, dass die ZuschauerInnen es gruselig finden könnten, wenn die Polizei auf Knopfdruck noch nach Wochen ihre Wege so genau nachvollziehen könnte? Und wie ist das mit den ZuschauerInnen selbst? Haben die inzwischen so viel Angst, dass sie in der Breite so eine dystopische Vision als „na ja, wenns der Sicherheit dient“ gerne ansehen?

    Sachdienliche Hinweise werden bei der Kontaktadresse dankbar angenommen.

    [1]In jedem zweiten Fall wird das Videomaterial dann auch noch hochgezoomt, als wäre es die slippy map auf osm.org. Ich glaube, diese Albernheit hat sich inzwischen zu einem Meme entwickelt. Läuft das unter dem Label „Navy CIS”?
    [2]Die Frage von oben, „wo genau“ die Polizei das „Bewegungsprofil“ her hat, könnte hier interessant werden, denn es ist vorstellbar, dass Google bei hinreichend unvorsichtig konfigurierten Telefonen Daten dieser Genauigkeit vorhält. Wenn das so ist, wäre es wirklich spannend, Statistiken über den staatlichen Zugriff auf diese Bestände zu bekommen. Ich sollte dazu mal sorgfältig https://lumendatabase.org (so heißen die chilling effects von einst inzwischen) lesen.
  • 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 …

« Seite 13 / 17 »

Letzte Ergänzungen