1 |
# $Id$ |
2 |
|
3 |
## @file linearPDEs.py |
4 |
|
5 |
""" |
6 |
@brief Functions and classes for linear PDEs |
7 |
""" |
8 |
|
9 |
import escript |
10 |
import util |
11 |
import numarray |
12 |
|
13 |
def identifyDomain(domain=None,data={}): |
14 |
""" |
15 |
@brief Return the Domain which is equal to the input domain (if not None) |
16 |
and is the domain of all Data objects in the dictionary data. |
17 |
An exception is raised if this is not possible |
18 |
|
19 |
@param domain |
20 |
@param data |
21 |
""" |
22 |
# get the domain used by any Data object in the list data: |
23 |
data_domain=None |
24 |
for d in data.itervalues(): |
25 |
if isinstance(d,escript.Data): |
26 |
if not d.isEmpty(): data_domain=d.getDomain() |
27 |
# check if domain and data_domain are identical? |
28 |
if domain == None: |
29 |
if data_domain == None: |
30 |
raise ValueError,"Undefined PDE domain. Specify a domain or use a Data class object as coefficient" |
31 |
else: |
32 |
if data_domain == None: |
33 |
data_domain=domain |
34 |
else: |
35 |
if not data_domain == domain: |
36 |
raise ValueError,"Domain of coefficients doesnot match specified domain" |
37 |
# now we check if all Data class object coefficients are defined on data_domain: |
38 |
for i,d in data.iteritems(): |
39 |
if isinstance(d,escript.Data): |
40 |
if not d.isEmpty(): |
41 |
if not data_domain==d.getDomain(): |
42 |
raise ValueError,"Illegal domain for coefficient %s."%i |
43 |
# done: |
44 |
return data_domain |
45 |
|
46 |
def identifyNumEquationsAndSolutions(dim,coef={}): |
47 |
# get number of equations and number of unknowns: |
48 |
numEquations=0 |
49 |
numSolutions=0 |
50 |
for i in coef.iterkeys(): |
51 |
if not coef[i].isEmpty(): |
52 |
res=_PDECoefficientTypes[i].estimateNumEquationsAndNumSolutions(coef[i].getShape(),dim) |
53 |
if res==None: |
54 |
raise ValueError,"Illegal shape %s of coefficient %s"%(coef[i].getShape().__str__(),i) |
55 |
else: |
56 |
numEquations=max(numEquations,res[0]) |
57 |
numSolutions=max(numSolutions,res[1]) |
58 |
return numEquations,numSolutions |
59 |
|
60 |
|
61 |
def _CompTuple2(t1,t2): |
62 |
""" |
63 |
@brief |
64 |
|
65 |
@param t1 |
66 |
@param t2 |
67 |
""" |
68 |
dif=t1[0]+t1[1]-(t2[0]+t2[1]) |
69 |
if dif<0: return 1 |
70 |
elif dif>0: return -1 |
71 |
else: return 0 |
72 |
|
73 |
class PDECoefficientType: |
74 |
""" |
75 |
@brief |
76 |
""" |
77 |
# identifier for location of Data objects defining coefficients |
78 |
INTERIOR=0 |
79 |
BOUNDARY=1 |
80 |
CONTACT=2 |
81 |
CONTINUOUS=3 |
82 |
# identifier in the pattern of coefficients: |
83 |
# the pattern is a tuple of EQUATION,SOLUTION,DIM where DIM represents the spatial dimension, EQUATION the number of equations and SOLUTION the |
84 |
# number of unknowns. |
85 |
EQUATION=3 |
86 |
SOLUTION=4 |
87 |
DIM=5 |
88 |
# indicator for what is altered if the coefficient is altered: |
89 |
OPERATOR=5 |
90 |
RIGHTHANDSIDE=6 |
91 |
BOTH=7 |
92 |
def __init__(self,where,pattern,altering): |
93 |
""" |
94 |
@brief Initialise a PDE Coefficient type |
95 |
""" |
96 |
self.what=where |
97 |
self.pattern=pattern |
98 |
self.altering=altering |
99 |
|
100 |
def getFunctionSpace(self,domain): |
101 |
""" |
102 |
@brief defines the FunctionSpace of the coefficient on the domain |
103 |
|
104 |
@param domain |
105 |
""" |
106 |
if self.what==self.INTERIOR: return escript.Function(domain) |
107 |
elif self.what==self.BOUNDARY: return escript.FunctionOnBoundary(domain) |
108 |
elif self.what==self.CONTACT: return escript.FunctionOnContactZero(domain) |
109 |
elif self.what==self.CONTINUOUS: return escript.ContinuousFunction(domain) |
110 |
|
111 |
def isAlteringOperator(self): |
112 |
""" |
113 |
@brief return true if the operator of the PDE is changed when the coefficient is changed |
114 |
""" |
115 |
if self.altering==self.OPERATOR or self.altering==self.BOTH: |
116 |
return not None |
117 |
else: |
118 |
return None |
119 |
|
120 |
def isAlteringRightHandSide(self): |
121 |
""" |
122 |
@brief return true if the right hand side of the PDE is changed when the coefficient is changed |
123 |
""" |
124 |
if self.altering==self.RIGHTHANDSIDE or self.altering==self.BOTH: |
125 |
return not None |
126 |
else: |
127 |
return None |
128 |
|
129 |
def estimateNumEquationsAndNumSolutions(self,shape=(),dim=3): |
130 |
""" |
131 |
@brief tries to estimate the number of equations in a given tensor shape for a given spatial dimension dim |
132 |
|
133 |
@param shape |
134 |
@param dim |
135 |
""" |
136 |
if len(shape)>0: |
137 |
num=max(shape)+1 |
138 |
else: |
139 |
num=1 |
140 |
search=[] |
141 |
for u in range(num): |
142 |
for e in range(num): |
143 |
search.append((e,u)) |
144 |
search.sort(_CompTuple2) |
145 |
for item in search: |
146 |
s=self.buildShape(item[0],item[1],dim) |
147 |
if len(s)==0 and len(shape)==0: |
148 |
return (1,1) |
149 |
else: |
150 |
if s==shape: return item |
151 |
return None |
152 |
|
153 |
def buildShape(self,e=1,u=1,dim=3): |
154 |
""" |
155 |
@brief builds the required shape for a given number of equations e, number of unknowns u and spatial dimension dim |
156 |
|
157 |
@param e |
158 |
@param u |
159 |
@param dim |
160 |
""" |
161 |
s=() |
162 |
for i in self.pattern: |
163 |
if i==self.EQUATION: |
164 |
if e>1: s=s+(e,) |
165 |
elif i==self.SOLUTION: |
166 |
if u>1: s=s+(u,) |
167 |
else: |
168 |
s=s+(dim,) |
169 |
return s |
170 |
|
171 |
_PDECoefficientTypes={ |
172 |
"A" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,PDECoefficientType.DIM,PDECoefficientType.SOLUTION,PDECoefficientType.DIM),PDECoefficientType.OPERATOR), |
173 |
"B" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,PDECoefficientType.DIM,PDECoefficientType.SOLUTION),PDECoefficientType.OPERATOR), |
174 |
"C" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,PDECoefficientType.SOLUTION,PDECoefficientType.DIM),PDECoefficientType.OPERATOR), |
175 |
"D" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,PDECoefficientType.SOLUTION),PDECoefficientType.OPERATOR), |
176 |
"X" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,PDECoefficientType.DIM),PDECoefficientType.RIGHTHANDSIDE), |
177 |
"Y" : PDECoefficientType(PDECoefficientType.INTERIOR,(PDECoefficientType.EQUATION,),PDECoefficientType.RIGHTHANDSIDE), |
178 |
"d" : PDECoefficientType(PDECoefficientType.BOUNDARY,(PDECoefficientType.EQUATION,PDECoefficientType.SOLUTION),PDECoefficientType.OPERATOR), |
179 |
"y" : PDECoefficientType(PDECoefficientType.BOUNDARY,(PDECoefficientType.EQUATION,),PDECoefficientType.RIGHTHANDSIDE), |
180 |
"d_contact" : PDECoefficientType(PDECoefficientType.CONTACT,(PDECoefficientType.EQUATION,PDECoefficientType.SOLUTION),PDECoefficientType.OPERATOR), |
181 |
"y_contact" : PDECoefficientType(PDECoefficientType.CONTACT,(PDECoefficientType.EQUATION,),PDECoefficientType.RIGHTHANDSIDE), |
182 |
"r" : PDECoefficientType(PDECoefficientType.CONTINUOUS,(PDECoefficientType.EQUATION,),PDECoefficientType.RIGHTHANDSIDE), |
183 |
"q" : PDECoefficientType(PDECoefficientType.CONTINUOUS,(PDECoefficientType.SOLUTION,),PDECoefficientType.BOTH), |
184 |
} |
185 |
|
186 |
class LinearPDE: |
187 |
""" |
188 |
@brief Class to define a linear PDE |
189 |
|
190 |
class to define a linear PDE of the form |
191 |
|
192 |
-(A_{ijkl}u_{k,l})_{,j} -(B_{ijk}u_k)_{,j} + C_{ikl}u_{k,l} +D_{ik}u_k = - (X_{ij})_{,j} + Y_i |
193 |
|
194 |
with boundary conditons: |
195 |
|
196 |
n_j*(A_{ijkl}u_{k,l}+B_{ijk}u_k)_{,j} + d_{ik}u_k = - n_j*X_{ij} + y_i |
197 |
|
198 |
and contact conditions |
199 |
|
200 |
n_j*(A_{ijkl}u_{k,l}+B_{ijk}u_k)_{,j} + d_contact_{ik}[u_k] = - n_j*X_{ij} + y_contact_i |
201 |
|
202 |
and constraints: |
203 |
|
204 |
u_i=r_i where q_i>0 |
205 |
|
206 |
""" |
207 |
DEFAULT_METHOD=util.DEFAULT_METHOD |
208 |
DIRECT=util.DIRECT |
209 |
CHOLEVSKY=util.CHOLEVSKY |
210 |
PCG=util.PCG |
211 |
CR=util.CR |
212 |
CGS=util.CGS |
213 |
BICGSTAB=util.BICGSTAB |
214 |
SSOR=util.SSOR |
215 |
GMRES=util.GMRES |
216 |
PRES20=util.PRES20 |
217 |
|
218 |
def __init__(self,**args): |
219 |
""" |
220 |
@brief initializes a new linear PDE. |
221 |
|
222 |
@param args |
223 |
""" |
224 |
|
225 |
# initialize attributes |
226 |
self.__debug=None |
227 |
self.__domain=None |
228 |
self.__numEquations=0 |
229 |
self.__numSolutions=0 |
230 |
self.cleanCoefficients() |
231 |
|
232 |
self.__operator=escript.Operator() |
233 |
self.__operator_isValid=False |
234 |
self.__righthandside=escript.Data() |
235 |
self.__righthandside_isValid=False |
236 |
self.__solution=escript.Data() |
237 |
self.__solution_isValid=False |
238 |
|
239 |
# check the arguments |
240 |
coef={} |
241 |
for arg in args.iterkeys(): |
242 |
if arg=="domain": |
243 |
self.__domain=args[arg] |
244 |
elif arg=="numEquations": |
245 |
self.__numEquations=args[arg] |
246 |
elif arg=="numSolutions": |
247 |
self.__numSolutions=args[arg] |
248 |
elif _PDECoefficientTypes.has_key(arg): |
249 |
coef[arg]=args[arg] |
250 |
else: |
251 |
raise ValueError,"Illegal argument %s"%arg |
252 |
|
253 |
# get the domain of the PDE |
254 |
self.__domain=identifyDomain(self.__domain,coef) |
255 |
|
256 |
# set some default values: |
257 |
self.__homogeneous_constraint=True |
258 |
self.__row_function_space=escript.Solution(self.__domain) |
259 |
self.__column_function_space=escript.Solution(self.__domain) |
260 |
self.__tolerance=1.e-8 |
261 |
self.__solver_method=util.DEFAULT_METHOD |
262 |
self.__matrix_type=self.__domain.getSystemMatrixTypeId(util.DEFAULT_METHOD,False) |
263 |
self.__sym=False |
264 |
self.__lumping=False |
265 |
self.__numEquations=0 |
266 |
self.__numSolutions=0 |
267 |
# now we can set the ceofficients: |
268 |
self._setCoefficient(**coef) |
269 |
|
270 |
def getCoefficient(self,name): |
271 |
""" |
272 |
@brief return the value of the coefficient name |
273 |
|
274 |
@param name |
275 |
""" |
276 |
return self.__coefficient[name] |
277 |
|
278 |
def setValue(self,**coefficients): |
279 |
""" |
280 |
@brief sets new values to coefficients |
281 |
|
282 |
@param coefficients |
283 |
""" |
284 |
self._setCoefficient(**coefficients) |
285 |
|
286 |
|
287 |
def _setCoefficient(self,**coefficients): |
288 |
""" |
289 |
@brief sets new values to coefficients |
290 |
|
291 |
@param coefficients |
292 |
""" |
293 |
|
294 |
# get the dictionary of the coefficinets been altered: |
295 |
alteredCoefficients={} |
296 |
for i,d in coefficients.iteritems(): |
297 |
if self.hasCoefficient(i): |
298 |
if d == None: |
299 |
alteredCoefficients[i]=escript.Data() |
300 |
elif isinstance(d,escript.Data): |
301 |
if d.isEmpty(): |
302 |
alteredCoefficients[i]=escript.Data() |
303 |
else: |
304 |
alteredCoefficients[i]=escript.Data(d,self.getFunctionSpaceOfCoefficient(i)) |
305 |
else: |
306 |
if self.__numEquations>0 and self.__numSolutions>0: |
307 |
alteredCoefficients[i]=escript.Data(d,self.getShapeOfCoefficient(i),self.getFunctionSpaceOfCoefficient(i)) |
308 |
else: |
309 |
alteredCoefficients[i]=escript.Data(d,self.getFunctionSpaceOfCoefficient(i)) |
310 |
else: |
311 |
raise ValueError,"Attempt to set undefined coefficient %s"%i |
312 |
# if numEquations and numSolutions is undefined we try identify their values based on the coefficients: |
313 |
if self.__numEquations<1 or self.__numSolutions<1: |
314 |
numEquations,numSolutions=identifyNumEquationsAndSolutions(self.getDomain().getDim(),alteredCoefficients) |
315 |
if self.__numEquations<1 and numEquations>0: self.__numEquations=numEquations |
316 |
if self.__numSolutions<1 and numSolutions>0: self.__numSolutions=numSolutions |
317 |
if self.debug() and self.__numEquations>0: print "PDE Debug: identified number of equations is ",self.__numEquations |
318 |
if self.debug() and self.__numSolutions>0: print "PDE Debug: identified number of solutions is ",self.__numSolutions |
319 |
|
320 |
# now we check the shape of the coefficient if numEquations and numSolutions are set: |
321 |
if self.__numEquations>0 and self.__numSolutions>0: |
322 |
for i in self.__coefficient.iterkeys(): |
323 |
if alteredCoefficients.has_key(i) and not alteredCoefficients[i].isEmpty(): |
324 |
if not self.getShapeOfCoefficient(i)==alteredCoefficients[i].getShape(): |
325 |
raise ValueError,"Expected shape for coefficient %s is %s but actual shape is %s."%(i,self.getShapeOfCoefficient(i),alteredCoefficients[i].getShape()) |
326 |
else: |
327 |
if not self.__coefficient[i].isEmpty(): |
328 |
if not self.getShapeOfCoefficient(i)==self.__coefficient[i].getShape(): |
329 |
raise ValueError,"Expected shape for coefficient %s is %s but actual shape is %s."%(i,self.getShapeOfCoefficient(i),self.__coefficient[i].getShape()) |
330 |
# overwrite new values: |
331 |
for i,d in alteredCoefficients.iteritems(): |
332 |
if self.debug(): print "PDE Debug: Coefficient %s has been altered."%i |
333 |
self.__coefficient[i]=d |
334 |
self.alteredCoefficient(i) |
335 |
|
336 |
# reset the HomogeneousConstraintFlag: |
337 |
self.__setHomogeneousConstraintFlag() |
338 |
if not "q" in alteredCoefficients and not self.__homogeneous_constraint: self.__rebuildSystem() |
339 |
|
340 |
def cleanCoefficients(self): |
341 |
""" |
342 |
@brief resets all coefficients to default values. |
343 |
""" |
344 |
self.__coefficient={} |
345 |
for i in _PDECoefficientTypes.iterkeys(): |
346 |
self.__coefficient[i]=escript.Data() |
347 |
|
348 |
def getShapeOfCoefficient(self,name): |
349 |
""" |
350 |
@brief return the shape of the coefficient name |
351 |
|
352 |
@param name |
353 |
""" |
354 |
if self.hasCoefficient(name): |
355 |
return _PDECoefficientTypes[name].buildShape(self.getNumEquations(),self.getNumSolutions(),self.getDomain().getDim()) |
356 |
else: |
357 |
raise ValueError,"Solution coefficient %s requested"%name |
358 |
|
359 |
def getFunctionSpaceOfCoefficient(self,name): |
360 |
""" |
361 |
@brief return the atoms of the coefficient name |
362 |
|
363 |
@param name |
364 |
""" |
365 |
if self.hasCoefficient(name): |
366 |
return _PDECoefficientTypes[name].getFunctionSpace(self.getDomain()) |
367 |
else: |
368 |
raise ValueError,"Solution coefficient %s requested"%name |
369 |
|
370 |
def alteredCoefficient(self,name): |
371 |
""" |
372 |
@brief annonced that coefficient name has been changed |
373 |
|
374 |
@param name |
375 |
""" |
376 |
if self.hasCoefficient(name): |
377 |
if _PDECoefficientTypes[name].isAlteringOperator(): self.__rebuildOperator() |
378 |
if _PDECoefficientTypes[name].isAlteringRightHandSide(): self.__rebuildRightHandSide() |
379 |
else: |
380 |
raise ValueError,"Solution coefficient %s requested"%name |
381 |
|
382 |
def __setHomogeneousConstraintFlag(self): |
383 |
""" |
384 |
@brief checks if the constraints are homogeneous and sets self.__homogeneous_constraint accordingly. |
385 |
""" |
386 |
self.__homogeneous_constraint=True |
387 |
q=self.getCoefficient("q") |
388 |
r=self.getCoefficient("r") |
389 |
if not q.isEmpty() and not r.isEmpty(): |
390 |
print (q*r).Lsup(), 1.e-13*r.Lsup() |
391 |
if (q*r).Lsup()>=1.e-13*r.Lsup(): self.__homogeneous_constraint=False |
392 |
if self.debug(): |
393 |
if self.__homogeneous_constraint: |
394 |
print "PDE Debug: Constraints are homogeneous." |
395 |
else: |
396 |
print "PDE Debug: Constraints are inhomogeneous." |
397 |
|
398 |
|
399 |
def hasCoefficient(self,name): |
400 |
""" |
401 |
@brief return true if name is the name of a coefficient |
402 |
|
403 |
@param name |
404 |
""" |
405 |
return self.__coefficient.has_key(name) |
406 |
|
407 |
def getFunctionSpaceForEquation(self): |
408 |
""" |
409 |
@brief return true if the test functions should use reduced order |
410 |
""" |
411 |
return self.__row_function_space |
412 |
|
413 |
def getFunctionSpaceForSolution(self): |
414 |
""" |
415 |
@brief return true if the interpolation of the solution should use reduced order |
416 |
""" |
417 |
return self.__column_function_space |
418 |
|
419 |
# ===== debug ============================================================== |
420 |
def setDebugOn(self): |
421 |
""" |
422 |
@brief |
423 |
""" |
424 |
self.__debug=not None |
425 |
|
426 |
def setDebugOff(self): |
427 |
""" |
428 |
@brief |
429 |
""" |
430 |
self.__debug=None |
431 |
|
432 |
def debug(self): |
433 |
""" |
434 |
@brief returns true if the PDE is in the debug mode |
435 |
""" |
436 |
return self.__debug |
437 |
|
438 |
#===== Lumping =========================== |
439 |
def setLumpingOn(self): |
440 |
""" |
441 |
@brief indicates to use matrix lumping |
442 |
""" |
443 |
if not self.isUsingLumping(): |
444 |
raise SystemError,"Lumping is not working yet! Talk to the experts" |
445 |
if self.debug() : print "PDE Debug: lumping is set on" |
446 |
self.__rebuildOperator() |
447 |
self.__lumping=True |
448 |
|
449 |
def setLumpingOff(self): |
450 |
""" |
451 |
@brief switches off matrix lumping |
452 |
""" |
453 |
if self.isUsingLumping(): |
454 |
if self.debug() : print "PDE Debug: lumping is set off" |
455 |
self.__rebuildOperator() |
456 |
self.__lumping=False |
457 |
|
458 |
def setLumping(self,flag=False): |
459 |
""" |
460 |
@brief set the matrix lumping flag to flag |
461 |
""" |
462 |
if flag: |
463 |
self.setLumpingOn() |
464 |
else: |
465 |
self.setLumpingOff() |
466 |
|
467 |
def isUsingLumping(self): |
468 |
""" |
469 |
@brief |
470 |
""" |
471 |
return self.__lumping |
472 |
|
473 |
#============ method business ========================================================= |
474 |
def setSolverMethod(self,solver=util.DEFAULT_METHOD): |
475 |
""" |
476 |
@brief sets a new solver |
477 |
""" |
478 |
if not solver==self.getSolverMethod(): |
479 |
self.__solver_method=solver |
480 |
if self.debug() : print "PDE Debug: New solver is %s"%solver |
481 |
self.__checkMatrixType() |
482 |
|
483 |
def getSolverMethod(self): |
484 |
""" |
485 |
@brief returns the solver method |
486 |
""" |
487 |
return self.__solver_method |
488 |
|
489 |
#============ tolerance business ========================================================= |
490 |
def setTolerance(self,tol=1.e-8): |
491 |
""" |
492 |
@brief resets the tolerance to tol. |
493 |
""" |
494 |
if not tol>0: |
495 |
raise ValueException,"Tolerance as to be positive" |
496 |
if tol<self.getTolerance(): self.__rebuildSolution() |
497 |
if self.debug() : print "PDE Debug: New tolerance %e",tol |
498 |
self.__tolerance=tol |
499 |
return |
500 |
def getTolerance(self): |
501 |
""" |
502 |
@brief returns the tolerance set for the solution |
503 |
""" |
504 |
return self.__tolerance |
505 |
|
506 |
#===== symmetry flag ========================== |
507 |
def isSymmetric(self): |
508 |
""" |
509 |
@brief returns true is the operator is considered to be symmetric |
510 |
""" |
511 |
return self.__sym |
512 |
|
513 |
def setSymmetryOn(self): |
514 |
""" |
515 |
@brief sets the symmetry flag to true |
516 |
""" |
517 |
if not self.isSymmetric(): |
518 |
if self.debug() : print "PDE Debug: Operator is set to be symmetric" |
519 |
self.__sym=True |
520 |
self.__checkMatrixType() |
521 |
|
522 |
def setSymmetryOff(self): |
523 |
""" |
524 |
@brief sets the symmetry flag to false |
525 |
""" |
526 |
if self.isSymmetric(): |
527 |
if self.debug() : print "PDE Debug: Operator is set to be unsymmetric" |
528 |
self.__sym=False |
529 |
self.__checkMatrixType() |
530 |
|
531 |
def setSymmetryTo(self,flag=False): |
532 |
""" |
533 |
@brief sets the symmetry flag to flag |
534 |
|
535 |
@param flag |
536 |
""" |
537 |
if flag: |
538 |
self.setSymmetryOn() |
539 |
else: |
540 |
self.setSymmetryOff() |
541 |
|
542 |
#===== order reduction ========================== |
543 |
def setReducedOrderOn(self): |
544 |
""" |
545 |
@brief switches to on reduced order |
546 |
""" |
547 |
self.setReducedOrderForSolutionOn() |
548 |
self.setReducedOrderForEquationOn() |
549 |
|
550 |
def setReducedOrderOff(self): |
551 |
""" |
552 |
@brief switches to full order |
553 |
""" |
554 |
self.setReducedOrderForSolutionOff() |
555 |
self.setReducedOrderForEquationOff() |
556 |
|
557 |
def setReducedOrderTo(self,flag=False): |
558 |
""" |
559 |
@brief sets order according to flag |
560 |
|
561 |
@param flag |
562 |
""" |
563 |
self.setReducedOrderForSolutionTo(flag) |
564 |
self.setReducedOrderForEquationTo(flag) |
565 |
|
566 |
|
567 |
#===== order reduction solution ========================== |
568 |
def setReducedOrderForSolutionOn(self): |
569 |
""" |
570 |
@brief switches to reduced order to interpolate solution |
571 |
""" |
572 |
new_fs=escript.ReducedSolution(self.getDomain()) |
573 |
if self.getFunctionSpaceForSolution()!=new_fs: |
574 |
if self.debug() : print "PDE Debug: Reduced order is used to interpolate solution." |
575 |
self.__column_function_space=new_fs |
576 |
self.__rebuildSystem(deep=True) |
577 |
|
578 |
def setReducedOrderForSolutionOff(self): |
579 |
""" |
580 |
@brief switches to full order to interpolate solution |
581 |
""" |
582 |
new_fs=escript.Solution(self.getDomain()) |
583 |
if self.getFunctionSpaceForSolution()!=new_fs: |
584 |
if self.debug() : print "PDE Debug: Full order is used to interpolate solution." |
585 |
self.__column_function_space=new_fs |
586 |
self.__rebuildSystem(deep=True) |
587 |
|
588 |
def setReducedOrderForSolutionTo(self,flag=False): |
589 |
""" |
590 |
@brief sets order for test functions according to flag |
591 |
|
592 |
@param flag |
593 |
""" |
594 |
if flag: |
595 |
self.setReducedOrderForSolutionOn() |
596 |
else: |
597 |
self.setReducedOrderForSolutionOff() |
598 |
|
599 |
#===== order reduction equation ========================== |
600 |
def setReducedOrderForEquationOn(self): |
601 |
""" |
602 |
@brief switches to reduced order for test functions |
603 |
""" |
604 |
new_fs=escript.ReducedSolution(self.getDomain()) |
605 |
if self.getFunctionSpaceForEquation()!=new_fs: |
606 |
if self.debug() : print "PDE Debug: Reduced order is used for test functions." |
607 |
self.__row_function_space=new_fs |
608 |
self.__rebuildSystem(deep=True) |
609 |
|
610 |
def setReducedOrderForEquationOff(self): |
611 |
""" |
612 |
@brief switches to full order for test functions |
613 |
""" |
614 |
new_fs=escript.Solution(self.getDomain()) |
615 |
if self.getFunctionSpaceForEquation()!=new_fs: |
616 |
if self.debug() : print "PDE Debug: Full order is used for test functions." |
617 |
self.__row_function_space=new_fs |
618 |
self.__rebuildSystem(deep=True) |
619 |
|
620 |
def setReducedOrderForEquationTo(self,flag=False): |
621 |
""" |
622 |
@brief sets order for test functions according to flag |
623 |
|
624 |
@param flag |
625 |
""" |
626 |
if flag: |
627 |
self.setReducedOrderForEquationOn() |
628 |
else: |
629 |
self.setReducedOrderForEquationOff() |
630 |
|
631 |
|
632 |
# ==== initialization ===================================================================== |
633 |
def __makeNewOperator(self): |
634 |
""" |
635 |
@brief |
636 |
""" |
637 |
return self.getDomain().newOperator( \ |
638 |
self.getNumEquations(), \ |
639 |
self.getFunctionSpaceForEquation(), \ |
640 |
self.getNumSolutions(), \ |
641 |
self.getFunctionSpaceForSolution(), \ |
642 |
self.__matrix_type) |
643 |
|
644 |
def __makeNewRightHandSide(self): |
645 |
""" |
646 |
@brief |
647 |
""" |
648 |
return escript.Data(0.,(self.getNumEquations(),),self.getFunctionSpaceForEquation(),True) |
649 |
|
650 |
def __makeNewSolution(self): |
651 |
""" |
652 |
@brief |
653 |
""" |
654 |
return escript.Data(0.,(self.getNumSolutions(),),self.getFunctionSpaceForSolution(),True) |
655 |
|
656 |
def __getFreshOperator(self): |
657 |
""" |
658 |
@brief |
659 |
""" |
660 |
if self.__operator.isEmpty(): |
661 |
self.__operator=self.__makeNewOperator() |
662 |
if self.debug() : print "PDE Debug: New operator allocated" |
663 |
else: |
664 |
self.__operator.setValue(0.) |
665 |
if self.debug() : print "PDE Debug: Operator reset to zero" |
666 |
return self.__operator |
667 |
|
668 |
def __getFreshRightHandSide(self): |
669 |
""" |
670 |
@brief |
671 |
""" |
672 |
if self.__righthandside.isEmpty(): |
673 |
self.__righthandside=self.__makeNewRightHandSide() |
674 |
if self.debug() : print "PDE Debug: New right hand side allocated" |
675 |
else: |
676 |
print "fix self.__righthandside*=0" |
677 |
self.__righthandside*=0. |
678 |
if self.debug() : print "PDE Debug: Right hand side reset to zero" |
679 |
return self.__righthandside |
680 |
|
681 |
# ==== rebuild switches ===================================================================== |
682 |
def __rebuildSolution(self,deep=False): |
683 |
""" |
684 |
@brief indicates the PDE has to be reolved if the solution is requested |
685 |
""" |
686 |
if self.__solution_isValid and self.debug() : print "PDE Debug: PDE has to be resolved." |
687 |
self.__solution_isValid=False |
688 |
if deep: self.__solution=escript.Data(deep) |
689 |
|
690 |
|
691 |
def __rebuildOperator(self,deep=False): |
692 |
""" |
693 |
@brief indicates the operator has to be rebuilt next time it is used |
694 |
""" |
695 |
if self.__operator_isValid and self.debug() : print "PDE Debug: Operator has to be rebuilt." |
696 |
self.__rebuildSolution(deep) |
697 |
self.__operator_isValid=False |
698 |
if deep: self.__operator=escript.Operator() |
699 |
|
700 |
def __rebuildRightHandSide(self,deep=False): |
701 |
""" |
702 |
@brief indicates the right hand side has to be rebuild next time it is used |
703 |
""" |
704 |
if self.__righthandside_isValid and self.debug() : print "PDE Debug: Right hand side has to be rebuilt." |
705 |
self.__rebuildSolution(deep) |
706 |
self.__righthandside_isValid=False |
707 |
if not self.__homogeneous_constraint: self.__rebuildOperator() |
708 |
if deep: self.__righthandside=escript.Data() |
709 |
|
710 |
def __rebuildSystem(self,deep=False): |
711 |
""" |
712 |
@brief annonced that all coefficient name has been changed |
713 |
""" |
714 |
self.__rebuildSolution(deep) |
715 |
self.__rebuildOperator(deep) |
716 |
self.__rebuildRightHandSide(deep) |
717 |
|
718 |
def __checkMatrixType(self): |
719 |
""" |
720 |
@brief reassess the matrix type and, if needed, initiates an operator rebuild |
721 |
""" |
722 |
new_matrix_type=self.getDomain().getSystemMatrixTypeId(self.getSolverMethod(),self.isSymmetric()) |
723 |
if not new_matrix_type==self.__matrix_type: |
724 |
if self.debug() : print "PDE Debug: Matrix type is now %d."%new_matrix_type |
725 |
self.__matrix_type=new_matrix_type |
726 |
self.__rebuildOperator(deep=True) |
727 |
|
728 |
#============ assembling ======================================================= |
729 |
def __copyConstraint(self,u): |
730 |
""" |
731 |
@brief copies the constrint condition into u |
732 |
""" |
733 |
q=self.getCoefficient("q") |
734 |
r=self.getCoefficient("r") |
735 |
if not q.isEmpty(): |
736 |
if r.isEmpty(): |
737 |
r2=escript.Data(0,u.getShape(),u.getFunctionSpace()) |
738 |
else: |
739 |
r2=escript.Data(r,u.getFunctionSpace()) |
740 |
u.copyWithMask(r2,escript.Data(q,u.getFunctionSpace())) |
741 |
|
742 |
def __applyConstraint(self,rhs_update=True): |
743 |
""" |
744 |
@brief applies the constraints defined by q and r to the system |
745 |
""" |
746 |
q=self.getCoefficient("q") |
747 |
r=self.getCoefficient("r") |
748 |
if not q.isEmpty() and not self.__operator.isEmpty(): |
749 |
# q is the row and column mask to indicate where constraints are set: |
750 |
row_q=escript.Data(q,self.getFunctionSpaceForEquation()) |
751 |
col_q=escript.Data(q,self.getFunctionSpaceForSolution()) |
752 |
u=self.__makeNewSolution() |
753 |
if r.isEmpty(): |
754 |
r_s=self.__makeNewSolution() |
755 |
else: |
756 |
r_s=escript.Data(r,self.getFunctionSpaceForSolution()) |
757 |
u.copyWithMask(r_s,col_q) |
758 |
if not self.__righthandside.isEmpty() and rhs_update: self.__righthandside-=self.__operator*u |
759 |
self.__operator.nullifyRowsAndCols(row_q,col_q,1.) |
760 |
|
761 |
def getOperator(self): |
762 |
""" |
763 |
@brief returns the operator of the PDE |
764 |
""" |
765 |
if not self.__operator_isValid: |
766 |
# some Constraints are applying for a lumpled stifness matrix: |
767 |
if self.isUsingLumping(): |
768 |
if self.getFunctionSpaceForEquation()==self.getFunctionSpaceForSolution(): |
769 |
raise TypeError,"Lumped matrix requires same order for equations and unknowns" |
770 |
if not self.getCoefficient("A").isEmpty(): |
771 |
raise Warning,"Lumped matrix does not allow coefficient A" |
772 |
if not self.getCoefficient("B").isEmpty(): |
773 |
raise Warning,"Lumped matrix does not allow coefficient B" |
774 |
if not self.getCoefficient("C").isEmpty(): |
775 |
raise Warning,"Lumped matrix does not allow coefficient C" |
776 |
if not self.getCoefficient("D").isEmpty(): |
777 |
raise Warning,"Lumped matrix does not allow coefficient D" |
778 |
if self.debug() : print "PDE Debug: New lumped operator is built." |
779 |
mat=self.__makeNewOperator(self) |
780 |
else: |
781 |
if self.debug() : print "PDE Debug: New operator is built." |
782 |
mat=self.__getFreshOperator() |
783 |
|
784 |
self.getDomain().addPDEToSystem(mat,escript.Data(), \ |
785 |
self.getCoefficient("A"), \ |
786 |
self.getCoefficient("B"), \ |
787 |
self.getCoefficient("C"), \ |
788 |
self.getCoefficient("D"), \ |
789 |
escript.Data(), \ |
790 |
escript.Data(), \ |
791 |
self.getCoefficient("d"), \ |
792 |
escript.Data(),\ |
793 |
self.getCoefficient("d_contact"), \ |
794 |
escript.Data()) |
795 |
if self.isUsingLumping(): |
796 |
self.__operator=mat*escript.Data(1,(self.getNumSolutions(),),self.getFunctionSpaceOfSolution(),True) |
797 |
else: |
798 |
self.__applyConstraint(rhs_update=False) |
799 |
self.__operator_isValid=True |
800 |
return self.__operator |
801 |
|
802 |
def getRightHandSide(self,ignoreConstraint=False): |
803 |
""" |
804 |
@brief returns the right hand side of the PDE |
805 |
|
806 |
@param ignoreConstraint |
807 |
""" |
808 |
if not self.__righthandside_isValid: |
809 |
if self.debug() : print "PDE Debug: New right hand side is built." |
810 |
self.getDomain().addPDEToRHS(self.__getFreshRightHandSide(), \ |
811 |
self.getCoefficient("X"), \ |
812 |
self.getCoefficient("Y"),\ |
813 |
self.getCoefficient("y"),\ |
814 |
self.getCoefficient("y_contact")) |
815 |
self.__righthandside_isValid=True |
816 |
if ignoreConstraint: self.__copyConstraint(self.__righthandside) |
817 |
return self.__righthandside |
818 |
|
819 |
def getSystem(self): |
820 |
""" |
821 |
@brief |
822 |
""" |
823 |
if not self.__operator_isValid and not self.__righthandside_isValid: |
824 |
if self.debug() : print "PDE Debug: New PDE is built." |
825 |
if self.isUsingLumping(): |
826 |
self.getRightHandSide(ignoreConstraint=True) |
827 |
self.getOperator() |
828 |
else: |
829 |
self.getDomain().addPDEToSystem(self.__getFreshOperator(),self.__getFreshRightHandSide(), \ |
830 |
self.getCoefficient("A"), \ |
831 |
self.getCoefficient("B"), \ |
832 |
self.getCoefficient("C"), \ |
833 |
self.getCoefficient("D"), \ |
834 |
self.getCoefficient("X"), \ |
835 |
self.getCoefficient("Y"), \ |
836 |
self.getCoefficient("d"), \ |
837 |
self.getCoefficient("y"), \ |
838 |
self.getCoefficient("d_contact"), \ |
839 |
self.getCoefficient("y_contact")) |
840 |
self.__operator_isValid=True |
841 |
self.__righthandside_isValid=True |
842 |
self.__applyConstraint() |
843 |
self.__copyConstraint(self.__righthandside) |
844 |
elif not self.__operator_isValid: |
845 |
self.getOperator() |
846 |
elif not self.__righthandside_isValid: |
847 |
self.getRightHandSide() |
848 |
return (self.__operator,self.__righthandside) |
849 |
|
850 |
def solve(self,**options): |
851 |
""" |
852 |
@brief solve the PDE |
853 |
|
854 |
@param options |
855 |
""" |
856 |
mat,f=self.getSystem() |
857 |
if self.isUsingLumping(): |
858 |
out=f/mat |
859 |
self.__copyConstraint(out) |
860 |
else: |
861 |
options[util.TOLERANCE_KEY]=self.getTolerance() |
862 |
options[util.METHOD_KEY]=self.getSolverMethod() |
863 |
options[util.SYMMETRY_KEY]=self.isSymmetric() |
864 |
if self.debug() : print "PDE Debug: solver options: ",options |
865 |
out=mat.solve(f,options) |
866 |
return out |
867 |
|
868 |
def getSolution(self,**options): |
869 |
""" |
870 |
@brief returns the solution of the PDE |
871 |
|
872 |
@param options |
873 |
""" |
874 |
if not self.__solution_isValid: |
875 |
if self.debug() : print "PDE Debug: PDE is resolved." |
876 |
self.__solution=self.solve(**options) |
877 |
self.__solution_isValid=True |
878 |
return self.__solution |
879 |
#============ some serivice functions ===================================================== |
880 |
def getDomain(self): |
881 |
""" |
882 |
@brief returns the domain of the PDE |
883 |
""" |
884 |
return self.__domain |
885 |
|
886 |
def getNumEquations(self): |
887 |
""" |
888 |
@brief returns the number of equations |
889 |
""" |
890 |
if self.__numEquations>0: |
891 |
return self.__numEquations |
892 |
else: |
893 |
raise ValueError,"Number of equations is undefined. Please specify argument numEquations." |
894 |
|
895 |
def getNumSolutions(self): |
896 |
""" |
897 |
@brief returns the number of unknowns |
898 |
""" |
899 |
if self.__numSolutions>0: |
900 |
return self.__numSolutions |
901 |
else: |
902 |
raise ValueError,"Number of solution is undefined. Please specify argument numSolutions." |
903 |
|
904 |
|
905 |
def checkSymmetry(self): |
906 |
""" |
907 |
@brief returns if the Operator is symmetric. This is a very expensive operation!!! The symmetry flag is not altered. |
908 |
""" |
909 |
raise SystemError,"checkSymmetry is not implemented yet" |
910 |
|
911 |
return None |
912 |
|
913 |
def getFlux(self,u): |
914 |
""" |
915 |
@brief returns the flux J_ij for a given u |
916 |
|
917 |
J_ij=A_{ijkl}u_{k,l}+B_{ijk}u_k-X_{ij} |
918 |
|
919 |
@param u argument of the operator |
920 |
|
921 |
""" |
922 |
raise SystemError,"getFlux is not implemented yet" |
923 |
return None |
924 |
|
925 |
def applyOperator(self,u): |
926 |
""" |
927 |
@brief applies the operator of the PDE to a given solution u in weak from |
928 |
|
929 |
@param u argument of the operator |
930 |
|
931 |
""" |
932 |
return self.getOperator()*escript.Data(u,self.getFunctionSpaceForSolution()) |
933 |
|
934 |
def getResidual(self,u): |
935 |
""" |
936 |
@brief return the residual of u in the weak from |
937 |
|
938 |
@param u |
939 |
""" |
940 |
return self.applyOperator(u)-self.getRightHandSide() |
941 |
|
942 |
class Poisson(LinearPDE): |
943 |
""" |
944 |
@brief Class to define a Poisson equstion problem: |
945 |
|
946 |
class to define a linear PDE of the form |
947 |
|
948 |
-u_{,jj} = f |
949 |
|
950 |
with boundary conditons: |
951 |
|
952 |
n_j*u_{,j} = 0 |
953 |
|
954 |
and constraints: |
955 |
|
956 |
u=0 where q>0 |
957 |
|
958 |
""" |
959 |
|
960 |
def __init__(self,domain=None,f=escript.Data(),q=escript.Data()): |
961 |
LinearPDE.__init__(self,domain=identifyDomain(domain,{"f":f, "q":q})) |
962 |
self._setCoefficient(A=numarray.identity(self.getDomain().getDim())) |
963 |
self.setSymmetryOn() |
964 |
self.setValue(f,q) |
965 |
|
966 |
def setValue(self,f=escript.Data(),q=escript.Data()): |
967 |
self._setCoefficient(Y=f,q=q) |
968 |
|
969 |
|
970 |
# $Log$ |
971 |
# Revision 1.2 2004/12/15 07:08:27 jgs |
972 |
# *** empty log message *** |
973 |
# |
974 |
# Revision 1.1.2.2 2004/12/14 03:55:01 jgs |
975 |
# *** empty log message *** |
976 |
# |
977 |
# Revision 1.1.2.1 2004/12/12 22:53:47 gross |
978 |
# linearPDE has been renamed LinearPDE |
979 |
# |
980 |
# Revision 1.1.1.1.2.7 2004/12/07 10:13:08 gross |
981 |
# GMRES added |
982 |
# |
983 |
# Revision 1.1.1.1.2.6 2004/12/07 03:19:50 gross |
984 |
# options for GMRES and PRES20 added |
985 |
# |
986 |
# Revision 1.1.1.1.2.5 2004/12/01 06:25:15 gross |
987 |
# some small changes |
988 |
# |
989 |
# Revision 1.1.1.1.2.4 2004/11/24 01:50:21 gross |
990 |
# Finley solves 4M unknowns now |
991 |
# |
992 |
# Revision 1.1.1.1.2.3 2004/11/15 06:05:26 gross |
993 |
# poisson solver added |
994 |
# |
995 |
# Revision 1.1.1.1.2.2 2004/11/12 06:58:15 gross |
996 |
# a lot of changes to get the linearPDE class running: most important change is that there is no matrix format exposed to the user anymore. the format is chosen by the Domain according to the solver and symmetry |
997 |
# |
998 |
# Revision 1.1.1.1.2.1 2004/10/28 22:59:22 gross |
999 |
# finley's RecTest.py is running now: problem in SystemMatrixAdapater fixed |
1000 |
# |
1001 |
# Revision 1.1.1.1 2004/10/26 06:53:56 jgs |
1002 |
# initial import of project esys2 |
1003 |
# |
1004 |
# Revision 1.3.2.3 2004/10/26 06:43:48 jgs |
1005 |
# committing Lutz's and Paul's changes to brach jgs |
1006 |
# |
1007 |
# Revision 1.3.4.1 2004/10/20 05:32:51 cochrane |
1008 |
# Added incomplete Doxygen comments to files, or merely put the docstrings that already exist into Doxygen form. |
1009 |
# |
1010 |
# Revision 1.3 2004/09/23 00:53:23 jgs |
1011 |
# minor fixes |
1012 |
# |
1013 |
# Revision 1.1 2004/08/28 12:58:06 gross |
1014 |
# SimpleSolve is not running yet: problem with == of functionsspace |
1015 |
# |
1016 |
# |