26x
002055
2026-06-25

Estudo de Parâmetros via Dlubal API

Este artigo técnico mostra, através de dois exemplos, como é possível realizar automaticamente estudos paramétricos através da definição de parâmetros globais e da API da Dlubal.

Geral

Os estudos paramétricos são um meio sensato e eficaz para adaptar simulações. Os objetivos podem ser de natureza variada: a descoberta de uma estrutura ideal com peso mínimo, garantindo simultaneamente a segurança e poupando recursos, a adaptação da finura da malha para obter resultados de simulação fiáveis, ou a variação das propriedades de material para verificar a sensibilidade da simulação e poder fazer afirmações fiáveis sobre a segurança estrutural. Para estes casos e muitos outros, uma modelação parametrizada é a solução ideal. Para não ter de calcular todas as variantes individualmente, pode ser utilizada a API da Dlubal. Desta forma, o cálculo do estudo paramétrico pode decorrer sem necessidade de supervisão.

Modelação parametrizada

Muitas funções no RFEM 6, RSTAB 9 e RSECTION já são parametrizáveis. Mais informações podem ser encontradas nos respetivos manuais:

Acesso ao modelo através da API

Para poder aceder ao modelo através da API, o pacote correspondente tem de estar instalado. Em alternativa a um editor externo, também pode utilizar diretamente a consola ou o gestor de scripts no programa principal. Mais informações estão disponíveis nos links seguintes.

Exemplo 1: Otimização de espessuras de parede

Este modelo é uma caixa simples com um cilindro cónico sobreposto. Ambas as peças são feitas de chapa de aço com diferentes espessuras de parede. Estas foram parametrizadas para realizar um estudo paramétrico. O objetivo aqui é encontrar a configuração cuja tensão equivalente de Von Mises, com peso mínimo, não exceda o limite de elasticidade. As cargas atuantes são o peso próprio e, num caso de carga de carga variável, uma carga pontual por componentes numa superfície rígida no topo do cone. O apoio é realizado de forma articulada em duas linhas na parte inferior da caixa. O modelo e uma vista da interface de utilizador com a parametrização podem ser consultados a seguir.

O script seguinte está dividido em secções. Na primeira secção, são importadas as bibliotecas utilizadas, seguindo-se a definição das variáveis. Aqui, o limite de elasticidade é indicado para fins de visualização, bem como a gama de variação dos parâmetros a variar, que estão ligados à espessura da chapa. São então definidas funções, que serão utilizadas mais tarde para a construção da matriz de variantes e a alteração dos parâmetros globais no RFEM 6.
A parte principal do programa começa com a construção da matriz de variantes dos parâmetros globais. Para tal, todas as mutações possíveis são geradas a partir das variações de parâmetros definidas por meio do produto cartesiano. Posteriormente, estas são ainda restringidas pela condição de que a espessura da parede na área inferior deve ser maior ou igual à da estrutura superior.
De seguida, é estabelecida a ligação com o RFEM 6 através da API da Dlubal. Neste caso, para o modelo ativo com a chave API depositada no ficheiro de configuração. Ambas as definições também podem ser especificadas na chamada da função.
Agora, é executado um ciclo no qual, sucessivamente, os parâmetros globais no RFEM 6 são adaptados de acordo com a mutação atual, o cálculo é realizado e os resultados são lidos. Para forçar uma adaptação do modelo e a eliminação dos resultados, a malha é eliminada e gerada de novo antes do cálculo. O máximo da tensão equivalente de Von Mises de todas as superfícies é determinado a partir dos resultados da primeira situação de dimensionamento. O peso total da estrutura é determinado a partir da força resultante na direção z no caso de carga de peso próprio. A partir desta, o peso é calculado pela divisão da aceleração gravítica.
Nas duas últimas secções, os resultados são guardados como uma tabela Excel e representados graficamente como um diagrama da tensão em relação ao peso da estrutura.

"""ParaS-VMStress_Thick-min.py

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

Executa um estudo paramétrico através da API RFEM da Dlubal, recolhe métricas de resultado, exporta e traça resultados.
e gráfico.

Secções:
- Imports
- Configuração
- Funções
- Execução principal
- Criação de gráficos
"""
# --------------------- Imports ---------------------
import itertools
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem


# --------------------- Configuração ---------------------
gamas_de_parametros = {
    't_1': {'min': 8, 'max': 12, 'step': 2},
    't_2': {'min': 4, 'max': 8, 'step': 2},
} # espessuras em mm
f_y = 235  # tensão de cedência em MPa

# --------------------- Funções ---------------------
def construir_grelha_parametros(gamas_param):
    """Constrói um conjunto de dados de mutação completo a partir de valores min/max/passo em mm."""
    colunas = []
    listas_valores = []
    for nome, limites in gamas_param.items():
        val_min = limites['min']
        val_max = limites['max']
        passo = limites['step']
        if passo <= 0:
            raise ValueError(f"A largura do passo para '{nome}' deve ser positiva.")
        if val_max < val_min:
            raise ValueError(f"O valor máximo para '{nome}' deve ser >= valor mínimo.")
        valores = np.arange(val_min, val_max + passo * 0.5, passo)
        colunas.append(nome)
        listas_valores.append(valores)
    combinacoes = list(itertools.product(*listas_valores))
    return pd.DataFrame(combinacoes, columns=colunas)
       
def set_glpa(dl_app, p_name, p_value,):
    """Define o parâmetro global p_name para o valor especificado 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"  Erro: Parâmetro '{p_name}' não encontrado!")

# --------------------- Execução principal ---------------------
# Construir conjunto de parâmetros
para_dfo = construir_grelha_parametros(gamas_de_parametros)
para_dfo = para_dfo[para_dfo['t_1'] >= para_dfo['t_2']]
para_df = para_dfo / 1000  # mm -> m
print(f"Conjunto de parâmetros:\n {para_dfo}")

# Ligar ao RFEM com o modelo atual e executar estudo paramétrico
with rfem.Application() as rf_app:
    # Verificar ligação e imprimir informações do modelo
    app_info = rf_app.get_application_info()
    print("Informação da Aplicação:", app_info)
    res_paras=pd.DataFrame(columns=['sigvm','sfz'])
    for i in para_df.index:
            # definir parâmetros globais
            print(f"Definir mutação {i} com parâmetros {[x for x in para_df.loc[i]]}")
            for p in para_df.columns:
                # print(f"Definir mutação {i} com parâmetro {p} para {para_df.loc[i, p]}")
                if not set_glpa(rf_app, p, para_df.loc[i, p]):
                    break
            # Remalhar
            rf_app.delete_mesh()
            rf_app.generate_mesh(skip_warnings=True)
            # Executar cálculo
            calculation = rf_app.calculate_all(skip_warnings=True)
            # Resultados
            if calculation.succeeded:
                # Tensões de 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                
                # peso próprio
                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 para aceleração gravítica)

                res_paras.loc[i] = pd.Series({'sigvm':sigvm,'sfz':sfz})
            else:
                print(f"Cálculo falhou para a mutação {i} com parâmetros {[x for x in para_df.loc[i]]}")
                res_paras.loc[i] = pd.Series({'sigvm':np.nan,'sfz':np.nan})

# Juntar parâmetros e resultados para exportação e criação de gráficos
para_out = pd.concat([para_dfo,res_paras], axis=1)

# ---------------------- Exportar Resultados ---------------------
para_out.to_excel('./ParaS-VMStress_Thick-min_results.xlsx')

# --------------------- Criação de gráficos ---------------------
fig, ax = plt.subplots(figsize=(12, 9))
ax.scatter(para_out['sfz'], para_out['sigvm'], color='tab:blue', s=50, label='Mutações')
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'Limite de elasticidade ({f_y} MPa)')
ax.set_title('Tensão vs. Massa para Mutações de Parâmetros')
ax.set_xlabel('Massa [kg]')
ax.set_ylabel('Tensão de Von Mises [MPa]')
ax.legend()
fig.tight_layout()
fig.savefig('./ParaS-VMStress_Thick-min_results.png', dpi=200)
plt.show()

Como se pode ver neste estudo paramétrico simples, em relação à configuração inicial com as espessuras de parede de 12 e 8 mm, com espessuras de parede de 8 mm cada, é possível poupar cerca de 30 % de material sem exceder o limite de elasticidade admissível. Os resultados podem ser vistos na figura seguinte.

Sugestão

A otimização através da redução da massa total, respeitando as especificações de dimensionamento, também é possível sem qualquer conhecimento de programação usando o add-on "Otimização do modelo" em combinação com um add-on de dimensionamento adequado:

Exemplo 2: Estudo de convergência da malha

Este exemplo já foi utilizado para o artigo técnico sobre estudos de convergência da malha. Trata-se de uma casca cilíndrica para a qual é realizada uma análise de encurvadura linear. O link para o artigo técnico, que contém uma descrição mais detalhada, e o modelo associado podem ser consultados a seguir:


O objetivo de um estudo de convergência da malha é ajustar a finura da malha para que não ocorra mais nenhuma alteração relevante nos resultados, sem atingir um número demasiado elevado de elementos, de modo a permitir um trabalho económico. Para tal, foi introduzido um parâmetro global (lfe) no presente modelo. Este foi depois passado para o refinamento da malha de superfície como tamanho da malha.

Sugestão

As configurações globais da malha também podem ser ajustadas diretamente através da API, sem usar parâmetros globais:

No script seguinte, o estudo de convergência da malha é realizado através da alteração gradual do tamanho de malha EF pretendido. A sua estrutura básica corresponde à do primeiro exemplo, pelo que aqui apenas serão abordadas as diferenças.
Para analisar a influência da finura da malha no tempo de cálculo, este é determinado a partir do tempo de processo da chamada de cálculo. Os fatores de carga críticos são determinados a partir da análise de estabilidade do primeiro caso de carga. De seguida, é efetuada uma determinação da alteração do fator de carga em relação ao passo anterior, para poder forçar uma paragem se a amplitude de variação for maior. Antes da emissão dos resultados, é realizada uma análise de convergência, que avalia a alteração relativa dos resultados.

"""ParaS-LBA_MeshConvergence-min.py

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

Executa um estudo de convergência de malha no RFEM variando o parâmetro global
`l_fe`, recolhe o fator de carga crítico, compara variantes, guarda e traça resultados.

Secções:
- Imports
- Configuração
- Execução principal
- Criação de gráficos
"""

# --------------------- Imports ---------------------
# Biblioteca padrão
import time

# Terceiros
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dlubal.api import rfem

# ------------------- Configuração -------------------
FE_SIZES_MM = [15, 12, 10, 9, 8, 7, 6, 5, 4, 3] # Lista de tamanhos de elementos finitos a variar em milímetros
PARAM_NAME = "l_fe"                             # Nome do parâmetro global no modelo
CONVERGENCE_THRESHOLD_PCT = 1.0                 # Limiar de convergência em percentagem
FCR_ANA = 1065                                  # Fator de carga crítico analítico (para referência no gráfico)

# ------------------- Funções -------------------
def set_glpa(dl_app, p_name, p_value,):
    """Define o parâmetro global p_name para o valor especificado 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"  Erro: Parâmetro '{p_name}' não encontrado!")
    
# ------------------- Execução principal -------------------
with rfem.Application() as rf_app:
    # Verificar ligação e imprimir informações do modelo
    app_info = rf_app.get_application_info()
    print("Informação da Aplicação:", 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  # Converter mm para m para uso no RFEM
        if not set_glpa(dl_app=rf_app, p_name=PARAM_NAME, p_value=l_fe_m):
            break
        # Remalhar
        rf_app.delete_mesh()
        rf_app.generate_mesh(skip_warnings=True)
        n_elem = rf_app.get_mesh_statistics().surface_2D_finite_elements
        # Executar cálculo
        t0 = time.perf_counter()
        calculation = rf_app.calculate_all(skip_warnings=True)
        t1 = time.perf_counter()
        c_time = t1 - t0
        # Resultados
        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"Mutação {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"Cálculo falhou para a mutação {i} com tamanho de malha {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
                })

# Calcular 1 / n_elements para o segundo gráfico
results['inv_n_elements'] = 1.0 / results['n_elements']
# Calcular desvio relativo do fator de carga crítico mínimo
results['rel_dev_f_cr_min'] = (results['f_cr'] - results['f_cr'].min()) / results['f_cr'].min() * 100

# Análise de convergência
print(f"\n  Análise de convergência (limiar: < {CONVERGENCE_THRESHOLD_PCT} % alteração):")
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"  Convergência atingida em l_fe = {convergence_point[0]:.1f} mm")
    print(f"  f_cr = {convergence_point[2]:.2f}  |  n_elements = {convergence_point[1]}")
else:
    print("  Convergência ainda não atingida – é necessária malha mais fina.")

# ---------------------- Exportar Resultados ---------------------
results.to_excel('./ParaS-LBA_MeshConvergence-min_results.xlsx')

# ------------------- Criação de gráficos -------------------
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Gráfico 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('Número de Elementos')
ax1.set_ylabel('Fator de Carga Crítico [kN]')
ax1.set_title('Fator de Carga Crítico vs. Número de Elementos')
ax1.grid(True, alpha=0.3)
ax1.legend()

# Gráfico 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 / Número de Elementos')
ax2.set_ylabel('Fator de Carga Crítico [kN]')
ax2.set_title('Fator de Carga Crítico vs. 1 / Número de Elementos')
ax2.grid(True, alpha=0.3)
ax2.legend()

# Gráfico 3: Desvio Relativo vs. Tempo de Cálculo
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('Tempo de Cálculo [s]')
ax3.set_ylabel('Desvio Relativo [%]')
ax3.set_title('Desvio Relativo vs. Tempo de Cálculo')
ax3.grid(True, alpha=0.3)

# Gráfico 4: Desvio Relativo 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('Tamanho da Malha EF [mm]')
ax4.set_ylabel('Desvio Relativo [%]')
ax4.set_title('Desvio Relativo vs. Tamanho da Malha EF')
ax4.grid(True, alpha=0.3)

fig.suptitle('Análise de Convergência da Malha', fontsize=14, fontweight='bold')
fig.tight_layout()
fig.savefig('./ParaS-LBA_MeshConvergence-min_results.png', dpi=200)
plt.show()

A figura seguinte mostra os diagramas gerados pelo script do programa, tal como são também apresentados e avaliados com mais detalhe no artigo técnico mencionado no início.


Autor

Marc trabalha na área de engenharia de produto, com especialização em geotecnia, e ajuda no apoio ao cliente. A sua experiência profissional é aplicada de forma específica na resolução de questões complexas.

Ligações


;