{"id":15573037,"url":"https://github.com/spirit-code/ovf","last_synced_at":"2025-04-24T02:14:01.489Z","repository":{"id":47655479,"uuid":"134859062","full_name":"spirit-code/ovf","owner":"spirit-code","description":"OVF (OOMMF Vector Field file format) parser library with C API and language bindings","archived":false,"fork":false,"pushed_at":"2021-09-05T20:31:13.000Z","size":485,"stargazers_count":8,"open_issues_count":9,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-24T02:13:52.238Z","etag":null,"topics":["api","cpp11","forschungszentrum-juelich","fortran","micromagnetism","ovf","parser","python","spin-dynamics","vector-field","vectorfield"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spirit-code.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-25T13:23:08.000Z","updated_at":"2023-10-23T02:25:55.000Z","dependencies_parsed_at":"2022-08-30T08:42:09.234Z","dependency_job_id":null,"html_url":"https://github.com/spirit-code/ovf","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spirit-code%2Fovf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spirit-code%2Fovf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spirit-code%2Fovf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spirit-code%2Fovf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spirit-code","download_url":"https://codeload.github.com/spirit-code/ovf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250546096,"owners_count":21448262,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","cpp11","forschungszentrum-juelich","fortran","micromagnetism","ovf","parser","python","spin-dynamics","vector-field","vectorfield"],"created_at":"2024-10-02T18:10:14.068Z","updated_at":"2025-04-24T02:14:01.468Z","avatar_url":"https://github.com/spirit-code.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"OVF Parser Library\n=================================\n**Simple API for powerful OOMMF Vector Field file parsing**\u003cbr /\u003e\n\n[OVF format specification](#specification)\n\n![Build Status](https://github.com/spirit-code/ovf/workflows/CI/badge.svg?branch=master)\n\n**[Python package](https://pypi.org/project/ovf/):** [![PyPI version](https://badge.fury.io/py/ovf.svg)](https://badge.fury.io/py/ovf)\n\n| Library Coverage | Python Bindings Coverage |\n| :--------------: | :----------------------: |\n| [![Library Coverage Status](https://codecov.io/gh/spirit-code/ovf/branch/master/graph/badge.svg)](https://codecov.io/gh/spirit-code/ovf/branch/master) | [![Python Bindings Coverage Status](https://coveralls.io/repos/github/spirit-code/ovf/badge.svg?branch=master)](https://coveralls.io/github/spirit-code/ovf?branch=master)|\n\nHow to use\n---------------------------------\n\nFor usage examples, take a look into the test folders: [test](https://github.com/spirit-code/ovf/tree/master/test), [python/test](https://github.com/spirit-code/ovf/tree/master/python/test) or [fortran/test](https://github.com/spirit-code/ovf/tree/master/fortran/test).\n\nExcept for opening a file or initializing a segment, all functions return status codes\n(generally `OVF_OK`, `OVF_INVALID` or `OVF_ERROR`).\nWhen the return code is not `OVF_OK`, you can take a look into the latest message,\nwhich should tell you what the problem was\n(`const char * ovf_latest_message(struct ovf_file *)` in the C API).\n\nIn C/C++ and Fortran, before writing a segment, make sure the `ovf_segment` you pass in is\ninitialized, i.e. you already called either `ovf_read_segment_header` or `ovf_segment_create`.\n\n### C/C++\n\nOpening and closing:\n\n- `struct ovf_file *myfile = ovf_open(\"myfilename.ovf\")` to open a file\n- `myfile-\u003efound` to check if the file exists on disk\n- `myfile-\u003eis_ovf` to check if the file contains an OVF header\n- `myfile-\u003en_segments` to check the number of segments the file should contain\n- `ovf_close(myfile);` to close the file and free resources\n\nReading from a file:\n\n- `struct ovf_segment *segment = ovf_segment_create()` to initialize a new segment and get the pointer\n- `ovf_read_segment_header(myfile, index, segment)` to read the header into the segment struct\n- create float data array of appropriate size...\n- `ovf_read_segment_data_4(myfile, index, segment, data)` to read the segment data into your float array\n- setting `segment-\u003eN` before reading allows partial reading of large data segments\n\nWriting and appending to a file:\n\n- `struct ovf_segment *segment = ovf_segment_create()` to initialize a new segment and get the pointer\n- `segment-\u003en_cells[0] = ...` etc to set data dimensions, title and description, etc.\n- `ovf_write_segment_4(myfile, segment, data, OVF_FORMAT_TEXT)` to write a file containing the segment header and data\n- `ovf_append_segment_4(myfile, segment, data, OVF_FORMAT_TEXT)` to append the segment header and data to the file\n\n### Python\n\nTo install the *ovf python package*, either build and install from source\nor simply use\n\n    pip install ovf\n\nTo use `ovf` from Python, e.g.\n\n```Python\nfrom ovf import ovf\nimport numpy as np\n\ndata = np.zeros((2, 2, 1, 3), dtype='f')\ndata[0,1,0,:] = [3.0, 2.0, 1.0]\n\nwith ovf.ovf_file(\"out.ovf\") as ovf_file:\n\n    # Write one segment\n    segment = ovf.ovf_segment(n_cells=[2,2,1])\n    if ovf_file.write_segment(segment, data) != -1:\n        print(\"write_segment failed: \", ovf_file.get_latest_message())\n\n    # Add a second segment to the same file\n    data[0,1,0,:] = [4.0, 5.0, 6.0]\n    if ovf_file.append_segment(segment, data) != -1:\n        print(\"append_segment failed: \", ovf_file.get_latest_message())\n```\n\n### Fortran\n\nThe Fortran bindings are written in object-oriented style for ease of use.\nWriting a file, for example:\n\n```fortran\ntype(ovf_file)      :: file\ntype(ovf_segment)   :: segment\ninteger             :: success\nreal(kind=4), allocatable :: array_4(:,:)\nreal(kind=8), allocatable :: array_8(:,:)\n\n! Initialize segment\ncall segment%initialize()\n\n! Write a file\ncall file%open_file(\"fortran/test/testfile_f.ovf\")\nsegment%N_Cells = [ 2, 2, 1 ]\nsegment%N = product(segment%N_Cells)\n\nallocate( array_4(3, segment%N) )\narray_4 = 0\narray_4(:,1) = [ 6.0, 7.0, 8.0 ]\narray_4(:,2) = [ 5.0, 4.0, 3.0 ]\n\nsuccess = file%write_segment(segment, array_4, OVF_FORMAT_TEXT)\nif ( success == OVF_OK) then\n    write (*,*) \"test write_segment succeeded.\"\n    ! write (*,*) \"n_cells = \", segment%N_Cells\n    ! write (*,*) \"n_total = \", segment%N\nelse\n    write (*,*) \"test write_segment did not work. Message: \", file%latest_message\n    STOP 1\nendif\n```\n\nFor more information on how to generate modern Fortran bindings,\nsee also https://github.com/MRedies/Interfacing-Fortran\n\n\nHow to embed it into your project\n---------------------------------\n\nIf you are using CMake, it is as simple as cloning this into a subdirectory,\ne.g. `thirdparty/ovf` and using it with `add_subdirectory`:\n\n```\nadd_subdirectory( ${PROJECT_SOURCE_DIR}/thirdparty/ovf )\nset( OVF_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/thirdparty/ovf/include )\ntarget_include_directories( myproject PRIVATE ${OVF_INCLUDE_DIRS} )\ntarget_link_libraries( myproject PUBLIC ${OVF_LIBRARIES_STATIC} )\n```\n\nIf you're not using CMake, you may need to put in some manual work.\n\n\nBuild\n---------------------------------\n\n### On Unix systems\n\nUsually:\n```\nmkdir build\ncd build\ncmake ..\nmake\n```\n\n### On Windows\n\nOne possibility:\n- open the folder in the CMake GUI\n- generate the VS project\n- open the resulting project in VS and build it\n\n### CMake Options\n\nThe following options are `ON` by default.\nIf you want to switch them off, just pass `-D\u003cOPTION\u003e=OFF` to CMake,\ne.g. `-DOVF_BUILD_FORTRAN_BINDINGS=OFF`.\n\n- `OVF_BUILD_PYTHON_BINDINGS`\n- `OVF_BUILD_FORTRAN_BINDINGS`\n- `OVF_BUILD_TEST`\n\nOn Windows, you can also set these from the CMake GUI.\n\n### Create and install the Python package\n\nInstead of `pip`-installing it, you can e.g. build everything\nand then install the package locally, where the `-e` flag will\nlet you change/update the package without having to re-install it.\n\n```\ncd python\npip install -e .\n```\n\n### Build without CMake\n\nThe following is an example of how to manually build the C library and\nlink it with bindings into a corresponding Fortran executable, using gcc.\n\nC library:\n```\ng++ -DFMT_HEADER_ONLY -Iinclude -fPIC -std=c++11 -c src/ovf.cpp -o ovf.cpp.o\n\n# static\nar qc libovf_static.a ovf.cpp.o\nranlib libovf_static.a\n\n# shared\ng++ -fPIC -shared -lc++ ovf.cpp.o -o libovf_shared.so\n```\n\nC/C++ test executable:\n```\ng++ -Iinclude -Itest -std=c++11 -c test/main.cpp -o main.cpp.o\ng++ -Iinclude -Itest -std=c++11 -c test/simple.cpp -o simple.cpp.o\n\n# link static lib\ng++ -lc++ libovf_static.a main.cpp.o simple.cpp.o -o test_cpp_simple\n\n# link shared lib\ng++ libovf_shared.so main.cpp.o simple.cpp.o -o test_cpp_simple\n```\n\nFortran library:\n```\ngfortran -fPIC -c fortran/ovf.f90 -o ovf.f90.o\n\nar qc libovf_fortran.a libovf_static.a ovf.f90.o\nranlib libovf_fortran.a\n```\n\nFortran test executable\n```\ngfortran -c fortran/test/simple.f90 -o simple.f90.o\ngfortran -lc++ libovf_fortran.a simple.f90.o -o test_fortran_simple\n```\n\nWhen linking statically, you can also link the object file `ovf.cpp.o` instead of `libovf_static.a`.\n\n*Note: depending on compiler and/or system, you may need `-lstdc++` instead of `-lc++`.*\n\n\n\nFile format v2.0 specification \u003ca name=\"specification\"\u003e\u003c/a\u003e\n=================================\n\nThis specification is written according to the\n[NIST user guide for OOMMF](https://math.nist.gov/oommf/doc/userguide20a0/userguide/OVF_2.0_format.html)\nand has been implemented, but not tested or verified against OOMMF.\n\n*Note: The OVF 2.0 format is a modification to the OVF 1.0 format that also supports fields across three spatial dimensions but having values of arbitrary (but fixed) dimension. The following is a full specification of the 2.0 format.*\n\n\nGeneral\n---------------------------------\n\n- An OVF file has an ASCII header and trailer, and data blocks that may be either ASCII or binary.\n- All non-data lines begin with a `#` character\n- Comments start with `##` and are ignored by the parser. A comment continues until the end of the line.\n- There is no line continuation character\n- Lines starting with a `#` but containing only whitespace are ignored\n- Lines starting with a `#` but containing an unknown keyword are are an error\n\nAfter an overall header, the file consists of segment blocks, each composed of a segment header, data block and trailer.\n\n- The field domain (i.e., the spatial extent) lies across three dimensions, with units typically expressed in meters or nanometers\n- The field can be of any arbitrary dimension `N \u003e 0` (This dimension, however, is fixed within each segment).\n\n\nHeader\n---------------------------------\n\n- The first line of an OVF 2.0 file must be `# OOMMF OVF 2.0`\n- The header should also contain the number of segments, specified as e.g. `# Segment count: 000001`\n- Zero-padding of the segment count is not specified\n\n\nSegments\n---------------------------------\n\n**Segment Header**\n\n- Each block begins with a `# Begin: \u003cblock type\u003e` line, and ends with a corresponding `# End: \u003cblock type\u003e` line\n- A non-empty non-comment line consists of a keyword and a value:\n    - A keyword consists of all characters after the initial `#` up to the first colon (`:`) character. Case is ignored, and all whitespace is removed\n    - Unknown keywords are errors\n    - The value consists of all characters after the first colon (`:`) up to a comment (`##`) or line ending\n- The order of keywords is not specified\n- None of the keywords have default values, so all are required unless stated otherwise\n\nEverything inside the `Header` block should be either comments or one of the following file keyword lines\n- `title`: long file name or title\n- `desc` (optional): description line, use as many as desired\n- `meshunit`: fundamental mesh spatial unit. The comment marker `##` is not allowed in this line. Example value: `nm`\n- `valueunits`: should be a (Tcl) list of value units. The comment marker `##` is not allowed in this line. Example value: `\"kA/m\"`. The length of the list should be one of\n    - `N`: each element denotes the units for the corresponding dimension index\n    - `1`: the single element is applied to all dimension indexes\n- `valuelabels`: This should be a `N`-item (Tcl) list of value labels, one for each value dimension. The labels identify the quantity in each dimension. For example, in an energy density file, `N` would be `1`, valueunits could be `\"J/m3\"`, and valuelabels might be `\"Exchange energy density\"`\n- `valuedim` (integer): specifies an integer value, `N`, which is the dimensionality of the field. `N \u003e= 1`\n- `xmin`, `ymin`, `zmin`, `xmax`, `ymax`, `zmax`: six separate lines, specifying the bounding box for the mesh, in units of `meshunit`\n- `meshtype`: grid structure; one of\n    - `rectangular`: Requires also\n        - `xbase`, `ybase`, `zbase`: three separate lines, denoting the origin (i.e. the position of the first point in the data section), in units of `meshunit`\n        - `xstepsize`, `ystepsize`, `zstepsize`: three separate lines, specifying the distance between adjacent grid points, in units of `meshunit`\n        - `xnodes`, `ynodes`, `znodes` (integers): three separate lines, specifying the number of nodes along each axis.\n    - `irregular`: Requires also\n        - `pointcount` (integer): number of data sample points/locations, i.e., nodes. For irregular grids only\n\n\n**Segment Data**\n\n- The data block start is marked by a line of the form  `# Begin: data \u003crepresentation\u003e` (and therefore closed by `# End: data \u003crepresentation\u003e`), where `\u003crepresentation\u003e` is one of\n    - `text`\n    - `binary 4`\n    - `binary 8`\n- In the Data block, for regular meshes each record consists of `N` values, where `N` is the value dimension as specified by the `valuedim` record in the Segment Header. For irregular meshes, each record consists of `N + 3` values, where the first three values are the x , y and z components of the node position.\n- It is common convention for the `text` data to be in `N` columns, separated by whitespace\n- Data ordering is generally with the x index incremented first, then the y index, and the z index last\n\nFor binary data:\n- The binary representations are IEEE 754 standardized floating point numbers in little endian (LSB) order. To ensure that the byte order is correct, and to provide a partial check that the file hasn't been sent through a non 8-bit clean channel, the first data value is fixed to `1234567.0` for 4-byte mode, corresponding to the LSB hex byte sequence `38 B4 96 49`, and `123456789012345.0` for 8-byte mode, corresponding to the LSB hex byte sequence `40 DE 77 83 21 12 DC 42`\n- The data immediately follows the check value\n- The first character after the last data value should be a newline\n\n\nExtensions made by this library\n---------------------------------\n\nThese extensions are mainly to help with data for atomistic systems.\n\n- The segment count is padded to 6 digits with zeros (this is so that segments can be appended and the count incremented without having to re-write the entire file)\n- Lines starting with a `#` but containing an unknown keyword are ignored.\n- `##` is always a comment and is allowed in all keyword lines, including `meshunit` and `valueunits`\n- All keywords have default values, so none are required\n- `csv` is also a valid ASCII data representation and corresponds to comma-separated columns of `text` type\n\n\nCurrent limitations of this library\n---------------------------------\n\n- naming of variables in structs/classes is inconsistent with the file format specifications\n- not all defaults in the segment are guaranteed to be sensible\n- `valueunits` and `valuelabels` are written and parsed, but not checked for dimensionality or content in either\n- `min` and `max` values are not checked to make sure they are sensible bounds\n- `irregular` mesh type is not supported properly, as positions are not accounted for in read or write\n\n\nExample\n---------------------------------\n\nAn example OVF 2.0 file for an irregular mesh with N = 2:\n\n```\n# OOMMF OVF 2.0\n#\n# Segment count: 1\n#\n# Begin: Segment\n# Begin: Header\n#\n# Title: Long file name or title goes here\n#\n# Desc: Optional description line 1.\n# Desc: Optional description line 2.\n# Desc: ...\n#\n## Fundamental mesh measurement unit.  Treated as a label:\n# meshunit: nm\n#\n# meshtype: irregular\n# pointcount: 5      ## Number of nodes in mesh\n#\n# xmin:    0.    ## Corner points defining mesh bounding box in\n# ymin:    0.    ## 'meshunit'.  Floating point values.\n# zmin:    0.\n# xmax:   10.\n# ymax:    5.\n# zmax:    1.\n#\n# valuedim: 2    ## Value dimension\n#\n## Fundamental field value units, treated as labels (i.e., unparsed).\n## In general, there should be one label for each value dimension.\n# valueunits:  J/m^3  A/m\n# valuelabels: \"Zeeman energy density\"  \"Anisotropy field\"\n#\n# End: Header\n#\n## Each data records consists of N+3 values: the (x,y,z) node\n## location, followed by the N value components.  In this example,\n## N+3 = 5, the two value components are in units of J/m^3 and A/m,\n## corresponding to Zeeman energy density and a magneto-crystalline\n## anisotropy field, respectively.\n#\n# Begin: data text\n0.5 0.5 0.5  500.  4e4\n9.5 0.5 0.5  300.  5e3\n0.5 4.5 0.5  400.  4e4\n9.5 4.5 0.5  200.  5e3\n5.0 2.5 0.5  350.  2.1e4\n# End: data text\n# End: segment\n```\n\nComparison to OVF 1.0\n---------------------------------\n\n- The first line reads `# OOMMF OVF 2.0` for both regular and irregular meshes.\n- In the segment header block\n    - the keywords `valuemultiplier`, `boundary`, `ValueRangeMaxMag` and `ValueRangeMinMag` of the OVF 1.0 format are not supported.\n    - the new keyword `valuedim` is required. This must specify an integer value, `N`, bigger or equal to one.\n    - the new `valueunits` keyword replaces the `valueunit` keyword of OVF 1.0, which is not allowed in OVF 2.0 files.\n    - the new `valuelabels` keyword is required.\n- In the segment data block\n    - The node ordering is the same as for the OVF 1.0 format.\n    - For data blocks using text representation with `N = 3`, the data block in OVF 1.0 and OVF 2.0 files are exactly the same. Another common case is `N = 1`, which represents scalar fields, such as energy density (in say, `J/m3` )","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspirit-code%2Fovf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspirit-code%2Fovf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspirit-code%2Fovf/lists"}