Skip to content

Commit a9031a6

Browse files
committed
Initial modal IDF commit
1 parent ecbc970 commit a9031a6

File tree

2 files changed

+98
-250
lines changed

2 files changed

+98
-250
lines changed

funtofem/driver/modal_idf_driver.py

+67-250
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
pass
3636

3737

38-
class FUNtoFEMmodal(FUNtoFEMDriver):
38+
class FUNtoFEMmodalDriver(FUNtoFEMDriver):
3939
def __init__(
4040
self,
4141
solvers,
@@ -46,8 +46,8 @@ def __init__(
4646
reload_funtofem_states=False,
4747
):
4848
"""
49-
Driver that is based on FUNtoFEMnlbgs, but uses an IDF
50-
implementation to solve the coupled problem and a modal
49+
Driver that is based on FUNtoFEMnlbgs, but uses an IDF
50+
implementation to solve the coupled problem and a modal
5151
reconstruction of the structural deformation.
5252
5353
Parameters
@@ -64,7 +64,7 @@ def __init__(
6464
whether to save and reload funtofem states
6565
"""
6666

67-
super(FUNtoFEMmodal, self).__init__(
67+
super(FUNtoFEMmodalDriver, self).__init__(
6868
solvers,
6969
comm_manager=comm_manager,
7070
transfer_settings=transfer_settings,
@@ -88,7 +88,7 @@ def manager(self, hot_start: bool = False, write_designs: bool = True):
8888

8989
def _initialize_adjoint_variables(self, scenario, bodies):
9090
"""
91-
Initialize the adjoint variables
91+
Initialize the adjoint variables stored in the body.
9292
9393
Parameters
9494
----------
@@ -105,8 +105,9 @@ def _initialize_adjoint_variables(self, scenario, bodies):
105105

106106
def _solve_steady_forward(self, scenario, steps=None):
107107
"""
108-
Solve the aerothermoelastic forward analysis using the nonlinear block Gauss-Seidel algorithm.
109-
Aitken under-relaxation is used here for stabilty.
108+
Evaluate the aerothermoelastic forward analysis. Does *not* solve
109+
the coupled problem.
110+
Evaluation path for e.g., aeroelastic is D->A->L->S.
110111
111112
Parameters
112113
----------
@@ -119,127 +120,53 @@ def _solve_steady_forward(self, scenario, steps=None):
119120
assert scenario.steady
120121
fail = 0
121122

122-
# # Determine if we're using the scenario's number of steps or the argument
123-
# if steps is None:
124-
# steps = scenario.steps
123+
# Transfer modal displacements and temperatures from structure to aerodynamic mesh
124+
for body in self.model.bodies:
125+
# Get the modal coordinates on the structure mesh and compute the product
126+
# to get effective disps.
127+
body.convert_modal_disps(scenario)
128+
body.convert_modal_temps(scenario)
129+
# At this stage, we've recreated u_s and T_s
130+
131+
body.transfer_disps(scenario)
132+
body.transfer_temps(scenario)
125133

126-
# flow uncoupled steps (mainly for aerothermal and aerothermoelastic analysis)
127-
for step in range(1, scenario.uncoupled_steps + 1):
128-
# Take a step in the flow solver for (just aerodynamic iteration)
129-
fail = self.solvers.flow.uncoupled_iterate(
130-
scenario, self.model.bodies, step
131-
)
134+
# Solve the flow problem
135+
for step in range(1, scenario.steps + 1):
136+
fail = self.solvers.flow.iterate(scenario, self.model.bodies, step)
132137

133-
# exit with failure if the flow iteration failed
134138
fail = self.comm.allreduce(fail)
135139
if fail != 0:
136140
if self.comm.Get_rank() == 0:
137-
print("Flow solver returned fail flag")
141+
print("Flow solver returned fail flag.")
138142
return fail
139143

140-
# Loop over the NLBGS steps in a loose coupling phase then tight coupling phase
141-
for i, nlbgs_steps in enumerate(
142-
[scenario.steps, scenario.post_tight_forward_steps]
143-
):
144-
if i == 1:
145-
self.solvers.flow.initialize_forward_tight_coupling(scenario)
146-
147-
for step in range(1, nlbgs_steps + 1):
148-
# Transfer displacements and temperatures
149-
for body in self.model.bodies:
150-
body.transfer_disps(scenario)
151-
body.transfer_temps(scenario)
152-
153-
# Take a step in the flow solver
154-
fail = self.solvers.flow.iterate(scenario, self.model.bodies, step)
155-
156-
fail = self.comm.allreduce(fail)
157-
if fail != 0:
158-
if self.comm.Get_rank() == 0:
159-
print("Flow solver returned fail flag")
160-
return fail
161-
162-
# Transfer the loads and heat flux
163-
for body in self.model.bodies:
164-
body.transfer_loads(scenario)
165-
body.transfer_heat_flux(scenario)
166-
167-
if self._debug:
168-
struct_loads = body.get_struct_loads(scenario)
169-
aero_loads = body.get_aero_loads(scenario)
170-
print(f"========================================")
171-
print(f"Inside nlbgs driver, step: {step}")
172-
if struct_loads is not None:
173-
print(
174-
f"norm of real struct_loads: {real_norm(struct_loads)}"
175-
)
176-
print(
177-
f"norm of imaginary struct_loads: {imag_norm(struct_loads)}"
178-
)
179-
print(f"aero_loads: {aero_loads}")
180-
if aero_loads is not None:
181-
print(f"norm of real aero_loads: {real_norm(aero_loads)}")
182-
print(
183-
f"norm of imaginary aero_loads: {imag_norm(aero_loads)}"
184-
)
185-
print(f"========================================\n", flush=True)
186-
187-
# Take a step in the FEM model
188-
fail = self.solvers.structural.iterate(
189-
scenario, self.model.bodies, step
190-
)
191-
192-
fail = self.comm.allreduce(fail)
193-
if fail != 0:
194-
if self.comm.Get_rank() == 0:
195-
print("Structural solver returned fail flag")
196-
return fail
144+
# Transfer forces and heat fluxes from aerodynamic to structure mesh
145+
for body in self.model.bodies:
146+
body.transfer_loads(scenario)
147+
body.transfer_heat_flux(scenario)
197148

198-
# Under-relaxation for solver stability
199-
for body in self.model.bodies:
200-
body.aitken_relax(self.comm, scenario)
149+
# Solve the structure problem
150+
fail = self.solvers.structural.iterate(scenario, self.model.bodies, step)
201151

202-
# check for early stopping criterion, exit if meets criterion
203-
exit_early = False
204-
# only exit early in the loose coupling phase
205-
if (
206-
scenario.early_stopping
207-
and step > scenario.min_forward_steps
208-
and i == 0
209-
):
210-
all_converged = True
211-
for solver in self.solvers.solver_list:
212-
forward_resid = abs(solver.get_forward_residual(step=step))
213-
if forward_resid != 0.0:
214-
if self.comm.rank == 0:
215-
print(
216-
f"f2f scenario {scenario.name}, forward resid = {forward_resid}",
217-
flush=True,
218-
)
219-
forward_tol = solver.forward_tolerance
220-
if forward_resid > forward_tol:
221-
all_converged = False
222-
break
152+
fail = self.comm.allreduce(fail)
153+
if fail != 0:
154+
if self.comm.Get_rank() == 0:
155+
print("Structural solver returned fail flag.")
156+
return fail
223157

224-
if all_converged:
225-
exit_early = True
226-
if exit_early and self.comm.rank == 0:
227-
print(
228-
f"F2F Steady Forward analysis of scenario {scenario.name} exited early"
229-
)
230-
print(
231-
f"\tat step {step} with tolerance {forward_resid} < {forward_tol}",
232-
flush=True,
233-
)
234-
if exit_early:
235-
break
158+
# Additional computation to transpose modal coordinate matrix
159+
for body in self.model.bodies:
160+
body.convert_modal_disps_transpose(scenario)
161+
body.convert_modal_temps_transpose(scenario)
236162

237163
return fail
238164

239165
def _solve_steady_adjoint(self, scenario):
240166
"""
241-
Solve the aeroelastic adjoint analysis using the linear block Gauss-Seidel algorithm.
242-
Aitken under-relaxation for stabilty.
167+
Evaluate the aerothermoelastic adjoint analysis. Does *not* solve
168+
the coupled problem.
169+
Evaluation path for e.g., aeroelastic is S^bar->L^bar->A^bar->D^bar.
243170
244171
Parameters
245172
----------
@@ -255,9 +182,34 @@ def _solve_steady_adjoint(self, scenario):
255182
body.transfer_disps(scenario)
256183
body.transfer_loads(scenario)
257184

185+
body.transfer_temps(scenario)
186+
body.transfer_heat_flux(scenario)
187+
258188
# Initialize the adjoint variables
259189
self._initialize_adjoint_variables(scenario, self.model.bodies)
260190

191+
# Take a step in the structural adjoint
192+
fail = self.solvers.structural.iterate_adjoint(
193+
scenario, self.model.bodies, step=0
194+
)
195+
196+
# Solve the flow adjoint
197+
for step in range(1, scenario.adjoint_steps + 1):
198+
# Get force and heat flux terms for flow solver
199+
for body in self.model.bodies:
200+
body.transfer_loads_adjoint(scenario)
201+
body.transfer_heat_flux_adjoint(scenario)
202+
203+
fail = self.solvers.flow.iterate_adjoint(scenario, self.model.bodies, step)
204+
205+
fail = self.comm.allreduce(fail)
206+
if fail != 0:
207+
if self.comm.Get_rank() == 0:
208+
print("Flow solver returned fail flag.")
209+
return fail
210+
211+
###
212+
261213
# loop over the adjoint NLBGS solver in a loose coupling phase
262214
for i, nlbgs_steps in enumerate(
263215
[scenario.adjoint_steps, scenario.post_tight_adjoint_steps]
@@ -365,68 +317,7 @@ def _solve_unsteady_forward(self, scenario, steps=None):
365317
366318
"""
367319

368-
assert not scenario.steady
369-
fail = 0
370-
371-
if not steps:
372-
if not self.fakemodel:
373-
steps = scenario.steps
374-
else:
375-
if self.comm.Get_rank() == 0:
376-
print(
377-
"No number of steps given for the coupled problem. Using default (1000)"
378-
)
379-
steps = 1000
380-
381-
for time_index in range(1, steps + 1):
382-
# Transfer displacements and temperatures
383-
for body in self.model.bodies:
384-
body.transfer_disps(scenario, time_index)
385-
body.transfer_temps(scenario, time_index)
386-
387-
# Take a step in the flow solver
388-
fail = self.solvers.flow.iterate(scenario, self.model.bodies, time_index)
389-
390-
fail = self.comm.allreduce(fail)
391-
if fail != 0:
392-
if self.comm.Get_rank() == 0:
393-
print("Flow solver returned fail flag")
394-
return fail
395-
396-
# Transfer the loads and heat flux
397-
for body in self.model.bodies:
398-
body.transfer_loads(scenario, time_index)
399-
body.transfer_heat_flux(scenario, time_index)
400-
401-
if self._debug:
402-
struct_loads = body.get_struct_loads(
403-
scenario, time_index=time_index
404-
)
405-
aero_loads = body.get_aero_loads(scenario, time_index=time_index)
406-
print(f"========================================")
407-
print(f"Inside nlbgs driver, step: {time_index}")
408-
if struct_loads is not None:
409-
print(f"norm of real struct_loads: {real_norm(struct_loads)}")
410-
print(
411-
f"norm of imaginary struct_loads: {imag_norm(struct_loads)}"
412-
)
413-
if aero_loads is not None:
414-
print(f"norm of real aero_loads: {real_norm(aero_loads)}")
415-
print(f"norm of imaginary aero_loads: {imag_norm(aero_loads)}")
416-
print(f"========================================\n", flush=True)
417-
418-
# Take a step in the FEM model
419-
fail = self.solvers.structural.iterate(
420-
scenario, self.model.bodies, time_index
421-
)
422-
423-
fail = self.comm.allreduce(fail)
424-
if fail != 0:
425-
if self.comm.Get_rank() == 0:
426-
print("Structural solver returned fail flag")
427-
return fail
428-
429-
return fail
320+
pass
430321

431322
def _solve_unsteady_adjoint(self, scenario):
432323
"""
@@ -446,78 +337,4 @@ def _solve_unsteady_adjoint(self, scenario):
446337
447338
"""
448339

449-
assert not scenario.steady
450-
fail = 0
451-
452-
# how many steps to take
453-
steps = scenario.steps
454-
455-
# Initialize the adjoint variables
456-
self._initialize_adjoint_variables(scenario, self.model.bodies)
457-
458-
# Loop over each time step in the reverse order
459-
for rstep in range(1, steps + 1):
460-
step = steps - rstep + 1
461-
462-
# load current state, affects MELD jacobians in the adjoint matrix (esp. load transfer)
463-
for body in self.model.bodies:
464-
body.transfer_disps(scenario, time_index=step)
465-
body.transfer_temps(scenario, time_index=step)
466-
467-
self.solvers.flow.set_states(scenario, self.model.bodies, step)
468-
# Due to the staggering, we linearize the transfer about t_s^(n-1)
469-
self.solvers.structural.set_states(scenario, self.model.bodies, step - 1)
470-
471-
# take a step in the structural adjoint
472-
fail = self.solvers.structural.iterate_adjoint(
473-
scenario, self.model.bodies, step
474-
)
475-
476-
fail = self.comm.allreduce(fail)
477-
if fail != 0:
478-
if self.comm.Get_rank() == 0:
479-
print("Structural solver returned fail flag")
480-
return fail
481-
482-
for body in self.model.bodies:
483-
body.transfer_loads_adjoint(scenario)
484-
body.transfer_heat_flux_adjoint(scenario)
485-
486-
fail = self.solvers.flow.iterate_adjoint(scenario, self.model.bodies, step)
487-
488-
fail = self.comm.allreduce(fail)
489-
if fail != 0:
490-
if self.comm.Get_rank() == 0:
491-
print("Flow solver returned fail flag")
492-
return fail
493-
494-
for body in self.model.bodies:
495-
body.transfer_disps_adjoint(scenario)
496-
body.transfer_temps_adjoint(scenario)
497-
498-
# extract and accumulate coordinate derivative every step
499-
self._extract_coordinate_derivatives(scenario, self.model.bodies, step)
500-
501-
# end of solve loop
502-
503-
# evaluate the initial conditions
504-
fail = self.solvers.flow.iterate_adjoint(scenario, self.model.bodies, step=0)
505-
fail = self.comm.allreduce(fail)
506-
if fail != 0:
507-
if self.comm.Get_rank() == 0:
508-
print("Flow solver returned fail flag")
509-
return fail
510-
511-
fail = self.solvers.structural.iterate_adjoint(
512-
scenario, self.model.bodies, step=0
513-
)
514-
fail = self.comm.allreduce(fail)
515-
if fail != 0:
516-
if self.comm.Get_rank() == 0:
517-
print("Structural solver returned fail flag")
518-
return fail
519-
520-
# extract coordinate derivative term from initial condition
521-
self._extract_coordinate_derivatives(scenario, self.model.bodies, step=0)
522-
523-
return 0
340+
pass

0 commit comments

Comments
 (0)