from dataclasses import dataclass, field import numpy as np import sympy as sp from typing import Dict, Iterable from Datenbank import Datenbankzugriff from Export import Export @dataclass class StochastischesModell: """Stochastisches Modell zur Aufstellung und Auswertung von Varianz-Kovarianz-Matrizen. Die Klasse stellt Methoden zur Verfügung für: - symbolischen Aufbau der Varianz-Kovarianz-Matrix der Beobachtungen Qll, - numerische Substitution von Qll aus der Datenbank, - symbolischen und numerischen Aufbau der Zusatz-Varianz-Kovarianz-Matrix QAA für Anschlusspunkte (weiche Lagerung, lA_*), - Ableitung zentraler Matrizen der Ausgleichung (P, Qxx, Qll_dach, Qvv) als Hilfsfunktionen. Die Grundidee ist, die stochastischen Zusammenhänge zunächst symbolisch abzubilden (SymPy) und anschließend mit Datenbankwerten zu substituieren. """ n_beob: int sigma_beob: Iterable[float] =None #σ a priori der einzelnen Beobachtung gruppe_beob: Iterable[int] =None #Gruppenzugehörigkeit jeder Beobachtung (Distanz, Richtung, GNSS, Nivellement,...,) sigma0_gruppe: Dict[int, float] = field(default_factory=dict) #σ0² für jede Gruppe def __init__(self, pfad_datenbank: str) -> None: """Initialisiert das stochastische Modell. Speichert den Pfad zur SQLite-Datenbank, initialisiert den Datenbankzugriff und legt Cache-Variablen an, die in den numerischen Auswertungen zur Rechenzeitersparnis wiederverwendet werden. :param pfad_datenbank: Pfad zur SQLite-Datenbank. :type pfad_datenbank: str :return: None :rtype: None """ self.pfad_datenbank = pfad_datenbank self.func_Qll_numerisch = None self.liste_symbole_lambdify = None self.db_zugriff = Datenbankzugriff(self.pfad_datenbank) def Qll_symbolisch(self, liste_beobachtungen_symbolisch: list) -> sp.Matrix: """Erstellt die symbolische Varianz-Kovarianz-Matrix Qll der Beobachtungen. Aus den symbolischen Beobachtungskennungen wird die Beobachtungsart abgeleitet (Tachymeter: SD/R/ZW, GNSS: gnssbx/gnssby/gnssbz, Geometrisches Nivellement: niv). Für jede Beobachtung wird eine symbolische Varianzgleichung aufgestellt und in Qll eingetragen. Berücksichtigte Gleichungen: - Tachymeter SD: σ = sqrt(σ_konstant² + (σ_streckenprop * SD / 1 000 000)²), Varianz = varkomp * σ² - Tachymeter R/ZW: σ = sqrt(σ_konstant² + (σ_konstant_SD / SD)²), Varianz = varkomp * σ² - GNSS: Diagonale und Korrelationen je Basislinie aus cxx/cyy/czz und cxy/cxz/cyz, jeweils skaliert mit s0² - Nivellement: σ = sqrt(n_wechselpunkte * σ_konstant² + σ_streckenprop² * distanz / 1000), Varianz = varkomp * σ² Die symbolische Matrix wird als CSV-Datei in Zwischenergebnisse\\Qll_Symbolisch.csv exportiert. :param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen. :type liste_beobachtungen_symbolisch: list :return: Symbolische Varianz-Kovarianz-Matrix Qll. :rtype: sp.Matrix """ liste_standardabweichungen_symbole = [] # Vorbereitung und Abfrage der notwendigen Listen und Dictionaries liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch] liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")] Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch)) dict_beobachtungenID_instrumenteID = self.db_zugriff.get_instrumenteID_beobachtungenID_dict() # Aufstellen der Symbolischen Gleichungen für die Einträge in der Qll-Matrix jeder Beobachtungsgruppe for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch): aufgeteilt_i = beobachtung_symbolisch_i.split("_") beobachtungenID_i = int(aufgeteilt_i[0]) instrumenteID_i = dict_beobachtungenID_instrumenteID[beobachtungenID_i] # SD = Schrägdistanzen | R = Richtung | ZW = Zenitwinkel # varkomp = Varianzkomponentenschätzung (Ist noch keine Erfolgt, ist dieser Faktor = 1 if aufgeteilt_i[1] == "SD" or aufgeteilt_i[1] == "R" or aufgeteilt_i[1] == "ZW": beobachtungsart_i = str(aufgeteilt_i[1]) if beobachtungsart_i == "SD": stabw_apriori_konstant = sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_i}_{instrumenteID_i}") stabw_apriori_streckenprop = sp.Symbol(f"stabw_apriori_streckenprop_{beobachtungsart_i}_{instrumenteID_i}") tachymeter_distanz = sp.Symbol(f"SD_{beobachtungenID_i}") sigma = sp.sqrt(stabw_apriori_konstant ** 2 + (stabw_apriori_streckenprop * tachymeter_distanz / 1000000) ** 2) liste_standardabweichungen_symbole.append(sigma) varianzkompontenschaetzung = sp.Symbol(f"varkomp_{instrumenteID_i}_Tachymeter_Streckenbeobachtungen") Qll[i, i] = (varianzkompontenschaetzung) * (sigma ** 2) elif beobachtungsart_i == "R" or beobachtungsart_i == "ZW": stabw_apriori_konstant = sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_i}_{instrumenteID_i}") stabw_apriori_konstant_distanz = sp.Symbol(f"stabw_apriori_konstant_SD_{instrumenteID_i}") tachymeter_distanz = sp.Symbol(f"SD_{beobachtungenID_i}") sigma = sp.sqrt( stabw_apriori_konstant ** 2 + (stabw_apriori_konstant_distanz / tachymeter_distanz) ** 2) liste_standardabweichungen_symbole.append(sigma) if beobachtungsart_i == "R": varianzkompontenschaetzung = sp.Symbol( f"varkomp_{instrumenteID_i}_Tachymeter_Richtungsbeobachtungen") if beobachtungsart_i == "ZW": varianzkompontenschaetzung = sp.Symbol( f"varkomp_{instrumenteID_i}_Tachymeter_Zenitwinkelbeobachtungen") Qll[i, i] = (varianzkompontenschaetzung) * sigma ** 2 # Setzen der 0-Einträge in der Qll-Matrix for j in range(i + 1, len(liste_beobachtungen_symbolisch)): beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j] aufgeteilt_j = beobachtung_symbolisch_j.split("_") beobachtungsart_j = str(aufgeteilt_j[1]) if beobachtungsart_i == "SD" and beobachtungsart_j == "SD": Qll[i, j] = 0 Qll[j, i] = 0 # GNSS # s0, sowie die kovarianzen cxx, ... entstammen direkt LeicaGeooffice if aufgeteilt_i [1] == "gnssbx" or aufgeteilt_i[1] == "gnssby" or aufgeteilt_i[1] == "gnssbz": beobachtungsart_i = str(aufgeteilt_i[1]) varianzkompontenschaetzung = sp.Symbol( f"varkomp_{instrumenteID_i}_GNSS-Rover_Basislinienbeobachtungen") if beobachtungsart_i == "gnssbx": cxx = sp.symbols(f"cxx_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") liste_standardabweichungen_symbole.append(cxx) Qll[i, i] = (varianzkompontenschaetzung) * (cxx * (s0 ** 2)) cxy = sp.Symbol(f"cxy_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") for j in range(i + 1, len(liste_beobachtungen_symbolisch)): beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j] aufgeteilt_j = beobachtung_symbolisch_j.split("_") if int(aufgeteilt_j[0]) == beobachtungenID_i and aufgeteilt_j[1] == "gnssby": Qll[i, j] = (varianzkompontenschaetzung) * (cxy * (s0 ** 2)) Qll[j, i] = (varianzkompontenschaetzung) * (cxy * (s0 ** 2)) break cxz = sp.Symbol(f"cxz_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") for j in range(i + 1, len(liste_beobachtungen_symbolisch)): beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j] aufgeteilt_j = beobachtung_symbolisch_j.split("_") if int(aufgeteilt_j[0]) == beobachtungenID_i and aufgeteilt_j[1] == "gnssbz": Qll[i, j] = (varianzkompontenschaetzung) * (cxz * (s0 ** 2)) Qll[j, i] = (varianzkompontenschaetzung) * (cxz * (s0 ** 2)) break if beobachtungsart_i == "gnssby": cyy = sp.symbols(f"cyy_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") liste_standardabweichungen_symbole.append(cyy) Qll[i, i] = (varianzkompontenschaetzung) * (cyy * (s0 ** 2)) cyz = sp.Symbol(f"cyz_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") for j in range(i + 1, len(liste_beobachtungen_symbolisch)): beobachtung_symbolisch_j = liste_beobachtungen_symbolisch[j] aufgeteilt_j = beobachtung_symbolisch_j.split("_") if int(aufgeteilt_j[0]) == beobachtungenID_i and aufgeteilt_j[1] == "gnssbz": Qll[i, j] = (varianzkompontenschaetzung) * (cyz * (s0 ** 2)) Qll[j, i] = (varianzkompontenschaetzung) * (cyz * (s0 ** 2)) break if beobachtungsart_i == "gnssbz": czz = sp.symbols(f"czz_{beobachtungenID_i}") s0 = sp.symbols(f"s0_{beobachtungenID_i}") liste_standardabweichungen_symbole.append(czz) Qll[i, i] = (varianzkompontenschaetzung) * (czz * (s0 ** 2)) # Geometrisches Nivellement if aufgeteilt_i[1] == "niv": beobachtungsart_i = str(aufgeteilt_i[1]) varianzkompontenschaetzung = sp.Symbol( f"varkomp_{instrumenteID_i}_Nivellier_Hoehendifferenzbeobachtungen") stabw_apriori_konstant = sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_i}_{instrumenteID_i}") stabw_apriori_streckenprop = sp.Symbol(f"stabw_apriori_streckenprop_{beobachtungsart_i}_{instrumenteID_i}") nivellement_distanz = sp.Symbol(f"niv_distanz_{beobachtungenID_i}") nivellement_anz_wechselpunkte = sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID_i}") # Berechnen der Standardabweichung unter Einbeziehung der Anzahl Wechselpunkte sigma = sp.sqrt(nivellement_anz_wechselpunkte * stabw_apriori_konstant ** 2 + stabw_apriori_streckenprop ** 2 * nivellement_distanz / 1000) liste_standardabweichungen_symbole.append(sigma) Qll[i, i] = (varianzkompontenschaetzung) * (sigma ** 2) Export.matrix_to_csv(r"Zwischenergebnisse\Qll_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll") return Qll def Qll_numerisch(self, Qll_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix: """Erstellt eine numerische Varianz-Kovarianz-Matrix aus einer symbolischen Qll-Matrix. Es werden die zur Substitution benötigten Werte aus der Datenbank abgefragt und den in Qll vorkommenden Symbolen zugeordnet, u. a.: - Genauigkeiten (stabw_apriori_konstant / stabw_apriori_streckenprop) je Instrument und Beobachtungsart, - Varianzkomponenten (varkomp_*) je Instrument und Beobachtungsgruppe, - gemessene Tachymeter-Distanzen SD_* zur Bildung der Winkel-/Zenitwinkel-Ansätze, - GNSS-Kovarianzen cxx, cxy, cxz, cyy, cyz, czz sowie s0 je Basislinie, - Geomtrisches Nivellement-Strecken und Anzahl Wechsel-/Standpunkte. Die Methode prüft, ob alle freien Symbole der Qll-Matrix substituierbar sind. Zur Rechenzeitersparnis wird ein Lambdify-Cache geführt. Die Matrix wird anschließend gezielt über Nicht-Null-Einträge befüllt, um unnötige Auswertung von Nullzellen zu vermeiden. Die numerische Matrix wird als CSV-Datei in Zwischenergebnisse\\Qll_Numerisch.csv exportiert. :param Qll_Matrix_Symbolisch: Symbolische Varianz-Kovarianz-Matrix Qll. :type Qll_Matrix_Symbolisch: sp.Matrix :param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen. :type liste_beobachtungen_symbolisch: list :return: Numerische Varianz-Kovarianz-Matrix Qll als Numpy-Array. :rtype: np.Matrix :raises ValueError: Falls Symbole in Qll_Matrix_Symbolisch enthalten sind, für die keine Substitutionen vorhanden sind. """ liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch] liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if not b.startswith("lA_")] # Abfragen der Zahlen zu den Symbolen aus der Datenbank dict_genauigkeiten = self.db_zugriff.get_genauigkeiten_dict() liste_beobachtungen_tachymeter = self.db_zugriff.get_beobachtungen_from_beobachtungenid() liste_beobachtungen_gnss = self.db_zugriff.get_beobachtungen_gnssbasislinien() liste_beobachtungen_nivellement = self.db_zugriff.get_beobachtungen_nivellement() liste_varianzkomponenten = self.db_zugriff.get_varianzkomponentenschaetzung() # Erstellen eines Dicts mit der zuordnung beobachtungenID : Distanz dict_beobachtungenID_distanz = {} for standpunkt, zielpunkt, beobachtungenID, beobachtungsgruppeID, tachymeter_richtung, tachymeter_zenitwinkel, tachymeter_distanz in liste_beobachtungen_tachymeter: dict_beobachtungenID_distanz[int(beobachtungenID)] = tachymeter_distanz # Erstellen eines Dicts mit den Genauigkeiten aus der Tabelle Genauigkeiten dict_genauigkeiten_neu = {} for genauigkeitenID, eintrag in dict_genauigkeiten.items(): instrumenteID = int(eintrag[0]) beobachtungsart = str(eintrag[1]) stabw_apriori_konstant = eintrag[2] stabw_apriori_streckenprop = eintrag[3] dict_genauigkeiten_neu[(instrumenteID, beobachtungsart)] = (stabw_apriori_konstant, stabw_apriori_streckenprop) substitutionen = {} # Erstellen eines Dicts mit den konstanten Anteilen der Standardabweichungen aus der Datenbank dict_konstante_sd = {} for (instrumenteID, beobachtungsart), (stabw_apriori_konstant, stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items(): if beobachtungsart == "Tachymeter_Strecke": if stabw_apriori_konstant is not None: dict_konstante_sd[instrumenteID] = float(stabw_apriori_konstant) # Zuordnen des numerischen Varianzfaktors zum entsprechenden Symbol für die Substitution for (varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten: substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_{beobachtungsgruppe.strip()}")] = float(varianz_varianzkomponentenschaetzung) # Zuordnen der numerischen Genauigkeitsangaben aus der Datenbank zum entsprechenden Symbol für die Substitution for (instrumenteID, beobachtungsart), (stabw_apriori_konstant, stabw_apriori_streckenprop) in dict_genauigkeiten_neu.items(): if beobachtungsart == "Tachymeter_Strecke": beobachtungsart_kurz = "SD" elif beobachtungsart == "Tachymeter_Richtung": beobachtungsart_kurz = "R" elif beobachtungsart == "Tachymeter_Zenitwinkel": beobachtungsart_kurz = "ZW" elif beobachtungsart == "Geometrisches_Nivellement": beobachtungsart_kurz = "niv" if stabw_apriori_konstant is not None: substitutionen[sp.Symbol(f"stabw_apriori_konstant_{beobachtungsart_kurz}_{instrumenteID}")] = float(stabw_apriori_konstant) if stabw_apriori_streckenprop is not None: wert = float(stabw_apriori_streckenprop) if beobachtungsart_kurz == "niv": wert = wert / 1000.0 substitutionen[sp.Symbol(f"stabw_apriori_streckenprop_{beobachtungsart_kurz}_{instrumenteID}")] = wert for instrumenteID, wert in dict_konstante_sd.items(): substitutionen[sp.Symbol(f"stabw_apriori_konstant_SD_{instrumenteID}")] = float(wert) liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch] # Zuordnen der numerischen gemessenen Distanz aus der Datenbank zum entsprechenden Symbol für die Substitution for beobachtung_symbolisch in liste_beobachtungen_symbolisch: aufgeteilt = beobachtung_symbolisch.split("_") beobachtungenID = int(aufgeteilt[0]) distanz = dict_beobachtungenID_distanz.get(beobachtungenID, None) if distanz is not None: substitutionen[sp.Symbol(f"SD_{beobachtungenID}")] = float(distanz) # Zuordnen der numerischen GNSS-Bestandteile aus der Tabelle Beobachtungen zum entsprechenden Symbol für die Substitution for gnss_beobachtungen in liste_beobachtungen_gnss: beobachtungenID = gnss_beobachtungen[0] gnss_s0 = gnss_beobachtungen[6] gnss_cxx = gnss_beobachtungen[7] gnss_cxy = gnss_beobachtungen[8] gnss_cxz = gnss_beobachtungen[9] gnss_cyy = gnss_beobachtungen[10] gnss_cyz = gnss_beobachtungen[11] gnss_czz = gnss_beobachtungen[12] substitutionen[sp.Symbol(f"cxx_{beobachtungenID}")] = float(gnss_cxx) substitutionen[sp.Symbol(f"cxy_{beobachtungenID}")] = float(gnss_cxy) substitutionen[sp.Symbol(f"cxz_{beobachtungenID}")] = float(gnss_cxz) substitutionen[sp.Symbol(f"cyy_{beobachtungenID}")] = float(gnss_cyy) substitutionen[sp.Symbol(f"cyz_{beobachtungenID}")] = float(gnss_cyz) substitutionen[sp.Symbol(f"czz_{beobachtungenID}")] = float(gnss_czz) substitutionen[sp.Symbol(f"s0_{beobachtungenID}")] = float(gnss_s0) # Geometrisches Nivellement for nivellement in liste_beobachtungen_nivellement: beobachtungenID = nivellement[0] niv_strecke = nivellement[4] niv_anz_standpkte = nivellement[5] substitutionen[sp.Symbol(f"niv_anz_wechselpunkte_{beobachtungenID}")] = float(niv_anz_standpkte) substitutionen[sp.Symbol(f"niv_distanz_{beobachtungenID}")] = float(niv_strecke) # Substituieren mit der Methode lambdify if (self.func_Qll_numerisch is None) or (set(self.liste_symbole_lambdify) != set(substitutionen.keys())): self.liste_symbole_lambdify = sorted(substitutionen.keys(), key=lambda s: str(s)) self.func_Qll_numerisch = sp.lambdify( self.liste_symbole_lambdify, Qll_Matrix_Symbolisch, modules="numpy", cse=True ) # Ausgeben der fehlenden Substitutionen fehlend = sorted(list(Qll_Matrix_Symbolisch.free_symbols - set(substitutionen.keys())), key=lambda s: str(s)) if fehlend: raise ValueError( f"Qll_numerisch: Fehlende Substitutionen ({len(fehlend)}): {[str(s) for s in fehlend[:80]]}") # Eingrenzen der Substitution auf nicht 0 Zellen rows = int(Qll_Matrix_Symbolisch.rows) cols = int(Qll_Matrix_Symbolisch.cols) Qll_numerisch = np.zeros((rows, cols), dtype=float) for i in range(rows): for j in range(cols): eintrag = Qll_Matrix_Symbolisch[i, j] if eintrag == 0: continue try: if hasattr(eintrag, "is_zero") and (eintrag.is_zero is True): continue except Exception: pass eintrag_num = eintrag.xreplace(substitutionen) Qll_numerisch[i, j] = float(eintrag_num) Export.matrix_to_csv( r"Zwischenergebnisse\Qll_Numerisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll_numerisch, "Qll" ) return Qll_numerisch def QAA_symbolisch(self, liste_beobachtungen_symbolisch: list) -> np.Matrix: """Erstellt die symbolische Varianz-Kovarianz-Matrix QAA der Anschlusspunkte (weiche Lagerung). Es werden ausschließlich Beobachtungen berücksichtigt, deren Kennung mit "lA_" beginnt. Für jede Anschlussbedingung wird eine (symbolische) Standardabweichung StabwAA_* angesetzt und mit der Varianzkomponente der Beobachtungsgruppe "Anschlusspunkte" multipliziert. Die symbolische Matrix wird als CSV-Datei in Zwischenergebnisse\\QAA_Symbolisch.csv exportiert. :param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen. :type liste_beobachtungen_symbolisch: list :return: Symbolische Varianz-Kovarianz-Matrix QAA. :rtype: sp.Matrix """ liste_standardabweichungen_symbole = [] liste_beobachtungen_symbolisch = [str(b) for b in liste_beobachtungen_symbolisch] liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")] Qll = sp.zeros(len(liste_beobachtungen_symbolisch), len(liste_beobachtungen_symbolisch)) instrumente_id_anschlusspunkte = self.db_zugriff.get_instrument_liste("Anschlusspunkte")[0][0] # Erstellen der Symbolischen Gleichungen für die QAA-Matrix for i, beobachtung_symbolisch_i in enumerate(liste_beobachtungen_symbolisch): aufgeteilt_i = beobachtung_symbolisch_i.split("_") datumskoordinate = str(aufgeteilt_i[1]) beobachtungsart_i = str(aufgeteilt_i[0]) if beobachtungsart_i == "lA": sigma = sp.Symbol(f"StabwAA_{datumskoordinate}") liste_standardabweichungen_symbole.append(sigma) varianzkompontenschaetzung = sp.Symbol( f"varkomp_{instrumente_id_anschlusspunkte}_Anschlusspunkte") liste_standardabweichungen_symbole.append(varianzkompontenschaetzung) Qll[i, i] = (varianzkompontenschaetzung) * sigma ** 2 Export.matrix_to_csv(r"Zwischenergebnisse\QAA_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll") return Qll def QAA_numerisch(self, QAA_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.Matrix: """Erstellt eine numerische Matrix aus einer symbolischen QAA-Matrix. Es werden die numerischen Standardabweichungen der Anschlussbedingungen sowie die Varianzkomponente der Gruppe "Anschlusspunkte" aus der Datenbank abgefragt und substituiert. Zur Rechenzeitersparnis wird ein Lambdify-Cache geführt, der bei Änderung der Symbolmenge neu aufgebaut. Die numerische Matrix wird als CSV-Datei in Zwischenergebnisse\\QAA_Numerisch.csv exportiert. :param QAA_Matrix_Symbolisch: Symbolische Varianz-Kovarianz-Matrix QAA. :type QAA_Matrix_Symbolisch: sp.Matrix :param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen. :type liste_beobachtungen_symbolisch: list :return: Numerische Varianz-Kovarianz-Matrix QAA als Numpy-Array. :rtype: np.Matrix """ # Symbolische Listen liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch] liste_beobachtungen_symbolisch = [b for b in liste_beobachtungen_symbolisch if b.startswith("lA_")] # Abfrage der numerischen Werte aus der Datenbank dict_stabwAA_vorinfo = self.db_zugriff.get_stabw_AA_Netzpunkte() liste_varianzkomponenten = self.db_zugriff.get_varianzkomponentenschaetzung() # Zuordnen des Symbols zum numerischen Wert substitutionen = {} for koordinate, stabwAA in dict_stabwAA_vorinfo.items(): substitutionen[sp.Symbol(str(koordinate).strip())] = float(stabwAA) for (varianzkomponenteID, instrumenteID, beobachtungsgruppe, varianz_varianzkomponentenschaetzung) in liste_varianzkomponenten: substitutionen[sp.Symbol(f"varkomp_{instrumenteID}_Anschlusspunkte")] = float(varianz_varianzkomponentenschaetzung) # Speichern der MAtrix in einer Instanzvariablen if not hasattr(self, "func_QAA_numerisch"): self.func_QAA_numerisch = None if not hasattr(self, "liste_symbole_lambdify_QAA"): self.liste_symbole_lambdify_QAA = [] # Substituieren if (self.func_QAA_numerisch is None) or (set(self.liste_symbole_lambdify_QAA) != set(substitutionen.keys())): self.liste_symbole_lambdify_QAA = sorted(substitutionen.keys(), key=lambda s: str(s)) self.func_QAA_numerisch = sp.lambdify( self.liste_symbole_lambdify_QAA, QAA_Matrix_Symbolisch, modules="numpy", cse=True ) liste_werte = [substitutionen[s] for s in self.liste_symbole_lambdify_QAA] QAA_numerisch = np.asarray(self.func_QAA_numerisch(*liste_werte), dtype=float) Export.matrix_to_csv( r"Zwischenergebnisse\QAA_Numerisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, QAA_numerisch, "QAA" ) return QAA_numerisch @staticmethod def berechne_P(Q_ll: np.ndarray) -> np.ndarray: """Berechnet die Gewichtsmatrix P aus einer Varianz-Kovarianz-Matrix Qll. Die Gewichtsmatrix wird als Inverse von Qll gebildet: P = inv(Qll). :param Q_ll: Varianz-Kovarianz-Matrix der Beobachtungen. :type Q_ll: np.ndarray :return: Gewichtsmatrix P. :rtype: np.ndarray """ P = np.linalg.inv(Q_ll) return P @staticmethod def berechne_Q_xx(N: np.ndarray) -> np.ndarray: """Berechnet die Kofaktormatrix der Unbekannten Qxx aus der Normalgleichungsmatrix N. Die Kofaktormatrix wird als Inverse der Normalgleichungsmatrix gebildet: Qxx = inv(N). Vor der Inversion wird geprüft, ob N quadratisch ist. :param N: Normalgleichungsmatrix. :type N: np.ndarray :return: Kofaktormatrix der Unbekannten Qxx. :rtype: np.ndarray :raises ValueError: Falls N nicht quadratisch ist. """ if N.shape[0] != N.shape[1]: raise ValueError("N muss eine quadratische Matrix sein") Qxx = np.linalg.inv(N) return Qxx @staticmethod def berechne_Q_ll_dach(A: np.ndarray, Q_xx: np.ndarray) -> np.ndarray: """Berechnet die (geschätzte) Varianz-Kovarianz-Matrix der Beobachtungen Qll_dach. Die Matrix wird gemäß Qll_dach = A @ Qxx @ A.T gebildet. :param A: Jacobi-Matrix. :type A: np.ndarray :param Q_xx: Kofaktormatrix der Unbekannten. :type Q_xx: np.ndarray :return: Geschätzte Varianz-Kovarianz-Matrix der Beobachtungen Qll_dach. :rtype: np.ndarray """ Q_ll_dach = A @ Q_xx @ A.T return Q_ll_dach @staticmethod def berechne_Qvv(Q_ll: np.ndarray, Q_ll_dach: np.ndarray) -> np.ndarray: """Berechnet die Varianz-Kovarianz-Matrix der Residuen Qvv. Die Residuenkovarianz wird als Differenz aus Beobachtungs-Kovarianz und dem durch das Modell erklärten Anteil gebildet: Qvv = Qll - Qll_dach. :param Q_ll: Varianz-Kovarianz-Matrix der Beobachtungen. :type Q_ll: np.ndarray :param Q_ll_dach: Geschätzte Varianz-Kovarianz-Matrix der Beobachtungen. :type Q_ll_dach: np.ndarray :return: Varianz-Kovarianz-Matrix der Residuen Qvv. :rtype: np.ndarray """ Q_vv = Q_ll - Q_ll_dach return Q_vv