gtool5 Fortran 90/95 ライブラリ 1.0.0-rc5
English
Loading...
Searching...
No Matches
dc_units.f90
Go to the documentation of this file.
1! -*- mode: f90; coding: utf-8 -*-
2!-----------------------------------------------------------------------
3! Copyright (c) 2000-2026 Gtool Development Group. All rights reserved.
4!-----------------------------------------------------------------------
51
53 !
54
55 use dc_types, only: dp, token, string
56 implicit none
57
58 private
59 public:: units
60 public:: clear, deallocate, add_okay
61 public:: assignment(=), operator(*), operator(/), operator(+)
62 private:: units_simplify
63
79 type units
80 real(dp):: factor
81 integer:: nelems
82 character(TOKEN), pointer:: name(:)
83 character(TOKEN):: offset
84 real(dp), pointer:: power(:)
85 end type units
86
87 interface clear
88 module procedure dcunitsclear
89 end interface
90
91 interface deallocate
92 module procedure dcunitsdeallocate
93 end interface
94
95 interface assignment(=)
96 module procedure dcunitsbuild, dcunitstostring
97 end interface
98
99 interface operator(*)
100 module procedure dcunitsmul
101 end interface
102
103 interface operator(/)
104 module procedure dcunitsdiv
105 end interface
106
107 interface operator(+)
108 module procedure dcunitsadd
109 end interface
110
111contains
112
131 subroutine units_simplify(u, name, power)
132 type(UNITS), intent(inout):: u
133 character(*), intent(in):: name(u%nelems)
134 real(DP), intent(in):: power(u%nelems)
135 integer:: i, n, j, onazi
136 integer:: table(u%nelems)
137
138 if (u%nelems < 1) return
139 table(:) = 0
140 n = 0
141 do, i = 1, u%nelems
142 if (name(i) == '') cycle
143 onazi = 0
144 do, j = 1, i - 1
145 if (name(j) == name(i)) then
146 onazi = j
147 endif
148 enddo
149 if (onazi > 0) then
150 table(i) = table(onazi)
151 else
152 n = n + 1
153 table(i) = n
154 endif
155 enddo
156 allocate(u%name(n), u%power(n))
157 u%power = 0.0_dp
158 do, i = 1, u%nelems
159 if (table(i) == 0) cycle
160 u%name(table(i)) = name(i)
161 u%power(table(i)) = u%power(table(i)) + power(i)
162 enddo
163 u%nelems = n
164 end subroutine units_simplify
165
179 type(units) function dcunitsmul(u1, u2) result(result)
180 type(units), intent(in):: u1, u2
181 integer:: n
182 character(TOKEN), allocatable:: name(:)
183 real(dp), allocatable:: power(:)
184 result%factor = u1%factor * u2%factor
185 result%nelems = u1%nelems + u2%nelems
186 result%offset = ""
187 n = result%nelems
188 if (n == 0) then
189 nullify(result%name, result%power)
190 return
191 endif
192 allocate(name(n), power(n))
193 name = (/u1%name, u2%name/)
194 power = (/u1%power, u2%power/)
195 call units_simplify(result, name, power)
196 deallocate(name, power)
197 end function dcunitsmul
198
212 type(units) function dcunitsdiv(u1, u2) result(result)
213 type(units), intent(in):: u1, u2
214 integer:: n, n1
215 character(TOKEN), allocatable:: name(:)
216 real(dp), allocatable:: power(:)
217 if (abs(u2%factor) < tiny(u2%factor)) then
218 result%factor = sign(u1%factor, 1.0_dp) * &
219 & sign(u2%factor, 1.0_dp) * &
220 & huge(1.0_dp)
221 else
222 result%factor = u1%factor / u2%factor
223 endif
224 result%nelems = u1%nelems + u2%nelems
225 result%offset = ""
226 n = result%nelems
227 if (n == 0) then
228 nullify(result%name, result%power)
229 return
230 endif
231 allocate(name(n), power(n))
232 n1 = u1%nelems
233 if (n1 >= 1) then
234 name(1:n1) = u1%name(1:n1)
235 power(1:n1) = u1%power(1:n1)
236 endif
237 n1 = n1 + 1
238 if (n >= n1) then
239 name(n1:n) = u2%name(1:u2%nelems)
240 power(n1:n) = -u2%power(1:u2%nelems)
241 endif
242 call units_simplify(result, name, power)
243 deallocate(name, power)
244 end function dcunitsdiv
245
265 type(units) function dcunitsadd(u1, u2) result(result)
266 type(units), intent(in):: u1, u2
267 type(units):: x
268 result%offset = u1%offset
269 result%nelems = u1%nelems
270 result%factor = u1%factor + u2%factor
271 x = u1 / u2
272 if (x%nelems == 0) then
273 nullify(result%name, result%power)
274 return
275 endif
276 if (all(abs(x%power(1:result%nelems)) < tiny(0.0_dp))) then
277 allocate(result%name(result%nelems), result%power(result%nelems))
278 result%name = u1%name
279 result%power = u1%power
280 return
281 endif
282 result%factor = 0.0
283 result%nelems = -1
284 result%offset = "MISMATCH"
285 nullify(result%name, result%power)
286 end function dcunitsadd
287
307 logical function add_okay(u1, u2) result(result)
308 type(units), intent(in):: u1, u2
309 type(units):: x
310 character(STRING):: debug
311 call clear(x)
312 x = u1 / u2
313 debug = u1
314 debug = u2
315 debug = x
316 if (x%nelems == 0) then
317 result = .true.
318 else if (all(abs(x%power(1:x%nelems)) < tiny(0.0_dp))) then
319 result = .true.
320 else
321 result = .false.
322 endif
323 call deallocate(x)
324 end function add_okay
325
339 subroutine dcunitsclear(u)
340 type(units), intent(inout):: u
341 nullify(u%name)
342 nullify(u%power)
343 u%factor = 1.0_dp
344 u%offset = ""
345 u%nelems = 0
346 end subroutine dcunitsclear
347
361 subroutine dcunitsdeallocate(u)
362 type(units), intent(inout):: u
363 if (associated(u%name)) deallocate(u%name)
364 if (associated(u%power)) deallocate(u%power)
365 u%factor = 1.0_dp
366 u%offset = ""
367 u%nelems = 0
368 end subroutine dcunitsdeallocate
369
389 subroutine dcunitstostring(string, u)
390 character(*), intent(out):: string
391 type(units), intent(in):: u
392 integer:: i, ip, npower
393 character(TOKEN):: buffer
394 character:: mul = '.'
395 real(DP), parameter:: allowed = epsilon(1.0_dp) * 16.0
396
397 if (u%nelems < 0) then
398 string = 'error from ' // u%offset
399 return
400 endif
401
402 write(buffer, "(1pg20.12)") u%factor
403 string = buffer
404 if (u%nelems < 1) return
405
406 if (abs(u%factor - 1.0) < allowed) then
407 string = ""
408 else if (abs(u%factor + 1.0) < allowed) then
409 string = "-"
410 endif
411
412 ip = len_trim(string) + 1
413 do, i = 1, u%nelems
414 npower = nint(u%power(i))
415 if (abs(1.0 - u%power(i)) < allowed) then
416 buffer = " "
417 else if (abs(npower - u%power(i)) < allowed) then
418 write(buffer, "(i10)") npower
419 buffer = adjustl(buffer)
420 else
421 write(buffer, "(1pg10.3)") u%power(i)
422 buffer = adjustl(buffer)
423 endif
424 if (buffer == '0') cycle
425 string = trim(string) // mul // trim(u%name(i)) // trim(buffer)
426 enddo
427 if (ip <= len(string)) string(ip:ip) = ' '
428 if (string(1:1) == " ") string = adjustl(string)
429 if (u%offset /= "") then
430 string = trim(string) // '@' // trim(u%offset)
431 endif
432 end subroutine dcunitstostring
433
465 subroutine dcunitsbuild(u, cunits)
466 use dcunits_com
467 type(units), intent(out):: u
468 character(*), intent(in):: cunits
469
470 ! 構築中の情報、乗算対象の列として保持する。
471 ! これは shift オペレータ付き単位を乗算できないことを示す。
472 type elem_units
473 character(TOKEN):: name
474 real(DP):: power, factor
475 end type elem_units
476 type(elem_units), target:: ustack(100)
477 integer:: ui = 1
478
479 ! 構文単位が占める乗算対象の stack における最初の添字
480 type paren_t
481 real(DP):: factor
482 integer:: factor_exp
483 logical:: factor_inv
484 integer:: power_exp
485 integer:: paren_exp
486 end type paren_t
487 type(paren_t):: pstack(50)
488 integer:: pi = 1
489
490 ! パーサの状態遷移
491 integer, parameter:: Y_INIT = 1, y_number = 2, y_name = 3, &
492 & y_nx = 4, y_ni = 5, y_mul = 6, y_shift = 7
493 integer:: yparse_status = y_init
494
495 ! 字句
496 integer:: ltype
497 integer:: ivalue(5)
498 real(DP):: dvalue
499 character(TOKEN):: cvalue
500 ! その他
501 integer:: i
502
503 ! --- 実行部 ---
504 ! 初期化
505 if (associated(u%name)) deallocate(u%name)
506 if (associated(u%power)) deallocate(u%power)
507 u%nelems = 0
508 u%offset = ""
509 u%factor = 1.0_dp
510 if (cunits == "") return
511 call dcunitssetline(cunits)
512 call ustack_clear
513 call pstack_clear
514 yparse_status = y_init
515
516 do
517 call dcunitsgettoken(ltype, ivalue, dvalue, cvalue)
518 select case(ltype)
519 case (s_integer)
520 select case(yparse_status)
521 case (y_init, y_mul)
522 pstack(pi)%factor = pstack(pi)%factor * ivalue(1)
523 yparse_status = y_number
524 case (y_name, y_nx)
525 i = pstack(pi)%power_exp
526 ustack(i:ui)%power = ustack(i:ui)%power * ivalue(1)
527 call power_next
528 yparse_status = y_ni
529 case (y_shift)
530 u%offset = cvalue
531 case default
532 call error
533 end select
534 case (s_real)
535 select case(yparse_status)
536 case (y_init, y_mul)
537 pstack(pi)%factor = pstack(pi)%factor * dvalue
538 yparse_status = y_number
539 case (y_name, y_nx)
540 i = pstack(pi)%power_exp
541 ustack(i:ui)%power = ustack(i:ui)%power * dvalue
542 call power_next
543 yparse_status = y_ni
544 case (y_shift)
545 u%offset = cvalue
546 case default
547 call error
548 end select
549 case (s_text)
550 select case(yparse_status)
551 case (y_init, y_number, y_mul)
552 ustack(ui)%name = cvalue
553 yparse_status = y_name
554 case (y_name, y_ni)
555 call ustack_grow
556 call power_next
557 ustack(ui)%name = cvalue
558 yparse_status = y_name
559 case (y_shift)
560 u%offset = cvalue
561 case default
562 call error
563 end select
564 case (s_exponent)
565 select case(yparse_status)
566 case (y_name)
567 yparse_status = y_nx
568 case default
569 call error
570 end select
571 case (s_multiply)
572 select case(yparse_status)
573 case (y_number, y_name)
574 call factor_next
575 yparse_status = y_mul
576 case default
577 call error
578 end select
579 case (s_divide)
580 select case(yparse_status)
581 case (y_number, y_name)
582 call factor_next
583 pstack(pi)%factor_inv = .true.
584 yparse_status = y_mul
585 case default
586 call error
587 end select
588 case (s_shift)
589 if (yparse_status == y_nx) call cancel_exp
590 call units_finalize
591 yparse_status = y_shift
592 case (s_openpar)
593 if (yparse_status == y_nx) call cancel_exp
594 call pstack_push
595 case (s_closepar)
596 call units_finalize
597 call pstack_pop
598 case (s_eof)
599 exit
600 case default
601 call error
602 end select
603 enddo
604 if (yparse_status == y_nx) call cancel_exp
605 call units_finalize
606
607 u%nelems = ui
608 u%factor = product(ustack(1:ui)%factor)
609 call units_simplify(u, ustack(1:ui)%name, ustack(1:ui)%power)
610
611 contains
612
613 subroutine cancel_exp
614 print *, "DCUnitsBuild: syntax error, operator(**) ignored"
615 end subroutine cancel_exp
616
617 subroutine error
618 print *, "DCUnitsBuild: unexpected token <", &
619 & trim(cvalue), "> ignored"
620 end subroutine error
621
622 subroutine power_next
623 ! power_exp の終了処理
624 call ustack_grow
625 pstack(pi)%power_exp = ui
626 end subroutine power_next
627
628 subroutine factor_next
629 ! factor_exp の終了処理
630 real(DP):: factor
631 i = pstack(pi)%factor_exp
632 factor = product(ustack(i:ui)%factor) * pstack(pi)%factor
633 if (pstack(pi)%factor_inv) then
634 ustack(i:ui)%power = -ustack(i:ui)%power
635 factor = 1.0_dp / factor
636 endif
637 ustack(i)%factor = factor
638 ustack(i+1:ui)%factor = 1.0_dp
639 call power_next
640 pstack(pi)%factor = 1.0_dp
641 pstack(pi)%factor_exp = ui
642 end subroutine factor_next
643
644 subroutine units_finalize
645 call factor_next
646 end subroutine units_finalize
647
648 subroutine ustack_clear
649 ui = 0
650 call ustack_grow
651 end subroutine ustack_clear
652
653 subroutine ustack_grow
654 if (ui >= size(ustack)) stop 'DCUnitsBuild: too many elements'
655 ui = ui + 1
656 ustack(ui)%name = ""
657 ustack(ui)%factor = 1.0_dp
658 ustack(ui)%power = 1.0_dp
659 end subroutine ustack_grow
660
661 subroutine pstack_clear
662 pi = 0
663 call pstack_push
664 end subroutine pstack_clear
665
666 subroutine pstack_push
667 if (pi >= size(pstack)) stop 'DCUnitsBuild: too many parens'
668 pi = pi + 1
669 call ustack_grow
670 pstack(pi)%factor_exp = ui
671 pstack(pi)%factor = 1.0_dp
672 pstack(pi)%factor_inv = .false.
673 pstack(pi)%power_exp = ui
674 pstack(pi)%paren_exp = ui
675 end subroutine pstack_push
676
677 subroutine pstack_pop
678 ! factor_exp の終了処理
679 call power_next
680 pi = pi - 1
681 end subroutine pstack_pop
682
683 end subroutine dcunitsbuild
684
686end module dc_units
subroutine cancel_exp
Definition dc_units.f90:614
種別型パラメタを提供します。
Definition dc_types.f90:55
integer, parameter, public token
単語やキーワードを保持する文字型変数の種別型パラメタ
Definition dc_types.f90:128
integer, parameter, public dp
倍精度実数型変数
Definition dc_types.f90:92
integer, parameter, public string
文字列を保持する 文字型変数の種別型パラメタ
Definition dc_types.f90:137
単位系処理用モジュール
Definition dc_units.f90:52
logical function, public add_okay(u1, u2)
Definition dc_units.f90:308
dc_units モジュール用の内部モジュール
integer, parameter, public s_real
実数シンボル
integer, parameter, public s_text
テキスト/名前トークンシンボル
integer, parameter, public s_multiply
乗算演算子シンボル
integer, parameter, public s_openpar
開き括弧シンボル
integer, parameter, public s_closepar
閉じ括弧シンボル
subroutine, public dcunitsgettoken(tokentype, ivalue, dvalue, cvalue)
subroutine, public dcunitssetline(line)
integer, parameter, public s_exponent
指数演算子シンボル
integer, parameter, public s_integer
整数シンボル
integer, parameter, public s_eof
ファイル/文字列終端シンボル
integer, parameter, public s_divide
除算演算子シンボル
integer, parameter, public s_shift
シフト演算子シンボル