26x
002055
25. Juni 2026

Parameterstudie mittels Dlubal API

Dieser Fachbeitrag zeigt an zwei Beispielen auf, wie mittels der Definition von globalen Parametern und der Dlubal API eine automatisierte Durchführung von Parameterstudien möglich ist.

Allgemeines

Parameterstudien sind ein sinnvolles und probates Mittel um Simulationen anzupassen. Die Ziele können hierbei unterschiedlicher Natur sein: Die Findung einer optimalen Struktur mit minimalem Gewicht um gleichzeitig Sicherheit zu gewährleisten und Ressourcen zu schonen, die Anpassung der Netzfeinheit um zuverlässige Simulationsergebnisse zu bekommen oder die Variation von Materialeigenschaften um die Sensitivität der Simulation zu prüfen und zuverlässige Aussagen zu der Tragsicherheit treffen zu können. Für diese Fälle und viele weitere bietet sich eine parametrisierte Modellierung an. Um nun nicht alle Varianten einzeln durchzurechnen kann die Dlubal API genutzt werden. Hierdurch kann die Berechnung der Parameterstudie ablaufen, ohne dass man diese überwachen muss.

Parametrisierte Modellierung

Viele Funktionen in RFEM 6, RSTAB 9 und RSECTION sind bereits parametrisierbar. Weitere Informationen sind in den diesbezüglichen Handbüchern auffindbar:

Modellzugriff mittels API

Um mittels API auf das Modell zugreifen zu können, muss das zugehörige Paket installiert sein. Ebenfalls können Sie statt eines externen Editors direkt die Konsole oder Skript-Manager im Hauptprogramm nutzen. Weitere Informationen sind unter den folgenden Links abrufbar.

Beispiel 1: Wandstärkenoptimierung

Bei diesem Modell handelt es sich um einen einfachen Kasten mit aufgesetztem konischem Zylinder. Beide Teile bestehen aus Stahlblech mit unterschiedlichen Wandstärken. Diese wurden parametrisiert um eine Parameterstudie durchzuführen. Das Ziel ist hierbei die Konfiguration zu finden, deren Vergleichsspannung nach von Mises bei minimalem Gewicht die Streckgrenze nicht überschreitet. Als Belastung wirken Eigengewicht sowie in einem Nutzlast-Lastfall eine komponentenweise Einzellast auf einer starren Fläche an der Oberseite des Konus. Die Lagerung erfolgt gelenkig an zwei Linien an der Unterseite des Kastens. Das Modell und eine Ansicht der Benutzeroberfläche mit Parametrisierung kann nachfolgend eingesehen werden.

Das nachfolgende Skript ist in Abschnitte aufgeteilt. Im ersten Abschnitt werden die genutzten Bibliotheken importiert, worauf die Definition der Variablen folgt. Hier ist die Streckgrenze zu visualisierungszwecken und die Spannweite der zu variierenden Parameter, welche mit der Blechdicke verknüpft sind, angegeben. Daraufhin werden Funktionen definiert, welche für den Aufbau der Variantenmatrix und die Änderung der globalen Parameter in RFEM 6 im späteren Verlauf genutzt werden.
Der Hauptprogrammteil beginnt mit dem Aufbau der Variantenmatrix der globalen Parameter. Hierfür werden aus den definierten Parametervariationen mittels kartesischem Produkt alle möglichen Mutationen erzeugt. Im Nachgang werden diese nochmals nach der Bedingung eingeschränkt, dass die Wandstärke im unteren Bereich größer oder gleich derer im Aufbau sein muss.
Daraufhin wird die Verbindung mit RFEM 6 über die Dlubal API hergestellt. In diesem Fall zum aktiven Modell mit dem in der Konfigurationsdatei hinterlegten API-Schlüssel. Beide Einstellungen können auch im Funktionsaufruf spezifiziert werden.
Nun wird eine Schleife durchlaufen, in der nacheinander die globalen Parameter in RFEM 6 entsprechend der aktuellen Mutation angepasst werden, die Berechnung durchgeführt und die Ergebnisse ausgelesen werden. Um eine Anpassung des Modells und das Löschen der Ergebnisse zu erzwingen wird vor der Berechnung das Netz gelöscht und neu generiert. Das Maximum der Vergleichsspannung nach von Mises aller Flächen wird aus den Ergebnissen der ersten Bemessungssituation bestimmt. Das Gesamtgewicht der Struktur wird aus der resultierenden Kraft in z-Richtung im Eigengewichtslastfall bestimmt. Aus dieser wird das Gewicht durch Division der Erdbeschleunigung berechnet.
In den letzten beiden Abschnitten werden die Ergebnisse als Exceltabelle abgespeichert und als Diagramm der Spannung zum Gewicht der Struktur geplottet.

"""ParaS-VMStress_Thick-min.py

Related RFEM 6 model: "Para_Plate_Joint.rf6"
Model: https://www.dlubal.com/en/downloads-and-information/examples-and-tutorials/models-to-download/006159

Run a parameter study via the Dlubal RFEM API, collect result metrics, export and plot results.
and chart.

Sections:
- Imports
- Configuration
- Functions
- Main execution
- Plotting
"""
# --------------------- Imports ---------------------
import itertools
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem


# --------------------- Configuration ---------------------
parameter_ranges = {
    't_1': {'min': 8, 'max': 12, 'step': 2},
    't_2': {'min': 4, 'max': 8, 'step': 2},
} # thicknesses in mm
f_y = 235  # yield strength in MPa

# --------------------- Functions ---------------------
def build_parameter_grid(param_ranges):
    """Build a complete mutation dataset from min/max/step values in mm."""
    columns = []
    value_lists = []
    for name, bounds in param_ranges.items():
        min_val = bounds['min']
        max_val = bounds['max']
        step = bounds['step']
        if step <= 0:
            raise ValueError(f"Step width for '{name}' must be positive.")
        if max_val < min_val:
            raise ValueError(f"Max value for '{name}' must be >= min value.")
        values = np.arange(min_val, max_val + step * 0.5, step)
        columns.append(name)
        value_lists.append(values)
    combinations = list(itertools.product(*value_lists))
    return pd.DataFrame(combinations, columns=columns)
       
def set_glpa(dl_app, p_name, p_value,):
    """Sets the global parameter p_name to the specified value p_value"""
    params = dl_app.get_object_list([rfem.global_parameters.GlobalParameter()])
    for p in params:
        if p.name == p_name:
            p.value = p_value
            dl_app.update_object(p)
            check=True
            return True
    if not check:
        raise ValueError(f"  Error: Parameter '{p_name}' not found!")

# --------------------- Main execution ---------------------
# Build parameter set
para_dfo = build_parameter_grid(parameter_ranges)
para_dfo = para_dfo[para_dfo['t_1'] >= para_dfo['t_2']]
para_df = para_dfo / 1000  # mm -> m
print(f"Parameter set:\n {para_dfo}")

# Connect to RFEM with current model and run parameter study
with rfem.Application() as rf_app:
    # Check connection and print model info
    app_info = rf_app.get_application_info()
    print("Application Info:", app_info)
    res_paras=pd.DataFrame(columns=['sigvm','sfz'])
    for i in para_df.index:
            # set global parameters
            print(f"Set mutation {i} with parameters {[x for x in para_df.loc[i]]}")
            for p in para_df.columns:
                # print(f"Set mutation {i} with parameter {p} to {para_df.loc[i, p]}")
                if not set_glpa(rf_app, p, para_df.loc[i, p]):
                    break
            # Remesh
            rf_app.delete_mesh()
            rf_app.generate_mesh(skip_warnings=True)
            # Run calculation
            calculation = rf_app.calculate_all(skip_warnings=True)
            # Results
            if calculation.succeeded:
                # Von Mises stresses
                res_sigvm = rf_app.get_results(
                    results_type=rfem.results.STATIC_ANALYSIS_SURFACES_EQUIVALENT_STRESSES_MISES_MESH_NODES,
                    filters=[
                        rfem.results.ResultsFilter(column_id='loading', filter_expression='DS1'),
                        ]      
                    ).data
                sigvm = res_sigvm['sigma_eqv_mises'].max()/10**6 # N/m^2 -> MPa                
                # self-weight
                sfz = rf_app.get_result_table(
                    table=rfem.results.ResultTable.STATIC_ANALYSIS_SUMMARY_TABLE,
                    loading=rfem.ObjectId(no=1,object_type=rfem.OBJECT_TYPE_LOAD_CASE)
                ).data
                sfz = float(sfz.loc[6].value)  / 10 # N -> kg (g=10 m/s^2 for gravity acceleration)

                res_paras.loc[i] = pd.Series({'sigvm':sigvm,'sfz':sfz})
            else:
                print(f"Calculation failed for mutation {i} with parameters {[x for x in para_df.loc[i]]}")
                res_paras.loc[i] = pd.Series({'sigvm':np.nan,'sfz':np.nan})

# Merge parameters and results for export and plotting
para_out = pd.concat([para_dfo,res_paras], axis=1)

# ---------------------- Export Results ---------------------
para_out.to_excel('./ParaS-VMStress_Thick-min_results.xlsx')

# --------------------- Plotting ---------------------
fig, ax = plt.subplots(figsize=(12, 9))
ax.scatter(para_out['sfz'], para_out['sigvm'], color='tab:blue', s=50, label='Mutations')
y_offset = (para_out['sigvm'].max() - para_out['sigvm'].min()) * 0.03
for x, y, mut, t1, t2 in zip(para_out['sfz'], para_out['sigvm'], para_out.index, para_out['t_1'], para_out['t_2']):
    ax.text(x , y+y_offset, f"M{int(mut)}, {t1:.0f}, {t2:.0f} mm", fontsize=8, va='top', ha='center')
ax.axhline(y=f_y, color='red', linestyle='--', linewidth=1, label=f'Yield Strength ({f_y} MPa)')
ax.set_title('Stress vs. Mass for Parameter Mutations')
ax.set_xlabel('Mass [kg]')
ax.set_ylabel('Von Mises Stress [MPa]')
ax.legend()
fig.tight_layout()
fig.savefig('./ParaS-VMStress_Thick-min_results.png', dpi=200)
plt.show()

Wie sich bei dieser einfachen Parameterstudie zeigt können zur Ausgangskonfiguration mit den Wandstärken 12 und 8 mm, mit Wandstärken von jeweils 8 mm, rund 30 % Material eingespart werden ohne die zulässige Streckgrenze zu übersteigen. Die Ergebnisse können im nachfolgenden Bild eingesehen werden.

Tipp

Die Optimierung durch Reduzierung der Gesamtmasse bei Einhaltung der Bemessungsvorgaben ist ebenfalls ohne jegliche Programmierkenntnisse mittels des Add-Ons "Modelloptimierung" in Kombination mit einem geeigneten Bemessungs-Add-On möglich:

Beispiel 2: Netzkonvergenzstudie

Dieses Beispiel wurde bereits für den Fachartikel zu Netzkonvergenzstudien genutzt. Es handelt sich hierbei um eine Zylinderschale für welche eine lineare Beulanalyse durchgeführt wird. Der Link zum Fachartikel, welcher eine genauere Beschreibung enthält, und das zugehörige Modell können nachfolgend eingesehen werden:


Ziel einer Netzkonvergenzstudie ist, die Netzfeinheit soweit anzupassen, dass keine relevante Änderung der Ergebnisse mehr eintritt, ohne eine zu hohe Elementanzahl zu erreichen um ein wirtschaftliches Arbeiten zu ermöglich. Hierfür wurde bei dem vorliegenden Modell ein globaler Parameter (lfe) eingeführt. Dieser wurde dann der Flächennetzverfeinerung als Netzgröße übergeben.

Tipp

Die globalen Netzeinstellungen können ebenfalls, ohne Nutzung der globalen Parameter, direkt mittels API angepasst werden:

Im nachfolgenden Skript wird die Netzkonvergenzstudie durch schrittweise Veränderung der angestrebten FE-Netzgröße durchgeführt. Dieses entspricht im Grundaufbau dem des ersten Beispiels, weshalb hier nur auf die Unterschiede eingegangen wird.
Um den Einfluss der Netzfeinheit auf die Berechnungszeit zu analysieren, wird diese aus der Prozesszeit des Berechnungsaufrufs bestimmt. Die Kritischen Lastfaktoren werden aus der Stabilitätsanalyse des ersten Lastfalls bestimmt. Daraufhin erfolgt eine Bestimmung der Veränderung des Lastfaktors zum vorigen Schritt um bei einer größeren Variationsbreite einen Abbruch erzwingen zu können. Vor der Ergebnisausgabe erfolgt eine Konvergenzanalyse, welche die relative Veränderung der Ergebnisse bewertet.

"""ParaS-LBA_MeshConvergence-min.py

Related RFEM 6 model: "MeshSensitivity-LBA_AlC.rf6"
Model: https://www.dlubal.com/en/downloads-and-information/examples-and-tutorials/models-to-download/006080

Run a mesh convergence study in RFEM by sweeping the global parameter
`l_fe`, collect critical load factor, compare variants, save and plot results.

Sections:
- Imports
- Configuration
- Main execution
- Plotting
"""

# --------------------- Imports ---------------------
# Standard library
import time

# Third-party
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem

# ------------------- Configuration -------------------
FE_SIZES_MM = [15, 12, 10, 9, 8, 7, 6, 5, 4, 3] # List of finite element sizes to sweep in millimetres
PARAM_NAME = "l_fe"                             # Name of the global parameter in the model
CONVERGENCE_THRESHOLD_PCT = 1.0                 # Convergence threshold in percentage
FCR_ANA = 1065                                  # Analytical critical load factor (for reference in the plot)

# ------------------- Functions -------------------
def set_glpa(dl_app, p_name, p_value,):
    """Sets the global parameter p_name to the specified value p_value"""
    params = dl_app.get_object_list([rfem.global_parameters.GlobalParameter()])
    for p in params:
        if p.name == p_name:
            p.value = p_value
            dl_app.update_object(p)
            check=True
            return True
    if not check:
        raise ValueError(f"  Error: Parameter '{p_name}' not found!")
    
# ------------------- Main execution -------------------
with rfem.Application() as rf_app:
    # Check connection and print model info
    app_info = rf_app.get_application_info()
    print("Application Info:", app_info)

    results = pd.DataFrame(columns=['l_fe_mm', 'n_elements', 'c_time_s', 'f_cr', 'delta_pct'])
    f_prev = None
    i = 0
    for l_fe in FE_SIZES_MM:
        i += 1
        l_fe_m = l_fe / 1000.0  # Convert mm to m for use in RFEM
        if not set_glpa(dl_app=rf_app, p_name=PARAM_NAME, p_value=l_fe_m):
            break
        # Remesh
        rf_app.delete_mesh()
        rf_app.generate_mesh(skip_warnings=True)
        n_elem = rf_app.get_mesh_statistics().surface_2D_finite_elements
        # Run calculation
        t0 = time.perf_counter()
        calculation = rf_app.calculate_all(skip_warnings=True)
        t1 = time.perf_counter()
        c_time = t1 - t0
        # Results
        if calculation.succeeded:
            f_cr = rf_app.get_results(
            results_type=rfem.results.STABILITY_ANALYSIS_CRITICAL_LOAD_FACTORS,
            filters=[
            rfem.results.ResultsFilter(column_id='loading', filter_expression='LC1'),
            ]      
            ).data.loc[0].f

            if f_prev is not None and f_prev != 0 and f_cr is not None:
                delta_pct = abs(f_cr - f_prev) / abs(f_prev) * 100.0
                delta_str = f"{delta_pct:>4.2f}"
            else:
                delta_pct = None
                delta_str = f"{'---':>4}"

            if f_cr is not None:
                print(f"Mutation {i}: l_fe = {l_fe:.1f} mm | n_elements = {n_elem} | f_cr = {f_cr:.2f} | delta = {delta_str} %")
            results.loc[i] = pd.Series({
                'l_fe_mm': l_fe, 'n_elements': n_elem, 'c_time_s': c_time, 
                'f_cr': f_cr, 'delta_pct': delta_pct
                })
            f_prev = f_cr
        else:
            print(f"Calculation failed for mutation {i} with mesh size {l_fe:.1f} mm")
            results.loc[i] = pd.Series({
                'l_fe_mm': l_fe, 'n_elements': n_elem, 'c_time_s': np.nan, 
                'f_cr': np.nan, 'delta_pct': np.nan
                })

# Calculate 1 / n_elements for the second plot
results['inv_n_elements'] = 1.0 / results['n_elements']
# Calculate relative deviation from minimum critical load factor
results['rel_dev_f_cr_min'] = (results['f_cr'] - results['f_cr'].min()) / results['f_cr'].min() * 100

# Convergence analysis
print(f"\n  Convergence analysis (threshold: < {CONVERGENCE_THRESHOLD_PCT} % change):")
convergence_point = None
for (l_mm, n, a, d) in results.loc[:, ['l_fe_mm', 'n_elements', 'f_cr', 'delta_pct']].itertuples(index=False):
    if d is not None and d < CONVERGENCE_THRESHOLD_PCT:
        convergence_point = (l_mm, n, a)
        break
if convergence_point:
    print(f"  Convergence reached at l_fe = {convergence_point[0]:.1f} mm")
    print(f"  f_cr = {convergence_point[2]:.2f}  |  n_elements = {convergence_point[1]}")
else:
    print("  Convergence not reached yet – finer mesh required.")

# ---------------------- Export Results ---------------------
results.to_excel('./ParaS-LBA_MeshConvergence-min_results.xlsx')

# ------------------- Plotting -------------------
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Plot 1: f_cr vs. n_elements
ax1 = axes[0, 0]
ax1.plot(results['n_elements'], results['f_cr'], 'o-', color='tab:blue', markersize=6)
ax1.axhline(y=FCR_ANA, color='red', linestyle='--', linewidth=1, label=f'$F_{{cr,ana}}$ = {FCR_ANA} kN')
ax1.set_xlabel('Number of Elements')
ax1.set_ylabel('Critical Load Factor [kN]')
ax1.set_title('Critical Load Factor vs. Number of Elements')
ax1.grid(True, alpha=0.3)
ax1.legend()

# Plot 2: f_cr vs. 1/n_elements
ax2 = axes[0, 1]
ax2.plot(results['inv_n_elements'], results['f_cr'], 'o-', color='tab:blue', markersize=6)
ax2.axhline(y=FCR_ANA, color='red', linestyle='--', linewidth=1, label=f'$F_{{cr,ana}}$ = {FCR_ANA} kN')
ax2.set_xlabel('1 / Number of Elements')
ax2.set_ylabel('Critical Load Factor [kN]')
ax2.set_title('Critical Load Factor vs. 1 / Number of Elements')
ax2.grid(True, alpha=0.3)
ax2.legend()

# Plot 3: Relative Deviation vs. Calculation Time
ax3 = axes[1, 0]
ax3.plot(results['c_time_s'], results['rel_dev_f_cr_min'], 'o-', color='tab:blue', markersize=6)
ax3.set_xlabel('Calculation Time [s]')
ax3.set_ylabel('Relative Deviation [%]')
ax3.set_title('Relative Deviation vs. Calculation Time')
ax3.grid(True, alpha=0.3)

# Plot 4: Relative Deviation vs. l_fe_mm
ax4 = axes[1, 1]
ax4.plot(results['l_fe_mm'], results['rel_dev_f_cr_min'], 'o-', color='tab:blue', markersize=6)
ax4.set_xlabel('FE Mesh Size [mm]')
ax4.set_ylabel('Relative Deviation [%]')
ax4.set_title('Relative Deviation vs. FE Mesh Size')
ax4.grid(True, alpha=0.3)

fig.suptitle('Mesh Convergence Analysis', fontsize=14, fontweight='bold')
fig.tight_layout()
fig.savefig('./ParaS-LBA_MeshConvergence-min_results.png', dpi=200)
plt.show()

Die nachfolgende Bild zeigt die vom Programmskript erzeugen Diagramme, wie sie ebenfalls im anfangs erwähnten Fachbeitrag dargestellt und genauer bewertet sind.


Autor

Marc ist im Product Engineering mit Schwerpunkt Geotechnik tätig und unterstützt zusätzlich im Customer Support. Seine fachliche Expertise bringt er gezielt in komplexe Fragestellungen ein.

Links


;