26x
002055
25.6.2026

Parametrická studie pomocí Dlubal API

Tento odborný příspěvek na dvou příkladech ukazuje, jak lze pomocí definice globálních parametrů a Dlubal API provádět automatizované parametrické studie.

Obecné

Parametrické studie jsou užitečným a osvědčeným prostředkem pro přizpůsobení simulací. Cíle zde mohou mít různou povahu: Nalezení optimální konstrukce s minimální hmotností pro současné zajištění bezpečnosti a úsporu zdrojů, přizpůsobení jemnosti sítě pro získání spolehlivých výsledků simulace nebo variace vlastností materiálu pro ověření citlivosti simulace a možnost učinit spolehlivé závěry o bezpečnosti konstrukce. Pro tyto případy a mnoho dalších se nabízí parametrizované modelování. Aby nebylo nutné počítat všechny varianty jednotlivě, lze využít Dlubal API. Tímto způsobem může výpočet parametrické studie probíhat bez nutnosti dohledu.

Parametrizované modelování

Mnoho funkcí v RFEM 6, RSTAB 9 a RSECTION je již parametrizovatelných. Další informace lze nalézt v příslušných manuálech:

Přístup k modelu pomocí API

Aby bylo možné k modelu přistupovat pomocí API, musí být nainstalován příslušný balíček. Místo externího editoru můžete také přímo použít konzoli nebo správce skriptů v hlavním programu. Další informace jsou k dispozici pod následujícími odkazy.

Příklad 1: Optimalizace tloušťky stěny

Tento model představuje jednoduchý kvádr s nasazeným kuželovým válcem. Obě části jsou vyrobeny z ocelové desky s různými tloušťkami stěn. Tyto byly parametrizovány za účelem provedení parametrické studie. Cílem je zde najít konfiguraci, jejíž srovnávací napětí podle von Mises při minimální hmotnosti nepřekračuje mez kluzu. Jako zatížení působí vlastní tíha a v jednom zatěžovacím stavu užitného zatížení komponentní osamělé zatížení na tuhé ploše na horní straně kužele. Uložení je provedeno kloubově na dvou liniích na spodní straně kvádru. Model a pohled na uživatelské prostředí s parametrizací lze vidět níže.

Následující skript je rozdělen do částí. V první části jsou importovány použité knihovny, načež následuje definice proměnných. Zde je pro účely vizualizace uvedena mez kluzu a rozpětí parametrů, které mají být variovány a které jsou spojeny s tloušťkou desky. Následně jsou definovány funkce, které budou v dalším průběhu použity pro sestavení matice variant a pro změnu globálních parametrů v RFEM 6. Hlavní část programu začíná sestavením matice variant globálních parametrů. K tomu jsou z definovaných variací parametrů pomocí kartézského součinu generovány všechny možné mutace. Následně jsou tyto ještě jednou omezeny podmínkou, že tloušťka stěny ve spodní oblasti musí být větší nebo rovna té v horní části. Poté je navázáno spojení s RFEM 6 přes Dlubal API. V tomto případě k aktivnímu modelu s API klíčem uloženým v konfiguračním souboru. Obě nastavení lze specifikovat i ve volání funkce. Nyní probíhá smyčka, ve které jsou postupně upravovány globální parametry v RFEM 6 podle aktuální mutace, je proveden výpočet a výsledky jsou načteny. Aby se vynutilo přizpůsobení modelu a vymazání výsledků, je před výpočtem smazána síť a vygenerována nová. Maximum srovnávacího napětí podle von Mises všech ploch je určeno z výsledků první návrhové situace. Celková hmotnost konstrukce je určena z výsledné síly ve směru z v zatěžovacím stavu vlastní tíhy. Z této síly je pak dělením tíhového zrychlení vypočítána hmotnost. V posledních dvou částech jsou výsledky uloženy jako excelová tabulka a vykresleny jako graf závislosti napětí na hmotnosti konstrukce.


"""ParaS-VMStress_Thick-min.py

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

Spuštění parametrické studie přes Dlubal RFEM API, sběr metrik výsledků, export a vykreslení výsledků.

Části:
- Importy
- Konfigurace
- Funkce
- Hlavní provádění
- Vykreslování
"""
# --------------------- Importy ---------------------
import itertools
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem


# --------------------- Konfigurace ---------------------
parameter_ranges = {
    't_1': {'min': 8, 'max': 12, 'step': 2},
    't_2': {'min': 4, 'max': 8, 'step': 2},
} # tloušťky v mm
f_y = 235  # mez kluzu v MPa

# --------------------- Funkce ---------------------
def build_parameter_grid(param_ranges):
    """Vytvoří úplnou sadu mutací z hodnot min/max/krok v 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"Šířka kroku pro '{name}' musí být kladná.")
        if max_val < min_val:
            raise ValueError(f"Max. hodnota pro '{name}' musí být >= min. hodnota.")
        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,):
    """Nastaví globální parametr p_name na zadanou hodnotu 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"  Chyba: Parametr '{p_name}' nenalezen!")

# --------------------- Hlavní provádění ---------------------
# Sestavení sady parametrů
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"Sada parametrů:\n {para_dfo}")

# Připojení k RFEM s aktuálním modelem a spuštění parametrické studie
with rfem.Application() as rf_app:
    # Kontrola připojení a výpis informací o modelu
    app_info = rf_app.get_application_info()
    print("Informace o aplikaci:", app_info)
    res_paras=pd.DataFrame(columns=['sigvm','sfz'])
    for i in para_df.index:
            # nastavení globálních parametrů
            print(f"Nastavuji mutaci {i} s parametry {[x for x in para_df.loc[i]]}")
            for p in para_df.columns:
                # print(f"Nastavuji mutaci {i} s parametrem {p} na {para_df.loc[i, p]}")
                if not set_glpa(rf_app, p, para_df.loc[i, p]):
                    break
            # Opětovné vytvoření sítě
            rf_app.delete_mesh()
            rf_app.generate_mesh(skip_warnings=True)
            # Spuštění výpočtu
            calculation = rf_app.calculate_all(skip_warnings=True)
            # Výsledky
            if calculation.succeeded:
                # Srovnávací napětí von Mises
                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                
                # vlastní tíha
                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 pro tíhové zrychlení)

                res_paras.loc[i] = pd.Series({'sigvm':sigvm,'sfz':sfz})
            else:
                print(f"Výpočet selhal pro mutaci {i} s parametry {[x for x in para_df.loc[i]]}")
                res_paras.loc[i] = pd.Series({'sigvm':np.nan,'sfz':np.nan})

# Sloučení parametrů a výsledků pro export a vykreslení
para_out = pd.concat([para_dfo,res_paras], axis=1)

# ---------------------- Export výsledků ---------------------
para_out.to_excel('./ParaS-VMStress_Thick-min_results.xlsx')

# --------------------- Vykreslování ---------------------
fig, ax = plt.subplots(figsize=(12, 9))
ax.scatter(para_out['sfz'], para_out['sigvm'], color='tab:blue', s=50, label='Mutace')
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'Mez kluzu ({f_y} MPa)')
ax.set_title('Napětí vs. Hmotnost pro mutace parametrů')
ax.set_xlabel('Hmotnost [kg]')
ax.set_ylabel('Srovnávací napětí von Mises [MPa]')
ax.legend()
fig.tight_layout()
fig.savefig('./ParaS-VMStress_Thick-min_results.png', dpi=200)
plt.show()

Jak ukazuje tato jednoduchá parametrická studie, oproti výchozí konfiguraci s tloušťkami stěn 12 a 8 mm lze při tloušťkách stěn po 8 mm ušetřit přibližně 30 % materiálu, aniž by byla překročena přípustná mez kluzu. Výsledky lze vidět na následujícím obrázku.

Tip

Optimalizace snížením celkové hmotnosti při dodržení návrhových požadavků je rovněž možná bez jakýchkoliv znalostí programování pomocí add-onu "Optimalizace modelu" v kombinaci s vhodným add-onem pro posouzení:

Příklad 2: Studie konvergence sítě

Tento příklad byl již použit pro odborný článek o studiích konvergence sítě. Jedná se zde o válcovou skořepinu, pro kterou je provedena lineární analýza boulení. Odkaz na odborný článek, který obsahuje podrobnější popis, a příslušný model lze vidět níže:

Cílem studie konvergence sítě je přizpůsobit jemnost sítě tak, aby již nedocházelo k relevantní změně výsledků, a přitom nedosáhnout příliš vysokého počtu elementů, aby byla umožněna ekonomická práce. Za tímto účelem byl u předloženého modelu zaveden globální parametr (lfe). Ten byl poté předán zjemnění plošné sítě jako velikost sítě.

Tip

Globální nastavení sítě lze rovněž upravit přímo pomocí API bez použití globálních parametrů:

V následujícím skriptu je studie konvergence sítě provedena postupnou změnou požadované velikosti sítě KP. Její základní struktura odpovídá prvnímu příkladu, proto se zde zaměříme pouze na rozdíly. Pro analýzu vlivu jemnosti sítě na dobu výpočtu je tato určena z času procesu volání výpočtu. Kritické součinitele zatížení jsou určeny z posouzení stability prvního zatěžovacího stavu. Následně je určena změna součinitele zatížení oproti předchozímu kroku, aby bylo možné vynutit přerušení při větší šíři variace. Před výstupem výsledků proběhne analýza konvergence, která vyhodnotí relativní změnu výsledků.


"""ParaS-LBA_MeshConvergence-min.py

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

Spuštění studie konvergence sítě v RFEM změnou globálního parametru
`l_fe`, sběr kritického součinitele zatížení, porovnání variant, uložení a vykreslení výsledků.

Části:
- Importy
- Konfigurace
- Hlavní provádění
- Vykreslování
"""

# --------------------- Importy ---------------------
# Standardní knihovna
import time

# Třetí strany
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem

# ------------------- Konfigurace -------------------
FE_SIZES_MM = [15, 12, 10, 9, 8, 7, 6, 5, 4, 3] # Seznam velikostí konečných prvků pro změnu v milimetrech
PARAM_NAME = "l_fe"                             # Název globálního parametru v modelu
CONVERGENCE_THRESHOLD_PCT = 1.0                 # Práh konvergence v procentech
FCR_ANA = 1065                                  # Analytický kritický součinitel zatížení (pro referenci v grafu)

# ------------------- Funkce -------------------
def set_glpa(dl_app, p_name, p_value,):
    """Nastaví globální parametr p_name na zadanou hodnotu 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"  Chyba: Parametr '{p_name}' nenalezen!")
    
# ------------------- Hlavní provádění -------------------
with rfem.Application() as rf_app:
    # Kontrola připojení a výpis informací o modelu
    app_info = rf_app.get_application_info()
    print("Informace o aplikaci:", 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  # Převod mm na m pro použití v RFEM
        if not set_glpa(dl_app=rf_app, p_name=PARAM_NAME, p_value=l_fe_m):
            break
        # Opětovné vytvoření sítě
        rf_app.delete_mesh()
        rf_app.generate_mesh(skip_warnings=True)
        n_elem = rf_app.get_mesh_statistics().surface_2D_finite_elements
        # Spuštění výpočtu
        t0 = time.perf_counter()
        calculation = rf_app.calculate_all(skip_warnings=True)
        t1 = time.perf_counter()
        c_time = t1 - t0
        # Výsledky
        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"Mutace {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"Výpočet selhal pro mutaci {i} s velikostí sítě {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
                })

# Výpočet 1 / n_elements pro druhý graf
results['inv_n_elements'] = 1.0 / results['n_elements']
# Výpočet relativní odchylky od minimálního kritického součinitele zatížení
results['rel_dev_f_cr_min'] = (results['f_cr'] - results['f_cr'].min()) / results['f_cr'].min() * 100

# Analýza konvergence
print(f"\n  Analýza konvergence (práh: < {CONVERGENCE_THRESHOLD_PCT} % změna):")
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"  Konvergence dosažena při l_fe = {convergence_point[0]:.1f} mm")
    print(f"  f_cr = {convergence_point[2]:.2f}  |  n_elements = {convergence_point[1]}")
else:
    print("  Konvergence dosud nebyla dosažena – je nutná jemnější síť.")

# ---------------------- Export výsledků ---------------------
results.to_excel('./ParaS-LBA_MeshConvergence-min_results.xlsx')

# ------------------- Vykreslování -------------------
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Graf 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('Počet elementů')
ax1.set_ylabel('Kritický součinitel zatížení [kN]')
ax1.set_title('Kritický součinitel zatížení vs. Počet elementů')
ax1.grid(True, alpha=0.3)
ax1.legend()

# Graf 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 / Počet elementů')
ax2.set_ylabel('Kritický součinitel zatížení [kN]')
ax2.set_title('Kritický součinitel zatížení vs. 1 / Počet elementů')
ax2.grid(True, alpha=0.3)
ax2.legend()

# Graf 3: Relativní odchylka vs. Čas výpočtu
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('Čas výpočtu [s]')
ax3.set_ylabel('Relativní odchylka [%]')
ax3.set_title('Relativní odchylka vs. Čas výpočtu')
ax3.grid(True, alpha=0.3)

# Graf 4: Relativní odchylka 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('Velikost sítě KP [mm]')
ax4.set_ylabel('Relativní odchylka [%]')
ax4.set_title('Relativní odchylka vs. Velikost sítě KP')
ax4.grid(True, alpha=0.3)

fig.suptitle('Analýza konvergence sítě', fontsize=14, fontweight='bold')
fig.tight_layout()
fig.savefig('./ParaS-LBA_MeshConvergence-min_results.png', dpi=200)
plt.show()

Následující obrázek ukazuje diagramy vytvořené programovým skriptem, tak, jak jsou rovněž zobrazeny a podrobněji vyhodnoceny v odborném článku zmíněném na začátku.


Autor

Marc působí v Product Engineering se zaměřením na geotechniku a navíc podporuje i Customer Support. Své odborné znalosti cíleně uplatňuje při řešení složitých otázek.

Odkazy


;