Welcome to ZaiZheLe Developer Zone-Open, Learning and Share
Welcome To Ask or Share your Answers For Others


0 votes
in Technique[技术] by (71.8m points)

openmdao - Passing Initial Guesses to Non-Linear Solver as a Function of Outputs of Other Components

I am working on a relatively simple model with 3 Explicit Components (Leg, Cable, BCan) and 1 Implicit (LegCableBal).

The implicit component has a number of residual equations. For most of the state variables, the best initial guess would be a value calculated as Output of the Explicit Components. For example, the final length of a cable element is best approximated by its initial length, which is calculated as 'output' by the Cable component based on the end point coordinates that are inputs to that component. I have been trying to use the guess_nonlinear method for the group, but I do not think I can access the outputs of the other Components, as they may have not executed yet. Within the Explicit Components, the outputs could default to the input values, and that would take care of it, but I do not know whether I can create pass-through (input--output) for output default values.
Is there a strategy for these situations? Or is a full rearrangement of the problem required? Thank you in advance!

enter image description here

Additionally, I am facing a problem with this setup:

self.add_subsystem('Leg',Leg(rho_w=rho_w,concrete=concrete,rebar=rebar, tie=tiesteel,tendon=tendon, nlreinf_sets=self.options['nlreinf_sets'],npreinf_sets=self.options['npreinf_sets'],nsreinf_sets=self.options['nsreinf_sets']), promotes_inputs=['*']) # 

self.add_subsystem('Cable1',Cable(rho_w=rho_w, mat=cable1mat, loss=C_1loss, Cflag=1), promotes_inputs=['L_L0','a','b','c'] )

self.add_subsystem('Cable2',Cable(rho_w=rho_w, mat=cable2mat, loss=C_2loss, Cflag=2), promotes_inputs=['L_L0','a','b','c'] )

self.add_subsystem('BCan',BCan(rho_w=rho_w, canmat=canmat,F_c=self.options['F_c']), promotes_inputs=['*'])
       self.add_subsystem('LegCableBal',LegCableBal(cable1mat=cable1mat,cable2mat=cable2mat,canmat=canmat), promotes_inputs='L_L0','b_eff','B_c','M_c','Fx_c'], promotes_outputs=['*'])

this is within the LegCableBal group, and I thought 'L_L0' (or other promoted inputs) would be assigned for all components when I issue: prob.set_val('L_L0', 31.) in my main where I set all initial values . This is not the case, in fact, L_L0 does not make it into any of the components, however variables such as 'D_L', which is only promoted by Leg, indeed get assigned fine after prob.set_val('D_L', 2.1). This assessment is from within def guess_nonlinear(self, inputs, outputs, residuals) where I am trying to use say outputs['L_Lf']= inputs['Leg.L_L0']. This returns [1.] (should be my assigned 31.), but inputs['Leg.D_L'] is the correct 2.1.

Just a snippet of the ImplicitComponent

N_C1 = outputs['N_C1'] [...]

residuals['N_C1'] = N_C1 - _N_C(E_pC1, A_C1, eps_C1) #Definition of N_C1
residuals['N_C2'] = N_C2 -_N_C(E_pC2, A_C2, eps_C2) #Definition of N_C2

   alpha = np.sqrt(N_e/(E_c * J_Lxxceff)) #definition of alpha

residuals['u_B']=  N_C1 * np.cos(Tht_C1f) - N_e * np.cos(Tht_Lf) + N_C2 * np.cos(Tht_C2f) +F_c    # force balance  along normal to stem's axis

residuals['u_B']= -N_C1 * np.sin(Tht_C1f) + N_e * np.sin(Tht_Lf) + N_C2 * np.sin(Tht_C2f) +B_eff  # force balance  along stem's axis

residuals['N_e']= (E_c * A_Leff * (L_Lf-L_L0) )/L_L0 - ( -N_e - (m_eff*g*np.cos(Tht_Lf))**2 / (12.*E_c*J_Lxxceff) * L_L0**4 * (1./ (L_L0**2 * alpha**2) + 12./ (L_L0**4 * alpha**4) - 24./ (L_L0**5*alpha**5) * np.tan(alpha*L_L0/2.) ) - E_c * A_Leff * (m_eff*g*np.cos(Tht_Lf))**2 / (24.* E_c**2 *J_Lxxceff**2) * L_L0**6  * (1./ (L_L0**4 * alpha**4) - 60./ (L_L0**7 * alpha**7) * np.tan(alpha*L_L0/2) + 24./ (L_L0**6 * alpha**6) + 12.*(1.-np.cos(alpha*L_L0))/(L_L0**6 * alpha**6 * np.sin(alpha*L_L0)**2 )  ) )``````

Here all the terms in these equations are either inputs coming from outputs of other components, or other states (e.g., 'N_C1') for which i have other residual equations.

In guess_nonlinear for the group LegCableBal, I resorted to brutally calling the compute of the other components to get the outputs available.

        #Get some guesses by executing the cable component, horrible but I am not sure how else to pass these initial guesses that should just be the outputs of other components
        Cable1=Cable(rho_w=self.options['rho_w'],loss=self.options['C_1loss'], mat=self.options['cable1mat'],Cflag=1)
        Cable2=Cable(rho_w=self.options['rho_w'],loss=self.options['C_2loss'], mat=self.options['cable2mat'],Cflag=2)

        #Now set the initial guesses
        outputs['L_C1f']= cable1_outs['L_C0']```

Thank you for your time Justin, R

Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Note: based on the N2 you've provided you should not need a group level solver at all! There is no group level coupling, as indicated by the lack of any lower-diagonal connections. Hence there is nothing to converge at the group level, and no solver is needed.

You should be able to do the full convergence inside the solve_nonlinear of the LegCableBal. If you need any extra data to make an initial guess inside that method, you can add extra inputs to that component. If you don't want to write your own solver, you can use OpenMDAO's Nonlinear solvers by assigning them at the component level the same as you would to any group. Alternatively, you can write you own solvers if you like.

You really should not need any guess_nonlinear methods at all here. Something seems amis, based on what you're asking and the N2 you've shown.

You are correct that guess_nonlinear at the group level won't have had any child components called yet, so their outputs will not be available. The guess_nonlinear method at any level of the hierarchy can only expect to have access to the inputs at that level.

So if you need computed values from upstream of some component as part of its guess, then what you need to do is add those values as extra inputs to that component and use it's guess_nonlinear method. This kind of approach is common when using a NewtonSolver, but not so much when using NonlinearBlockGaussSeidel (NLBGS)

With NLBGS, you always have some kind of cyclic connection. The most common case would be something where you have to start with a guess of some value (we'll say length), then at the end of a chain of calcs you end up with a computed copy of that value. You want to iterate till they match. In this case, you don't need any implicit components at all, because you can just connect the computed output directly to the input at the start of the chain. This is how things are done in the Sellar problem in the user guide. In this case, the only value that needs a guess is the one at the start of the compute chain, but you couldn't use the computed value for that because you don't have it yet. So guess_nonlinear at the group level could do some kind of estimation based on any inputs you do know, but component level guess_nonlinear doesn't make sense.

You've said that you do have an implicit component, so I'll assume you need it for some reason. It does some implicit calcs internally in its own solve_nonlinear method. (note: if you have an implicit component, using NLBGS then it MUST have its own solve_nonlinear method it it won't work). If you have put the implicit component as the fist to run in your model, then you might have trouble guessing values for it. You could move it to the end instead, running all the others first, then pass whatever computed values you need to it as additional inputs (even if the inputs are only needed for a "guess"). However in this case you don't need a guess_nonlinear at all, because you can compute the guesses as the first step in your solve_nonlinear calculation.

If you can distill this down into a runable script (or provide a link to a gist or something), I can provide a more concrete answer as to what I would do... but you really should not need a solver at the level you have it in the hierarchy.

Welcome to ZaiZheLe Developer Zone-Open, Learning and Share