[Ifeffit] Hard restraints?

Scott Calvin SCalvin at slc.edu
Wed Feb 9 11:53:28 CST 2005


Hi Matt,

>
>In a simplified case, you'd like to constrain two coordination
>numbers to be positive _and_ to add up to some value, right? 
>   guess n1_var = 5
>   def   n1     = abs(n1_var)
>   def   n2     = 12 - n1
>
>or, if you're worried about n1 > 12, n2<0, you could do:
>   guess n1_var = 5
>   def   n1     = max(0,min(n1_var,12))
>   def   n2     = 12 - n1
>
>Is that good enough, or am I not seeing the whole complicated
>picture?

That's a very good (less complicated) example of what I'm thinking 
of. In the example you just gave, you have to guarantee that n2 is 
positive by putting limits on n1. Suppose we make it just slightly 
more complicated, with three coordination numbers that have to be 
positive and add up to some value. I guess it could be done this way:

   guess n1_var = 5
   def  n1   = max(0,min(n1_var,12))
   guess n2_var = 5
   def  n2   = max(0,min(n2_var,12-n1))
   def n3    = 12 - n1 - n2

So that strategy might work. It might get very complicated, though; 
we have something like six constrained sums with variables often 
appearing in more than one sum. For example, the fraction of copper 
atoms in nominally copper sites appears in the sum requiring the 
total amount of copper to match the stoichiometry, and in the sum 
requiring the total number of atoms + vacancies in nominally copper 
sites to match the number of copper sites. We'll look at it, but I 
think it may be very unwieldy. Your next suggestion, though, should 
do it:


>
>
>
>I think this is possible without a change to the code.  You can
>add a restraint penalty when some value is outside a range
>[lo_val,hi_val].  It's a bit scary looking, but this will do it:
>
>   lo_val   = 0.7    ## lower bound
>   up_val   = 1.0    ## upper bound
>   scale    = 1.000  ## scale can be increased to weight this restraint
>   target   = 0.8    ## this could be a variable, say S02
>
>   ## use penalty as a restraint in feffit()
>   def  penalty  = (max((target-lo_val), abs(target-lo_val)) -
>                    min((target-up_val),-abs(target-up_val)) +
>                    lo_val - up_val)*scale
>
>This penalty can be used as a restraint, and will be 0 when the
>target value is between the lower and upper bound.
>
>To set a penalty only for going below the lower bound, it's just
>   def penalty = max((target-lo_val),abs(target-lo_val))-(target- lo_val)
>
>and if the lower bound is 0, it's even simpler:
>   def penalty = max(target,abs(target))-target

Yes! Very clever. I've used a similar strategy to implement 
"if-then-else" type assignments. This seems considerably easier to 
implement in complicated situations than the previous method.


>Even that is scary enough, and the whole thing may suggest a
>change to the code to make a simple function that provides a
>restraint penalty when a variable is outside some user-selected
>bounds, perhaps as
>     bound(x, lo_val, hi_val)  # not implemented!
>
>Is this way of setting a penalty what you had in mind, or am I
>missing the point?

Yes, that's exactly what I had in mind. I would certainly find it 
very useful...this isn't the first system I've analyzed where it 
would have helped, although I've usually been able to get around it 
with judicious use of abs() or max/min.

I am curious if others would find this feature helpful. Now that 
you've pointed out how to do the same thing with the current 
restraints, I don't really need the shortcut...you've seen enough of 
my fits to know I don't shy away from long expressions. :) But of 
course it would make life easier.

--Scott Calvin
Sarah Lawrence College



More information about the Ifeffit mailing list