Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
5d18dca
Cleaned up CommonMethods
EvertBunschoten May 19, 2026
b6e9bc8
Created separete flamelet solver classes
EvertBunschoten May 26, 2026
a789f62
updated method name for generating a single flamelet
EvertBunschoten May 26, 2026
767fb1b
Added dictionary for available flamelet solvers
EvertBunschoten May 26, 2026
30f9df0
Added equilibrium solver and restructured methods for improved readab…
EvertBunschoten May 26, 2026
6441286
Commented out redundant sections, still needs proper testing
EvertBunschoten May 26, 2026
b44806c
New setters for flamelet types
EvertBunschoten May 26, 2026
e2a209c
Include flamelet types in properties
EvertBunschoten May 26, 2026
7180401
Added interpolated burner flame solver
EvertBunschoten Jun 1, 2026
20db0b9
Merge branch 'develop' of https://github.com/su2code/SU2_DataMiner in…
EvertBunschoten Jun 1, 2026
297d0bb
Setter function for flamelet types
EvertBunschoten Jun 1, 2026
8deba53
Removed commented out sections
EvertBunschoten Jun 1, 2026
cd12dd4
Ensured correct restart behavior, added counter-flow diffusion flame …
EvertBunschoten Jun 1, 2026
a4b674c
precommit
EvertBunschoten Jun 1, 2026
0b67e83
added counter-flow diffusion flame to available options
EvertBunschoten Jun 2, 2026
4064013
Backwards compatibility
EvertBunschoten Jun 2, 2026
afafcdb
Prevent config settings from being overwritten by the ComputeBoundary…
EvertBunschoten Jun 2, 2026
63f2c97
Added specifiers for type of flamelet
EvertBunschoten Jun 2, 2026
526589e
Update regression tests
EvertBunschoten Jun 2, 2026
6dd15cf
code cleaning for progress variable optimizer
EvertBunschoten Jun 5, 2026
2abe18e
Use optimizer function for config update
EvertBunschoten Jun 5, 2026
7aca259
store flamelet solutions as yaml for 1D solutions
EvertBunschoten Jun 5, 2026
06797b5
Fixed bug where data loaded from solution was not properly passed to …
EvertBunschoten Jun 17, 2026
f3e259e
precommit
EvertBunschoten Jun 17, 2026
e54f9d1
Compatibility with new flamelet solver structure
EvertBunschoten Jun 17, 2026
644acc6
Fixed restart bug for flamelet solvers
EvertBunschoten Jun 22, 2026
ea2c347
Merge branch 'code_cleaning' of https://github.com/su2code/SU2_DataMi…
EvertBunschoten Jun 22, 2026
e79565d
Fixed restart bug for counter-flow diffusion flamelets
EvertBunschoten Jun 22, 2026
5fbe4a6
Solve strained flamelets for reactant temperature range
EvertBunschoten Jun 22, 2026
adb02f5
Process non-premixed flamelets and clip solution to mixture fraction …
EvertBunschoten Jun 22, 2026
8740b2b
Fixed bug where counter-flow diffusion flames were incorrectly ignored
EvertBunschoten Jun 22, 2026
5cfb705
Include plot label in flamelet types
EvertBunschoten Jun 22, 2026
ba54efd
Flamelet data plotter made compatible with new flamelet solver structure
EvertBunschoten Jun 22, 2026
38dfa8e
Added banner to flamelet solvers file
EvertBunschoten Jun 22, 2026
bdc419b
precommit
EvertBunschoten Jun 22, 2026
b9b7d95
precommit
EvertBunschoten Jun 22, 2026
961f025
Ommit non-premixed flamelets from optimization
EvertBunschoten Jun 22, 2026
b60907a
Update number of nodes per flamelet
EvertBunschoten Jun 23, 2026
1e01e0e
Fixed bug where flamelet types was reset in config
EvertBunschoten Jun 23, 2026
dd5a0ed
Default number of nodes per flamelet should be set to None
EvertBunschoten Jun 23, 2026
fcded18
Detect automatic resolution determination based on number of nodes li…
EvertBunschoten Jun 23, 2026
a7d9096
Update path for flamelet regression test
EvertBunschoten Jun 23, 2026
c474cdd
Update reference files for regression tests
EvertBunschoten Jun 23, 2026
d5dffed
fix whitespace
EvertBunschoten Jun 23, 2026
b544256
remove unused modules, move counter-flow diffusion flame calculation …
EvertBunschoten Jul 10, 2026
92498af
Fixed a bug where grid refinement settings were overwritten in the ba…
EvertBunschoten Jul 10, 2026
9085828
Enable compatibility for flamelet data plotting with new flamelet sol…
EvertBunschoten Jul 10, 2026
12db0c1
fix trailing white spaces
EvertBunschoten Jul 10, 2026
3e22677
Loading the flamelet solution made backwards compatible with older ve…
EvertBunschoten Jul 10, 2026
f69b062
Adjusted file path for flamelet data file to test in MLPCpp compatibi…
EvertBunschoten Jul 10, 2026
5d66310
Updated reference for 1D flamelet regression test
EvertBunschoten Jul 14, 2026
81c2ddd
Updated training history
EvertBunschoten Jul 14, 2026
7169efb
Added commentary
EvertBunschoten Jul 14, 2026
7d74742
Additional diagnostics for ML-FGM regression test
EvertBunschoten Jul 14, 2026
f70e2e7
Temporarily only run ML-FGM test
EvertBunschoten Jul 14, 2026
bdca5e8
Diagnostics for ML-FGM training test
EvertBunschoten Jul 14, 2026
3fa0fdd
Report training data size
EvertBunschoten Jul 14, 2026
01ea44e
Print first 100 samples of the full training data set
EvertBunschoten Jul 14, 2026
fcf7c8d
sort flamelet data files before extracting training data
EvertBunschoten Jul 14, 2026
51d8583
Updated reference training history
EvertBunschoten Jul 14, 2026
7b188ec
precommit
EvertBunschoten Jul 14, 2026
69dbca5
re-enable the other regression tests
EvertBunschoten Jul 14, 2026
43a48f1
Added comments to adiabatic and burner-stabilized flamelet solvers
EvertBunschoten Jul 14, 2026
12582e9
Update Common/DataDrivenConfig.py
EvertBunschoten Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 77 additions & 62 deletions Common/CommonMethods.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,68 +31,84 @@ def ComputeLewisNumber(flame:ct.Solution):
Le_species = flame.thermal_conductivity/flame.cp_mass/flame.density_mass/(flame.mix_diff_coeffs+1e-15)
return Le_species

def avg_Le_start_end(Le_sp:np.ndarray):
Le_av = 0.5*(Le_sp[0] + Le_sp[-1])
return Le_av

def avg_Le_arythmic(Le_sp:np.ndarray):
Le_av = np.average(Le_sp)
return Le_av

def avg_Le_min_max(Le_sp:np.ndarray):
Le_av = 0.5*(np.min(Le_sp)+np.max(Le_sp))
return Le_av

def avg_Le_unity(Le_sp:np.ndarray):
Le_av = np.ones(np.shape(Le_sp))
return Le_av

def avg_Le_const(Le_sp:np.ndarray, Le_const:float):
Le_av = Le_const * np.ones(np.shape(Le_sp))
return Le_av

def avg_Le_local(Le_sp:np.ndarray):
return Le_sp


def GetReferenceData(dataset_file:str, x_vars:list[str], train_variables:list[str],dtype=np.float32):
"""Read csv file and collect input-output pairs into a numpy array of a set data type.

:param dataset_file: file path name to csv file
:type dataset_file: str
:param x_vars: controlling variables
:type x_vars: list[str]
:param train_variables: output variables
:type train_variables: list[str]
:param dtype: data type by which to output data arrays, defaults to np.float32
:type dtype: dtype, optional
:return: data arrays for controlling variable and dependent variable data.
:rtype: np.ndarray[float],np.ndarray[float]
def avg_Le_const(from_flamelet_solution:np.ndarray, replacment_value:float):
"""Replace local solution of the species Lewis number with a constant value

:param from_flamelet_solution: species Lewis number from flamelet solution
:type from_flamelet_solution: np.ndarray
:param replacment_value: constant species Lewis number
:type replacment_value: float
:return: array with constant species Lewis numbers
:rtype: np.ndarray[float]
"""

# Open data file and get variable names from the first line
fid = open(dataset_file, 'r')
line = fid.readline()
fid.close()
line = line.strip()
line_split = line.split(',')
if(line_split[0][0] == '"'):
varnames = [s[1:-1] for s in line_split]
else:
varnames = line_split

# Get indices of controlling and train variables
iVar_x = [varnames.index(v) for v in x_vars]
iVar_y = [varnames.index(v) for v in train_variables]

# Retrieve respective data from data set
D = np.loadtxt(dataset_file, delimiter=',', skiprows=1, dtype=dtype)
X_data = D[:, iVar_x]
Y_data = D[:, iVar_y]

return X_data, Y_data

def write_SU2_MLP(file_out:str, weights:list[np.ndarray], biases:list[np.ndarray],activation_function_name:str,train_vars:list[str], controlling_vars:list[str], scaler_function:str,scaler_function_vals_in:list[list[float]],scaler_function_vals_out:list[float],additional_header_info_function=None):
const_species_lewis_number = replacment_value * np.ones(np.shape(from_flamelet_solution))
return const_species_lewis_number

class readStateDataFromFile:
__dataset_filepath:str=""
__controlling_variable_names:list[str]=[]
__state_variable_names:list[str]=[]
__dtype:type=np.float32
__varnames_from_file:list[str]=None

def __new__(self, dataset_filepath:str, controlling_variables:list[str], state_variables:list[str],dtype=np.float32):
"""Load data for a set of controlling variables and for a set of state variables from a csv file.

:param dataset_filepath: _description_
:type dataset_filepath: str
:param controlling_variables: _description_
:type controlling_variables: list[str]
:param state_variables: _description_
:type state_variables: list[str]
:param dtype: _description_, defaults to np.float32
:type dtype: _type_, optional
:return: _description_
:rtype: _type_
"""
self.__dataset_filepath=dataset_filepath
self.__controlling_variable_names = controlling_variables.copy()
self.__state_variable_names = state_variables.copy()
self.__dtype=dtype

self.__varnames_from_file = self.__getLabelsFromCSVFile(self)
self.__checkIfVarInFile(self, self.__controlling_variable_names)
self.__checkIfVarInFile(self, self.__state_variable_names)

return self.__loadDataFromFile(self)

def __getLabelsFromCSVFile(self):
fid = open(self.__dataset_filepath, 'r')
first_line = fid.readline()
fid.close()
line_split = first_line.strip().split(',')
varnames = [varname.strip("\"").strip("\'") for varname in line_split]
return varnames

def __checkIfVarInFile(self, varnames_to_check:list[str]):
unsupported_vars = []
for var in varnames_to_check:
if var not in self.__varnames_from_file:
unsupported_vars.append(var)
if any(unsupported_vars):
raise Exception("The variables %s were not found in the file" % (",".join(var for var in unsupported_vars)))
return

def __loadDataFromFile(self):
index_controlling_variables = [self.__varnames_from_file.index(v) for v in self.__controlling_variable_names]
index_state_variables = [self.__varnames_from_file.index(v) for v in self.__state_variable_names]

# Retrieve respective data from data set
data_from_csv_file = np.loadtxt(self.__dataset_filepath, delimiter=',', skiprows=1, dtype=self.__dtype)
if data_from_csv_file.shape[1] != len(self.__varnames_from_file):
raise Exception("Number of labels is not equal to the number of data entries in %s" % self.__dataset_filepath)

data_controlling_variables = data_from_csv_file[:, index_controlling_variables]
data_state_variables = data_from_csv_file[:, index_state_variables]
return data_controlling_variables, data_state_variables



def writeMLPForSU2(file_out:str, weights:list[np.ndarray], biases:list[np.ndarray],activation_function_name:str,train_vars:list[str], controlling_vars:list[str], scaler_function:str,scaler_function_vals_in:list[list[float]],scaler_function_vals_out:list[float],additional_header_info_function=None):
"""Write ASCII file that can be loaded into SU2 through the MLPCpp submodule containing the network weights and biases.

:param file_out: MLP output file name
Expand All @@ -117,7 +133,6 @@ def write_SU2_MLP(file_out:str, weights:list[np.ndarray], biases:list[np.ndarray
:type additional_header_info_function: function, optional
"""
n_layers = len(weights)+1

weights_for_output = weights
biases_for_output = biases

Expand Down
4 changes: 2 additions & 2 deletions Common/Config_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

from Common.Properties import DefaultProperties, ActivationFunctionOptions
from Common.CommonMethods import write_SU2_MLP
from Common.CommonMethods import writeMLPForSU2

class Config:
"""Base class for the SU2 DataMiner configuration.
Expand Down Expand Up @@ -434,7 +434,7 @@ def WriteSU2MLP(self, file_name_out:str):
:param file_name_out: MLP file name
:type file_name_out: str
"""
return write_SU2_MLP(file_name_out,\
return writeMLPForSU2(file_name_out,\
weights=self._MLP_weights,\
biases=self._MLP_biases,\
activation_function_name=self._activation_function,\
Expand Down
85 changes: 64 additions & 21 deletions Common/DataDrivenConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
#---------------------------------------------------------------------------------------------#
# Importing DataMiner classes and functions
#---------------------------------------------------------------------------------------------#
from Common.Properties import DefaultSettings_NICFD, DefaultSettings_FGM
from Common.Properties import DefaultSettings_NICFD, DefaultSettings_FGM, FlameletSolverOptions
from Common.Config_base import Config
from Common.CommonMethods import *

Expand Down Expand Up @@ -787,6 +787,8 @@ class Config_FGM(Config):
__generate_equilibrium:bool = DefaultSettings_FGM.include_equilibrium # Generate chemical equilibrium data
__generate_counterflames:bool = DefaultSettings_FGM.include_counterflames # Generate counter-flow diffusion flamelets.

__flamelet_types:list[str] = [FlameletSolverOptions[0]]

__write_MATLAB_files:bool = False # Write TableGenerator compatible flamelet files.

gas:ct.Solution = None # Cantera solution object.
Expand All @@ -801,7 +803,7 @@ class Config_FGM(Config):

__lookup_variables:list[str] = ["Heat_Release"] # Extra look-up variables to read from flamelet data

__Np_per_flamelet:int = 2**DefaultSettings_FGM.batch_size_exponent # Number of data points to interpolate from flamelet data.
__Np_per_flamelet:int = None # Number of data points to interpolate from flamelet data.

# MLP output groups and architecture information.
__MLP_output_groups:list[list[str]] = None # Output variables for each MLP.
Expand Down Expand Up @@ -1489,6 +1491,45 @@ def GetMixtureStatus(self):
"""
return self.__run_mixture_fraction

def includeFlameletType(self, flamelet_type:str="FREEFLAME"):

if flamelet_type not in FlameletSolverOptions:
raise Exception("%s is not recognized as a viable flamelet type" % flamelet_type)

if flamelet_type not in self.__flamelet_types:
self.__flamelet_types.append(flamelet_type)

return

def setFlameletTypes(self, flamelet_types:list[str]):
self.__flamelet_types = flamelet_types.copy()
self.__checkFlameletTypes()
return

def __checkFlameletTypes(self):
self.__generate_burnerflames = "BURNERFLAME" in self.__flamelet_types
self.__generate_freeflames = "FREEFLAME" in self.__flamelet_types
self.__generate_counterflames = "COUNTERFLAME" in self.__flamelet_types
self.__generate_equilibrium = "EQUILIBRIUM" in self.__flamelet_types
self.__generate_extra_interpolated_burnerflames = "BURNERFLAME_INT" in self.__flamelet_types
return

def excludeFlameletType(self, flamelet_type:str):
if flamelet_type not in FlameletSolverOptions:
raise Exception("%s is not recognized as a viable flamelet type" % flamelet_type)

if flamelet_type in self.__flamelet_types:
self.__flamelet_types.remove(flamelet_type)

self.__checkFlameletTypes()
if len(self.__flamelet_types) == 0:
raise Exception("At least one flamelet type should be included in the manifold")

return

def getFlameletTypes(self):
return self.__flamelet_types

def RunFreeFlames(self, input:bool=DefaultSettings_FGM.include_freeflames):
"""
Include adiabatic free flame data in the manifold.
Expand All @@ -1498,8 +1539,8 @@ def RunFreeFlames(self, input:bool=DefaultSettings_FGM.include_freeflames):

"""
self.__generate_freeflames = input
self.includeFlameletType("FREEFLAME")
return

def RunBurnerFlames(self, input:bool=DefaultSettings_FGM.include_burnerflames):
"""
Include burner-stabilized flame data in the manifold.
Expand All @@ -1509,6 +1550,7 @@ def RunBurnerFlames(self, input:bool=DefaultSettings_FGM.include_burnerflames):

"""
self.__generate_burnerflames = input
self.includeFlameletType("BURNERFLAME")
return

def RunEquilibrium(self, input:bool=DefaultSettings_FGM.include_equilibrium):
Expand All @@ -1520,6 +1562,7 @@ def RunEquilibrium(self, input:bool=DefaultSettings_FGM.include_equilibrium):

"""
self.__generate_equilibrium = input
self.includeFlameletType("EQUILIBRIUM")
return

def RunCounterFlames(self, input:bool=DefaultSettings_FGM.include_counterflames):
Expand All @@ -1531,6 +1574,7 @@ def RunCounterFlames(self, input:bool=DefaultSettings_FGM.include_counterflames)

"""
self.__generate_counterflames = input
self.includeFlameletType("COUNTERFLAME")
return

def RunExtraInterpolatedBurnerFlames(self, input:bool=True):
Expand All @@ -1542,6 +1586,7 @@ def RunExtraInterpolatedBurnerFlames(self, input:bool=True):

"""
self.__generate_extra_interpolated_burnerflames = input
self.includeFlameletType("INT_BURNERFLAME")
return

def GenerateFreeFlames(self):
Expand Down Expand Up @@ -1877,7 +1922,7 @@ def AverageLewisNumber(self, Le_sp:np.ndarray, iSp:int):
Le_av = self.__Le_avg_method(Le_sp)
return Le_av

def SetAverageLewisNumbers(self, mixture_status:float=None, reactant_temperature:float=None):
def SetAverageLewisNumbers(self, equivalence_ratio:float=None, reactant_temperature:float=None):
"""Define the constant specie Lewis numbers for a given mixture status and reactant temperature.

:param mixture_status: equivalence ratio or mixture fraction, defaults to the average of the manifold bounds.
Expand All @@ -1886,12 +1931,10 @@ def SetAverageLewisNumbers(self, mixture_status:float=None, reactant_temperature
:type reactant_temperature: float, optional
:raises Exception: if negative mixture status value or temperature is provided.
"""
if mixture_status != None:
if mixture_status < 0:
if equivalence_ratio:
if equivalence_ratio < 0:
raise Exception("Mixture status value should be positive.")
if self.__run_mixture_fraction and mixture_status > 1:
raise Exception("Mixture fraction should be between zero and one.")
if reactant_temperature != None:
if reactant_temperature:
if reactant_temperature < 0:
raise Exception("Reactant temperature should be positive.")

Expand All @@ -1905,19 +1948,20 @@ def SetAverageLewisNumbers(self, mixture_status:float=None, reactant_temperature
T_reactants = reactant_temperature
self.__Le_avg_T_unb = T_reactants

if (mixture_status == None):
if (self.__Le_avg_eq_ratio == None):
mixture_status_gas = 0.5*(self.__mix_status_lower + self.__mix_status_upper)
if not equivalence_ratio:
if not self.__Le_avg_eq_ratio:
if self.__run_mixture_fraction:
mixture_status_gas = 0.5
else:
mixture_status_gas = 0.5*(self.__mix_status_lower + self.__mix_status_upper)
else:
mixture_status_gas = self.__Le_avg_eq_ratio
else:
mixture_status_gas = mixture_status
mixture_status_gas = equivalence_ratio
self.__Le_avg_eq_ratio = mixture_status_gas
self.gas.TP =T_reactants, DefaultSettings_FGM.pressure
if self.__run_mixture_fraction:
self.gas.set_mixture_fraction(self.__Le_avg_eq_ratio, self.__fuel_string, self.__oxidizer_string)
else:
self.gas.set_equivalence_ratio(self.__Le_avg_eq_ratio, self.__fuel_string, self.__oxidizer_string)

self.gas.set_equivalence_ratio(self.__Le_avg_eq_ratio, self.__fuel_string, self.__oxidizer_string)

Le_reactants = ComputeLewisNumber(self.gas)
self.gas.equilibrate("HP")
Expand Down Expand Up @@ -2370,7 +2414,7 @@ def UpdateMLPHyperParams(self, trainer):
def WriteSU2MLP(self, file_name_out:str, group_idx:int=-1):
if group_idx == -1:
for iGroup in range(self.GetNMLPOutputGroups()):
write_SU2_MLP(file_name_out+"_"+str(iGroup+1),\
writeMLPForSU2(file_name_out+"_"+str(iGroup+1),\
weights=self._MLP_weights[iGroup],\
biases=self._MLP_biases[iGroup],\
activation_function_name=self.__activation_function[iGroup],\
Expand All @@ -2382,7 +2426,7 @@ def WriteSU2MLP(self, file_name_out:str, group_idx:int=-1):
additional_header_info_function=self.__write_progress_variable_definition)
else:
iGroup = group_idx
write_SU2_MLP(file_name_out+"_"+str(iGroup+1),\
writeMLPForSU2(file_name_out+"_"+str(iGroup+1),\
weights=self._MLP_weights[iGroup],\
biases=self._MLP_biases[iGroup],\
activation_function_name=self.__activation_function[iGroup],\
Expand All @@ -2404,7 +2448,7 @@ def __writeNULLMLP(self, header):
scaler_function="robust"
scaler_function_vals_in = self._scaler_function_vals_in[0]
scaler_function_vals_out = np.zeros([1,2])
write_SU2_MLP(header+"_NULL",weights=weights,biases=biases,activation_function_name=activation_function,controlling_vars=control_vars,train_vars=train_vars,scaler_function=scaler_function,scaler_function_vals_in=scaler_function_vals_in,scaler_function_vals_out=scaler_function_vals_out,additional_header_info_function=self.__write_progress_variable_definition)
writeMLPForSU2(header+"_NULL",weights=weights,biases=biases,activation_function_name=activation_function,controlling_vars=control_vars,train_vars=train_vars,scaler_function=scaler_function,scaler_function_vals_in=scaler_function_vals_in,scaler_function_vals_out=scaler_function_vals_out,additional_header_info_function=self.__write_progress_variable_definition)
return
def __write_progress_variable_definition(self, fid):
fid.write("Progress variable definition: " + "+".join(("%+.6e*%s" % (w, s)) for w, s in zip(self.__pv_weights, self.__pv_definition)))
Expand All @@ -2419,7 +2463,6 @@ def SaveConfig(self):
:type file_name: str
"""
self.__SynchronizeSettings()

file = open(self._config_name+'.cfg','wb')
pickle.dump(self, file)
file.close()
Expand Down
1 change: 1 addition & 0 deletions Common/Properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,4 @@ class DefaultSettings_FGM(DefaultProperties):
"sigmoid" : tf.keras.activations.sigmoid,\
"swish" : tf.keras.activations.swish}

FlameletSolverOptions:list[str] = ["FREEFLAME", "BURNERFLAME", "EQUILIBRIUM", "INT_BURNERFLAME","COUNTERFLAME"]
Loading
Loading