1!/*****************************************************************************/ 2! * 3! * Elmer, A Finite Element Software for Multiphysical Problems 4! * 5! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland 6! * 7! * This library is free software; you can redistribute it and/or 8! * modify it under the terms of the GNU Lesser General Public 9! * License as published by the Free Software Foundation; either 10! * version 2.1 of the License, or (at your option) any later version. 11! * 12! * This library is distributed in the hope that it will be useful, 13! * but WITHOUT ANY WARRANTY; without even the implied warranty of 14! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15! * Lesser General Public License for more details. 16! * 17! * You should have received a copy of the GNU Lesser General Public 18! * License along with this library (in file ../LGPL-2.1); if not, write 19! * to the Free Software Foundation, Inc., 51 Franklin Street, 20! * Fifth Floor, Boston, MA 02110-1301 USA 21! * 22! *****************************************************************************/ 23! 24! * 25! * Authors: Juhani Kataja 26! * 27! 28! ****************************************************************************/ 29 30MODULE zirka ! Pointwise zirka {{{ 31USE ISO_C_BINDING, ONLY: C_INT, C_LOC, C_PTR, C_F_POINTER 32USE GeneralUtils 33USE DefUtils 34implicit none 35 36private 37 38INTEGER, PARAMETER :: STACKSIZEINCREMENT = 20 39INTEGER, PARAMETER :: POS_SATURATION = 1 40INTEGER, PARAMETER :: NEG_SATURATION = -1 41INTEGER, PARAMETER :: NO_SATURATION = 0 42 43TYPE, public :: SplineLoop_t ! {{{ 44 REAL(KIND=dp), allocatable :: BHasc(:,:), BHdesc(:,:), BHsat(:,:) 45 REAL(KIND=dp), pointer :: r_asc(:) => NULL(), r_desc(:) => NULL() , r_sat(:) => NULL() ! Needs to be pointer required by InterpolateCurve 46 real(kind=dp), private :: BLimits(2) 47 LOGICAL, private :: newmethod = .false. 48 CONTAINS 49 PROCEDURE, private :: eval_1 => EvalSplineLoop 50 procedure, private :: eval_2 => EvalSplineLoopSingle 51 generic, public :: eval => eval_1, eval_2 52END TYPE SplineLoop_t ! }}} 53 54TYPE :: RevCurve_t ! {{{ 55 REAL(kind=dp) :: Bp, Bq 56 REAL(kind=dp) :: a, b, c, dBrev, dHrev 57 INTEGER :: depth ! if 0, this is ascending or descending master curve depending on Bp and Bq 58 CLASS(RevCurve_t), POINTER :: parent => NULL() 59 TYPE(SplineLoop_t), POINTER :: bigloop => NULL() 60 PROCEDURE(SimpleEvalRevCurve), POINTER :: simple_eval => SimpleEvalRevCurve 61 CONTAINS 62 ! PROCEDURE :: simple_eval => SimpleEvalRevCurve 63 PROCEDURE :: eval => RecurEvalCurve 64END TYPE RevCurve_t ! }}} 65 66type, public :: ZirkaABC_t ! {{{ 67 REAL(KIND=dp), dimension(1:4) :: Coeffs = [7.73,2.76,-28.63,28.36] 68 REAL(KIND=dp) :: b_mult = 0.22 69 REAL(KIND=dp) :: c_mult = 0.125 70 contains 71 procedure, public :: GetABC 72end type ! }}} 73 74TYPE, public :: MasterCurve_t ! {{{ 75 TYPE(RevCurve_t), POINTER :: children(:) ! We should always have revcurve_t % depth + 2 to be right index here.. 76 CLASS(RevCurve_t), POINTER, public :: Head => NULL() 77 TYPE(RevCurve_t), POINTER, private :: rc_asc => NULL(), rc_desc => NULL() ! Should this a pointer? 78 REAL(KIND=dp), PUBLIC :: B0(3) 79 REAL(KIND=dp), ALLOCATABLE, PRIVATE :: BH(:,:) 80 REAL(KIND=dp), POINTER, PRIVATE :: r_bh(:) => null() 81 TYPE(ZirkaABC_t), POINTER, PRIVATE :: ABCparams => NULL() 82 integer, private :: CacheSubSample = 2 83 integer, private :: saturation 84 contains 85 procedure, public :: eval => mc_eval 86 procedure, public :: printme => mc_printme 87 procedure, public :: addstack => mc_addstack 88 procedure, public :: drive => HBDrive 89 procedure, public :: insaturation 90END TYPE MasterCurve_t ! }}} 91 92TYPE, public :: GlobalHysteresisModel_t ! {{{ 93 TYPE(MasterCurve_t), ALLOCATABLE :: Curves(:,:) 94 TYPE(SplineLoop_t), POINTER :: Masterloop => NULL() 95 TYPE(ZirkaABC_t), POINTER :: ABCparams => NULL() 96 integer :: CacheSubSample 97 logical :: initialized = .false. 98END TYPE GlobalHysteresisModel_t ! }}} 99 100 101interface MasterCurve_t 102 module procedure init_master_curve 103end interface 104 105interface printeval 106 module procedure rc_printeval, mc_printeval 107end interface 108 109public :: InitSplineLoop, printeval 110 111CONTAINS 112 113!------------------------------------------------------------------------------- 114!> Returns true if rc is ascending 115PURE FUNCTION ascending(rc) result(Asc) ! {{{ 116 CLASS(RevCurve_t), intent(in) :: rc 117 LOGICAL :: asc 118!------------------------------------------------------------------------------- 119 if (rc % Bp < rc % Bq) then 120 asc = .true. 121 return 122 end if 123 asc = .false. 124END FUNCTION ! }}} 125!------------------------------------------------------------------------------- 126 127!------------------------------------------------------------------------------- 128!> Returns true if rc is descending 129PURE FUNCTION descending(rc) result(desc) ! {{{ 130 CLASS(RevCurve_t), intent(in) :: rc 131 LOGICAL :: desc 132!------------------------------------------------------------------------------- 133 desc = .not. ascending(rc) 134END FUNCTION ! }}} 135!------------------------------------------------------------------------------- 136 137!------------------------------------------------------------------------------- 138!> Returns saturatino value of MasterCurve_t mc 139FUNCTION InSaturation(mc, B) RESULT(saturation) ! {{{ 140 class(MasterCurve_t) :: mc 141 real(kind=dp) :: B 142 integer :: saturation 143 if (B < mc % head % bigloop % blimits(1)) then 144 saturation = NEG_SATURATION 145 return 146 end if 147 if (B > mc % head % bigloop % blimits(2)) then 148 saturation = POS_SATURATION 149 return 150 end if 151 saturation = NO_SATURATION 152END FUNCTION InSaturation ! }}} 153!------------------------------------------------------------------------------- 154 155!------------------------------------------------------------------------------- 156!> Returns a-b-c parameters appearing in the Zirka model 157SUBROUTINE GetABC(this, dBout, dBrev, a, b, c) ! {{{ 158 IMPLICIT NONE 159 !------------------------------------------------------------------------------- 160 class(ZirkaABC_t) :: this 161 REAL(KIND=dp), INTENT(IN) :: dBout, dBrev 162 REAL(KIND=dp), INTENT(OUT) :: a, b, c 163 !------------------------------------------------------------------------------- 164 REAL(KIND=dp) :: beta 165 INTEGER :: k 166 !------------------------------------------------------------------------------- 167 168 beta = dBrev/dBout 169 170 a = 0.0_dp 171 DO k = 1,4 172 a = a + this % Coeffs(k)*(beta**(k-1)) 173 END DO 174 a = a * dBrev 175 b = this % b_mult*(1-beta) 176 c = this % c_mult 177 178END SUBROUTINE ! }}} 179!------------------------------------------------------------------------------- 180 181!------------------------------------------------------------------------------- 182!> Initialize spline loop from ascending hysteretic curve and single 183!> valued curve for saturation region 184!> BHasc and BHsingle are assumed to be of shape (N,2) 185!------------------------------------------------------------------------------- 186FUNCTION InitSplineLoop(BHasc, BHsingle) RESULT(Loop) ! {{{ 187 !------------------------------------------------------------------------------- 188 IMPLICIT NONE 189 REAL(KIND=dp), intent(in) :: BHasc(:,:), BHsingle(:,:) 190 TYPE(SplineLoop_t), POINTER :: Loop 191 !------------------------------------------------------------------------------- 192 INTEGER :: N, M 193 N = size(BHasc,1) 194 M = size(BHsingle,1) 195 196 allocate(loop) 197 allocate(loop % BHasc(n, 2), loop % BHdesc(n,2), loop%BHsat(m,2), & 198 loop%r_asc(n), loop % r_desc(n), loop % r_sat(m)) 199 200 loop % BHasc(:,1:2) = BHasc(:,1:2) 201 loop % BHdesc(:,1) = -BHasc(N:1:-1,1) 202 loop % BHdesc(:,2) = -BHasc(N:1:-1,2) 203 loop % BHsat = BHsingle 204 205 CALL CubicSpline(N, loop % BHasc(1:N, 1), loop % BHasc(1:N, 2), loop % r_asc) 206 CALL CubicSpline(N, loop % BHdesc(1:N, 1), loop % BHdesc(1:N, 2), loop % r_desc) 207 CALL CubicSpline(M, loop % BHsat(1:M, 1), loop % BHsat(1:M, 2), loop % r_sat) 208 loop % newmethod = .true. 209 loop % blimits(1) = bhasc(1,1) 210 loop % blimits(2) = - loop % blimits(1) 211 212END FUNCTION InitSplineLoop ! }}} 213 214function init_master_curve(bigloop, ABCParams, & 215 init, b0, initseq, n_cachesubsample) result(mc)! {{{ 216 IMPLICIT NONE 217 TYPE(MasterCurve_t) :: mc 218 TYPE(ZirkaABC_t), POINTER :: ABCParams 219 TYPE(RevCurve_t), POINTER :: rca, rcd 220 TYPE(SplineLoop_t), POINTER :: bigloop 221 REAL(KIND=dp), optional, intent(in) :: B0(3) 222 INTEGER :: k, N 223 integer, optional :: n_cachesubsample 224 logical, optional :: init 225 real(kind=dp), optional :: initseq(:) 226 227 if (present(n_cachesubsample)) mc % CacheSubSample = n_cachesubsample 228 229 mc % ABCParams => ABCParams 230 ! allocate(mc) 231 allocate(mc % children(1:STACKSIZEINCREMENT)) 232 233 DO k = 2,UBOUND(mc % children,1) 234 mc % children(k) % parent => mc % children(k-1) 235 mc % children(k) % bigloop => bigloop 236 END DO 237 mc % children(1) % bigloop => bigloop 238 allocate(mc % rc_asc, mc % rc_desc) 239 mc % children(1) % parent => mc % rc_desc 240 241 rca => mc % rc_asc 242 rcd => mc % rc_desc 243 rca % bigloop => bigloop 244 rcd % bigloop => bigloop 245 246 N = size(rca % bigloop % BHAsc,1) 247 248 rcd % Bp = rcd % bigloop % BHdesc(N,1) 249 rcd % Bq = rcd % bigloop % BHasc(1,1) 250 rcd % depth = 0 251 rcd % parent => rca 252 rcd % simple_eval => SimpleEvalDescendingSpline 253 254 rca % Bp = rca % bigloop % BHasc(1,1) 255 rca % Bq = rca % bigloop % BHdesc(N,1) 256 rca % depth = 0 257 rca % parent => rcd 258 rca % simple_eval => SimpleEvalAscendingSpline 259 260 mc % head => rcd 261 262 if (present(init) .or. present(initseq)) then 263 if (init .or. present(initseq)) then 264 if (.not. present(initseq)) then ! {{{ 265 block 266 integer, parameter :: nr = 12 267 real :: up 268 up = rcd % bigloop % bhasc(N,1) 269 do n=0,nr-2 ! nr-1 270 call mc % drive( up * ((-0.5_dp)**n), cache=.false.) 271 end do 272 n = nr-1 273 call mc % drive( up * ((-0.5_dp)**n), cache=.true.) 274 end block 275 else 276 do n=1,size(initseq)-1 277 call mc % drive(initseq(n), cache = .false.) 278 end do 279 n = size(initseq) 280 call mc % drive(initseq(n), cache = .true.) 281 end if ! }}} 282 end if 283 end if 284 285 if (present(b0)) then 286 mc % b0(1:3) = b0(1:3) 287 else 288 mc % b0 = [0.0_dp, 1.0_dp, 0.0_dp] 289 end if 290 291END function ! }}} 292 293 294 295! Returns curve where B can be evaluated. On saturation, returns the descending (positive saturation) or ascending (negative 296! saturation). 297! 298RECURSIVE FUNCTION RecurseDepth(rc, B) result (rc_p)! {{{ 299 IMPLICIT NONE 300 CLASS(RevCurve_t), POINTER, INTENT(IN) :: rc 301 CLASS(RevCurve_t), POINTER :: rc_p 302 REAL(kind=dp) :: B 303 integer :: tmp 304 305#if DEBUG>1 306 integer :: branch 307 branch = -1 308#endif 309 310 tmp = rc % depth 311 ! print *, 'recursedepth: depth, B, Bp, Bq', tmp, B, rc % Bp, rc % Bq 312 outer: block 313 if (rc % Bp < rc % Bq) then ! Ascending curve (here be dragons) 314 315 if (rc % Bp < B .and. B < rc % Bq) THEN 316 rc_p => rc 317#if DEBUG>1 318 branch = 1 319 print *, 'branch: ', branch, rc_p % depth 320#endif 321 exit outer 322 end if 323 if (rc % Bq <= B) then 324 325 if (rc % depth == 0 .and. Ascending(rc)) then ! in positive saturation return descending master 326 rc_p => rc % parent 327#if DEBUG>1 328 branch = 2 329 print *, 'branch: ', branch, rc_p % depth 330#endif 331 exit outer 332 end if 333#if DEBUG>1 334 branch = 3 335 print *, 'branch: ', branch 336#endif 337 rc_p => RecurseDepth(rc % parent % parent, B) ! TODO: Think these through again 338 exit outer 339 end if 340 if (rc % Bp >= B) then 341 if (rc % depth == 0 .and. ascending(rc)) then ! in negative saturation return ascending master 342 rc_p => rc 343#if DEBUG>1 344 branch = 4 345 print *, 'branch: ', branch, rc_p % depth 346#endif 347 exit outer ! Negative saturation 348 end if 349#if DEBUG>1 350 branch = 5 351 print *, 'branch: ', branch 352#endif 353 rc_p => RecurseDepth(rc % parent, B) 354 exit outer 355 end if 356 else ! Descending curve : rc % Bp > rc % Bq 357 if (rc % Bp > B .and. B > rc % Bq) then ! inside 358 rc_p => rc 359#if DEBUG>1 360 branch = 6 361 print *, 'branch: ', branch, rc_p % depth 362#endif 363 exit outer 364 end if 365 if (B <= rc%Bq) then 366 if (rc % depth == 0 .and. descending(rc)) then ! in negative saturation return ascending master 367 rc_p => rc % parent 368#if DEBUG>1 369 branch = 7 370 print *, 'branch: ', branch, rc_p % depth 371#endif 372 exit outer 373 end if 374#if DEBUG>1 375 branch = 8 376 print *, 'branch: ', branch 377#endif 378 rc_p => RecurseDepth(rc % parent % parent, B) 379 end if 380 if (B >= rc % Bp) then 381 if (rc % depth == 0 .and. descending(rc)) then ! in positive saturation return descending master 382 rc_p => rc 383#if DEBUG>1 384 branch = 9 385 print *, 'branch: ', branch, rc_p % depth 386#endif 387 exit outer 388 end if 389#if DEBUG>1 390 branch = 10 391 print *, 'branch: ', branch 392#endif 393 rc_p => RecurseDepth(rc % parent, B) 394 exit outer 395 end if 396 end if 397 end block outer 398 399END FUNCTION ! }}} 400 401SUBROUTINE EvalSplineLoop(this, B, Hasc, Hdesc, dHasc, dHdesc) ! {{{ 402 IMPLICIT NONE 403 CLASS(SplineLoop_t), INTENT(IN) :: this 404 REAL(KIND=dp), INTENT(IN) :: B 405 REAL(KIND=dp), INTENT(OUT) :: Hasc, Hdesc 406 REAL(KIND=dp), INTENT(OUT), optional :: dHasc, dHdesc 407 408 if (this % newmethod) then 409 if (B < this % blimits(1) .or. B > this % blimits(2)) then ! negative saturation 410 call this % eval(B, Hasc) 411 Hdesc = Hasc 412 return 413 end if 414 end if 415 416 Hasc = InterpolateCurve( & 417 this%BHasc(:,1), & 418 this%BHasc(:,2), & 419 B, this%r_asc) 420 421 Hdesc = InterpolateCurve( & 422 this%BHdesc(:,1), & 423 this%BHdesc(:,2), & 424 B, this%r_desc) 425 426 if (present(dHasc)) then 427 dHasc = DerivateCurve( & 428 this%BHasc(:,1), & 429 this%BHasc(:,2), & 430 B, this%r_asc) 431 end if 432 433 if (present(dHdesc)) then 434 Hdesc = DerivateCurve( & 435 this%BHdesc(:,1), & 436 this%BHdesc(:,2), & 437 B, this%r_desc) 438 end if 439 440 441END SUBROUTINE EvalSplineLoop ! }}} 442 443SUBROUTINE EvalSplineLoopSingle(this, B, HSingle) ! {{{ 444 IMPLICIT NONE 445 CLASS(SplineLoop_t), INTENT(IN) :: this 446 REAL(KIND=dp), VALUE :: B 447 REAL(KIND=dp), INTENT(OUT) :: HSingle 448 logical :: neg 449 450 neg = B < 0 451 if (neg) B = -B 452 453 HSingle = InterpolateCurve( & 454 this % BHsat(:,1), & 455 this % BHsat(:,2), & 456 B, this % r_sat) 457 458 if (neg) Hsingle = -Hsingle 459 460END SUBROUTINE EvalSplineLoopSingle ! }}} 461 462! Evaluate rc at B. Don't check if B is consistent with rc%Bp and rc%Bq 463RECURSIVE FUNCTION SimpleEvalRevCurve(rc, B) result(H) ! {{{ 464 CLASS(RevCurve_t), INTENT(IN), target :: rc 465 REAL(KIND=dp), INTENT(IN) :: B 466 REAL(KIND=dp) :: H 467 !------------------------------------------------------------------------------- 468 REAL(KIND=dp) :: Hap, Hp, x, dHout, dH, HMAsc, HMDesc 469 !------------------------------------------------------------------------------- 470#if 0 471 depthtest: if (rc%depth > 2) then 472 Hp = rc % parent % simple_eval(B) 473 Hap = rc % parent % parent % simple_eval(B) 474 else 475 call rc % bigloop % eval(B, HMAsc, HMDesc) 476 IF (rc % depth == 2) then 477 Hap = HMDesc 478 Hp = rc % parent % simple_eval(B) 479 exit depthtest 480 end if 481 IF (rc % depth == 1 ) then 482 Hap = HMAsc 483 Hp = HMDesc 484 exit depthtest 485 END IF 486 IF (rc % depth == 0) H = HMDesc 487 IF (rc % depth == -1) H = HMAsc 488 endif depthtest 489#else 490 Hp = rc % parent % simple_eval(B) 491 Hap = rc % parent % parent % simple_eval(B) 492#endif 493 if (rc % depth > 0) then 494 x = (rc % Bq - B )/rc % dBrev 495 dHout = Hap - Hp 496 dH = rc % dHrev*(1.0_dp-rc % b) * x * exp(-rc%a*(1.0_dp-x)) + dHout* rc % b* (x**rc%c) 497 H = Hap - dH 498 end if 499END FUNCTION ! }}} 500 501RECURSIVE FUNCTION SimpleEvalAscendingSpline(rc, B) result(H) ! {{{ 502 CLASS(Revcurve_t), INTENT(in), target :: rc 503 REAL(KIND=dp), intent(in) :: B 504 REAL(KIND=dp) :: H, Hdesc 505 call rc % bigloop % eval(B, H, Hdesc) 506END FUNCTION ! }}} 507 508RECURSIVE FUNCTION SimpleEvalDescendingSpline(rc, B) result(H) ! {{{ 509 CLASS(Revcurve_t), INTENT(in), target :: rc 510 REAL(KIND=dp), intent(in) :: B 511 REAL(KIND=dp) :: H, Hasc 512 call rc % bigloop % eval(B, Hasc, H) 513END FUNCTION ! }}} 514 515! Evaluate master curve at B, 516function mc_eval(mc, B, dhdb, cached) result(H) ! {{{ 517 class(MasterCurve_t) :: mc 518 real(kind=dp), intent(in) :: B 519 real(kind=dp) :: H 520 real(kind=dp), optional, intent(out) :: dhdb 521 real(kind=dp) :: dhasc, dhdesc, hasc, hdesc 522 logical, optional :: cached 523 524 if (present(cached) .and. cached ) then 525 H = InterpolateCurve( & 526 mc % BH(:,1), & 527 mc % BH(:,2), & 528 B, mc % r_BH) 529 if (present(dhdb)) then 530 dhdb = DerivateCurve( & 531 mc % BH(:,1), & 532 mc % BH(:,2), & 533 B, mc % r_BH) 534 end if 535 else 536 H = RecurEvalCurve(mc % head, B) 537 if(present(dhdb)) dhdb = 0.0_dp 538 end if 539end function ! }}} 540 541FUNCTION RecurEvalCurve(rc, B) result (H) ! {{{ 542 IMPLICIT NONE 543 CLASS(RevCurve_t), TARGET, INTENT(IN) :: rc 544 REAL(KIND=dp), INTENT(IN) :: B 545 CLASS(RevCurve_t), POINTER :: rc_p 546 real(kind=dp) :: H 547 integer :: d 548 549 rc_p => rc 550 d = rc_p % depth 551 rc_p => RecurseDepth(rc_p, B) 552 H = rc_p % simple_eval(B) 553 554END FUNCTION ! }}} 555 556SUBROUTINE HBDrive(mc, B, cache) ! {{{ 557 IMPLICIT NONE 558 CLASS(MasterCurve_t), INTENT(INOUT) :: mc 559 CLASS(RevCurve_t), POINTER :: rc 560 REAL(KIND=dp), INTENT(IN) :: B 561 !------------------------------------------------------------------------------- 562 CLASS(RevCurve_t), POINTER :: rc_p 563 integer :: m, n, n_cache 564 logical, optional :: cache 565 logical :: cache_ 566 real(kind=dp) :: bmax, bmin 567 !------------------------------------------------------------------------------- 568 569#if DEBUG>1 570 print *, 'hbdrive driving stack to ', B 571#endif 572 rc_p => RecurseDepth(mc % head, B) 573 call AddStack(rc_p, mc, B) 574 rc => rc_p 575 576 if (.not. present(cache)) then 577 cache_ = .true. 578 else 579 cache_ = cache 580 end if 581 582 if(cache_) then 583#if DEBUG>1 584 print *, rc % bigloop % blimits(1:2) 585#endif 586 n = size(rc % bigloop % bhsat,1) 587 bmax = rc % bigloop % bhsat(n,1) 588 bmin = -rc % bigloop % bhsat(n,1) 589 n_cache = size(rc % bigloop % bhsat,1) / mc % cachesubsample 590 591 IF(.not. ALLOCATED(mc % BH)) ALLOCATE(mc % BH(n_cache,2)) 592 IF(.not. associated(mc % r_bh)) ALLOCATE(mc % r_bh(n_cache)) 593 594 DO n = 1, size(mc % BH,1) 595 associate( Bhere => (bmin + (n-1)*(bmax-bmin)/n_cache ) ) 596 mc % BH(n,1) = Bhere 597 mc % BH(n,2) = mc % eval(Bhere, cached =.false.) 598 end associate 599 END DO 600 n = size(mc % bh,1) 601 CALL CubicSpline(n, mc % BH(:,1), mc % BH(:,2), mc % r_bh, monotone=.true.) 602 end if 603 604END SUBROUTINE !}}} 605 606subroutine mc_addstack(mc, B) ! {{{ 607 class(MasterCurve_t) :: mc 608 real(kind=dp), intent(in) :: B 609 610 call addstack(mc % head, mc, B) 611end subroutine ! }}} 612 613subroutine mc_printme(mc) ! {{{ 614 class(MasterCurve_t) :: mc 615 call PrintRevCurve(mc % head) 616end subroutine ! }}} 617 618SUBROUTINE AddStack(parent, master, B) ! {{{ 619 IMPLICIT NONE 620 CLASS (RevCurve_t), INTENT(INOUT), POINTER :: parent 621 TYPE(MasterCurve_t), INTENT(INOUT) :: master 622 CLASS(RevCurve_t), POINTER:: x 623 REAL(KIND=dp), INTENT(IN) :: B 624 !------------------------------------------------------------------------------- 625 real(KIND=dp) :: HMAsc, HMDesc, Hpp, hp, dBout 626 integer :: d, d_add 627 628 ! TODO: This looks weird 629 ! print *, 'recurse: ', parent % depth 630 ! Check for saturation 631#if 0 632 if (descending(parent) .and. B > parent % Bp) then ! depth == 0 => descending outer 633 master % head => parent 634 return 635 end if 636 if (ascending(parent) .and. B < parent % Bp) then ! depth == -1 => ascending outer 637 master % head => parent 638 return 639 end if 640#else 641 if (descending(parent)) then ! descending master 642 if ( B <= parent % Bq ) then ! in negative saturation return ascending master 643 master % head => parent % parent 644 return 645 end if 646 if ( B > parent % Bp ) then ! in positive saturation return descending master 647 master % head => parent 648 return 649 end if 650 end if 651 if (ascending(parent)) then ! ascending master 652 if ( B >= parent % Bq ) then ! in positive saturation return descending master 653 master % head => parent % parent 654 return 655 end if 656 if ( B < parent % Bp ) then ! in negative saturation return ascending master 657 master % head => parent 658 return 659 end if 660 end if 661#endif 662 663 664 ! Not in saturation 665 parent => check_reallocate(parent, parent % depth + 1) 666 x => master % children(parent % depth + 1) 667 x % depth = parent % depth + 1 668 669 ! parent => master % children ( parent % depth + d_add - 1) 670 x % parent => parent 671 x % Bp = B 672 x % Bq = x % parent % Bp 673 x % dBrev = x%Bq - x%Bp 674 dBout = x % Bq - parent % parent % Bp 675 call master % ABCparams % GetABC(abs(dBout), abs(x % dBrev), x%a, x%b, x%c) 676 Hpp = x % parent % parent % simple_eval(B) 677 Hp = x % parent % simple_eval(B) 678 x % dHrev = Hpp - Hp; 679 ! parent => x 680 master % head => x 681 682 683 CONTAINS 684 function check_reallocate(oldparent, newdepth) result(newparent) ! {{{ 685 IMPLICIT NONE 686 INTEGER :: newdepth 687 !------------------------------------------------------------------------------- 688 TYPE(Revcurve_t), POINTER :: newchildren(:) 689 ! type(Revcurve_t), pointer :: x 690 TYPE(SplineLoop_t), POINTER :: bigloop 691 INTEGER :: bufbound, k 692 CLASS(RevCurve_t), POINTER :: oldparent, newparent 693 !------------------------------------------------------------------------------- 694 bufbound = ubound(master % children, 1) 695 ! bigloop => master % children(1) % bigloop 696 bigloop => oldparent % bigloop 697 ! nullify(parent) 698 699 if (newdepth > 1) THEN 700 IF (newdepth > bufbound) THEN 701 allocate(newchildren(1:bufbound + STACKSIZEINCREMENT)) 702 newchildren(1:bufbound) = master % children(1:bufbound) 703 do k =1,bufbound 704 nullify(master % children(k) % bigloop) 705 end do 706 nullify(master % head) 707 708 deallocate(master % children) 709 allocate(master % children(1:bufbound + STACKSIZEINCREMENT), source=newchildren) 710 do k =1,newdepth 711 master % children(k) % bigloop => bigloop 712 end do 713 714 master % head => master % children(newdepth) 715 END IF 716 master % children(newdepth) % bigloop => bigloop 717 master % head => master % children(newdepth) 718 do k = 2, newdepth 719 master % children(k) % parent => master % children(k-1) 720 end do 721 newparent => master % children(newdepth-1) 722 ! master % children(-1) % parent => master % children(0) 723 else 724 newparent => oldparent 725 end if 726 727 728 END function !}}} 729END SUBROUTINE AddStack ! }}} 730 731subroutine mc_printeval(mc, B, mc2) ! {{{ 732 class(MasterCurve_t) :: mc 733 class(MasterCurve_t), optional:: mc2 734 real(kind=dp) :: B 735 if (present(mc2)) then 736 call printeval(mc % head, B, mc2 % head) 737 else 738 call printeval(mc % head, B) 739 end if 740 741end subroutine ! }}} 742 743SUBROUTINE rc_printeval(rc, B, rc0) ! {{{ 744 implicit none 745 class(revcurve_t), pointer, intent(in) :: rc 746 class(revcurve_t), pointer, intent(in), optional :: rc0 747 real(kind=dp), intent(in) :: B 748 real(kind=dp) :: X, X0 749 class(revcurve_t), pointer :: rc_p, rc0_p 750 integer :: k 751 rc_p => rc 752 if (present(rc0)) rc0_p => rc0 753 k = rc_p % depth 754 do while (.not. associated(rc_p, rc_p % parent % parent)) 755 if(present(rc0)) X0 = rc0_p % simple_eval(B) 756 X = rc_p % simple_eval(B) 757 if (present(rc0)) print *, X, X0, X-X0 758 if (.not. present(rc0)) print *, X, rc_p % depth ! , c_loc(rc_p), c_loc(rc_p % parent) 759 rc_p => rc_p % parent 760 if(present(rc0)) rc0_p => rc0_p % parent 761 end do 762 X = rc_p % simple_eval(B) 763 if(present(rc0)) X0 = rc0_p % simple_eval(B) 764 if (present(rc0)) print *, X, X0, X-X0 765 if (.not. present(rc0)) print *, X, rc_p % depth ! , c_loc(rc_p), c_loc(rc_p % parent) 766END SUBROUTINE rc_printeval ! }}} 767! Debugging purposes only 768subroutine PrintRevCurve(rc0) ! {{{ 769 CLASS(RevCurve_t), POINTER, intent(in) :: rc0 770 CLASS(RevCurve_t), POINTER :: rc 771 772 rc=>rc0 773 774 write (*,*) 'current depth:', rc0 % depth 775 do while (.not. associated(rc%parent % parent, rc) .and. associated(rc)) 776 write (*,*) 'depth, Bp, Bq, dBrev, dHrev:', rc%depth, rc%Bp, rc%Bq, rc % dBrev, rc % dHrev 777 rc => rc % parent 778 end do 779 write (*,*) 'depth, Bp, Bq, dBrev, dHrev:', rc%depth, rc%Bp, rc%Bq, rc % dBrev, rc % dHrev 780 rc => rc % parent 781 write (*,*) 'depth, Bp, Bq, dBrev, dHrev:', rc%depth, rc%Bp, rc%Bq, rc % dBrev, rc % dHrev 782 rc => rc % parent 783end subroutine ! }}} 784 785!------------------------------------------------------------------------------- 786SUBROUTINE FreeRevCurve(mc) ! {{{ 787!------------------------------------------------------------------------------- 788 implicit none 789 TYPE(MasterCurve_t) :: mc 790!------------------------------------------------------------------------------- 791 DEALLOCATE(mc % children) 792END SUBROUTINE ! }}} 793!------------------------------------------------------------------------------- 794 795END MODULE ! }}} 796 797module ZirkaUtils ! Utils for 2D/3D calculations {{{ 798use zirka 799use DefUtils 800 801character(len=*), parameter :: default_zirka_variable_name = 'zirka_ipvar' 802 803contains 804!------------------------------------------------------------------------------- 805! Initialization for 2D case 806!------------------------------------------------------------------------------- 807SUBROUTINE InitHysteresis(Model,Solver) ! {{{ 808!------------------------------------------------------------------------------- 809 IMPLICIT NONE 810 TYPE(Model_t) :: Model 811 TYPE(Solver_t) :: Solver 812!------------------------------------------------------------------------------- 813 integer :: elemind, t, ipindex, n, nd 814 TYPE(GaussIntegrationPoints_t) :: IP 815 TYPE(SplineLoop_t), POINTER :: bigloop 816 REAL(kind=dp), POINTER :: BHasc(:,:), BHsingle(:,:), initseq(:,:) 817 TYPE(Nodes_t) :: Nodes 818 TYPE(Element_t), POINTER::Element 819 type(ValueList_t), pointer :: material 820 type(ValueListEntry_t), pointer :: ZirkaEntry 821 INTEGER, PARAMETER :: n_dir_default = 8 822 logical :: found, zeroinit, HasZirka 823 integer :: n_dir, n_zirka_mat, n_initialized, n_cachesubsample 824 CHARACTER(len=MAX_NAME_LEN) :: str 825 type(GlobalHysteresisModel_t), POINTER :: zirkamodel 826 type(Variable_t), POINTER :: hystvar 827 type(ZirkaABC_t), POINTER :: ABCParams 828 real(kind=dp), POINTER :: zcoeff(:,:) 829 real(kind=dp) :: b_mult, c_mult 830!------------------------------------------------------------------------------- 831 n_zirka_mat = 0 832 n_initialized = 0 833 834 835 do n = 1,model % NumberOfMaterials ! {{{ 836 material => model % materials(n) % values 837 ZirkaEntry => listfind(material, 'zirka material', haszirka) 838 if (.not. HasZirka) CYCLE 839 if (.not. ZirkaEntry % LValue) CYCLE 840 n_zirka_mat = n_zirka_mat + 1 841 842 if(ListGetLogical(material, 'zirka initialized', HasZirka)) then 843 n_initialized = n_initialized + 1 844 CYCLE 845 end if 846 847 zirkamodel => NULL() 848 BHasc => NULL() 849 BHSingle => NULL() 850 851 hystvar => GetZirkaVariable(Material) 852 853 if (.not. associated(hystvar)) HystVar => CreateZirkaVariable(Material) 854 ! call fatal('InitHysteresis', 'hystvar > ' // trim(str) //' < not available') 855 856 n_dir = ListGetInteger(material, 'Zirka Directions', found) 857 if (.NOT. found) n_dir = ListGetInteger(material, 'n_dir', found) 858 IF(.NOT. Found) n_dir = n_dir_default 859 860 zeroinit = ListGetLogical(material, 'Init to zero', zeroinit) 861 862 ipindex = GetIpCount(usolver=solver, ipvar=hystvar) 863 allocate(zirkamodel) 864 allocate(zirkamodel % curves(n_dir, ipindex)) 865 ZirkaEntry % PROCEDURE = TRANSFER(c_loc(zirkamodel), ZirkaEntry % PROCEDURE) 866 867 ! Initialize saturation loops and saturation curve 868 call GetConstRealArray(Material, BHasc, 'Ascending BH curve', found) 869 if(.not. Found) call fatal('InitHysteresis', 'Ascending BH curve not found') 870 call GetConstRealArray(Material, BHSingle, 'Single valued BH curve', found) 871 if(.not. Found) call fatal('InitHysteresis', 'Single valued BH curve not found') 872 873 zirkamodel % masterloop => InitSplineLoop(BHasc, BHSingle) 874 zirkamodel % CacheSubSample = ListGetInteger(Material, 'Zirka Spline Cache Subsample', Found) 875 if(.not. Found) Zirkamodel % CacheSubSample = 2 876 877 ! Set up zirka ABC data here 878 allocate(ABCParams) 879 zirkamodel % ABCParams => ABCParams 880 call GetConstRealArray(Material, zcoeff, 'Zirka model coefficients', found) 881 if(Found .and. size(zcoeff,1) == 4) then 882 ABCparams % coeffs(1:4) = zcoeff(1:4,1) 883 end if 884 885 b_mult = GetCReal(Material, 'Zirka model b multiplier', found) 886 if(found) zirkamodel % abcparams % b_mult = b_mult 887 888 c_mult = GetCReal(Material, 'Zirka model c multiplier', found) 889 if(found) zirkamodel % abcparams % c_mult = c_mult 890 end do ! }}} 891 892 if (.not. n_zirka_mat == n_initialized) then ! only init if some 893 do elemind = 1,getnofactive() ! {{{ 894 element => GetActiveElement(elemind) 895 896 material => GetMaterial() 897 ZirkaEntry => ListFind(material, 'zirka material', haszirka) 898 if (.not. HasZirka) CYCLE 899 900 initseq => null() 901 call GetConstRealArray(Material, initseq, 'zirka init sequence', found) 902 if(.not. found) initseq => null() 903 904 if(ListGetLogical(material, 'zirka initialized', HasZirka)) CYCLE 905 906 IP = GaussPoints(element) 907 908 inithystblock: block ! {{{ 909 REAL(KIND=dp) :: phi 910 integer :: i 911 912 zirkamodel => GetZirkaPointer(material) 913 914 if(.not. associated(zirkamodel)) then 915 print *, 'zirkamodel is not associated, cycling' ! DEBUG 916 cycle 917 end if 918 919 do t = 1, ip%N 920 ipindex = getipindex(t, usolver=solver, element=element, ipvar=hystvar) 921 if (ipindex == 0) CYCLE 922 do i = 1,n_dir 923 phi = (i-1.0)*pi/n_dir 924 ! print *, 'hello', i, t, ipindex ! DEBUG 925 if(.not. associated(initseq)) then 926 zirkamodel % curves(i, ipindex) = & 927 MasterCurve_t(zirkamodel % masterloop, ABCparams, & 928 init=zeroinit, b0=[sin(phi), cos(phi), 0.0_dp], & 929 n_cachesubsample = zirkamodel % CacheSubSample) 930 else 931 zirkamodel % curves(i, ipindex) = & 932 MasterCurve_t(zirkamodel % masterloop, ABCparams, & 933 init=.true., b0=[sin(phi), cos(phi), 0.0_dp], initseq=initseq(:,1), & 934 n_cachesubsample = zirkamodel % CacheSubSample) 935 end if 936 end do 937 end do 938 end block inithystblock ! }}} 939 940 end do ! }}} 941 end if 942 943 ! set initialized to true 944 do n = 1,model % NumberOfMaterials ! {{{ 945 Material => Model % Materials(n) % Values 946 ZirkaEntry => ListFind(material, 'zirka material', haszirka) 947 if (.not. HasZirka) CYCLE 948 949 if(ListGetLogical(material, 'zirka initialized', HasZirka)) cycle 950 call ListAddLogical(material, 'zirka initialized', .true.) 951 end do ! }}} 952 write(message, '(A,I0,A,I0)') 'Found ',n_zirka_mat,' Zirka hysteresis models and skipped initialization of ', n_initialized 953 call Info('InitHysteresis',message,level=10) 954 955!------------------------------------------------------------------------------- 956END SUBROUTINE InitHysteresis ! }}} 957!------------------------------------------------------------------------------- 958 959!------------------------------------------------------------------------------- 960! Returns a pointer to the object containing hysteresis model of given material 961!------------------------------------------------------------------------------- 962FUNCTION GetZirkaPointer(material) result(ZirkaModelPtr) ! {{{ 963!------------------------------------------------------------------------------- 964 TYPE(ValueList_t), POINTER, intent(in) :: Material 965 TYPE(GlobalHysteresisModel_t), POINTER :: ZirkaModelPtr 966!------------------------------------------------------------------------------- 967 TYPE(ValueListEntry_t), POINTER :: ZirkaEntry 968 TYPE(C_PTR) :: c_zirka_ptr 969 logical :: haszirka 970 971 ZirkaModelPtr => Null() 972 ZirkaEntry => ListFind(material, 'zirka material', haszirka) 973 IF(.NOT. HasZirka) RETURN 974 975 c_zirka_ptr = transfer(ZirkaEntry % PROCEDURE, c_zirka_ptr) 976 call C_F_POINTER(c_zirka_ptr, ZirkaModelPtr) 977END FUNCTION ! }}} 978!------------------------------------------------------------------------------- 979 980FUNCTION CreateZirkaVariable(Material) RESULT(var) 981!------------------------------------------------------------------------------- 982 USE MainUtils 983 IMPLICIT NONE 984 TYPE(ValueList_t), POINTER, intent(in) :: Material 985 TYPE(Variable_t), POINTER :: var 986!------------------------------------------------------------------------------- 987 CHARACTER(len=MAX_NAME_LEN) :: str 988 type(mesh_t), pointer :: mesh 989 logical :: found 990 991 mesh => getmesh() 992 993 str = ListGetString(material, 'Zirka variable', found) 994 if(.not. found) str = default_zirka_variable_name !'zirka' 995 var => VariableGet(mesh % variables, str) 996 if(associated(var)) THEN 997 write (Message, '(A)') 'Attempting to create existing variable > ' // trim(str) // ' <' 998 call warn('CreateZirkaVariable', Message) 999 return 1000 END IF 1001 BLOCK 1002 LOGICAL :: HasZirka, Found 1003 TYPE(ValueListEntry_t), POINTER :: zmat 1004 CHARACTER(len=MAX_NAME_LEN) :: maskname, mask_sec 1005 INTEGER, POINTER :: Perm(:) 1006 REAL(KIND=dp), POINTER :: Values(:) 1007 type(Solver_t), POINTER :: PSolver 1008 type(Model_t) :: Model 1009 integer :: nsize 1010 1011 1012 PSolver => GetSolver() 1013 maskname = 'zirka material' 1014 mask_sec = 'material' 1015 zmat => ListFind( material , 'Zirka Material' , Found) 1016 IF (Found .and. zmat % LValue) THEN 1017 call CreateIpPerm(PSolver, Perm, maskname, mask_sec) 1018 nsize = maxval(perm) 1019 allocate(Values(nsize)) 1020 call VariableAdd( PSolver % Mesh % Variables, PSolver % Mesh, PSolver, & 1021 trim(str), 1, Values, Perm, output = .TRUE., TYPE=Variable_on_gauss_points) 1022 END IF 1023 END BLOCK 1024 var => VariableGet(mesh % variables, str) 1025END FUNCTION CreateZirkaVariable 1026 1027!------------------------------------------------------------------------------- 1028! Returns a pointer to the variable holding hysteresis models 1029!------------------------------------------------------------------------------- 1030FUNCTION GetZirkaVariable(Material) RESULT(ZirkaVariable) ! {{{ 1031!------------------------------------------------------------------------------- 1032 TYPE(ValueList_t), POINTER, intent(in) :: Material 1033 TYPE(Variable_t), POINTER :: ZirkaVariable 1034!------------------------------------------------------------------------------- 1035 CHARACTER(len=MAX_NAME_LEN) :: str 1036 type(mesh_t), pointer :: mesh 1037 logical :: found 1038 1039 mesh => getmesh() 1040 1041 str = ListGetString(material, 'Zirka variable', found) 1042 if(.not. found) str = default_zirka_variable_name! 'zirka' 1043 ZirkaVariable => VariableGet(mesh % variables, str) 1044 if(associated(zirkaVariable)) return 1045 1046!------------------------------------------------------------------------------- 1047END FUNCTION ! }}} 1048!------------------------------------------------------------------------------- 1049 1050!------------------------------------------------------------------------------- 1051! Sets the hysteresis state globally in 2D case 1052!------------------------------------------------------------------------------- 1053SUBROUTINE DriveHysteresis(model, solver) ! {{{ 1054!------------------------------------------------------------------------------- 1055 IMPLICIT NONE 1056 type(model_t) :: model 1057 type(solver_t) :: solver 1058!------------------------------------------------------------------------------- 1059 integer :: elemind, ipindex, n, nd 1060 TYPE(GaussIntegrationPoints_t) :: IP 1061 ! type(RevCurve_t), POINTER :: rc 1062 TYPE(SplineLoop_t), POINTER :: bigloop 1063 REAL(kind=dp) :: H(1:5), Basc(1:5), Bdesc(1:5) 1064 TYPE(Nodes_t) :: Nodes 1065 TYPE(Element_t), POINTER::Element 1066 TYPE(Variable_t), POINTER :: hystvar 1067 TYPE(GlobalHysteresisModel_t), pointer :: zirkamodel 1068 logical :: found 1069!------------------------------------------------------------------------------- 1070#if DEBUG>0 1071 integer :: printed 1072 printed = 0 1073#endif 1074 1075 do elemind = 1,getnofactive() 1076 element => GetActiveElement(elemind) 1077 1078 n = GetElementNOFNodes(Element) 1079 nd = GetElementNOFDOFs(Element) 1080 IP = GaussPoints(element) 1081 if (.not. ListGetLogical(GetMaterial(Element), 'zirka material',found)) CYCLE 1082 zirkamodel => GetZirkaPointer(GetMaterial(Element)) 1083 hystvar => GetZirkaVariable(GetMaterial(Element)) 1084 1085 1086drivehystblock: block 1087 REAL(KIND=dp) :: Basis(nd),dBasisdx(nd,3),DetJ, B_ip(3), POT(nd), Agrad(3) 1088 LOGICAL :: einfostat 1089 REAL(kind=dp), parameter :: zstab=1.0e-5 1090 integer :: counter, t, n_dir 1091 1092 CALL GetLocalSolution(POT,UElement=Element,USolver=Solver) 1093 CALL GetElementNodes(Nodes) 1094 1095 counter = 0 1096 do t = 1, ip%N 1097 ipindex = getipindex(t, usolver=solver, element=element, ipvar=hystvar) 1098 if (ipindex == 0) cycle 1099#if DEBUG>0 1100 if (printed == 0) then ! DEBUG 1101 call zirkamodel % curves(1,ipindex) % printme() 1102 printed = printed + 1 1103 end if 1104#endif 1105 1106 counter = counter + 1 1107 einfostat = ElementInfo( & 1108 Element, Nodes, IP % U(t), IP % V(t), & 1109 IP % W(t), detJ, Basis, dBasisdx ) 1110 ! B_ip(1) = -SUM(POT*dBasisdx(:,2)) 1111 ! B_ip(2) = SUM(POT*dBasisdx(:,1)) 1112 ! B_ip(3) = 0.0_dp 1113 1114 Agrad = matmul(POT, dbasisdx) 1115 B_ip(1) = Agrad(2) 1116 B_ip(2) = -Agrad(1) 1117 b_ip(3) = 0.0_dp 1118#if DEBUG>0 1119 if (printed < 2) then ! DEBUG 1120 print *, 'B_ip = ', B_ip, printed 1121 printed = printed + 1 1122 end if 1123#endif 1124 1125 do n_dir = 1, ubound(zirkamodel % curves, 1) 1126 call zirkamodel % curves(n_dir, ipindex) % & 1127 drive(sum(zirkamodel % curves(n_dir, ipindex) % B0 * B_ip)) 1128 end do 1129#if DEBUG>0 1130 if (printed < 3) then ! DEBUG 1131 call zirkamodel % curves(1,ipindex) % printme() 1132 printed = printed + 1 1133 end if 1134#endif 1135 end do 1136 1137end block drivehystblock 1138 1139 end do 1140!------------------------------------------------------------------------------- 1141END SUBROUTINE DriveHysteresis ! }}} 1142!------------------------------------------------------------------------------- 1143 1144!------------------------------------------------------------------------------- 1145! Add magnetic field strength to long vector force used in MagnetoDynamicsCalcFields 1146!------------------------------------------------------------------------------- 1147SUBROUTINE GetHystereticMFS(Element, Force, pSolver, HasZirka, CSymmetry) ! {{{ 1148!------------------------------------------------------------------------------- 1149 use DefUtils 1150 type(element_t), intent(in) :: element 1151 real(kind=dp), intent(inout) :: force(:,:) 1152 type(solver_t), intent(in) :: pSolver 1153 logical, intent(out) :: HasZirka 1154 LOGICAL, OPTIONAL :: CSymmetry 1155!------------------------------------------------------------------------------- 1156 type(ValueList_t), pointer :: material 1157 1158 LOGICAL :: einfostat 1159 integer :: n, nd, t, n_dir 1160 type(GaussIntegrationPoints_t) :: IP 1161 type(GlobalHysteresisModel_t), pointer :: zirkamodel 1162 type(variable_t), pointer :: hystvar 1163 type(nodes_t) :: nodes 1164 LOGICAL :: CSymmetry_ 1165 1166 IF(.NOT. present(CSymmetry)) THEN 1167 CSymmetry_ = .FALSE. 1168 ELSE 1169 CSymmetry_ = CSymmetry 1170 END IF 1171 1172 Material => GetMaterial(Element) 1173 HasZirka = ListGetLogical(Material, 'zirka material', HasZirka) 1174 if (.not. HasZirka) RETURN 1175 zirkamodel => GetZirkaPointer(GetMaterial(Element)) 1176 hystvar => GetZirkaVariable(GetMaterial(Element)) 1177 1178 n = GetElementNOFNodes(Element) 1179 nd = GetElementNOFDOFs(Element) 1180 IP = GaussPoints(element) 1181 block 1182 REAL(KIND=dp) :: Basis(nd),dBasisdx(nd,3),DetJ, B_ip(2), POT(nd),H_ip(2), & 1183 Agrad(3), x, Alocal 1184 integer :: p, ipindex 1185 1186 call getlocalsolution(pot, uelement=element, usolver=psolver) 1187 CALL GetElementNodes(Nodes) 1188 1189 DO t=1,IP % n 1190 ipindex = getipindex(t, usolver=psolver, element=element, ipvar=hystvar) 1191 if (ipindex == 0) cycle 1192 H_ip = 0.0_dp 1193 einfostat = ElementInfo( & 1194 Element, Nodes, IP % U(t), IP % V(t), & 1195 IP % W(t), detJ, Basis, dBasisdx ) 1196 1197 ! No symmetries yet 1198 Agrad = matmul(POT, dbasisdx) 1199 B_ip(1) = Agrad(2) 1200 B_ip(2) = -Agrad(1) 1201 IF( CSymmetry ) THEN 1202 Alocal = sum(Pot(1:n)*basis(1:n)) 1203 x = SUM( Basis(1:n) * Nodes % x(1:n) ) 1204 detJ = detJ * x 1205 B_ip = -B_ip 1206 B_ip(2) = B_ip(2) + Alocal/x 1207 END IF 1208 1209 do n_dir = 1, ubound(zirkamodel % curves, 1) 1210 associate(B0 => zirkamodel % curves(n_dir, ipindex) % B0) 1211 H_ip = H_ip + zirkamodel % curves(n_dir,ipindex) % & 1212 eval(sum(B_ip*B0(1:2)), cached = .true.) * & 1213 B0(1:2) 1214 end associate 1215 end do 1216 do p=1,n 1217 FORCE(p,1:2) = FORCE(p,1:2) + ip%s(t)*detJ*H_ip(1:2)*Basis(p) 1218 end do 1219 end do 1220 1221 end block 1222 1223!------------------------------------------------------------------------------- 1224END SUBROUTINE ! }}} 1225!------------------------------------------------------------------------------- 1226end module ! }}} 1227 1228