Skip to content

Commit 0fbc3c9

Browse files
committed
Working thermal radiation adjoint (with test solvers)
1 parent 940c98d commit 0fbc3c9

File tree

3 files changed

+31
-71
lines changed

3 files changed

+31
-71
lines changed

funtofem/interface/radiation_interface.py

+20-25
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class RadiationInterface(SolverInterface):
4343
4444
"""
4545

46-
def __init__(self, comm, model, conv_hist=False, complex_mode=False):
46+
def __init__(self, comm, model, conv_hist=False, complex_mode=False, debug=False):
4747
"""
4848
The instantiation of the thermal radiation interface class will populate the model
4949
with the aerodynamic surface mesh, body.aero_X and body.aero_nnodes.
@@ -60,6 +60,9 @@ def __init__(self, comm, model, conv_hist=False, complex_mode=False):
6060
self.comm = comm
6161
self.conv_hist = conv_hist
6262
self.complex_mode = complex_mode
63+
self.debug = debug
64+
65+
self.sigma_sb = 5.670374419e-8
6366

6467
# setup forward and adjoint tolerances
6568
super().__init__()
@@ -206,16 +209,15 @@ def iterate(self, scenario: Scenario, bodies: list[Body], step):
206209
f.write("{0:03d} ".format(step))
207210

208211
for ibody, body in enumerate(bodies, 1):
209-
aero_X = body.get_aero_nodes()
210-
aero_id = body.get_aero_node_ids()
211212
aero_nnodes = body.get_num_aero_nodes()
212213

213214
aero_temps = body.get_aero_temps(scenario, time_index=step)
214-
print(f"aero_temps: {aero_temps}")
215-
heat_rad = self.calc_heat_flux(aero_temps, scenario)
216-
217215
heat_flux = body.get_aero_heat_flux(scenario, time_index=step)
218-
heat_flux += heat_rad
216+
217+
if aero_temps is not None and aero_nnodes > 0:
218+
heat_rad = self.calc_heat_flux(aero_temps, scenario)
219+
220+
heat_flux += heat_rad
219221

220222
return 0
221223

@@ -255,17 +257,16 @@ def iterate_adjoint(self, scenario: Scenario, bodies: list[Body], step):
255257

256258
nfuncs = scenario.count_adjoint_functions()
257259
for ibody, body in enumerate(bodies, 1):
258-
# Get the adjoint-Jacobian product for the heat flux
260+
# Get the adjoint-Jacobian product for the aero temperature
259261
aero_flux_ajp = body.get_aero_heat_flux_ajp(scenario)
260262
aero_nnodes = body.get_num_aero_nodes()
261-
aero_flux = body.get_aero_heat_flux(scenario, time_index=step)
262263
aero_temps = body.get_aero_temps(scenario, time_index=step)
263264

264265
aero_temps_ajp = body.get_aero_temps_ajp(scenario)
265266

266-
if aero_flux_ajp is not None and aero_nnodes > 0:
267-
# Solve the aero heat flux computation adjoint
268-
# dR/dhA^{T} * psi_R = - dQ/dhA^{T} * psi_Q = - aero_flux_ajp
267+
if aero_temps_ajp is not None and aero_nnodes > 0:
268+
# Add contribution to aero_temps_ajp from radiation
269+
# dR/dhR^{T} * psi_R = dA_dhA^{T} * psi_A = - dQ/dhA^{T} * psi_Q = - aero_flux_ajp
269270
psi_R = aero_flux_ajp
270271

271272
rad_heat_deriv = self.calc_heat_flux_deriv(aero_temps, scenario)
@@ -276,13 +277,11 @@ def iterate_adjoint(self, scenario: Scenario, bodies: list[Body], step):
276277
for func in range(nfuncs):
277278
lam[:, func] = psi_R[:, func] * rad_heat_deriv[:]
278279

279-
if not self.complex_mode:
280-
lam = lam.astype(np.double)
281-
282-
for func in range(nfuncs):
283-
print(f"aero_flux_ajp: {aero_flux_ajp[:, func]}")
284-
print(f"lam: {lam[:, func]}")
285-
aero_flux_ajp[:, func] += lam[:, func]
280+
for ifunc in range(nfuncs):
281+
if self.debug and self.comm.rank == 0:
282+
print(f"aero_temps_ajp: {aero_temps_ajp[:, func]}")
283+
print(f"lam: {lam[:, func]}")
284+
aero_temps_ajp[:, ifunc] += lam[:, ifunc]
286285

287286
return
288287

@@ -313,11 +312,10 @@ def calc_heat_flux(self, temps, scenario=None):
313312
F_v = scenario.F_v
314313
T_v = scenario.T_v
315314

316-
sigma_sb = 5.670374419e-8
317315
rad_heat = np.zeros_like(temps, dtype=TransferScheme.dtype)
318316

319317
for indx, temp_i in enumerate(temps):
320-
rad_heat[indx] = -sigma_sb * emis * F_v * (temp_i**4 - T_v**4)
318+
rad_heat[indx] = -self.sigma_sb * emis * F_v * (temp_i**4 - T_v**4)
321319

322320
return rad_heat
323321

@@ -331,16 +329,13 @@ def calc_heat_flux_deriv(self, temps, scenario=None):
331329
if scenario is None:
332330
emis = 0.8
333331
F_v = 1.0
334-
T_v = 0.0
335332
else:
336333
emis = scenario.emis
337334
F_v = scenario.F_v
338-
T_v = scenario.T_v
339335

340-
sigma_sb = 5.670374419e-8
341336
rad_heat_deriv = np.zeros_like(temps, dtype=TransferScheme.dtype)
342337

343338
for indx, temp_i in enumerate(temps):
344-
rad_heat_deriv[indx] = -4 * sigma_sb * emis * F_v * temp_i**3
339+
rad_heat_deriv[indx] = -4 * self.sigma_sb * emis * F_v * temp_i**3
345340

346341
return rad_heat_deriv

funtofem/interface/test_interfaces/_test_struct_solver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def __init__(self, npts, struct_dvs):
9292
* (np.random.rand(self.npts, 3 * self.npts) - 0.5*0)
9393
)
9494
self.c2 = (
95-
0.01
95+
0.5
9696
* thermal_scale
9797
* (np.random.rand(self.npts, len(self.struct_dvs)) - 0.5*0)
9898
)

tests/unit_tests/framework/test_radiationFlux_adjoint.py

+10-45
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ def _setup_model_and_driver(self):
2323

2424
# Create a structural variable
2525
for i in range(5):
26-
thickness = np.random.rand()
26+
thickness = np.random.rand() * 200
2727
svar = Variable(
28-
"thickness %d" % (i), value=thickness, lower=0.01, upper=0.1
28+
"thickness %d" % (i), value=thickness, lower=0.01, upper=10.0
2929
)
3030
plate.add_variable("structural", svar)
3131

@@ -44,7 +44,7 @@ def _setup_model_and_driver(self):
4444
temp = Function("temperature", analysis_type="structural")
4545
steady.add_function(temp)
4646
steady.set_temperature(T_ref=200, T_inf=300)
47-
steady.set_therm_rad_vals()
47+
steady.set_therm_rad_vals(emis=0.8)
4848

4949
# Add the steady-state scenario
5050
model.add_scenario(steady)
@@ -64,46 +64,9 @@ def _setup_model_and_driver(self):
6464
solvers, transfer_settings=transfer_settings, model=model
6565
)
6666

67-
return model, driver
67+
model.print_summary()
6868

69-
# def test_model_derivatives(self):
70-
# model, driver = self._setup_model_and_driver()
71-
72-
# # Check whether to use the complex-step method or now
73-
# complex_step = False
74-
# epsilon = 1e-6
75-
# rtol = 1e-6
76-
# if TransferScheme.dtype == complex:
77-
# complex_step = True
78-
# epsilon = 1e-30
79-
# rtol = 1e-9
80-
81-
# # Manual test of the disciplinary solvers
82-
# scenario = model.scenarios[0]
83-
# bodies = model.bodies
84-
# solvers = driver.solvers
85-
86-
# fail = solvers.flow.test_adjoint(
87-
# "flow",
88-
# scenario,
89-
# bodies,
90-
# epsilon=epsilon,
91-
# complex_step=complex_step,
92-
# rtol=rtol,
93-
# )
94-
# assert fail == False
95-
96-
# solvers.structural.test_adjoint(
97-
# "structural",
98-
# scenario,
99-
# bodies,
100-
# epsilon=epsilon,
101-
# complex_step=complex_step,
102-
# rtol=rtol,
103-
# )
104-
# assert fail == False
105-
106-
# return
69+
return model, driver
10770

10871
def test_coupled_derivatives(self):
10972
model, driver = self._setup_model_and_driver()
@@ -119,7 +82,7 @@ def test_coupled_derivatives(self):
11982

12083
# Solve the forward analysis
12184
driver.solve_forward()
122-
driver.solve_adjoint()
85+
# driver.solve_adjoint()
12386

12487
# Get the functions
12588
functions = model.get_functions()
@@ -134,6 +97,8 @@ def test_coupled_derivatives(self):
13497
driver.solve_adjoint()
13598
grads = model.get_function_gradients()
13699

100+
print(f"Gradients: {grads[0][:]}")
101+
137102
# Set the new variable values
138103
if complex_step:
139104
variables[0].value = variables[0].value + 1j * epsilon
@@ -156,7 +121,7 @@ def test_coupled_derivatives(self):
156121
pass_ = False
157122
if driver.comm.rank == 0:
158123
pass_ = abs(rel_error) < rtol
159-
print("Approximate gradient = ", deriv.real)
124+
print("Approx. gradient (CS) = ", deriv.real)
160125
print("Adjoint gradient = ", grads[0][0].real)
161126
print("Relative error = ", rel_error.real)
162127
print("Pass flag = ", pass_)
@@ -170,7 +135,7 @@ def test_coupled_derivatives(self):
170135
pass_ = False
171136
if driver.comm.rank == 0:
172137
pass_ = abs(rel_error) < rtol
173-
print("Approximate gradient = ", deriv)
138+
print("Approx. gradient (FD) = ", deriv)
174139
print("Adjoint gradient = ", grads[0][0])
175140
print("Relative error = ", rel_error)
176141
print("Pass flag = ", pass_)

0 commit comments

Comments
 (0)