2017년 11월 21일 화요일

포인터와 레퍼런스(C++)

C 와 C++보다는 Fortran, Python을 주로 쓰다보니
여전히 pointer 나 reference 를 사용하는 C, C++은 어렵게 느껴진다.
기본적인 차이는 Fortran은 call by reference가 기본이라
사실은 언제나 pointer를 사용하는 셈이라는 것에 비해, C++은
call by value가 기본이기 때문에 Fortran처럼 사용하기 위해서는
함수에서 pointer를 사용해야 한다는 것이다.

포인터는 기본적으로 변수의 주소를 가리키는 것이라고는 알고 있지만,
가장 헷갈리는 것은 * 와 & 의 사용법과 함수에서의 input/output으로의 사용법이다.
문제를 더 복잡하게 만드는 것은 C/C++ 에서는 array와 C-string 을 pointer로
취급한다는 사실과 reference의 경우에는 implicit referencing과 dereference가
자동으로 일어난다는 점, 그리고 * 와 &가 문맥에 따라 다른 의미로 사용된다는
점이다. 이 때문에 포트란이나 파이쏜에 익숙한 내가 보기에는 일관되지 않은 사용법이
나타난다.

내가 이해하는 범위에서 포인터,레퍼런스 등의 사용법을 살펴보도록 하자.

(1) a, *a, &a 의 차이가 무엇일까?

일반적인 변수는 'int a' 와 같은 형태로 선언한다. 이 때, 컴파일러는 이 변수마다 주소를 할당하고 프로그램에서는 이 주소에 값을 저장한다. ( 집이 변수, 집 안의 물건이 변수값, 집의 주소 의 관계로 생각할 수 있다.) b=a 와 같은 명령은 집 a안의 물건을 집 b 에 복사하는 역할을 한다.

포인터는 'int* a_ptr' 과 같이 선언한다. 이 때, 'a_ptr'은 포인터로써 주소값을 가진다.
'*' 는 포인터(주소)가 가리키는 집안의 물건을 꺼내는 역할을 한다. (즉, '*' 는 포인터를 물건으로 바꾸는 역할을 한다.)

한편, '&'는 변수의 주소를 나타낸다. ' &a' 는 a 라는 변수의 주소를 준다.
(즉, '&'는 물건을 주소로 바꾸는 역할을 한다.)

int thing; // thing은 일반 변수(집) 이다.
int* thing_ptr ;// thing_ptr은 포인터(주소)로 선언한다.

thing  : 변수(집)
          thing = 4; // thing 에 숫자 4를 넣는다.

&thing : 변수 thing의 주소

thing_ptr : 포인터(주소)
          thing_ptr = &thing ;
          // &thing은 thing의 주소값이고,
           thing_ptr은 따라서 thing의 포인터가 된다.
           따라서 이후로는 thing과 *thing_ptr은 같은 것을 나타낸다.

*thing_ptr : 어떤 것(이 경우는 정수).
           other = *thing_ptr;
           // *thing_ptr은 thing_ptr 주소가 나타내는 변수(집)이므로,
           other라는 집에 thing_ptr이 가리키는 것을 넣는다.
           여기서, other는 정수값이 된다.

            *thing_ptr = 5; (?)
            // *thing_ptr은 thing_ptr 주소가 나타내는 변수(집)이므로,
                       thing 에 5라는 숫자를 넣는 역할을 한다. (?)

*thing :  thing은 포인터가 아니므로 틀린 문법.

&thing_ptr : 포인터의 포인터.

좀 더 정확한 pointer와 reference의 차이는 앞의 글 참조.

(주의?) c++의 character pointer 는 다른 포인터와 달리 string의 pointer 로 취급한다?

char some_characters[10] = "Hello";   // A simple set of characters
char *char_ptr = &some_characters[0];  // Pointer to a character
std::cout << some_characters << "\n" // output is "Hello"
std::cout << &some_characters << "\n"l //output is an address
std::cout << some_characters[0] << "\n"l //output is "H"
std::cout << &some_characters[0] << "\n"l //output is "Hello"
std::cout << char_ptr << '\n'; // output is "Hello"
                // So with string pointers, the string itself is printed.
std::cout << *char_ptr << "\n";      // output is "H"
std::cout << &char_ptr << "\n";  // output is an address

In a similar way, one can define string pointer directly,
```c++

char *test_ptr = "abcde"; // equivalent two step   
std::cout << "String pointer test= " << test_ptr << "\n"; //output is "abcde"
std::cout << " *test= " << *test_ptr << "\n";             //output is "a"      
std::cout << " &test= " << &test_ptr << "\n";             //output is an address  
```

(주의) c++의 pointer 와 array는 같이 방식으로 취급된다. 

char array[10] = "012345678";
char *array_ptr = &array[0];

array_ptr=&array[0];
array_ptr= array ; // these two lines are quivalent. 

When passing an array to a procedure, C++ will 
automatically change the array into a pointer

한편, string이 아닌 숫자의 array의 경우는

int array[10] // initialized as 0,1,2,3,4,5,6,7,8,9

std::cout << " array= " << array << "\n"; // output is an address
std::cout << "&array= " << &array << "\n"; //output is an address same as above
std::cout << "*array= " << *array << "\n"; //output is 0
std::cout << " array[1]= " << array[1] << "\n"; //output is 1
std::cout << "*array[0]= " << *array[0] << "\n"; // error ! this setence is wrong
std::cout << "&array[1]= " << &array[1] << "\n"; // output is an address

즉, array 는 array[0]의 위치를 가리키는 pointer 이다. (array와 &array[0]는 동일)

반면, array[0]나 array[1] 은 integer 이고, pointer가 아님. 

함수가 다음과 같이 정의 되었을 때
void init_array_1(int data[])
void init_array_2(int *data_ptr)

다음 둘은 같은 역할을 한다. 
init_array_1(array);
init_array_1(&array[0]);
그리고 
init_array_2(array)
도 비슷하게 사용가능하다.  





2017년 11월 20일 월요일

(퍼옴) Pass by value and Pass by reference

Here is  a link to the original page:

https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value

Though the author writes that the analogy is not exact, it feels like the old explanation is
more natural and easy.
In short,

Analogy: Transferring a webpage,

Pass by reference: give the URL of webpage and authority to change.
Pass by value: give the printout of webpage

By default Fortran uses 'pass by reference' and C++ uses 'pass by value'.

In Fortran, subroutine calls f(x,y) and do something to the x and y then
it makes difference. In Fortran, one uses INTENT to avoid accidental change of contents.

In C++, since the default is 'pass by value', it does not change the original variable.
However, it would be inefficient to pass all big object by value. So, sometimes passing
a reference by using ' & var' is more convenient.

Difference between '&' and '*' in C++:

They are similar in the sense that they refers something in the memory.

Here is an original explanation:
https://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1047588532&id=1043284376

https://stackoverflow.com/questions/28778625/whats-the-difference-between-and-in-c

In short, '&' is used for objects reference but '*' is for a pointer of address.
( ** is used for a pointer of a pointer. For example, "char** argv" or "char* argv[]"
argv is an array of characters. Each char* is a pointer of a first character in string.
Then, char** is a double pointer for a pointer of a pointer to a character. )


* and & as type modifiers

  • int i declares an int.
  • int* p declares a pointer to an int.
  • int& r = i declares a reference to an int, and initializes it to reference i.
    C++ only. Note that references must be assigned at initialization, therefore int& r; is not possible.
Similarly:
  • void foo(int i) declares a function taking an int (by value, i.e. as a copy).
  • void foo(int* p) declares a function taking a pointer to an int.
  • void foo(int& r) declares a function taking an int by reference. (Again, C++ only)

* and & as operators

  • foo(i) calls foo(int i). The parameter is passed as a copy.
  • foo(*p) dereferences the int pointer p and calls foo(int i) with the int pointed to by p.
  • foo(&i) takes the address of the int i and calls foo(int* i) with that address.

설명: 비록 C++ compiler 는 (int* p) and  (int *p) 를 구분하지 않지만,의미는 다르다.
(1) (int* p) 는 p 라는 포인터를 선언하는데, 그 포인터가 가리키는 것이 정수라는 의미이다. 
    foo(int* p) 라고 정의하면, 함수 foo는 포인터를 변수로 받는데, 그 포인터는 정수 변수를 가리킨다는 것이다. 때문데 foo를 사용할 때는 foo( &i) 와 같이 사용하거나, foo(p) 로 pointer를 주어야한다.
(2) 반면, foo(int& r) 로 선언하는 경우는 foo가 변수 r을 받되 call by reference로 받는다는 의미이다. 때문에 사용할 때는 foo(i) 로 사용한다. ( foo(p)나 foo(&r) 은 옳지않다.)


(tl;dr) So in conclusion, depending on the context:

2017년 8월 21일 월요일

How to import existing fortran code into python

Since there are many existing fortran codes, it would be better to use them
in python. One possible way to do it is to create a shared object(.so) file by using f2py. 
Simple example of using f2py can be found easily. 
But, in case of complicate fortran program which use 'make',  or in case of the subroutine we want to use is a part of larger library, it is not clear how to 
achieve it. 

The steps are 
(1) Prepare a module code which includes all the subroutines which wants to be 
    ported to python. 
    It is recommended the subroutines includes both input and outputs explicitly 
    by 'intent(in)' and 'intent(out)' properties. 

(2) create a static library for the subroutine (first compile for object files)     
     ar crsv lib[name].a [object files]

(3) use f2py to create signature file (.pyf) . 
    f2py [source file] -m [package name] -h [signature file name]

    Here the [source file] contains the subroutines or its wrapper to be imported to python. signature file (.pyf) contains a module for python which contains subroutines.
Importing sub module does not work well. Thus, always prepare [source file]
as a wrapper with subroutines not modules.

(4) edit the signature fil( .pyf) as necessary. (Only leave the subroutines to be imported) 

(5) created shared object library(.so) file using f2py 
    (If path of library is the same, "-L." would work?) 
    f2py -c [signature file .pyf] [source file] -L[absolute path for library]-l[library name] -llapack

(6) In the python,
    import [package name]

     and now one can use subroutines,
    [packagename].[subroutine]

(7) in case that the library(.so) file is located in different folder,
    add the library path before import

    import sys
    sys.path.insert(0,'[Library Path]')



  • The simple case: If there is only one fortran file, one can do 
  • (1) edit/comment the fortran file "my_lib.f90" with "!f2py " comments or explicit "intent" expressions.
  • (2)  Use following command to create  " my_lib.so " file which can be imported in python by "import my_lib" , 

f2py -c -m my_lib my_lib.f90

# Another way to use f2py is 
   directly add "cf2py intent([in/out]) [variable]" in the fortran source code 
   and compile 
   "python -m numpy.f2py -c -m [module name] [fortran source]" 

# In Windows, there seems to be some issue with the version of mingw-w64. 
  It seems I have to use "x86_64" version of mingw-w64 instead of "i686" version. 
  More details on the installation. 
  (https://python-at-risoe.pages.windenergy.dtu.dk/compiling-on-windows/configuration.html) 

# In Windows, one can use "ar" to create library as like LINUX with mingw-w64.

# Currently(2025.02.25), there seems to be a problem using f2py and "meson" build system in Windows... I am not sure how to fix the error. The same code can be compiled in linux.  --> this can be solved by install "meson" and "ninja" package with "pip" 

#----custumization of docstring of f2py: 
One may want to use custum docstring for f2py generated functions. 
To do this, one needs to overwrite the docstrings. 
(1) Suppose original fortran code have docstring between each "subroutine" and "implicit none" and python package is created by f2py. 
(2) Use the following script after loading the package. 

#-----replace f2py generated docsting with custom docstring from fortran file.  
import re
import special_py as sp # Replace with your actual f2py module name

def extract_fortran_docstrings_by_block(fortran_file_path):
    """
    Parses a Fortran source file to extract documentation blocks 
    between 'subroutine ...' and 'implicit none'.
    """
    docstrings = {}
    with open(fortran_file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # Regex pattern to capture the text between 'subroutine' (plus the name)
    # and 'implicit none'. Uses re.DOTALL (re.S) for multiline matching.
    # Note: This assumes 'implicit none' appears reliably after the docs you want.
    pattern = re.compile(
        r"subroutine\s+(\w+)\s*\(.*?\).*?(?P<doc_block>.*?)\s*implicit none", 
        re.IGNORECASE | re.DOTALL
    )

    for match in pattern.finditer(content):
        func_name = match.group(1).lower()
        doc_block = match.group('doc_block')

        # Clean up the doc block: remove the '!' prefix and format for Python
        clean_doc_lines = []
        for line in doc_block.strip().splitlines():
            stripped_line = line.strip()
            if stripped_line.startswith('!'):
                # Append everything after the '!' and a single space
                clean_doc_lines.append(stripped_line[1:].strip())
            else:
                # If a line doesn't start with '!' (e.g., blank lines), include it as a blank line
                clean_doc_lines.append("")
        
        docstrings[func_name] = "\n".join(clean_doc_lines).strip()

    return docstrings

def set_custom_docstrings(module, docstring_map):
    """
    Overwrites the docstring for specified functions within a module.
    """
    for func_name, custom_doc in docstring_map.items():
        func_obj = getattr(module, func_name, None)
        if func_obj is not None:
            func_obj.__doc__ = custom_doc
            # Optional: print(f"Docstring for '{func_name}' updated.")

# --- Main Execution ---

# 1. Define the path to your original Fortran source file
FORTRAN_SOURCE = 'special_functions.f90' # Replace with the correct file path

# 2. Extract the docstrings from the Fortran file
try:
    fortran_docs = extract_fortran_docstrings_by_block(FORTRAN_SOURCE)
    
    # 3. Apply the extracted docstrings to the imported Python module functions
    set_custom_docstrings(sp, fortran_docs)
    print(f"Successfully updated docstrings for {len(fortran_docs)} functions.")

    # Verification (Example for airya)
    print("\n--- Updated Docstring for sp.airya ---")
    print(sp.airya.__doc__)

except FileNotFoundError:
    print(f"Error: Could not find the Fortran source file at '{FORTRAN_SOURCE}'")
except Exception as e:
    # Handle potential encoding errors or regex errors
    print(f"An error occurred during processing: {e}")








python library path 설정

Note that the python path is not the same as the system $PATH.

(1) To list the python path,
     import sys
     print(sys.path)

(2) To insert a path,
    sys.path.insert(0, '{PATH TO INSERT}')

    This enables one to import library from other folder.

2017년 5월 16일 화요일

Tikz-Feynman package for Feynman diagram

There have been several packages to draw Feynman diagrams in tex environment.
But I have used two:

Axodraw ( Jaxodraw)
Feynmf (Feynmp)

However, one is rather old and non-standard and the other is not easy to use.

New package "Tikz-Feynman" seems to be good in look and easy in usage.
However, when I try to use the package the diagrams are weird and different shapes
from examples.
The problem was that I compiled them with 'pdflatex'.
It looks like the "Tikz-Feynman" is only compatible with 'lualatex'.
Since it is not written in the manual or webpage, it was difficult to know.

On the other hand, I found each package have advantage and disadvantage.
It is mostly how the vertices are positioned by the command.
For me, Feynmf seems to be better than others. But, because it use metafont,
it is rather slow and I have problem with TexStudio using Feynmf.
(I had to delete metafont whenever I modify them).

2017년 4월 13일 목요일

Shell model code NUSHELL tips

1. In old version of NUSHELL, be careful for the 'psd' model space.
   The single particle levels defined in the 'psd.sp' and interactions like 'psdmk.int'
   may be not consistent.
   The level ordering in original 'psd.sp' file is

   1 1 1 3  (p3/2)
   2 1 1 1  (p1/2)

   But, it have to be changed as follows to be consistent with the definitions in 'psdmk.int' interaction file.

   1 1 1 1  (p1/2)
   2 1 1 3  (p3/2)


---UNDER CONSTRUCTION

2017년 3월 29일 수요일

Python referencing( copying variables, objects)

In many language (at least, in Fortran), '=' makes a copy of a variable, so

a=1; b=a;  b=2

makes a=1 and b=2. Thus any change of the copy does not change the original.
This is the same for python, if the variable is a simple object.

However, in python, 'list1= list2' does not make two lists. Instead it refers the same reference object. Since, if a new value is assigned to the list, it may be okay.

a=[1,2,3]; b=a; b=[4,5,6]

makes a as [1,2,3] and b as [4,5,6] since the last  assignment 'b=[4,5,6]'
can be considered as a new assignment.

However, if we modify its elements,

a=[1,2,3]; b=a ; b[1]=7

makes both a and b as [1,7,3] (instead of a as [1,2,3], b as [1,7,3] )
since a and b were both refer the same object.

To obtain the originally desired effect, one can use

(1) b=a[:]  or b[:]=a[:]
(2) b=list(a)
(3) b=copy.copy(a)