@@ -33,10 +33,10 @@ Base.@kwdef mutable struct FUSEparameters__ActorFluxMatcher{T<:Real} <: Paramete
3333 xtol:: Entry{T} = Entry {T} (" -" , " Tolerance on the solution vector" ; default= 1E-3 , check= x -> @assert x > 0.0 " must be: xtol > 0.0" )
3434 algorithm:: Switch{Symbol} =
3535 Switch {Symbol} (
36- [:basic_polyalg , :polyalg , :broyden , :anderson , :simple_trust , :trust , :simple , :old_anderson , :custom , :none ],
36+ [:default , : basic_polyalg , :polyalg , :broyden , :anderson , :simple_trust , :simple_dfsane , :trust , :simple , :old_anderson , :custom , :none ],
3737 " -" ,
3838 " Optimizing algorithm used for the flux matching" ;
39- default= :basic_polyalg
39+ default= :default
4040 )
4141 custom_algorithm:: Entry{NonlinearSolve.AbstractNonlinearSolveAlgorithm} =
4242 Entry {NonlinearSolve.AbstractNonlinearSolveAlgorithm} (" -" , " User-defined custom solver from NonlinearSolve" )
@@ -116,7 +116,7 @@ function _step(actor::ActorFluxMatcher{D,P}) where {D<:Real,P<:Real}
116116
117117 # Apply replay profiles if any evolve options are set to :replay
118118 evolve_densities = evolve_densities_dictionary (cp1d, par)
119- if par. evolve_Te == :replay || par. evolve_Ti == :replay || par. evolve_rotation == :replay ||
119+ if par. evolve_Te == :replay || par. evolve_Ti == :replay || par. evolve_rotation == :replay ||
120120 (! isempty (evolve_densities) && any (evolve == :replay for (_, evolve) in evolve_densities))
121121 finalize (step (actor. actor_replay))
122122 end
@@ -160,24 +160,36 @@ function _step(actor::ActorFluxMatcher{D,P}) where {D<:Real,P<:Real}
160160 end
161161
162162 autodiff = NonlinearSolve. ADTypes. AutoFiniteDiff ()
163- if typeof (dd) . parameters[ 1 ] <: ForwardDiff.Dual
163+ if D <: ForwardDiff.Dual
164164 autodiff = NonlinearSolve. ADTypes. AutoForwardDiff ()
165165 end
166166
167+ algorithm = if par. algorithm === :default
168+ if actor. actor_ct. actor_turb. par. model === :TGLFNN
169+ # combines speed and robustness, but needs smooth derivatives
170+ :basic_polyalg
171+ else
172+ # derivative-free method
173+ :simple_dfsane
174+ end
175+ else
176+ par. algorithm
177+ end
178+
167179 # Different defaults for gradient-based methods
168180 if par. max_iterations != 0
169181 max_iterations = abs (par. max_iterations)
170- elseif par . algorithm in (:trust , :simple_trust , :broyden , :polyalg )
182+ elseif algorithm in (:trust , :simple_trust , :broyden , :polyalg )
171183 max_iterations = 50
172184 else
173185 max_iterations = 500
174186 end
175187
176188 res = try
177- if par . algorithm == :none
189+ if algorithm == :none
178190 res = (zero= opt_parameters,)
179191
180- elseif par . algorithm == :simple
192+ elseif algorithm == :simple
181193 ftol = Inf # always use xtol condition as in NonlinearSolve.jl
182194 res = flux_match_simple (actor, opt_parameters, initial_cp1d, z_scaled_history, err_history, max_iterations, ftol, par. xtol, prog)
183195
@@ -198,10 +210,10 @@ function _step(actor::ActorFluxMatcher{D,P}) where {D<:Real,P<:Real}
198210 problem = NonlinearSolve. NonlinearProblem (f!, opt_parameters, initial_cp1d)
199211
200212 # 3. Algorithm selection
201- alg = if par . algorithm == :old_anderson
213+ alg = if algorithm == :old_anderson
202214 NonlinearSolve. NLsolveJL (; method= :anderson , m= 4 , beta= - par. step_size * 5 )
203215
204- elseif par . algorithm == :anderson
216+ elseif algorithm == :anderson
205217 NonlinearSolve. FixedPointAccelerationJL (;
206218 algorithm= :Anderson ,
207219 m= 5 , # history length
@@ -210,29 +222,32 @@ function _step(actor::ActorFluxMatcher{D,P}) where {D<:Real,P<:Real}
210222 replace_invalids= :ReplaceVector
211223 )
212224
213- elseif par . algorithm == :broyden
225+ elseif algorithm == :broyden
214226 NonlinearSolve. Broyden (; autodiff)
215227
216- elseif par . algorithm == :trust
228+ elseif algorithm == :trust
217229 NonlinearSolve. TrustRegion (; autodiff)
218230
219- elseif par . algorithm == :simple_trust
231+ elseif algorithm == :simple_trust
220232 NonlinearSolve. SimpleTrustRegion (; autodiff)
221233
222- elseif par. algorithm === :basic_polyalg
234+ elseif algorithm == :simple_dfsane
235+ NonlinearSolve. SimpleDFSane ()
236+
237+ elseif algorithm === :basic_polyalg
223238 NonlinearSolve. NonlinearSolvePolyAlgorithm ((NonlinearSolve. Broyden (; autodiff),
224239 NonlinearSolve. SimpleTrustRegion (; autodiff)))
225240
226- elseif par . algorithm == :polyalg
241+ elseif algorithm == :polyalg
227242 # Default NonlinearSolve algorithm
228243 NonlinearSolve. FastShortcutNonlinearPolyalg (; autodiff)
229244
230- elseif par . algorithm == :custom
245+ elseif algorithm == :custom
231246 ismissing (par. custom_algorithm) && error (" custom_algorithm must be set to a NonlinearSolve algorithm for algorithm=:custom" )
232247 par. custom_algorithm
233248
234249 else
235- error (" Unsupported algorithm: $(par . algorithm) " )
250+ error (" Unsupported algorithm: $(algorithm) " )
236251 end
237252
238253 # 4. Solve with matching tolerances and iteration limits
@@ -268,7 +283,7 @@ function _step(actor::ActorFluxMatcher{D,P}) where {D<:Real,P<:Real}
268283 end
269284
270285 # detect cases where all optimization calls failed
271- if par . algorithm != :none && isempty (err_history)
286+ if algorithm != :none && isempty (err_history)
272287 flux_match_errors (actor, opt_parameters, initial_cp1d)
273288 error (" FluxMatcher failed" )
274289 end
@@ -825,13 +840,13 @@ function pack_z_profiles(cp1d::IMAS.core_profiles__profiles_1d{D}, par::Override
825840 # append!(z_profiles, z_rot)
826841 # Use TGYRO approach: evolve normalized rotation shear f_rot = (dω/dr) / w0_norm
827842 w0_norm = calculate_w0_norm (cp1d. electrons. temperature[1 ])
828-
843+
829844 # Calculate rotation shear dω/dr
830845 dw_dr = IMAS. gradient (cp1d. grid. rho_tor_norm, cp1d. rotation_frequency_tor_sonic)[cp_gridpoints]
831-
846+
832847 # Convert to normalized rotation shear f_rot
833848 f_rot = dw_dr ./ w0_norm
834-
849+
835850 append! (z_profiles, f_rot)
836851 push! (profiles_paths, (:rotation_frequency_tor_sonic ,))
837852 push! (fluxes_paths, (:momentum_tor ,))
@@ -910,13 +925,13 @@ function unpack_z_profiles(
910925 # cp1d.rotation_frequency_tor_sonic = IMAS.profile_from_z_transport(cp1d.rotation_frequency_tor_sonic .+ 1, cp1d.grid.rho_tor_norm, cp_rho_transport, z_profiles[counter+1:counter+N])
911926 # Use TGYRO approach: convert normalized rotation shear back to rotation frequency
912927 w0_norm = calculate_w0_norm (cp1d. electrons. temperature[1 ])
913-
928+
914929 # Get normalized rotation shear f_rot from z_profiles
915930 f_rot_evolved = z_profiles[counter+ 1 : counter+ N]
916-
931+
917932 # Convert to rotation shear: dω/dr = f_rot * w0_norm
918933 dw_dr_evolved = f_rot_evolved .* w0_norm
919-
934+
920935 # Use the new profile_from_rotation_shear_transport function
921936 cp1d. rotation_frequency_tor_sonic = IMAS. profile_from_rotation_shear_transport (
922937 cp1d. rotation_frequency_tor_sonic,
@@ -1133,7 +1148,7 @@ end
11331148
11341149Calculate TGYRO normalization frequency w0_norm = cs/R₀ where:
11351150
1136- cs = sound speed at axis = √(kB*Te(0)/md)
1151+ cs = sound speed at axis = √(kB*Te(0)/md)
11371152"""
11381153function calculate_w0_norm (Te_axis)
11391154 cs_axis = sqrt (IMAS. mks. k_B * Te_axis / IMAS. mks. m_d) # sound speed at axis
@@ -1149,29 +1164,29 @@ Replay profiles from replay_dd to current dd for channels set to :replay
11491164function _step (replay_actor:: ActorReplay , actor:: ActorFluxMatcher , replay_dd:: IMAS.dd )
11501165 dd = actor. dd
11511166 par = actor. par
1152-
1167+
11531168 time0 = dd. global_time
11541169 cp1d = dd. core_profiles. profiles_1d[time0]
11551170 replay_cp1d = replay_dd. core_profiles. profiles_1d[time0]
11561171 rho = cp1d. grid. rho_tor_norm
1157-
1172+
11581173 # Get the transport grid boundary for blending
11591174 rho_nml = par. rho_transport[end ]
11601175 rho_ped = par. rho_transport[end ]
1161-
1176+
11621177 # Replay electron temperature if set to :replay
11631178 if par. evolve_Te == :replay
11641179 cp1d. electrons. temperature = IMAS. blend_core_edge (replay_cp1d. electrons. temperature, cp1d. electrons. temperature, rho, rho_nml, rho_ped)
11651180 end
1166-
1167- # Replay ion temperature if set to :replay
1181+
1182+ # Replay ion temperature if set to :replay
11681183 if par. evolve_Ti == :replay
11691184 Ti_replay = IMAS. blend_core_edge (replay_cp1d. t_i_average, cp1d. t_i_average, rho, rho_nml, rho_ped)
11701185 for ion in cp1d. ion
11711186 ion. temperature = Ti_replay
11721187 end
11731188 end
1174-
1189+
11751190 # Replay rotation if set to :replay
11761191 if par. evolve_rotation == :replay
11771192 i_nml = IMAS. argmin_abs (rho, rho_nml)
@@ -1181,15 +1196,15 @@ function _step(replay_actor::ActorReplay, actor::ActorFluxMatcher, replay_dd::IM
11811196 ω_core[1 : i_nml] = ω_core[1 : i_nml] .- ω_core[i_nml] .+ ω_edge[i_nml]
11821197 cp1d. rotation_frequency_tor_sonic = ω_core
11831198 end
1184-
1199+
11851200 # Handle density replays
11861201 evolve_densities = evolve_densities_dictionary (cp1d, par)
11871202 if ! isempty (evolve_densities)
11881203 # Replay electron density if set to :replay
11891204 if evolve_densities[:electrons ] == :replay
11901205 cp1d. electrons. density_thermal = IMAS. blend_core_edge (replay_cp1d. electrons. density_thermal, cp1d. electrons. density_thermal, rho, rho_nml, rho_ped; method= :scale )
11911206 end
1192-
1207+
11931208 # Replay ion densities if set to :replay
11941209 for (ion, replay_ion) in zip (cp1d. ion, replay_cp1d. ion)
11951210 ion_symbol = Symbol (ion. label)
@@ -1199,7 +1214,7 @@ function _step(replay_actor::ActorReplay, actor::ActorFluxMatcher, replay_dd::IM
11991214 end
12001215 end
12011216 end
1202-
1217+
12031218 # Ensure quasi neutrality if needed
12041219 for (species, evolve) in evolve_densities
12051220 if evolve == :quasi_neutrality
@@ -1208,6 +1223,6 @@ function _step(replay_actor::ActorReplay, actor::ActorFluxMatcher, replay_dd::IM
12081223 end
12091224 end
12101225 end
1211-
1226+
12121227 return replay_actor
12131228end
0 commit comments