Communitygithub.com

molecular-dynamics

Führen Sie Molekulardynamik-Simulationen mit OpenMM und MDAnalysis durch und analysieren Sie diese. Richten Sie Protein-/Kleinstmolekül-Systeme ein, definieren Sie Kraftfelder, führen Sie Energieminimierung und Produktions-MD durch und analysieren Sie Trajektorien (RMSD, RMSF, Kontaktkarten, freie Energieflächen). Für Strukturbiologie, Wirkstoffbindung und Biophysik.

Was ist molecular-dynamics?

Diese Fähigkeit (Skill) ermöglicht es KI-Agenten, Molekulardynamik (MD) Simulationen mit den führenden Open-Source-Bibliotheken OpenMM und MDAnalysis durchzuführen und zu analysieren. Sie deckt den gesamten Workflow ab: Systemvorbereitung (z. B. aus PDB-Dateien mit Modeller), Energieminimierung, NVT-Äquilibrierung und NPT-Produktionsläufe, alles mit standardmäßiger GPU-Unterstützung für hohe Leistung. Die Fähigkeit umfasst die Integration mit PDBFixer zur Aufbereitung von Roh-PDB-Dateien und mit dem OpenFF Toolkit zur Parametrisierung kleiner Moleküle. Für die Analyse werden umfassende MDAnalysis-Funktionen bereitgestellt, um RMSD (Root Mean Square Deviation), RMSF (Root Mean Square Fluctuation), Protein-Ligand-Kontakte und mehr zu berechnen. Dies ist besonders nützlich für strukturbiologische Forschungsaufgaben wie die Untersuchung von Proteinmutationsauswirkungen, die Charakterisierung von Arzneimittelbindungsmodi oder die Modellierung von Protein-Protein-Wechselwirkungen. Der Skill ist für Plattformen wie Claude Code optimiert und befolgt Standard-Best-Practices für MD-Simulationen, einschließlich periodischer Randbedingungen und PME (Particle Mesh Ewald) für die Elektrostatik.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/K-Dense-AI/scientific-agent-skills/tree/main/skills/molecular-dynamics

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Molekulardynamik

Übersicht

Molekulardynamik (MD)-Simulationen modellieren rechnerisch die zeitliche Entwicklung molekularer Systeme durch Integration der Newtonschen Bewegungsgleichungen. Dieser Skill deckt zwei komplementäre Werkzeuge ab:

  • OpenMM (https://openmm.org/): Hochleistungs-MD-Simulationsengine mit GPU-Unterstützung, Python-API und flexibler Kraftfeldunterstützung
  • MDAnalysis (https://mdanalysis.org/): Python-Bibliothek zum Lesen, Schreiben und Analysieren von MD-Trajektorien aus allen gängigen Simulationspaketen

Installation:

conda install -c conda-forge openmm mdanalysis nglview
# oder
uv pip install openmm mdanalysis

Wann dieser Skill verwendet werden sollte

Verwenden Sie Molekulardynamik, wenn:

  • Proteinstabilitätsanalyse: Wie wirkt sich eine Mutation auf die Proteindynamik aus?
  • Wirkstoffbindungssimulationen: Charakterisieren Sie den Bindungsmodus und die Verweilzeit eines Liganden
  • Konformationsabtastung: Erkunden Sie die Proteinflexibilität und Konformationsänderungen
  • Protein-Protein-Wechselwirkung: Modellieren Sie Grenzflächendynamik und Bindungsenergetik
  • RMSD/RMSF-Analyse: Quantifizieren Sie strukturelle Fluktuationen aus einer Referenzstruktur
  • Freie-Energie-Schätzung: Berechnen Sie die freie Bindungsenergie oder die freie Konformationsenergie
  • Membransimulationen: Modellieren Sie Proteine in Lipiddoppelschichten
  • Intrinsisch ungeordnete Proteine: Untersuchen Sie konformationelle Ensembles von IDPs

Kern-Workflow: OpenMM-Simulation

1. Systemvorbereitung

from openmm.app import *
from openmm import *
from openmm.unit import *
import sys

def prepare_system_from_pdb(pdb_datei, kraftfeld_name="amber14-all.xml",
                              wasser_modell="amber14/tip3pfb.xml"):
    """
    Bereitet ein OpenMM-System aus einer PDB-Datei vor.

    Args:
        pdb_datei: Pfad zur bereinigten PDB-Datei (verwenden Sie PDBFixer für rohe PDB-Dateien)
        kraftfeld_name: Kraftfeld-XML-Datei
        wasser_modell: Wassermodell-XML-Datei

    Returns:
        pdb, kraftfeld, system, topologie
    """
    # PDB laden
    pdb = PDBFile(pdb_datei)

    # Kraftfeld laden
    kraftfeld = ForceField(kraftfeld_name, wasser_modell)

    # Wasserstoffatome hinzufügen und solvatisieren
    modellierer = Modeller(pdb.topology, pdb.positions)
    modellierer.addHydrogens(kraftfeld)

    # Lösungsmittelbox hinzufügen (10 Å Abstand, 150 mM NaCl)
    modellierer.addSolvent(
        kraftfeld,
        model='tip3p',
        padding=10*angstroms,
        ionicStrength=0.15*molar
    )

    print(f"System: {modellierer.topology.getNumAtoms()} Atome, "
          f"{modellierer.topology.getNumResidues()} Reste")

    # System erstellen
    system = kraftfeld.createSystem(
        modellierer.topology,
        nonbondedMethod=PME,         # Particle Mesh Ewald für langreichweitige Elektrostatik
        nonbondedCutoff=1.0*nanometer,
        constraints=HBonds,           # Wasserstoffbrückenbindungen einschränken (ermöglicht 2-fs-Schrittweite)
        rigidWater=True,
        ewaldErrorTolerance=0.0005
    )

    return modellierer, system

2. Energieminimierung

from openmm.app import *
from openmm import *
from openmm.unit import *

def minimize_energy(modellierer, system, ausgabe_pdb="minimiert.pdb",
                     max_iterationen=1000, toleranz=10.0):
    """
    Energieminimierung des Systems, um sterische Hinderungen zu beseitigen.

    Args:
        modellierer: Modeller-Objekt mit Topologie und Positionen
        system: OpenMM-System
        ausgabe_pdb: Pfad zum Speichern der minimierten Struktur
        max_iterationen: Maximale Anzahl von Minimierungsschritten
        toleranz: Konvergenzkriterium in kJ/mol/nm

    Returns:
        Simulationsobjekt mit minimierten Positionen
    """
    # Integrator einrichten (spielt für die Minimierung keine Rolle)
    integrator = LangevinMiddleIntegrator(300*kelvin, 1/picosecond, 0.004*picoseconds)

    # Simulation erstellen
    # GPU verwenden, falls verfügbar (CUDA oder OpenCL), sonst auf CPU zurückgreifen
    try:
        platform = Platform.getPlatformByName('CUDA')
        eigenschaften = {'DeviceIndex': '0', 'Precision': 'mixed'}
    except Exception:
        try:
            platform = Platform.getPlatformByName('OpenCL')
            eigenschaften = {}
        except Exception:
            platform = Platform.getPlatformByName('CPU')
            eigenschaften = {}

    simulation = Simulation(
        modellierer.topology, system, integrator,
        platform, eigenschaften
    )
    simulation.context.setPositions(modellierer.positions)

    # Anfangsenergie überprüfen
    zustand = simulation.context.getState(getEnergy=True)
    print(f"Anfangsenergie: {zustand.getPotentialEnergy()}")

    # Minimieren
    simulation.minimizeEnergy(
        tolerance=toleranz*kilojoules_per_mole/nanometer,
        maxIterations=max_iterationen
    )

    zustand = simulation.context.getState(getEnergy=True, getPositions=True)
    print(f"Minimierte Energie: {zustand.getPotentialEnergy()}")

    # Minimierte Struktur speichern
    with open(ausgabe_pdb, 'w') as f:
        PDBFile.writeFile(simulation.topology, zustand.getPositions(), f)

    return simulation

3. NVT-Äquilibrierung

from openmm.app import *
from openmm import *
from openmm.unit import *

def run_nvt_aequilibrierung(simulation, n_schritte=50000, temperatur=300,
                            berichtsintervall=1000, ausgabe_praefix="nvt"):
    """
    NVT-Äquilibrierung: konstantes N, V, T.
    Äquilibriert Geschwindigkeiten auf Zieltemperatur.

    Args:
        simulation: OpenMM-Simulation (nach Minimierung)
        n_schritte: Anzahl der MD-Schritte (50000 × 2fs = 100 ps)
        temperatur: Temperatur in Kelvin
        berichtsintervall: Schritte zwischen Datenberichten
        ausgabe_praefix: Dateipräfix für Trajektorie und Log
    """
    # Positionsbeschränkungen für Rückgrat während NVT hinzufügen
    # (Optional: schwere Atome einschränken)

    # Temperatur einstellen
    simulation.context.setVelocitiesToTemperature(temperatur*kelvin)

    # Berichterstatter hinzufügen
    simulation.reporters = []

    # Logdatei
    simulation.reporters.append(
        StateDataReporter(
            f"{ausgabe_praefix}_log.txt",
            berichtsintervall,
            step=True,
            potentialEnergy=True,
            kineticEnergy=True,
            temperature=True,
            volume=True,
            speed=True
        )
    )

    # DCD-Trajektorie (kompaktes Binärformat)
    simulation.reporters.append(
        DCDReporter(f"{ausgabe_praefix}_traj.dcd", berichtsintervall)
    )

    print(f"Führe NVT-Äquilibrierung durch: {n_schritte} Schritte ({n_schritte*2/1000:.1f} ps)")
    simulation.step(n_schritte)
    print("NVT-Äquilibrierung abgeschlossen")

    return simulation

4. NPT-Äquilibrierung und Produktion

def run_npt_produktion(simulation, n_schritte=500000, temperatur=300, druck=1.0,
                        berichtsintervall=5000, ausgabe_praefix="npt"):
    """
    NPT-Produktionslauf: konstantes N, P, T.

    Args:
        n_schritte: Produktionsschritte (500000 × 2fs = 1 ns)
        temperatur: Temperatur in Kelvin
        druck: Druck in bar
        berichtsintervall: Schritte zwischen Berichten
    """
    # Monte-Carlo-Barostat zur Druckkontrolle hinzufügen
    system = simulation.context.getSystem()
    system.addForce(MonteCarloBarostat(druck*bar, temperatur*kelvin, 25))
    simulation.context.reinitialize(preserveState=True)

    # Berichterstatter aktualisieren
    simulation.reporters = []
    simulation.reporters.append(
        StateDataReporter(
            f"{ausgabe_praefix}_log.txt",
            berichtsintervall,
            step=True,
            potentialEnergy=True,
            temperature=True,
            density=True,
            speed=True
        )
    )
    simulation.reporters.append(
        DCDReporter(f"{ausgabe_praefix}_traj.dcd", berichtsintervall)
    )

    # Prüfpunkte speichern
    simulation.reporters.append(
        CheckpointReporter(f"{ausgabe_praefix}_checkpoint.chk", 50000)
    )

    print(f"Führe NPT-Produktion durch: {n_schritte} Schritte ({n_schritte*2/1000000:.2f} ns)")
    simulation.step(n_schritte)
    print("Produktions-MD abgeschlossen")
    return simulation

Trajektorienanalyse mit MDAnalysis

1. Trajektorie laden

import MDAnalysis as mda
from MDAnalysis.analysis import rms, align, contacts
import numpy as np
import matplotlib.pyplot as plt

def load_trajectory(topologie_datei, trajektorien_datei):
    """
    Lädt eine MD-Trajektorie mit MDAnalysis.

    Args:
        topologie_datei: PDB-, PSF- oder andere Topologiedatei
        trajektorien_datei: DCD-, XTC-, TRR- oder andere Trajektorie
    """
    u = mda.Universe(topologie_datei, trajektorien_datei)
    print(f"Universum: {u.atoms.n_atoms} Atome, {u.trajectory.n_frames} Frames")
    print(f"Zeitspanne: 0 bis {u.trajectory.totaltime:.0f} ps")
    return u

2. RMSD-Analyse

def compute_rmsd(u, selection="backbone", referenz_frame=0):
    """
    Berechnet den RMSD ausgewählter Atome relativ zum Referenzframe.

    Args:
        u: MDAnalysis-Universum
        selection: Atomauswahlzeichenfolge (MDAnalysis-Syntax)
        referenz_frame: Frame-Index für die Referenzstruktur

    Returns:
        numpy-Array mit (Zeit, RMSD)-Werten
    """
    # Trajektorie ausrichten, um RMSD zu minimieren
    aligner = align.AlignTraj(u, u, select=selection, in_memory=True)
    aligner.run()

    # RMSD berechnen
    R = rms.RMSD(u, select=selection, ref_frame=referenz_frame)
    R.run()

    rmsd_daten = R.results.rmsd  # Spalten: Frame, Zeit, RMSD
    return rmsd_daten

def plot_rmsd(rmsd_daten, titel="RMSD über die Zeit", ausgabedatei="rmsd.png"):
    """Zeichnet RMSD über die Simulationszeit."""
    fig, ax = plt.subplots(figsize=(10, 4))
    ax.plot(rmsd_daten[:, 1] / 1000, rmsd_daten[:, 2], 'b-', linewidth=0.5)
    ax.set_xlabel("Zeit (ns)")
    ax.set_ylabel("RMSD (Å)")
    ax.set_title(titel)
    ax.axhline(rmsd_daten[:, 2].mean(), color='r', linestyle='--',
               label=f'Mittelwert: {rmsd_daten[:, 2].mean():.2f} Å')
    ax.legend()
    plt.tight_layout()
    plt.savefig(ausgabedatei, dpi=150)
    return fig

3. RMSF-Analyse (Flexibilität pro Rest)

def compute_rmsf(u, selection="backbone", start_frame=0):
    """
    Berechnet die RMSF pro Rest (Flexibilität).

    Returns:
        residue_ids, rmsf_werte Arrays
    """
    # Atome auswählen
    atome = u.select_atoms(selection)

    # RMSF berechnen
    R = rms.RMSF(atome)
    R.run(start=start_frame)

    # Nach Rest mitteln
    residue_ids = []
    rmsf_pro_rest = []
    for res in u.select_atoms(selection).residues:
        res_atome = res.atoms.intersection(atome)
        if len(res_atome) > 0:
            residue_ids.append(res.resid)
            rmsf_pro_rest.append(R.results.rmsf[res_atome.indices].mean())

    return np.array(residue_ids), np.array(rmsf_pro_rest)

4. Protein-Ligand-Kontakte

def analyze_contacts(u, protein_sel="protein", ligand_sel="resname LIG",
                      radius=4.5, start_frame=0):
    """
    Verfolgt Protein-Ligand-Kontakte über die Trajektorie.

    Args:
        radius: Kontaktdistanz-Cutoff in Angstrom
    """
    protein = u.select_atoms(protein_sel)
    ligand = u.select_atoms(ligand_sel)

    kontakt_frames = []
    for ts in u.trajectory[start_frame:]:
        # Proteinatome innerhalb des Radius um den Liganden finden
        distanzen = contacts.contact_matrix(
            protein.positions, ligand.positions, radius
        )
        kontakt_reste = set()
        for i in range(distanzen.shape[0]):
            if distanzen[i].any():
                kontakt_reste.add(protein.atoms[i].resid)
        kontakt_frames.append(kontakt_reste)

    return kontakt_frames

Kraftfeld-Auswahlhilfe

SystemEmpfohlenes KraftfeldWassermodell
StandardproteineAMBER14 (amber14-all.xml)TIP3P-FB
Proteine + kleine MoleküleAMBER14 + GAFF2TIP3P-FB
MembranproteineCHARMM36mTIP3P
NukleinsäurenAMBER99-bsc1 oder AMBER14TIP3P
Ungeordnete Proteineff19SB oder CHARMM36mTIP3P

Werkzeuge zur Systemvorbereitung

PDBFixer (für rohe PDB-Dateien)

from pdbfixer import PDBFixer
from openmm.app import PDBFile

def fix_pdb(eingabe_pdb, ausgabe_pdb, ph=7.0):
    """Behebt häufige PDB-Probleme: fehlende Reste, Atome, fügt H hinzu, standardisiert."""
    fixer = PDBFixer(filename=eingabe_pdb)
    fixer.findMissingResidues()
    fixer.findNonstandardResidues()
    fixer.replaceNonstandardResidues()
    fixer.removeHeterogens(True)    # Wasser/Liganden entfernen
    fixer.findMissingAtoms()
    fixer.addMissingAtoms()
    fixer.addMissingHydrogens(ph)

    with open(ausgabe_pdb, 'w') as f:
        PDBFile.writeFile(fixer.topology, fixer.positions, f)

    return ausgabe_pdb

GAFF2 für kleine Moleküle (über OpenFF-Toolkit)

# Für die Ligandenparametrisierung das OpenFF-Toolkit oder ACPYPE verwenden
# uv pip install openff-toolkit
from openff.toolkit import Molecule, ForceField as OFFForceField
from openff.interchange import Interchange

def parameterize_ligand(smiles, ff_name="openff-2.0.0.offxml"):
    """Erzeugt GAFF2/OpenFF-Parameter für ein kleines Molekül."""
    mol = Molecule.from_smiles(smiles)
    mol.generate_conformers(n_conformers=1)

    off_ff = OFFForceField(ff_name)
    interchange = off_ff.create_interchange(mol.to_topology())
    return interchange

Best Practices

  • Vor MD immer minimieren: Rohe PDB-Strukturen weisen sterische Hinderungen auf
  • Vor der Produktion äquilibrieren: NVT (50–100 ps) → NPT (100–500 ps) → Produktion
  • GPU verwenden: Simulationen sind auf GPU (CUDA/OpenCL) 10–100× schneller
  • 2-fs-Schrittweite mit HBonds-Einschränkungen: Standard; 4 fs mit HMR (Wasserstoffmassen-Neuverteilung)
  • Nur äquilibrierte Trajektorie analysieren: Erste 20–50 % als Äquilibrierung verwerfen
  • Prüfpunkte speichern: MD-Läufe können fehlschlagen; Prüfpunkte ermöglichen Neustarts
  • Periodische Randbedingungen: Für solvatisierte Systeme erforderlich
  • PME für Elektrostatik: Genauer als Cutoff-Methoden für geladene Systeme

Zusätzliche Ressourcen

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

adaptyv

How to use the Adaptyv Bio Foundry API and Python SDK for protein experiment design, submission, and results retrieval. Use this skill whenever the user mentions Adaptyv, Foundry API, protein binding assays, protein screening experiments, BLI/SPR assays, thermostability assays, or wants to submit protein sequences for experimental characterization. Also trigger when code imports `adaptyv`, `adaptyv_sdk`, or `FoundryClient`, or references `foundry-api-public.adaptyvbio.com`.

aeon

This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.

alphagenome

Look up precomputed AlphaGenome Atlas effects for any GRCh38 single-nucleotide variant (AVI score with Phred and 18 SHAP feature attributions, plus raw and quantile scores for RNA-seq, DNase, ATAC, ChIP-TF, ChIP-histone, CAGE, PRO-cap, splicing, polyadenylation and contact-map tracks), score variants or scan windows on demand with the AlphaGenome model for human and mouse (variant scoring, in silico mutagenesis, REF-versus-ALT track prediction), and build Atlas website deep links. Use when the user mentions AlphaGenome, AlphaGenome Atlas, AVI or AlphaGenome Variant Impact, DeepMind variant effect prediction, or wants to prioritise or mechanistically interpret non-coding, regulatory, splicing, enhancer, promoter, or chromatin-accessibility effects of SNVs from a VCF, credible set, or region. Research use only; not a clinical tool.

analytical-method-validation

Plan, execute, and document validation, verification, and transfer of analytical procedures under the governing framework - ICH Q2(R2) and Q14, USP <1220>/<1225>/<1226>, ICH M10 bioanalytical, CLSI EP, or ISO/IEC 17025. Use for HPLC, LC-MS/MS, GC, CE, ICP-MS, dissolution, qNMR, qPCR, NIR, and ligand binding or cell-based assays whenever the question is whether a procedure is fit for its intended purpose. Triggers include

anndata

Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.

arbor

Autonomously improve a real artifact (code, training recipe, agent harness, data pipeline, prompt) against an objective and an evaluator, using Hypothesis Tree Refinement (HTR) from the Arbor paper. Use this whenever someone wants to iteratively optimize something over many experiments without overfitting — e.g.

arboreto

Infer gene regulatory networks (GRNs) from gene expression data using scalable algorithms (GRNBoost2, GENIE3). Use when analyzing transcriptomics data (bulk RNA-seq, single-cell RNA-seq) to identify transcription factor-target gene relationships and regulatory interactions. Supports distributed computation for large-scale datasets.

astropy

Core Python library for astronomy and astrophysics workflows that need Astropy APIs, including units/quantities, coordinates, FITS I/O, tables, time systems, WCS, and cosmology. Use when implementing or debugging astronomical data analysis code with Astropy.

autoskill

Observe the user

benchling-integration

Benchling Python SDK and REST API integration for registry entities, inventory, ELN entries, workflows, Benchling Apps, and Data Warehouse queries. Use when automating lab data with benchling-sdk or the v2 API.

bgpt-paper-search

Search scientific papers and retrieve structured experimental data extracted from full-text studies via the BGPT MCP server. Returns 25+ fields per paper including methods, results, sample sizes, quality scores, and conclusions. Use for literature reviews, evidence synthesis, and finding experimental details not available in abstracts alone.

bids

>

biopython

Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.

bioservices

Unified Python interface to 40+ bioinformatics services. Use when querying multiple databases (UniProt, KEGG, ChEMBL, Reactome) in a single workflow with consistent API. Best for cross-database analysis, ID mapping across services. For quick single-database lookups use gget; for sequence/file manipulation use biopython.

bulk-rnaseq

End-to-end bulk RNA-seq orchestrator — takes raw FASTQ reads through QC and trimming (FastQC, fastp/Trim Galore), alignment and quantification (STAR, Salmon, featureCounts), assembles a gene-level counts matrix, then hands off to differential expression (pydeseq2), pathway/GSEA enrichment (pathway-enrichment), and publication figures (scientific-visualization). Use whenever the user has bulk RNA-seq reads or quant output and wants a complete, reproducible differential-expression workflow — e.g.

cellxgene-census

Query the CZ CELLxGENE Census programmatically for versioned public single-cell and spatial transcriptomics data. Use when you need population-scale cell metadata, gene expression slices, Census summary counts, source H5AD URIs/downloads, embeddings, spatial Census data, or reference atlas comparisons across organisms, tissues, diseases, assays, and cell types. For analyzing your own local single-cell data use scanpy, anndata, or scvi-tools.

cirq

Google quantum computing framework. Use when targeting Google Quantum AI hardware, designing noise-aware circuits, or running quantum characterization experiments. Best for Google hardware, noise modeling, and low-level circuit design. For IBM hardware use qiskit; for quantum ML with autodiff use pennylane; for physics simulations use qutip.

citation-management

Comprehensive citation management for academic research. Search OpenAlex, PubMed, and Google Scholar for papers, extract accurate metadata, validate citations, and generate properly formatted BibTeX entries. This skill should be used when you need to find papers, verify citation information, convert DOIs to BibTeX, or ensure reference accuracy in scientific writing.

clinical-decision-support

Prepare and validate research-only clinical decision-support evaluation, evidence-profile, cohort, survival, biomarker/model, privacy, and governance artifacts. Use for aggregate or synthetic research documentation and traceability—not patient care or live clinical operation.

clinical-reports

Create safety-bounded draft structures and run local deterministic checks for clinical case, diagnostic, trial, safety, and aggregate research reports. Use only with synthetic, de-identified, or aggregate inputs and verified source-fact manifests; every output requires qualified review.

Verwandte Skills