diff --git a/Ccore/ccore_common.pro b/Ccore/ccore_common.pro
index 6a6918031a33731a7d06e771f69d7340d7aaf184..c6d1186f71891b7fa350e778bc937a755aa1a632 100644
--- a/Ccore/ccore_common.pro
+++ b/Ccore/ccore_common.pro
@@ -61,7 +61,8 @@ DesignSpace =
 Optimizer = 
   DefineNumber[0, Name "Optimization/Algorithm",
                Choices {0="scipy", 
-                        1="conveks"}];
+                        1="conveks-MMA",
+                        2="conveks-GCMMA"}];
 w = 
   DefineNumber[0, Name "Optimization/Results/w", 
 	       Graph "030000000000000000000000000000000000"];
diff --git a/Ccore/onelab_optimize.py b/Ccore/onelab_optimize.py
index e15cc02eda8b959a0713ad8d0398a4d82fbfa502..6833da8aed98137cedb0b8d7305d61e58fa7c0ab 100644
--- a/Ccore/onelab_optimize.py
+++ b/Ccore/onelab_optimize.py
@@ -112,7 +112,7 @@ def Optimize(Optimizer):
             print 'Stopped by Gmsh'
             return
         print res
-    elif Optimizer==1: # 'mma'
+    elif Optimizer==1 or Optimizer==2: # 'mma'
         numVariables = len(x)
         initialPoint = np.zeros(numVariables)
         lowerBound = np.zeros(numVariables)
@@ -134,7 +134,10 @@ def Optimize(Optimizer):
         
         # Get iteration count (here it will be 1 - could be different in case of restart)
         it = conveks.mma.getOuterIteration()
-        change_mma = 1.0
+        innerit = 0; inneritTot = 0; change_mma = 1.0
+        all_objectives = []
+        print 'outer iter.   inner iter.   change   obj.   constr.'
+
         while it <= maxIter :
             c.setNumber('Optimization/01Current iteration', value=it, readOnly=1,
                          attributes={'Highlight':'LightYellow'})
@@ -145,7 +148,12 @@ def Optimize(Optimizer):
             obj = fobj(xFromMMA)
             if (c.getString(c.name+'/Action') == 'stop'): break
             grad_obj = grad(xFromMMA)
-            
+            constraints = np.array([np.sum(xFromMMA)/np.sum(upperBound)-1.0])
+            grad_constraints = np.ones(numVariables)/np.sum(upperBound)
+            all_objectives.append(obj)
+            #print '%3i  %3i  %.4e %.4e  %.4e'%(it,innerit,change_mma,obj,constraints[0])
+            #print xFromMMA
+
             if it == 1:
                 fscale = 1.0
             #    fscale = 1.0 / np.abs(obj)
@@ -164,7 +172,37 @@ def Optimize(Optimizer):
 
             # call MMA and update the current point
             #conveks.mma.setMoveLimits(lowerBound, upperBound, 0.01) # parametre interessant
-            conveks.mma.updateCurrentPoint(grad_objective)
+            #conveks.mma.updateCurrentPoint(grad_objective)
+            conveks.mma.updateCurrentPoint(objective,grad_objective,constraints,grad_constraints)
+
+            if Optimizer==2: # we use GCMMA, so update the approximation to be conservative
+                # Evaluate the objective at the new point
+                xFromMMA = conveks.mma.getCurrentPoint()
+                objective_new = fobj(xFromMMA)
+                constraints_new = np.array([np.sum(xFromMMA)/np.sum(upperBound)-1.0])
+
+                # check if the approximation is conservative at the current point
+                conserv = conveks.mma.isConservativeSubProblem(objective_new,constraints_new) 
+
+                innerit = 0
+                print '\t',innerit,xFromMMA,objective_new,objective
+                while innerit <= 15 and conserv==0:
+                    innerit += 1
+
+                    # adapt the subproblem to make it conservative and generate a new point
+                    conveks.mma.updateCurrentPoint(objective,grad_objective,constraints,grad_constraints,
+                        objectiveAtUpdatedPoint=objective_new,constraintsAtUpdatedPoint=constraints_new)
+
+                    # evaluate the objective and the constraints at the new point
+                    xFromMMA = conveks.mma.getCurrentPoint()
+                    objective_new = fobj(xFromMMA)
+                    constraints_new = np.array([np.sum(xFromMMA)/np.sum(upperBound)-1.0])
+                    print '\t',innerit,xFromMMA,objective_new,objective,constraints_new,constraints
+
+                    # and check if the subproblem is conservative at this point
+                    conserv = conveks.mma.isConservativeSubProblem(objective_new,constraints_new) 
+                inneritTot += innerit
+
             change_mma = conveks.mma.getDesignChange()
             if change_mma < maxChange: break
             it = conveks.mma.getOuterIteration()
@@ -172,6 +210,8 @@ def Optimize(Optimizer):
         print
         print 'Converged: %4d %3.6f %3.6f %3.6f' %(it, obj, change_rel, change_mma),
         print xFromMMA, grad_obj
+        print 'objective'
+        for i in all_objectives:print i
         
         # This should be called at the end of MMA
         conveks.mma.finalize()
diff --git a/Lbracket/topo.pro b/Lbracket/topo.pro
index c16cfe3f0e381d04a90cfa2c3c917a1ad4575af3..9aa2aeaaf83539c1874483b55db733517a8af78c 100644
--- a/Lbracket/topo.pro
+++ b/Lbracket/topo.pro
@@ -3,9 +3,19 @@ Struct OPT::YOUNG_LAW [ Enum, simp, modifiedSimp, ramp, none];
 DefineConstant [
   Opt_ResDir = StrCat[CurrentDir,"res_opt/"]
   Opt_ResDir_Onelab = "Optimization/Results/"
-  densityFieldInit = {0.5, Name "Optimization/3Density/0Inital value"}
-  Opt_filter_radius = {0.008, Name "Optimization/3Density/2filter radius"}
   Flag_PrintLevel = {1, Name "General/Verbosity", Visible 1}
+  
+  // Optimization parameters
+  Optimizer = {0,Choices {0="Conveks - MMA",1="Conveks - GCMMA"},Name "Optimization/00Optimizer"}
+  Opt_maxIter = {1600, Name "Optimization/01Max iterations"}
+  Opt_maxChange = {0.01, Name "Optimization/02Max change"}
+
+  // Density field parameters 
+  densityFieldInit = {0.5, Name "Optimization/3Density/0Inital value"}
+  Opt_filter_exists = {1, Name "Optimization/3Density/1filter", Choices{0,1}}
+  Opt_filter_radius = {0.008, Name "Optimization/3Density/2filter radius", Visible Opt_filter_exists}
+
+  // Interpolation parameters
   Flag_opt_matlaw = {OPT::YOUNG_LAW.modifiedSimp,
     Choices {
       OPT::YOUNG_LAW.simp="simp",
@@ -19,12 +29,21 @@ DefineConstant [
        || Flag_opt_matlaw == OPT::YOUNG_LAW.ramp
        || Flag_opt_matlaw == OPT::YOUNG_LAW.modifiedSimp) }
   Flag_opt_stress_penal = {2.5,
-    Name "Optimization/5Young Interpolation/2penalization of stress",
+    Name "Optimization/6Von-Mises parameters/1penalization of stress",
     Visible Flag_opt_matlaw != OPT::YOUNG_LAW.none}
+  PARAM_pnorm = {8,Name "Optimization/6Von-Mises parameters/2Pnorm penalization"}
+
+  // Model parameters
   Flag_EPC = 1
   iP = 1
+  Flag_getGradient = 1
 ];
 
+fobj = DefineNumber[0, Name "Optimization/Results/objective", 
+	Graph "0300000000000000000000000000000000000"];
+maxConstr = DefineNumber[0, Name "Optimization/Results/max(|Constraints|)", 
+	Graph "0000000003000000000000000000000000000"];
+
 Group {
   Sur_Clamp~{iP} = Region[{1001}];
   Vol_Force~{iP} = Region[{}];
@@ -535,11 +554,6 @@ Return
 // We also make use of the adjoint method to compute their
 // sensitivity wit respect to the density field.
 
-DefineConstant[
-  PARAM_pnorm = {8,
-    Name "Optimization/7Parameter(s) of performances/Von-Mises/Pnorm penalization"}
-];
-
 PostProcessing{
   { Name ObjectiveConstraints; NameOfFormulation Elast_u;
     Quantity {
@@ -763,10 +777,12 @@ Resolution {
       PostOperation[Get_ObjectiveConstraints];
 
       // Compute the sensitivity of objective/constraints
-      PostOperation[Get_GradOf_Volume];
-      Generate[LAM];
-      SolveAgainWithOther[LAM,SYS];
-      PostOperation[Get_GradOf_VonMises];
+      If(Flag_getGradient)
+        PostOperation[Get_GradOf_Volume];
+        Generate[LAM];
+        SolveAgainWithOther[LAM,SYS];
+        PostOperation[Get_GradOf_VonMises];
+      EndIf
     }
   }
 }
diff --git a/Lbracket/topo.py b/Lbracket/topo.py
index 1606aaeb653cc6e514a07c17f30457e1a85d95ff..bc333d105b411bf33cd4a10da527dcbf501c0bbb 100644
--- a/Lbracket/topo.py
+++ b/Lbracket/topo.py
@@ -39,15 +39,15 @@ c.openProject(topo_geo)
 # dry getdp run (without -solve or -pos option) to get model parameters in the GUI
 c.runSubClient('myGetDP', mygetdp)
 
-# define now optimization parameters
+# get now optimization parameters
 # some of them as Onelab parameter, to be editable in the GUI
-maxIter = c.defineNumber('Optimization/00Max iterations', value=1600)
-maxChange = c.defineNumber('Optimization/01Max change', value=1e-2)
-filtering = c.defineNumber('Optimization/02Filtering/00Filter densities', value=1, choices=[0,1])
+Optimizer = c.getNumber('Optimization/00Optimizer')
+maxIter = c.getNumber('Optimization/01Max iterations')
+maxChange = c.getNumber('Optimization/02Max change')
+filtering = c.getNumber('Optimization/3Density/1filter')
 
 # end of check phase. We're done if we do not start the optimization
-if c.action == 'check' :
-   exit(0)
+if c.action == 'check': exit(0)
 
 # mesh the geometry to create the density (background) mesh
 c.runSubClient('myGmsh', mygmsh + '-2')
@@ -78,6 +78,44 @@ def setElementTable(var, ele, val):
    data = np.insert(data, 0, numElements)
    c.setNumber(var, values=data, visible=0)
 
+def get_objective(xFromOpti, flag_grad=1):
+    if filtering:
+        setElementTable('Optimization/Results/filterInput', elements, xFromOpti)
+        c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
+        xFromOpti = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
+
+    setElementTable('Optimization/Results/designVariable', elements, xFromOpti)
+
+    c.runSubClient('myGetDP', mygetdp + '-solve ComputePerformancesAndGradient '\
+        + '-setnumber Flag_getGradient '+str(flag_grad))
+
+    # get the value of the objective function and of the constraints
+    # as well as their sensitivity with respect to design variables at `x'
+    objective = c.getNumber('Optimization/Results/pnorm of Von-Mises')
+    constraints = np.array([c.getNumber('Optimization/Results/VolumeDomain')\
+        / (0.5*c.getNumber('Optimization/Results/VolumeDomainFull')) - 1.0])
+
+    return objective, constraints
+
+def get_grad(xFromOpti):
+    grad_objective = np.asarray(c.getNumbers('Optimization/Results/GradOfVonMises')[2::2])
+    grad_constraints = np.asarray(c.getNumbers('Optimization/Results/GradOfVolume')[2::2])\
+        /(0.5*c.getNumber('Optimization/Results/VolumeDomainFull'))
+    if filtering:
+        # apply the chain rule for the sensitivity of objective
+        setElementTable('Optimization/Results/filterInput', elements, grad_objective)
+        c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
+        grad_objective = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
+
+        # apply the chain rule for the sensitivity of constraints
+        d_con_dx = grad_constraints.reshape((1, numVariables))
+        for k,dfe in enumerate(d_con_dx):
+            setElementTable('Optimization/Results/filterInput', elements, dfe)
+            c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
+            d_con_dx[k] = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
+        grad_constraints = d_con_dx.reshape((1 * numVariables,))
+    return grad_objective, grad_constraints
+
 # number of design variables and number of constraints
 numVariables = len(x)
 
@@ -93,77 +131,116 @@ conveks.initialize()
 # Initialize the MMA optimizer
 conveks.mma.initialize(x, lowerBound, upperBound)
 
+
 # Set appropriate options for MMA
-conveks.mma.option.setNumber('General.Verbosity', 4)
-conveks.mma.option.setNumber('SubProblem.move', 0.1)
+conveks.mma.option.setNumber('General.Verbosity', 0)
+if Optimizer==0:conveks.mma.option.setNumber('SubProblem.move', 0.1)
 
 # Get iteration count (here it will be 1 - could be different in case of restart)
 it = conveks.mma.getOuterIteration()
-change = 1.0
+innerit = 0; inneritTot = 0; change = 1.0; kkt_norm=1.0
+all_objectives = []
 
-while change > maxChange and it <= maxIter and c.getString('topo/Action') != 'stop':
+# get (copy of) current point
+x = conveks.mma.getCurrentPoint()
 
-    c.setNumber('Optimization/01Current iteration', value=it, readOnly=1,
-                attributes={'Highlight':'LightYellow'})
-    c.setNumber('Optimization/02Current change', value=change, readOnly=1,
-                attributes={'Highlight':'LightYellow'})
+# compute objective function and constraints
+# as well as their sensitivity with respect to design variables at `x'
+objective,constraints = get_objective(x) 
+grad_objective,grad_constraints = get_grad(x)
 
-    # get (copy of) current point
-    x = conveks.mma.getCurrentPoint()
+# send data to onelab server
+c.setNumber('Optimization/Results/objective', value=objective) 
+c.addNumberChoice('Optimization/Results/objective', value=objective) 
+c.setNumber('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+c.addNumberChoice('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
 
-    if filtering:
-        setElementTable('Optimization/Results/filterInput', elements, x)
-        c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
-        x = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
+# show current iteration
+print 'iter.  inner iter.   obj.   max(constr.)   L2-norm(kkt)   point'
+print '%3i  %3i   %2.4e   %2.4e   %2.4e'%(it-1, innerit, objective, np.max(np.abs(constraints)), kkt_norm),
+print x[0:5]
 
-    setElementTable('Optimization/Results/designVariable', elements, x)
+while change > maxChange and it <= maxIter and c.getString('topo/Action') != 'stop':
 
-    # compute objective function and constraints as well as their sensitivity
-    # with respect to design variables at `x'
-    c.runSubClient('myGetDP', mygetdp + '-solve ComputePerformancesAndGradient')
+    # Solve MMA
+    conveks.mma.updateCurrentPoint(objective,grad_objective,constraints,grad_constraints)
+
+    # Let us check now if the MMA approximation is conservative 
+    # at the new point; and adapt the latter if it is not the case.
+    if Optimizer == 1:
+        # Evaluate the objective at the new point and check if 
+        # the approximation is conservative at the current point
+        obj_new,c_new = get_objective(conveks.mma.getCurrentPoint())
+        conserv = conveks.mma.isConservativeSubProblem(obj_new, c_new) 
+
+        innerit = 0
+        print '\t it.  new-point (xn)       f(xc)       f(xn)      max(c(xc))     max(c(xn))'
+        print '\t%3i'%(innerit),
+        print conveks.mma.getCurrentPoint()[0:5],
+        print '  %.4e  %.4e  %.4e  %.4e'%(objective,obj_new,np.max(constraints), np.max(c_new))
+    
+        while innerit <= 15 and conserv==0:
+            innerit += 1
+
+            # adapt the subproblem to make it conservative and generate a new point
+            conveks.mma.updateCurrentPoint(objective,grad_objective,
+                objectiveAtUpdatedPoint=obj_new,constraintsAtUpdatedPoint=c_new)
+
+            # evaluate the objective and the constraints at the new point
+            # and check if the subproblem is conservative at this point
+            obj_new,c_new = get_objective(conveks.mma.getCurrentPoint())
+            conserv = conveks.mma.isConservativeSubProblem(obj_new, c_new) 
+
+            # show inner iteration
+            print '\t%3i'%(innerit),
+            print conveks.mma.getCurrentPoint()[0:5],
+            print '  %.4e  %.4e  %.4e  %.4e'%(objective,obj_new,np.max(constraints), np.max(c_new))
+
+        inneritTot += innerit
 
-    # get the value of the objective function and of the constraints
+    # get (copy of) current point
+    x = conveks.mma.getCurrentPoint()
+
+    # compute objective function and constraints
     # as well as their sensitivity with respect to design variables at `x'
-    objective = c.getNumber('Optimization/Results/pnorm of Von-Mises')
-    constraints = np.array([c.getNumber('Optimization/Results/VolumeDomain')\
-        / (0.5*c.getNumber('Optimization/Results/VolumeDomainFull')) - 1.0])
-    grad_objective = np.array(c.getNumbers('Optimization/Results/GradOfVonMises')[2::2])
-    grad_constraints = np.asarray(c.getNumbers('Optimization/Results/GradOfVolume')[2::2])\
-        /(0.5*c.getNumber('Optimization/Results/VolumeDomainFull'))
+    objective,constraints = get_objective(x) 
+    grad_objective,grad_constraints = get_grad(x)
 
-    if filtering:
-        # apply the chain rule for the sensitivity of objective
-        setElementTable('Optimization/Results/filterInput', elements, grad_objective)
-        c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
-        grad_objective = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
+    # evaluate the L2-norm of KKT 
+    kkt_norm = conveks.mma.getKKTNorm(constraints,grad_objective,grad_constraints)
 
-        # apply the chain rule for the sensitivity of constraints
-        d_con_dx = grad_constraints.reshape((len(constraints), numVariables))
-        for k,dfe in enumerate(d_con_dx):
-            setElementTable('Optimization/Results/filterInput', elements, dfe)
-            c.runSubClient('myGetDP', mygetdp + '-solve FilterDensity')
-            d_con_dx[k] = np.array(c.getNumbers('Optimization/Results/filterOutput')[2::2])
-        grad_constraints = d_con_dx.reshape((len(constraints) * numVariables,))
-
-    if it == 1: fscale = 1.0 / np.abs(objective)
-    objective *= fscale
-    grad_objective *= fscale
-
-    print('*'*50)
-    print('iteration: ', it)
-    print('change: ', change)
-    print('objective:', objective)
-    print('constraints:', constraints)
-    c.sendInfo('Optimization: it. {}, obj. {}, constr. {}, change {}'.format(it,objective,constraints[0], change))
-
-    # call MMA and update the current point
-    #conveks.mma.setMoveLimits(lowerBound, upperBound, 0.1)
-    conveks.mma.updateCurrentPoint(grad_objective, constraints, grad_constraints)
-    change = conveks.mma.getDesignChange()
+    # show the current state
+    print '%3i  %3i  %2.4e  %2.4e  %2.4e'%(it, innerit, objective, np.max(np.abs(constraints)), kkt_norm),
+    print x[0:5]
+
+    # get the outer iteration count here as well as the 
+    # change between two successive points 
     it = conveks.mma.getOuterIteration()
+    change = conveks.mma.getDesignChange()
+
+    # send data to the onelab server
+    c.setNumber('Optimization/Results/01Current iteration', value=it, readOnly=1)
+    c.setNumber('Optimization/Results/02Current change', value=change, readOnly=1)
+    c.setNumber('Optimization/Results/03KKT L2-norm', value=kkt_norm, readOnly=1)
+    c.setNumber('Optimization/Results/objective', value=objective) 
+    c.addNumberChoice('Optimization/Results/objective', value=objective) 
+    c.setNumber('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+    c.addNumberChoice('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+
+# show the problem at the converged state 
+print '*'*80
+print 'Optimization has converged after %i iterations.'%(it-1)
+print '  > point           :',x[0:5]
+print '  > objective       : %.4e'%(objective)
+print '  > max(constraints): %.4e'%(np.max(np.abs(constraints)))
+print '  > L2-norm of kkt  : %.4e'%(kkt_norm)
+print '  > inner iterations: %i'%(inneritTot)
+print '*'*80
 
 # This should be called at the end of MMA
 conveks.mma.finalize()
 
 # This should be called at the end
-conveks.finalize()
\ No newline at end of file
+conveks.finalize()
+
+print all_objectives
\ No newline at end of file
diff --git a/Team25/shape.geo b/Team25/shape.geo
index f20bc36c8800a0368c6d4a97425ed8ad5f4f6ac2..097ccdc7053efd1b58b34c4e71ae06a1a3286f6d 100644
--- a/Team25/shape.geo
+++ b/Team25/shape.geo
@@ -4,7 +4,7 @@ DefineConstant[
   R1 = 6*mm
   L2 = 18*mm
   L3 = 15*mm
-  L4 = {L2/L3*Sqrt[L3^2-(12.5*mm)^2], Name "Constructive parameters/L4"}
+  L4 = {L2/L3*Sqrt[L3^2-(12.5*mm)^2], Name "Optimization/parameters/L4", Closed 1}
   angleMeasure = 50*deg
   lc = 12*mm
   lcd = lc/15
@@ -20,7 +20,7 @@ Point(1) = {0, 0, 0, lcd};
 
 For k In {0:10}
   theta = k * 90/10 * deg;
-  DefineConstant[Rs~{k} = {R1, Name Sprintf["Constructive parameters/spline radius %g",k+1]}];
+  DefineConstant[Rs~{k} = {R1, Name Sprintf["Optimization/parameters/spline radius %g",k+1], Closed 1}];
   Point(2020+k) = {Rs~{k}*Cos[theta], Rs~{k}*Sin[theta], 0, lcd};  
 EndFor
 
@@ -33,7 +33,7 @@ BSpline(4022) = {2026, 2027, 2028, 2029, 2030};
 For k In {0:9}
   theta = k * 50/10 * deg;
   DefineConstant[Rso~{k} = {L2*L3/Sqrt[(L3*Cos[theta])^2+(L2*Sin[theta])^2], 
-    Name Sprintf["Constructive parameters/outer spline radius %g",k+1]}];
+    Name Sprintf["Optimization/parameters/outer spline radius %g",k+1], Closed 1}];
   Point(6020+k) = {Rso~{k}*Cos[theta], Rso~{k}*Sin[theta], 0, lcd};  
 EndFor
 
@@ -64,8 +64,8 @@ Point(1022) = {(9.5+2.25)*mm, 0, 0, lcd};
 
 Point(23) = {20*mm, 12.5*mm, 0, lcd};
 
-//DefineConstant[edge_x_1 = {20*mm-L4, Name "Constructive parameters/edge radius 1"}];
-//DefineConstant[edge_y_1 = {(12.5-2)*mm, Name "Constructive parameters/edge radius 2"}];
+//DefineConstant[edge_x_1 = {20*mm-L4, Name "Optimization/parameters/edge radius 1"}];
+//DefineConstant[edge_y_1 = {(12.5-2)*mm, Name "Optimization/parameters/edge radius 2"}];
 //Point(25) = {edge_x_1, edge_y_1, 0, lcd};
 
 Circle(1016) = {1022, 1, 1021};
diff --git a/Team25/shape.pro b/Team25/shape.pro
index d27346ff049911ac8378bd370267f910b4dc0a92..8cdb3025268bee99fc1a06dae822d81de79cb969 100644
--- a/Team25/shape.pro
+++ b/Team25/shape.pro
@@ -73,8 +73,17 @@ DefineConstant [
   Nb_max_iter = 30
   relaxation_factor = 1
   stop_criterion = 1e-5
+  Optimizer = {0,Choices {0="Conveks - MMA",1="Conveks - GCMMA"},Name "Optimization/00Optimizer"}
+  Opt_maxIter = {1600, Name "Optimization/01Max iterations"}
+  Opt_maxChange = {0.0001, Name "Optimization/02Max change"}
+  Opt_maxKKT = {0.001, Name "Optimization/03Max KKT norm"}
 ];
 
+fobj = DefineNumber[0, Name "Optimization/Results/objective", 
+	Graph "0300000000000000000000000000000000000"];
+maxConstr = DefineNumber[0, Name "Optimization/Results/max(|Constraints|)", 
+	Graph "0000000003000000000000000000000000000"];
+
 Group {
   // One starts by giving explicit meaningful names to the Physical regions
   // defined in the "shape.geo" mesh file.
diff --git a/Team25/shape.py b/Team25/shape.py
index 310805d381998a5e4ad26cbb38d7b52544bc2ea8..60b3200212242777a6db62276fbfbcd47f4d8d51 100644
--- a/Team25/shape.py
+++ b/Team25/shape.py
@@ -21,13 +21,13 @@ if(not len(mygetdp)):
    c.sendError('Please run a GetDP model interactively once with Gmsh to ' +
                'initialize the solver location')
    exit(0)
-
 c.sendInfo('Will use gmsh={0} and getdp={1}'.format(mygmsh, mygetdp))
 
 # append correct path to model file names
-file_geo = c.getPath('shape.geo')
-file_msh = c.getPath('shape.msh')
-file_pro = c.getPath('shape.pro')
+modelName = 'shape'
+file_geo = c.getPath(modelName+'.geo')
+file_msh = c.getPath(modelName+'.msh')
+file_pro = c.getPath(modelName+'.pro')
 
 # build command strings with appropriate parameters and options
 mygetdp += ' -p 0 ' + file_pro + ' -msh ' + file_msh + ' '
@@ -39,56 +39,50 @@ c.openProject(file_geo)
 # dry getdp run (without -solve or -pos option) to get model parameters in the GUI
 c.runSubClient('myGetDP', mygetdp)
 
-# define optimization parameters as Onelab parameter (editable in the GUI)
-maxIter = c.defineNumber('Optimization/00Max iterations', value=100)
-maxChange = c.defineNumber('Optimization/01Max change', value=1e-5)
+# read optimization parameters as Onelab parameter (editable in the GUI)
+Optimizer = c.getNumber('Optimization/00Optimizer')
+maxIter = c.getNumber('Optimization/01Max iterations')
+maxChange = c.getNumber('Optimization/02Max change')
+maxKKTNorm = c.getNumber('Optimization/03Max KKT norm')
 
 # end of check phase. We're done if we do not start the optimization
-if c.action == 'check' :
-   exit(0)
+if c.action == 'check' :exit(0)
 
-x = {}
+# Let us define now the design variables as a list 
+# for which the entries are defiend as follows:
+# ['onelab/path', value, lower-bound, upper-bound] 
+allParameters = []
+xe = 6.0e-3
 for k in xrange(11):
-    xe = 6.0e-3
-    x[k] = ['Constructive parameters/spline radius '+str(k+1),xe, xe-4.0e-3, xe+4.0e-3, 'Rs_'+str(k)]
-sizeX = len(x.keys())
+    allParameters.append(['Optimization/parameters/spline radius '+str(k+1),xe, xe-4.0e-3, xe+4.0e-3, 'Rs_'+str(k)])
 
 L2 = 18.0e-3;L3 = 15.0e-3
 for k in xrange(10):
     theta = float(k) * 50.0/10.0 * np.pi/180.0
     xe = L2*L3/((L3*np.cos(theta))**2.0+(L2*np.sin(theta))**2.0)**0.5
-    x[sizeX+k] = ['Constructive parameters/outer spline radius '+str(k+1), xe, xe-4.0e-3, xe+1.5e-3, 'Rso_'+str(k)]
+    allParameters.append(['Optimization/parameters/outer spline radius '+str(k+1), xe, xe-4.0e-3, xe+1.5e-3, 'Rso_'+str(k)])
 
-x[len(x.keys())] = ['Constructive parameters/L4', 0.00994987, 0.00994987-4.0e-3, xe+4.0e-3, 'L4']
+allParameters.append(['Optimization/parameters/L4', 0.00994987, 0.00994987-4.0e-3, xe+4.0e-3, 'L4'])
+
+# we create a dictionnary based on the list of design variables
+x = {}
+for k in xrange(len(allParameters)):
+    x[k] = allParameters[k]
 
+# some utility functions:
+# (1) to read the values from a table  
 def readSimpleTable(path):
     with open(path) as FileObj:
         return np.array([float(lines.split()[-1]) for lines in FileObj])
 
+# (2) to set the values of a node table
 def setNodeTable(var, nodes, val):
     data = np.ravel(np.column_stack((nodes, val)))
     data = np.insert(data, 0, float(len(nodes)))
     c.setNumber(var, values=data, visible=0)
 
-def getVelocityField(xvar, perturb=1e-6):
-    for id, var in xvar.iteritems():
-        c.runNonBlockingSubClient('myGmsh', mygmsh \
-          + ' -setnumber PerturbMesh 1'\
-          + ' -setnumber VelocityTag '+str(id)\
-          + ' -setnumber '+var[-1]+' '+str(var[1]+perturb)\
-          + ' -run')
-    c.waitOnSubClients()
-    # send the x,y,z components of each velocity field
-    for label, var in x.iteritems():
-        d = c.getNumbers('Optimization/Results/velocity_'+str(label))
-        if label==0:nodes = np.array(d[1::4])
-        setNodeTable('Optimization/Results/velocity_'+str(label)+'_1',nodes,np.array(d[2::4]))
-        setNodeTable('Optimization/Results/velocity_'+str(label)+'_2',nodes,np.array(d[3::4]))
-        setNodeTable('Optimization/Results/velocity_'+str(label)+'_3',nodes,np.array(d[4::4]))
-        c.setNumber(var[0],value=x[label][1])
-
 # number of design variables and number of constraints
-numVariables = len(x.keys())
+numVariables = len(x)
 
 # initial value of the variables in the design space,
 # the lower bound for design variables,
@@ -97,34 +91,16 @@ initialPoint = np.zeros(numVariables)
 lowerBound = np.zeros(numVariables)
 upperBound = np.zeros(numVariables)
 for label, var in x.iteritems():
-  initialPoint[label] = var[1]
-  lowerBound[label] = var[2]
-  upperBound[label] = var[3]
-
-# Initialize conveks
-conveks.initialize()
-
-# Initialize the MMA optimizer
-conveks.mma.initialize(initialPoint, lowerBound, upperBound)
-
-# Set some options for MMA
-conveks.mma.option.setNumber('General.Verbosity', 4)
-
-# Get iteration count (here it will be 1 - could be different in case of restart)
-it = conveks.mma.getOuterIteration()
-change = 1.0
-
-while it <= maxIter and c.getString('shape/Action') != 'stop':
-
-    c.setNumber('Optimization/01Current iteration', value=it, readOnly=1,
-        attributes={'Highlight':'LightYellow'})
-
-    # get (copy of) current point
-    xFromMMA = conveks.mma.getCurrentPoint()
+    initialPoint[label] = var[1]
+    lowerBound[label] = var[2]
+    upperBound[label] = var[3]
 
+# We define the function which returns the objective and 
+# constraints values (as a list) for a given point 'xFromOpti'. 
+def get_objective(xFromOpti):
     # send the current point to GetDP model
     for label, var in x.iteritems():
-        x[label][1] = xFromMMA[label]
+        x[label][1] = xFromOpti[label]
         c.setNumber(var[0],value=x[label][1])
 
     # reload the geometry in GUI to see the geometrical changes
@@ -133,12 +109,31 @@ while it <= maxIter and c.getString('shape/Action') != 'stop':
     # mesh the geometry
     c.runSubClient('myGmsh', mygmsh + ' -2')
 
-    # compute objective function and constraints
+    # run Fem and evaluate the objective function
     c.runSubClient('myGetDP', mygetdp + '-solve GetPerformances')
     objective = np.sum(readSimpleTable(c.getPath('res_opt/w.txt')))
+    constraints = np.array([np.sum(xFromOpti)/np.sum(upperBound)-1]);  
+    return objective,constraints
 
+# We also define the function which returns the gradient of the 
+# objective and constraints (as a C-style array) for a given point. 
+def get_grad(perturb=1e-6):
     # generate the velocity field of each design variable
-    getVelocityField(x)
+    for id, var in x.iteritems():
+        c.runNonBlockingSubClient('myGmsh', mygmsh \
+          + ' -setnumber PerturbMesh 1'\
+          + ' -setnumber VelocityTag '+str(id)\
+          + ' -setnumber '+var[-1]+' '+str(var[1]+perturb)\
+          + ' -run')
+    c.waitOnSubClients()
+    # send the x,y,z components of each velocity field
+    for label, var in x.iteritems():
+        d = c.getNumbers('Optimization/Results/velocity_'+str(label))
+        if label==0:nodes = np.array(d[1::4])
+        setNodeTable('Optimization/Results/velocity_'+str(label)+'_1',nodes,np.array(d[2::4]))
+        setNodeTable('Optimization/Results/velocity_'+str(label)+'_2',nodes,np.array(d[3::4]))
+        setNodeTable('Optimization/Results/velocity_'+str(label)+'_3',nodes,np.array(d[4::4]))
+        c.setNumber(var[0],value=x[label][1])
 
     # compute the sensitivity with respect to design variables at `x'
     for dv, var in x.iteritems():
@@ -148,20 +143,127 @@ while it <= maxIter and c.getString('shape/Action') != 'stop':
     c.waitOnSubClients()
     grad_objective = np.asarray([np.sum(readSimpleTable(c.getPath('res_opt/Grad_w_wrt_dv_'+str(dv)+'.txt')))\
         for dv in xrange(numVariables)])
+    grad_constraints = np.ones(numVariables)/np.sum(upperBound);  
+
+    return grad_objective,grad_constraints
 
-    print('*'*50)
-    print('iteration: ', it)
-    print('change: ', change)
-    print('current point:', xFromMMA)
-    print('objective:', objective)
-    c.sendInfo('Optimization: it. {}, obj. {}'.format(it,objective))
+# Initialize conveks
+conveks.initialize()
 
-    # call MMA and update the current point
-    conveks.mma.setMoveLimits(lowerBound, upperBound, 0.1)
-    conveks.mma.updateCurrentPoint(grad_objective)
+# Initialize the MMA optimizer
+conveks.mma.initialize(initialPoint, lowerBound, upperBound)
+
+# Set some options for MMA
+conveks.mma.option.setNumber('General.Verbosity', 0*4)
+
+# Get iteration count (here it will be 1 - could be different in case of restart)
+it = conveks.mma.getOuterIteration()
+innerit = 0; inneritTot = 0; change = 1.0; kkt_norm = 1.0
+
+# we kkep track of the objective values in all_objectives 
+all_objectives = []
+
+# get (copy of) current point
+xFromMMA = conveks.mma.getCurrentPoint()
+
+# compute objective function and constraints
+# as well as their sensitivity with respect to design variables at `x'
+objective,constraints = get_objective(xFromMMA) 
+grad_objective,grad_constraints = get_grad()
+
+# send data to onelab server
+c.setNumber('Optimization/Results/objective', value=objective) 
+c.addNumberChoice('Optimization/Results/objective', value=objective) 
+c.setNumber('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+c.addNumberChoice('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+
+# show current iteration
+print 'iter.  inner iter.   obj.   max(constr.)   L2-norm(kkt)     change   point'
+print '%3i  %3i   %.4e   %.4e   %.4e   %.4e'%(it-1, innerit, objective, np.max(np.abs(constraints)), kkt_norm, change),
+print xFromMMA[0:5]
+
+while it <= maxIter and change > maxChange \
+    and c.getString('shape/Action') != 'stop':
+
+    # Solve MMA
+    if Optimizer==0:conveks.mma.setMoveLimits(lowerBound, upperBound, 0.1)
+    conveks.mma.updateCurrentPoint(objective,grad_objective,constraints,grad_constraints)
+
+    # Let us check now if the MMA approximation is conservative 
+    # at the new point; and adapt the latter if it is not the case.
+    if Optimizer == 1:
+        # Evaluate the objective at the new point and check if 
+        # the approximation is conservative at the current point
+        obj_new,c_new = get_objective(conveks.mma.getCurrentPoint())
+        conserv = conveks.mma.isConservativeSubProblem(obj_new, c_new) 
+
+        innerit = 0
+        print '\t it.  new-point (xn)       f(xc)       f(xn)      max(c(xc))     max(c(xn))'
+        print '\t%3i'%(innerit),
+        print conveks.mma.getCurrentPoint()[0:5],
+        print '  %.4e  %.4e  %.4e  %.4e'%(objective,obj_new,np.max(constraints), np.max(c_new))
+    
+        while innerit <= 15 and conserv==0:
+            innerit += 1
+
+            # adapt the subproblem to make it conservative and generate a new point
+            conveks.mma.updateCurrentPoint(objective,grad_objective,
+                objectiveAtUpdatedPoint=obj_new,constraintsAtUpdatedPoint=c_new)
+
+            # evaluate the objective and the constraints at the new point
+            # and check if the subproblem is conservative at this point
+            obj_new,c_new = get_objective(conveks.mma.getCurrentPoint())
+            conserv = conveks.mma.isConservativeSubProblem(obj_new, c_new) 
+
+            # show inner iteration
+            print '\t%3i'%(innerit),
+            print conveks.mma.getCurrentPoint()[0:5],
+            print '  %.4e  %.4e  %.4e  %.4e'%(objective,obj_new,np.max(constraints), np.max(c_new))
+
+        inneritTot += innerit
+
+    # get (copy of) current point
+    xFromMMA = conveks.mma.getCurrentPoint()
+
+    # compute objective function and constraints
+    # as well as their sensitivity with respect to design variables at `x'
+    objective,constraints = get_objective(xFromMMA) 
+    grad_objective,grad_constraints = get_grad()
+    all_objectives.append(objective)
+
+    # evaluate the L2-norm of KKT 
+    kkt_norm = conveks.mma.getKKTNorm(constraints,grad_objective,grad_constraints)
+
+    # get the change between two successive points 
     change = conveks.mma.getDesignChange()
+
+    # show the current state
+    print '%3i  %3i   %.4e   %.4e   %.4e   %.4e'%(it, innerit, objective, np.max(np.abs(constraints)), kkt_norm, change),
+    print xFromMMA[0:5]
+
+    # get the outer iteration count here
     it = conveks.mma.getOuterIteration()
 
+    # send data to the onelab server
+    c.setNumber('Optimization/Results/01Current iteration', value=it, readOnly=1)
+    c.setNumber('Optimization/Results/02Current change', value=change, readOnly=1)
+    c.setNumber('Optimization/Results/03Current KKT norm', value=kkt_norm, readOnly=1)
+    c.setNumber('Optimization/Results/objective', value=objective) 
+    c.addNumberChoice('Optimization/Results/objective', value=objective) 
+    c.setNumber('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+    c.addNumberChoice('Optimization/Results/max(|Constraints|)', value=np.max(np.abs(constraints))) 
+
+# show the problem at the converged state 
+print '*'*80
+print 'Optimization has converged after %i iterations.'%(it-1)
+print '  > point           :',xFromMMA[0:5]
+print '  > objective       : %.4e'%(objective)
+print '  > max(constraints): %.4e'%(np.max(np.abs(constraints)))
+print '  > L2-norm of kkt  : %.4e'%(kkt_norm)
+print '  > inner iterations: %i'%(inneritTot)
+print '  > change          : %.4e'%(change)
+print '*'*80
+
 # This should be called at the end of MMA
 conveks.mma.finalize()