Giter Club home page Giter Club logo

fidimag's People

Contributors

davidcortesortuno avatar fangohr avatar logicabrity avatar marijanbeg avatar mvousden avatar rlc2v07 avatar rpep avatar takluyver avatar ww1g11 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fidimag's Issues

break up helper.py

The file fidimag/fidimag/common/helper.py contains a series of unrelated functions. Some of them, like init_vector and init_scalar have been superseded by the functions in fidimag/common.field.py. What about the others?

Break this module up in more sensibly named ones.

Calling fidimag.common.helper.init_vector with some numpy arrays results in unexpected behaviour.

If the numpy array has three elements, either apply it over all mesh points, or explain how the value is mapped explicitly. Example pertinent to hysteresis:

import fidimag
import numpy as np
mesh = fidimag.common.CuboidMesh(nx=10)
sim = fidimag.micro.Sim(mesh)
sim.set_m([0., 0., -1])  # Set magnetisation opposing zeeman field, so the energy is non-zero.
sim.add(fidimag.micro.Zeeman([0., 0., 0.], name="zeeman"))  # Creating Zeeman energy with list works fine.
zeemanAxis = np.array([0., 0., 1])
fieldStrength = 3e5
sim.get_interaction("zeeman").update_field(zeemanAxis * fieldStrength)  # Should update Zeeman energy elementwise with the new value, one would think.
try:
    assert sim.get_interaction("zeeman").compute_energy() != 0  # Which fails, because the energy is not updated correctly.
except AssertionError:
    print("This means the energy has not changed, when being updated with a 3-element numpy array.")
sim.get_interaction("zeeman").update_field(list(zeemanAxis * fieldStrength))  # Again, updating with list works fine.
assert sim.get_interaction("zeeman").compute_energy() != 0
print("But it has changed when being updated with a 3-element list.")

fidimag/notebook docker image doesn't work with python 2

Tested
docker run -p 30000:8888 fidimag/notebook after docker pull fidimag/notebook.

Then connect to notebook on host with at http://192.168.99.100:30000 and create notebook with Python 3 as kernel. Then import fidimag works.

Create a notebook with python 2 as kernel, and we run into this error:

ImportErrorTraceback (most recent call last)
<ipython-input-1-0d847c3660e9> in <module>()
----> 1 import fidimag

/home/jovyan/work/fidimag/fidimag/__init__.py in <module>()
     11     os.chdir(cwd)
     12 from . import common
---> 13 from . import atomistic
     14 from . import micro
     15 

/home/jovyan/work/fidimag/fidimag/atomistic/__init__.py in <module>()
----> 1 from .sim import Sim
      2 from .exchange import UniformExchange, Exchange
      3 from .anisotropy import Anisotropy
      4 from .zeeman import Zeeman, TimeZeeman
      5 from .demag import Demag

/home/jovyan/work/fidimag/fidimag/atomistic/sim.py in <module>()
----> 1 from .llg import LLG
      2 from .sllg import SLLG
      3 from .llg_stt_slonczewski_type import LLG_STT_Slonczewski
      4 
      5 

/home/jovyan/work/fidimag/fidimag/atomistic/llg.py in <module>()
      3 import os
      4 import numpy as np
----> 5 import fidimag.extensions.clib as clib
      6 import fidimag.extensions.cvode as cvode
      7 import fidimag.common.helper as helper

ImportError: No module named club

This suggests that fidimag is not compiled for Python 2. If we can't fix this, we should state somewhere that one needs to use python 3.

Handle time errors

We should probably raise an error when t < current simulation time when calling sim.driver.run_until(t)

Bug: Hanging on large mesh

If you create a large mesh, fidimag simply hangs:

mesh = fidimag.common.CuboidMesh(nx=1000, ny=1000, nz=1000, dx=2, dy=2, dz=2)

if the computer does not have enough memory. Reproduced on my Macbook and on a large memory VM.
I propose we should do a rough calculation of how much memory is needed and throw an error if the computer doesn't have enough memory to store the array.

Bug: VTK Saving Problem

Something is going wrong with saving VTK files; I've have errors of the form:

Error reading ascii data. Possible mismatch of datasize with declaration.

when loading up VTK files produced by Fidimag. I'll put a MWE up shortly.

Polish setup.py to allow pip install of package

Currently, it creates a package with name UNKNOWN.

bin:fidimag fangohr$ pip install .
Processing /Users/fangohr/git/fidimag
Installing collected packages: UNKNOWN
  Running setup.py install for UNKNOWN ... done
Successfully installed UNKNOWN-0.0.0

The very least it should install 'fidimag' not UNKNOWN. Should also test the installation works.

track time being spent in integration

I'm working on adding different time integrators to fidimag. Time integration for us typically consists of these computations:

  • the effective field
  • the RHS of the equation of motion, i.e. solve the LLG

the tasks above are done 1+ times, depending on the time integration scheme

  • the new magnetisation state according to the integrator
  • (optional) some extra info like a Jacobian times vector product for CVODE

Like Hans says

We should probably have some kind of reporting how much time is spend on what. We could use the aeon module — just to have a high level understanding of how much time cvode needs to compute that Jacobian, for example.

Improve Documentation

Completed: Add to/complete micro-magnetic documentation.
Todo: Begin atomistic documentation.
Completed: Fix installation documentation (documentation currently instructs to run an old install script)

Jacobian not correctly implemented

The backwards differences integrator from CVODE profits greatly from knowing the product of the Jacobian of our RHS with a given vector m. There are stubs of this functionality in fidimag/common/sundials/cvode.pyx already.

I haved added code to carry out the computation in fidimag/micro/llg.py but now fidimag segfaults. Which brings me back to the cython code.

  • find the cause of the segfault
  • fix it

Investigate OpenMP Scheduling

Currently, all Fidimag OpenMP code is of the form

#pragma omp parallel for private(someVariables)

which defaults to a static scheduler

There may be some performance benefits in switching to dynamic scheduling such as

#pragma omp parallel for private(someVariables) schedule(dynamic,8)

with a small chunksize, though it will need testing on a case by case basis.

Testing fails due to an ipython notebook example needs long running time

Sometimes we got "No output has been received in the last 10 minutes, this potentially indicates a stalled build or something wrong with the build itself" while the output ends with "isolated_skyrmion.ipynb::Cell 13". I guess this is because the system in the skyrmion example is not small so it takes a lot of time to finish it. So shall we skip the test on this skyrmion example?

Relaxations (sometimes) complete instantly

Relaxation operations complete when the difference in magnetisation between integrator time steps reaches a certain value. This can cause some simulations to end prematurely (see MWE below).

In this example the dm/dt, while low initially, increases sharply in time, then decreases gradually as the magnetisation tends to the Zeeman direction. I would like to see the interesting physics.

The first question is whether or not this is really a bug (since the behaviour is somewhat expected), but I would like to "relax" my system to a certain stopping dm/dt.

My proposed solution is to store the last N max(dm/dt) values, and only stop the integration if there is a decreasing trend. I welcome thoughts on this.

# This script demonstrates that the stopping condition for dm/dt causes
# simulations to stop prematurely in some cases, as the initial dm/dt value is
# too low.
#
# This was run on revision 134b60c.
import fidimag


# Create a single-spin system with only Zeeman energy for simplicity. The
# magnetisation is initialised against the Zeeman direction.
sim = fidimag.micro.Sim(fidimag.common.CuboidMesh(nx=1))
sim.Ms = 9.5e4
sim.add(fidimag.micro.Zeeman([0., 0., -sim.Ms]))
stopping_dmdt = 1  # degree per nanosecond

# Case 1: The relaxation of this system takes one integrator step, and stops
# immediately because dm/dt is below the threshold denoted by the stopping
# criteria.
sim.set_m([1e-4, 1e-4, 1])
sim.relax(stopping_dmdt=stopping_dmdt)
assert abs(sim.spin[2] + 1) < 0.5

# Case 2: This two-stage relaxation mimics a hysteresis loop. The first
# relaxation is identical to the one above with a canted initial spin. However,
# the second relaxation fails for the same reasons as case 1.
sim.set_m([1e-2, 1e-2, 1])
sim.interactions[0].update_field([0., 0., -sim.Ms])
sim.relax(stopping_dmdt=stopping_dmdt)
sim.interactions[0].update_field([0., 0., sim.Ms])
sim.relax(stopping_dmdt=stopping_dmdt)
assert abs(sim.spin[2] - 1) < 0.5

Cuboid mesh neighbours

In the Cuboid mesh file, @ww1g11 commented the line where we don't allow a cell to be its own neighbour. Is this change necessary? Because I think a 1 cell mesh case is not physically feasible/correct and we should put back the commented lines.

Also, how would this work with the nearest neighbours? I think we should at least have 3 lattice sites, and again, avoid a cell to be its own next nearest neighbour, because physically is not very correct (having exchange or DMI with its own)

Enhancement: Implement a logger

I'm currently getting some 'kernel died' errors which could be from segfaults, but it's hard to isolate where this problem is coming from because we have no logger in Fidimag. I suggest therefore that we add one.

user defined Jacobian for CVODE doesn't work

At the moment, we tell CVODE to estimate the Jacobian. We know that providing our own computation makes time integration work more efficiently.

As example fidimag/examples/micromagnetic/nmag_example_2_box/sim.py (not a test yet because it fails) shows, the computation hangs somewhere.

Need to fix this.

simplify constant.py

The file fidimag/common/constant.py has a class that doesn't need to exist, as the module itself already provides a namespace for the physical contants it contains.

Refactoring this involves some search and replace action.

macOS Sierra problem with shared libraries with Anaconda Python

Macs use a different way of specifying shared library paths to Linux. Generally they can be set using the environment variable DYLD_LIBRARY_PATH. The full path or relative can also be specified in the dylib (Mac version of .so).

Since Sundials switched to a CMake install, I've had trouble getting Sundials to link correctly on Mac - the FFTW path is resolved correctly when checking the dylib with otool, but not the Sundials Path. @ww1g11 had similar issues and wrote a script which uses install_name_tool to fix the paths by setting the full path. However, we should be able to fix this issue at the linking stage (maybe by setting "-Wl,rpath=LIB_DIR") and avoid the need to run this.

Building broken on Ubuntu 16.04

Recent changes mean that Fidimag does not currently build on Ubuntu 16.04.
Currently investigating why - for some reason the Atomistic code is not imported. May be due to recent update to Cython.

Magnetic moment data in VTK files

For the atomistic simulations, we should save the magnetic moments magnitudes mu_s in units of Bohr magnetons, since the actual values are of the order of ~10^-23, which is too small to filter with Paraview or Mayavi

Update interaction labels

When adding interactions, the names are unintuitive. For example, I got this error:

Failed to find the interaction with name 'zeeman', available interactions: ['exch', 'dmi', 'Zeeman', 'anis'].

Thus, we should use: Anisotropy, Zeeman, DMI, etc.

Improve developing notes on documentation

The current document is outdated (we changed the mesh) and there is no much information on Fidimag's structure, although we should possibly update this document after refactoring.

In addition, we should add some documentation on how to create new energy files using C -> Cython -> Python

Merge Exchange functions

The Exchange class has two functions: UniformExchange and Exchange. We should merge them in a single Exchange function. The Exchange already checks if the exchange constant is a number or a function

Skyrmion number z-coordinate not used

When computing the skymion number in a 3D mesh, the z-coordinate is not considered. Either document the behaviour, or allow the user to pass a z-coordinate (or cell) on which to compute the skyrmion number.

check if we could use bmcage/odes

There's a python interface to sundials solvers at https://github.com/bmcage/odes
Maybe we can use it instead of our homegrown code?

We like having one magnetisation object that gets updated as we progress through the simulation (and firing off appropriate tasks as we go along), while bmcage/odes is more of a "return all magnetisation snapshots after being done simulating" kind of deal, if we have a look at the example code:

solution = ode('cvode', van_der_pol, old_api=False).solve(np.linspace(t0,500,200), y0)
pylab.plot(solution.values.t, solution.values.y[:,0], label='Van der Pol oscillator')

But maybe it supports our approach as well, or can be shoehorned into what we need.

Investigate.

Rename compute_skymrion_number

This function has the incorrect spelling of "skyrmion". Replace all instances of "compute_skymrion_number" with "compute_skyrmion_number", and fix all tests that may or may not break as a result!

Good luck.

Conda install: Version numbering

If we want to be able to install Fidimag through Conda, we ought to create a release version. We could either checkout a release as is now, or come up with some features or aims such as:

• Tidy up repository
• Make sure code is commented + documented
• Add new feature X

Does anyone have any suggestions for things we need to do before making a release version? Or is it worth creating a release as is. I'm going to take a look at how to do the packaging up of sundials + fftw in order to do this.

use vector_field and scalar_field instead of np.zeros and friends

In fidimag/common/field.py the functions vector_field and scalar_field are defined. I think they convey what they do better than

        # actual code from fidimag/micro/llg.py
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

Both vector_field and scalar_field need v as optional argument (default set to None so that the function field just stops after calling np.zeros) to conveniently support this kind of use, since they expect to be set to some value at the moment.

Also replace the uses of init_vector and init_scalar from fidimag/common/helper.py, which relates to #25.

As to why? For one, it makes the code clearer and it also elegantly allows us to change the type of our fields in the future. They're raw np.arrays now, but they don't have to be. Should we decide on something more advanced, we would just need to change the type of object returned by the field function.

FloatingPointError in atomistic llg

In one of my simulations I'm getting a FloatingPointError when the llg calls the spin_length function. It seems that some spins that are zero, are represented by a very small number:

spin : [ 0.          0.70710678 -0.70710678]
step=1, time=1e-13, max_dmdt=0.215 ode_step=0
spin : [ 0.          0.70710678 -0.70710678]
step=2, time=3.53e-12, max_dmdt=0.216 ode_step=3.43e-12
spin : [ 0.          0.70710678 -0.70710678]
step=3, time=3.79e-11, max_dmdt=0.216 ode_step=3.43e-11
spin : [ 0.          0.70710678 -0.70710678]
step=4, time=3.81e-10, max_dmdt=0.216 ode_step=3.43e-10
spin : [ 0.          0.70710678 -0.70710678]
step=5, time=3.81e-09, max_dmdt=0.216 ode_step=3.43e-09
spin : [ 0.          0.70710678 -0.70710678]
step=6, time=3.81e-08, max_dmdt=0.216 ode_step=3.43e-08
spin : [ 0.          0.70710677 -0.7071068 ]
step=7, time=3.81e-07, max_dmdt=0.216 ode_step=3.43e-07
spin : [ 0.          0.70710664 -0.70710692]
step=8, time=3.81e-06, max_dmdt=0.216 ode_step=3.43e-06
spin : [ 0.          0.70710539 -0.70710817]
step=9, time=3.81e-05, max_dmdt=0.216 ode_step=3.43e-05
spin : [ 0.          0.70709973 -0.70711383]
step=10, time=0.000193, max_dmdt=0.216 ode_step=0.000155
spin : [ 0.          0.70708614 -0.70712742]
step=11, time=0.000565, max_dmdt=0.216 ode_step=0.000372
spin : [ 0.          0.70705933 -0.70715423]
step=12, time=0.0013, max_dmdt=0.216 ode_step=0.000734
spin : [ 0.          0.70700611 -0.70720743]
step=13, time=0.00276, max_dmdt=0.216 ode_step=0.00146
spin : [ 0.          0.70691448 -0.70729903]
step=14, time=0.00526, max_dmdt=0.216 ode_step=0.00251
spin : [ 0.          0.70682285 -0.7073906 ]
step=15, time=0.00777, max_dmdt=0.216 ode_step=0.00251
spin : [ 0.          0.70673121 -0.70748216]
step=16, time=0.0103, max_dmdt=0.216 ode_step=0.00251
spin : [ 0.          0.70663956 -0.70757369]
step=17, time=0.0128, max_dmdt=0.215 ode_step=0.00251
spin : [ 0.          0.70646962 -0.70774337]
step=18, time=0.0174, max_dmdt=0.215 ode_step=0.00465
spin : [ 0.          0.7063657  -0.70784709]
step=19, time=0.0203, max_dmdt=0.215 ode_step=0.00284
spin : [ 0.          0.7061864  -0.70802597]
step=20, time=0.0252, max_dmdt=0.215 ode_step=0.00491
spin : [  3.67751620e-131   7.06007079e-001  -7.08204776e-001]
step=21, time=0.0301, max_dmdt=0.214 ode_step=0.00491
spin : [ -8.84428918e-120   7.05827738e-001  -7.08383515e-001]
step=22, time=0.035, max_dmdt=0.214 ode_step=0.00491
spin : [ -2.15089946e-119   7.05648377e-001  -7.08562183e-001]
step=23, time=0.0399, max_dmdt=0.213 ode_step=0.00491
spin : [ -8.49783632e-103   7.05377407e-001  -7.08831937e-001]
step=24, time=0.0473, max_dmdt=0.213 ode_step=0.00741
spin : [ -4.56490129e-94   7.05106390e-01  -7.09101529e-01]
step=25, time=0.0547, max_dmdt=0.212 ode_step=0.00741
Traceback (most recent call last):

In the output, there is a very small number at the 20th step.

When spin_length tries to square the spin, the simulation throws a numpy underflow error. I wonder if this is also the cause of other problems in Fidimag. For now, it's difficult for me to debug, because my system doesn't have a trivial mesh geometry.

Refactor setup.py

Couple of problems:
a) We're not currently using the correct way to link libraries; Distutils Extension has specifically for this and we're currently using 'extra_link_args'.

b) Currently cannot compile FIDIMAG for use on Iridis due to BLAS and LAPACK compiler flags. Soton HPC advise use of MKL for BLAS, but that needs handling with the link arguments in setup.py. Here we need to:
• Compile Sundials with given BLAS/LAPACK implementation - need to refactor the install-sundials.sh script and add include directories to specific include and library dirs.
• Link BLAS/LAPACK correctly when creating Extension modules.

Work out and implement how to recover gracefully from a CVODE failure

Often see an error like:


[CVSPGMR ERROR]  CVSpgmrSolve
  The Jacobian x vector routine failed in an unrecoverable manner.


[CVODES ERROR]  CVode
  At t = 2.86539e-10, the solve routine failed in an unrecoverable manner.

Currently, this is not handled, and Python gets stuck. I suggest we:

  1. Catch the function return code
  2. Raise an error in Python

Enhancement: Spin Transfer Torque along an arbitrary axis

Currently we can set a current along the x, y or z axes, and there're helper functions set_jx, set_jy, set_jz to do this. I propose that we unify this into a single function set_j, which takes a tuple as an argument along which the current is directed.

Docker: share current working directory with container

Extend Dockerfile/example so that files in the directory where the docker command (docker run -ti fangohr/fidimag) is entered can be seen and modified inside the docker environment.

Best to test this on Linux first, as sharing the file system on OS X is a bit harder.

Move LLG related codes to a super class

Many of the micromagnetic driver classes inherit from the llg.py file, which is itself a driver. For good practice we should move most of the LLG code into a super class that contains the initialisation and saving functions. We can use the Sim class for this since currently it does not contain any relevant code and we avoid creating a new file that could possibly break most of the tests. We can also leave the RHS functions for Sundials as an abstract function on Sim, so every driver sets its own way of integrating the LLG equation.

We already started discussing this with Weiwei for the atomistic part of the code, and there is a comment that is worth considering when trying to make this change:
48e9eb1#commitcomment-17922207

The easiest is to start with the micromagnetic code.

[ENH] Increasing Demag Performance

Looking into the demag calculation, it goes roughly as follows:

  • Demag tensor created
    • FFTW Initialised
    • FFTW Plan Created, with arrays to store spin data.
      When the compute_field function is called internally:
    • The magnetisation data is copied into three arrays; one for each component
    • 3-D FFT of each component is taken
    • Convolved with Demag tensor
    • Convolved data transformed back by transforming back
    • Data scaled and copied into an array that Python has passed a pointer to.

I don't think we actually need to do this copying operation, as there is a way of creating a different type of plan which can be created with fftw_create_plan_specific. The FFTW documentation suggests this as the best way of performing FFTW calculations, recommending that specific plans are always used [1]. With this, you can specify padding.

So we could maybe try:

  • create FFTW plan that directly uses the array, rather than copying first. The FFT needs to be done out-of-place, as we don't want to overwrite the spin array. I think we can specify padding in the specific plan, where padding should be = to (size_of(double) + standard padding by compiler) * 2
  • Perform FFT by directly accessing the spin elements, into the Python created field array with the same padding.
  • Convolve this data and then perform an in-place FFT.
  • Scale this data appropriately.

Clean up - tests

We have files all over the repository called things like integrators_test.py, cuboid_mesh_test.py.

Do we need these, as they're not executed every time? And if so, I think it might be better to move them to the tests directory.

Installation Problems - Sundials

@davidcortesortuno, @mvousden and I have all run into a problem with building Fidimag which relates to Sundials 2.6.2 installation. On importing, we get the error:

'Undefined symbol dcopy_'

which seems to relate to BLAS.

A workaround is to install Fidimag through an Anaconda environment, and in that case everything seems to work correctly - probably because there's a version of BLAS that is packaged with Anaconda and this is found in the path. However, it would be good to find out exactly what the origin of this error is, because currently, following the standard install procedure, you cannot get Anaconda running on Ubuntu 16.04 in Python 3 installed through apt-get.

Error messages when loading magnetisation from file

If we generate an atomistic simulation and save the magnetisation array mu_s, then generate another simulation with a different mesh and load the magnetisation file, we get no error message at all.

This is a MWE,

import fidimag

# Create simulation
mesh = fidimag.common.CuboidMesh(nx=10, ny=2, nz=1, 
                                                                dx=1, dy=1, dz=1)
sim = fidimag.atomistic.Sim(mesh)
# Set the magnetisation and save it to mu_s.npy
sim.mu_s = 3
np.save('mu_s.npy', sim.mu_s)

# Create another simulation with less elements in the x direction
mesh2 = fidimag.common.CuboidMesh(nx=5, ny=2, nz=1, 
                                                                  dx=1, dy=1, dz=1)
sim2 = fidimag.atomistic.Sim(mesh2)
# Set the magnetisation loading the other simulation file
sim2.mu_s = np.load('mu_s.npy')

print(sim2.mu_s)

then we got no error message and the print statement says array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), which means nothing was done.

I guess a similar behaviour is expected from a micromagnetic simulation. We should implement a proper checking of the mesh sizes when loading magnetisation or spin arrays.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.